How does wrapping an unsafe python method (e.g. os.chdir) in a class make it safe for threads / exceptions?

In the question How do I “cd” in python , the accepted answer recommended wrapping the os.chdir call in the class in order to return a safe exception to the source directory. Here is the recommended code:

class Chdir: def __init__( self, newPath ): self.savedPath = os.getcwd() os.chdir(newPath) def __del__( self ): os.chdir( self.savedPath ) 

Can someone describe how this works to rule out a safe safe exception?

+4
source share
4 answers

Thread safety and exception safety are actually not exactly the same. The wrapper of the os.chdir call in a class like this is an attempt to make it safe for exceptions, not thread safe .

Exception safety is what you often hear about, what C ++ developers are talking about. This is hardly mentioned in the Python community. From Boost Security Exception in document common components :

Informally, the safety of exceptions to a component means that it exhibits reasonable behavior when an exception is thrown during its execution. For most people, the term “reasonable” includes all the usual expectations for error handling: these resources should not leak, and the program must remain in a clearly defined state so that execution can continue.

Thus, the idea in the code snippet below is to ensure that if an exception occurs, the program will return to a well-defined state. In this case, the process will be returned in the directory in which it was launched, regardless of whether os.chdir fails or something causes an exception and an instance of "Chdir" to be deleted.

This template for using an object that exists only for cleaning is the “ Resource Initialization” or “RAII” form. This method is very popular in C ++, but not so popular in Python for several reasons:

  • Python has a try ... finally , which serves pretty much the same purpose and is a more common idiom in Python.
  • Destructors ( __del__ ) in Python are unreliable / unpredictable in some implementations, so using them in this way is somewhat discouraged. In cpython, they turn out to be very reliable and predictable if loops are not involved (i.e. when the deletion is processed by reference counting), but in other implementations (Jython and I also consider IronPython) deletion occurs when the garbage collector approaches this, perhaps much later. (Interestingly, this does not stop most Python programmers from relying on __del__ to close their open files.)
  • Python has garbage collection, so you don’t need to be as careful about cleaning as in C ++. (I'm not saying that you don’t need to be careful at all, just in normal situations you can rely on gc to do the right thing for you.)

A more "pythonic" way of writing the above code:

 saved_path = os.getcwd() os.chdir(new_path) try: # code that does stuff in new_path goes here finally: os.chdir(saved_path) 
+7
source

A direct answer to the question: this is not so, the published code is terrible.

Something like the following might make sense to make it “safe for exception” (but it is much better to avoid chdir and use full paths):

  saved_path = os.getcwd() try: os.chdir(newPath) do_work() finally: os.chdir(saved_path) 

And this exact behavior can also be written to the context manager.

+5
source

__del__ is called when an instance is to be destroyed. So when you instantiate this class, the current working directory is stored in the instance attribute, and then, well, os.chdir is called. When an instance is destroyed (for some reason), the current directory changes to its old value.

This looks a little wrong for me. As far as I know, you should call the parent __del__ in your __del__ override, so it should be something like this:

 class Chdir(object): def __init__(self, new_path): self.saved_path = os.getcwd() os.chdir(new_path) def __del__(self): os.chdir(self.saved_path) super(Chdir, self).__del__() 

That is, if I am missing something, of course.

(By the way, can you do the same with the contextmanager?)

+1
source

This code is neither thread safe nor exception safe. Actually, I'm not quite sure what you mean by safe exception. The following code comes to mind:

 try: # something thrilling except: pass 

And this is a terrible idea. Exceptions are not intended to protect. Well-written code should catch exceptions and do something useful with them.

-2
source

All Articles