[Tutor] What's the best way to ask forgiveness here?

Evert Rol evert.rol at gmail.com
Mon Sep 13 21:07:03 CEST 2010


> I've been coding Python long enough that 'asking forgiveness instead of permission' is my first instinct, but the resulting code is sometimes clumsy, and I wonder if someone can suggest something I'm missing, or at least validate what's going on here in some way. 
> 
> What I'm trying to do is write a file to a directory. However, the directory may not exist the first time I try to write a file there, so I'm going to first try to write the file, and if I get an exception, create the directory (er, *try* to), and *then* write the file there.

That would work.
Though I would just try to create the error directory first. If that fails, I check the error message/code in the except clause: if it indicates the directory already exists, I pass the exception, otherwise reraise it.
Then I continue with creating the file within the directory I now am certain of exists.
Note 1: certain is only half: some other process could remove the directory in the split second between my code creating it and my code creating the file. If you think that could happen, you'll need to look at the other ways to do this.
Note 2: the directory can exist, but not have write access to your process. So between the try-except for creating a directory and the try-except for creating a file, you may put in a try-except for chmod. Of course, if you're not the owner, both the chmod and file creation will fail (I'm assuming some *nix platform here, btw).

> Here's my first shot at the code: 
> 
>         try:  
>             self.save_file(picfile_fullpath, picdata)
>         except IOError as err:
>             # directory doesn't exist. Try to create it.

Careful: you're not checking the actually error given by the exception. There may be more than one reason that the file can't be created (examples: the permissions mentioned above, or some file creation limit in a directory).


>             try:  
>                 os.makedirs(picfile_fullpath)
>             except OSError as oserr:
>                 logging.error("Can't create file path: %s (%s)" % (picfile_fullpath, oserr))
>             else: 
>                 # Created dir, now write file.
>                 try:  
>                     self.save_file(picfile_fullpath, picdata)
>                 except IOError as err:
>                     logging.error("Bailing. Couldn't save file %s (%s)" % (picfile_fullpath, err)) 
>                     return False
> 
> Doesn't this seem less readable than the 'ask permission' equivalent? I think it does, but in this case asking permission for every single operation when the dir will only need to be created a single time (and then may be written to several hundred times) is pretty wasteful.

One of the things I once read (but I forgot where) on this issue, is the usual "it depends". It depends whether you except that often, the directory doesn't exist and the file can't directly be created. In that case, if may be quicker to ask permission first (simple if-statement). If you expect that in general, you can immediately create the file within the directory (so an exception is unlikely to occur), then use a try-except clause.
But don't take my word for it; I'd be curious what others on this list say about this.


> 
> I suppose I could set some sentinel variable and check for it in a while loop, but then I need some other scaffolding code to make sure I don't infinitely loop trying to create the directory, and probably some other stuff I'm forgetting, so it strikes me as being just as messy. 
> 
> Is there a clean sort of pattern to apply in instances like this? 

I guess not, though readability of code is an important thing to consider. In this case, I don't find the correctly unreadable, but at some point, these things do tend to get out of hand and you're better off coding it differently (perhaps even using functions).


  Evert



More information about the Tutor mailing list