1

I am designing a class that has undo/redo functionality and has to temporarily store a lot of data. I'm currently implementing a "temporary" file by overloading the del operator to delete the file when the class is garbage collected, but I have to believe there is a better way to do this. I have tried using the tempfile module, but it doesn't work because the shelve module expects a name, not a file object (grr).

Anyway, was wondering if anyone had a better way to do this. Important parts of the code are below.

import os, shelve
from time import time
class DataHandlerUser(DataHandler):
   def __init__(self, data):
      # storing items
      self.__unredofilename = os.path.dirname(__file__) + '/.undoredo' + str(time()) + '.pyworkbooks'
      try:
         os.remove(self.__unredofilename)
      except OSError: pass

      self._undoredoBuffer = shelve.open(self.__unredofilename)
      # ... rest of init


   def __del__(self):
      # simple check to make sure not tampered with
      if '.undoredo' not in self.__unredofilename or '.pyworkbooks' not in self.__unredofilename:
         raise Exception('Critical Error: Internal filename for undo/redo operations tampered with')
      try:
         os.remove(self.__unredofilename)
      except OSError: pass
Garrett Berg
  • 2,585
  • 1
  • 22
  • 21

2 Answers2

4

Depending on how your code is run you can still encounter a race condition where two different processes obtain the same timestamp and same filename, rare as that might be. Adding the current process id would help mitigate this, but I would recommend you stick with using the tempfile module.

If you just need the temporary file's name, you can use tempfile.mkstemp and close the returned file descriptor before using the filename:

import os, tempfile
fd, self._undo_fname = tempfile.mkstemp(suffix='.undoredo', dir='/tmp')
os.close(fd)
self._undo_buffer = shelve.open(self._undo_fname)
samplebias
  • 37,113
  • 6
  • 107
  • 103
  • Thanks, this is a great answer for how I should make the file name. Do you think anything of how I got rid of the temporary file? I was thinking that there should be a better way, but perhaps not. I like using the idea of the '/tmp' directory, for some reason I thought you would need to be root to get there! (I'm kind of new to linux and was under the impression that everything below my home directory was root). Thanks for letting me know this gem of info! – Garrett Berg Apr 05 '11 at 00:28
  • You're welcome, glad I could help. You can omit the __dir='/tmp'__ argument and it will use the operating system's default temp directory (which coincidentally is __/tmp__ on Unix), which is better cross-platform behavior. I think using `__del__` is okay in this case since the shelve's lifespan is tied to the __DataHandler__. You also may want to call `self._undoredoBuffer.close()` before removing the shelve filename. – samplebias Apr 05 '11 at 00:39
  • 1
    This causes an error `db type could not be determined`. You should store an empty db in the file before you close it – John La Rooy Apr 05 '11 at 02:00
  • awesome. Thanks for telling me about automatic temp directory. Coincidentally I was looking for a good spot to store logs, and you just gave me that too! – Garrett Berg Apr 05 '11 at 19:04
2

shelve uses anydbm to detect the type of the database used in the file.

You could create a tempfile with mkstemp() and put an empty bsddb (or whatever you prefer) in there and then pass that filename to shelve

John La Rooy
  • 295,403
  • 53
  • 369
  • 502