1

Python version: 2.7. OS: Windows 10 64-bit.

Note: I have found a way around the issue described below that doesn't use try/except/else statements. I am asking the question below only because I am curious as to why the code behaves the way it does, and if there is a way to do what I am trying to do using try/except/else.

I have a file called blah.py, with the following code:

import os

def makeFolder(dirName, num = 0):
    try:
        os.mkdir(dirName + '_' + str(num)) #error if argument of os.mkdir already exists
    except:
        makeFolder(dirName, num = num + 1)
    else:
        return dirName + '_' + str(num)

Now I go to Powershell, and type:

import blah
myStr = blah.makeFolder('myFolder')
print myStr
print type(myStr)

It does what I would expect - a folder called myFolder_0 is created, and it prints myFolder_0 and <type 'str'>. Now, still in Powershell, I type:

myStr1 = blah.makeFolder('myFolder')
print myStr1
print type(myStr1)

This time it makes a folder called myFolder_1, as I expect, but instead of printing myFolder_1 and <type 'str'>, it prints None and <type 'NoneType'>. It will continue doing this every subsequent time I use blah.makeFolder('myFolder').

The behavior is also weirdly different if I put the commands I typed in Powershell inside of the script. I made a file called blah2.py, which is the same as blah.py, but with a script at the end:

import os

def makeFolder(dirName, num = 0):
    try:
        os.mkdir(dirName + '_' + str(num)) #error if argument of os.mkdir already exists
    except:
        makeFolder(dirName, num = num + 1)
    else:
        return dirName + '_' + str(num)

myStr = makeFolder('myFolder')
print myStr
print type(myStr)

myStr1 = makeFolder('myFolder')
print myStr1
print type(myStr1)

Then in Powershell:

python blah2.py

This time it makes myFolder_0 and prints myFolder_0 and <type 'str'>, (so the myStr block works as in blah.py), and then goes into an infinite recursion (so the myStr1 block doesn't work). So for reasons I don't understand, the behavior is different than it is during the interactive session. If I type python blah2.py again, it makes myFolder_1 and prints None and <type 'NoneType'> (myStr block), then goes into infinite recursion again (myStr1 block).

Why does the script behave differently than the interactive session, why does infinite recursion happen in the script, and is there a version of my code that still uses try/except/else, but works?

user3558855
  • 301
  • 4
  • 13
  • Welcome to StackOverflow! I just want to commend you on asking an excellent question, including a nice compact example. Well done! – Christian Ternus Aug 20 '16 at 23:34
  • 1
    How does "Powershell" relate here? Did you just typo "Python shell" or are you actually using PowerShell somehow? – user94559 Aug 20 '16 at 23:37
  • 2
    `blah2.py` doesn't look right to me... it executes `blah.makeFolder`, but `blah` isn't defined anywhere? – user94559 Aug 20 '16 at 23:39
  • you are getting the non-type because when the except statement gets triggered, the else statement is not going to get triggered, so you are not returning anything. And the return of your recursive calls is not being taken into account on your function – engineer14 Aug 20 '16 at 23:44
  • 1
    always handle a particular exception a blank `except` statement would trap all possible exceptions in python. which could lead to a lot of assumptions and weird behaviour :)...`except OSError` – danidee Aug 20 '16 at 23:49
  • I think the other answers have covered most of what I was starting to write up, but I'd add a strong suggestion to narrow your `except` block so that it only catches the expception type you expect (e.g. an `OSError` in this case). This will let other, unexpected errors still escape and give you a traceback. – Blckknght Aug 20 '16 at 23:50
  • Christian Ternus: Thanks! smarx: I meant I went into Powershell and typed 'python' to start an interactive session, I always forget the name of the actual tool I'm using when I'm doing that. – user3558855 Aug 21 '16 at 00:13

2 Answers2

5

Your code works fine for me if I add a return to the recursive call:

import os

def makeFolder(dirName, num = 0):
    try:
        os.mkdir(dirName + '_' + str(num))
    except OSError:
        return makeFolder(dirName, num = num + 1)
    else:
        return dirName + '_' + str(num)

print(makeFolder('myFolder')) # myFolder_0
print(makeFolder('myFolder')) # myFolder_1

As to why you're seeing what you're seeing... there's definitely something else going on here. The code you shared for blah2.py couldn't possibly work, since blah isn't defined anywhere. My guess is that you're running different code without realizing it. (Maybe a different copy of the file in a different directory?)

danidee
  • 8,840
  • 2
  • 33
  • 55
user94559
  • 57,357
  • 6
  • 98
  • 99
  • beat me to it :), he should also handle the appropriate exception which would be `OSError` instead of having an except that traps every possible exception – danidee Aug 20 '16 at 23:46
  • Great, this works, thanks! Regarding the blah2 stuff, two things: First, in the original post there was a typo, my blah2.py file did not have blah.makeFolder, just makeFolder. I will edit momentarily. Second, I restarted my computer and ran it again, and then it started doing the same thing as the interactive session, so I guess there was just something weird in memory. – user3558855 Aug 21 '16 at 00:18
0

My first thought is that the inconsistent behavior you're seeing might be because some directories already exist if you're running them one after the other. To prevent this, try adding this code to the beginning to clean up any directories created by a previous run:

import os

for dirname in [f for f in os.listdir('.') if 
                (os.path.isdir(f) and f.startswith('myFolder'))]:
     os.rmdir(dirname)

But here's how to fix the actual issue:

import os

def makeFolder(dirName, num = 0):
    try:
        os.mkdir(dirName + '_' + str(num)) #error if argument of os.mkdir already exists
    except:
        return makeFolder(dirName, num = num + 1)
    else:
        return dirName + '_' + str(num)
Christian Ternus
  • 8,283
  • 22
  • 39