AniMir
AniMir

Reputation: 160

How to create a file and perform an action on this file right after creating it?

I'm using python 3.6 to generate files. Then, I need to send these files through automated mails. Here's a bit of code :

myFiles = []
myFiles.append(getFirstFile()) # creates a file and returns its path
                               # its name is '2019-06-24_15-01-57_hist'
myFiles.append(getSecondFile()) # creates another file and returns its path

# myFiles is a list of strings
sendAutoMail(myFiles) # sends an automatic mail with the files attached

All these functions work fine when not implemented in the same script. But now that I put all these together here's the error I get from the sendAutoMail() function :

FileNotFoundError: [Errno 2] No such file or directory: '2019-06-24_15-01-57_hist'

When I look in the directory, the files are created indeed.

Creating files with getFirstFile() and getSecondFile(), and then run sendAutoMail([File1, File2]) in two steps seems to work fine. However it won't work in one unique script.

Any idea ?

EDIT : okay here are the functions, not sure this will help

def getFirstFile(mean, variance): # prend en paramètres une moyenne et une variance
    import matplotlib.pyplot as plt
    import numpy as np
    import scipy.stats as stats
    import math

    mu = mean
    sigma = math.sqrt(variance)
    x = np.linspace(mu - 3*sigma, mu + 3*sigma, 100)
    plt.grid(True)
    plt.plot(x, stats.norm.pdf(x, mu, sigma)) #création d'une courbe normale centrée en mu et d'écart type sigma
    # plt.show()
    filename = dateFileName()+"_firstFunction"
    plt.savefig(filename)

def dateFileName():
    import datetime
    ladate = str(datetime.datetime.now()) # récupération de la date et formattage
    ladate = ladate.split(sep=".")
    ladate = ladate[0].replace(" ","_")
    ladate = ladate.replace(":","-")
    return ladate

I am using the date as a file name because I don't know any other alternative to ensure the filename is unique.

Upvotes: 0

Views: 91

Answers (1)

Luiz Ferraz
Luiz Ferraz

Reputation: 1525

Digging into the source code of matplotlib I found that calling plt.savefig(filename) using a path (the name alone is a relative path) will then call the function Figure.print_[format](filename, ...) with format being the save format like PDF, PNG, TIFF.... The default is PNG.

Figure.print_png uses Pillow to write the image, specifically Pillow.Image.save. That function opens the file, writes to it and closes the file.

The problem is, it closes the file directly and only that. When closing a file directly, python uses the default system buffer and flushing cycle, so when a file is closed directly, it is not immediately written to disk, or at least not completely (depending on the size a portion of it might have been saved).

To prevent that from happening, open the file on your code inside a with statement, which flush and close the file when exiting like this:

def getFirstFile(mean, variance): # prend en paramètres une moyenne et une variance
    import matplotlib.pyplot as plt
    import numpy as np
    import scipy.stats as stats
    import math

    mu = mean
    sigma = math.sqrt(variance)
    x = np.linspace(mu - 3*sigma, mu + 3*sigma, 100)
    plt.grid(True)
    plt.plot(x, stats.norm.pdf(x, mu, sigma)) #création d'une courbe normale centrée en mu et d'écart type sigma
    # plt.show()
    filename = dateFileName()+"_firstFunction"
    with open(filename, 'r+b') as fl:
        plt.savefig(fl)
    return filename

That should solve your problem, on some edge cases it might be that the last buffered portion will still have not been save. In this case you can force writing the buffer to disk with os.fsync(fl) right before the return filename statement.

Upvotes: 1

Related Questions