0

I'm working on a library-type checkout/checkin system. When the user clicks exit, the program calls a close_window function which dumps the current dictionary objects into pickle files before the window is destroyed.

def close_window(self):
    if messagebox.askokcancel("Quit", "You want to close the program now?"):
        patrons.dump_data()

        self.master.destroy()

When the program is started again, it calls a load_data function which loads the pickled files. Somehow I ran into MemoryError when exiting the system and one of the pickled files was overwritten with an empty file. From the documentation, I gather that MemoryError occurs when the program creates too many objects and runs out of memory. I am not sure why this happened in my case since I am not dealing with large data. The pickled file that got overwritten was only 1 KB.

How can I ensure my pickled file is not overwritten with an empty file when MemoryError occurs? This can lead to serious data loss. I am new to programming and am using this project to learn. It is possible I've done something seriously wrong to lead to the memory error or maybe I just need more computer memory. In any case, it doesn't make sense to overwrite a saved file with an empty file whether memory error or not.

Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Python34\lib\tkinter\__init__.py", line 1487, in __call__
    return self.func(*args)
  File "C:/Python34/Lib/site-packages/toolkit/main.py", line 435, in close_window
    patrons.dump_data()
  File "C:\Python34\Lib\site-packages\toolkit\patrons.py", line 20, in dump_data
    pickle.dump(patronslist, f)
MemoryError

This error is partially discussed in MemoryError while pickling data in python. Here though, I got an empty file, not even a partial file. And I want to know if there is a workaround for this problem. Perhaps saving the pickled data to a temporary file. If no memory error occurred during the save, then the temp file can be used to overwrite the permanent file (but this may yet trigger another MemoryError right?).

I run Win7 x86, 3 GB RAM, Python 3.4.1

Community
  • 1
  • 1
sedeh
  • 7,083
  • 6
  • 48
  • 65
  • 1
    Yes. That is how you would solve it (use temp file & rename), and with `os.rename` (https://docs.python.org/2/library/os.html#os.rename), I don't think there's any chance of getting an memory error (maybe some sort of file error though). – Gerrat Aug 19 '14 at 21:50
  • @Gerrat Do you think my answer below will work? Any further thoughts? – sedeh Aug 20 '14 at 15:11

1 Answers1

1

Based on Gerrat's comment above, I wonder if the following is a good way to go about this:

patrons.py
def dump_data():
    with open("./pickled_dicts/temp_patrons.pkl", 'wb') as f:
        global patronslist
        pickle.dump(patronslist, f)


main.py
def close_window(self):
    if messagebox.askokcancel("Quit", "You want to close the program now?"):
        try:
            patrons.dump_data()
            os.remove("./pickled_dicts/patrons.pkl")
            os.rename("./pickled_dicts/temp_patrons.pkl", "./pickled_dicts/patrons.pkl")
        except MemoryError:
            messagebox.showerror("Memory Problem", "Your computer experienced memory problem. Your last session was not saved.")     
        self.master.destroy()

Essentially, I am first saving the dictionary object to a temporary file (temp_patrons.pkl) which is renamed to my permanent file (patrons.pkl) assuming no MemoryError. If MemoryError, then the original patrons.pkl remains.

sedeh
  • 7,083
  • 6
  • 48
  • 65
  • Looks better. As an additional suggestion, I'd remove the `global patronslist`. Pass `patronslist` in as an argument. 99% of the time, using `global` is unnecessary and just evidence of a poor design. – Gerrat Aug 20 '14 at 15:30
  • @Gerrat I hear you. I made it `global` because `patrons.py` also contains another function, `def load_data()`. I'm not sure how else to access `patronslist` in both functions without making it `global`. Thoughts? – sedeh Aug 20 '14 at 15:35
  • I can't be sure without seeing your code, but I'd guess that `load_data()` should be returning `patronslist`, or stuffing it in a variable on an instance of a class (`self.patronslist` ?) – Gerrat Aug 20 '14 at 15:46