3

I have some python (v2.7) code that uses OS X's built in PyObjC bindings to get the executable's path from a bundle via NSBundle bundleWithPath:

    #bundlePath is a string/path to a .app or .kext

    mainBundle = Foundation.NSBundle.bundleWithPath_(bundlePath)
    if mainBundle:
        binaryPath = mainBundle.executablePath()

A customer reported that this was throwing an exception, and emailed me the backtrace:

   ...
   File "utils.py", line 184, in getBinaryFromBundle
        binaryPath = mainBundle.executablePath()
        AttributeError: 'NoneType' object has no attribute 'executablePath'

Unfortunately, I can't replicative this on my box. NSBundle bundleWithPath: always returns either None, or a valid bundle object, which the code seems to handle as expected.

>>> print Foundation.NSBundle.bundleWithPath_(None)
    None
>>> print Foundation.NSBundle.bundleWithPath_("invalid/path")
    None
>>> print Foundation.NSBundle.bundleWithPath_("/Applications/Calculator.app/")
    NSBundle </Applications/Calculator.app> (not yet loaded)

Sure I could wrap this code in a try/except, but my question is, why isn't the if statement preventing this exception?

update 1
Based on suggestions, I've confirmed that:
1) indentation is correct
2) the customer is using the correct (most recent) code

...still trying to figure this one out!

patrick
  • 380
  • 2
  • 14
  • If you set `mainBundle = None` right after the `mainBundle = Foundation.…` line, what happens? – Ry- Jul 19 '14 at 01:08
  • I set `mainBundle = None` where you suggested as well as added a `print` statement inside the `if:` and an `else:` statement with a `print` statement as well. I then ran the entire program; only the `print` statement within the `else` was executed :/ – patrick Jul 19 '14 at 01:16
  • Check your indentation. A good portion of the time, errors like these are due to mixed tabs and spaces. (A good portion of the time, they're not due to mixed tabs and spaces, but if this guess turns out to be wrong, at least we've narrowed down the problem.) The `-tt` flag can help with this. – user2357112 Jul 19 '14 at 01:19
  • I did not know about the `-tt` option - very cool! However, I'm pretty sure that it's not an indentation issue :/ I just ran it with the `-tt` option and it didn't complain. Also, I'm using PyCharm as my IDE which does automatic indentations and also warns of mixing tabs/spaces – patrick Jul 19 '14 at 01:30
  • 3
    Is your customer running an older version of your code that lacks that guarding if-statement? – Steven Rumbalski Jul 19 '14 at 03:27
  • That thought crossed my mind - but the old version of the code did not have the `binaryPath` variable. It just did a `return Foundation.NSBundle.bundleWithPath_(bundlePath).executablePath()`. Also the line number in the exception traceback that he provided (line 184) matched up correctly with the code in the new version – patrick Jul 19 '14 at 17:34

1 Answers1

1

The issue is not that the bundleWithPath call is returning something unexpected. As you said, None is a valid return value for it.

This suggests that the client's machine has a strange version of python in which

if None:
    print('hello, strange world')
else:
    print('hello, world')

would actually print hello strange world.

I've never seen this myself, but it's the only explanation for what you are seeing, and there are a truckload of different python interpreters, so it's not shocking to me that one of them is a bit odd.

The commonly accepted practice for checking if a variable is None is to do so explicitly anyway -- it's a whole lot clearer to read, IMHO.

if mainBundle is not None:
    ...

The whole practice of coercing NoneType to False for boolean expressions leads to some weird and unclear semantics, especially when you have code that can produce True, False, or None.


I know this doesn't answer the "why" part of the question, but I'm chalking it up to a strange python version. It's hard to say more without having detailed information about the client's computer, or at least knowing the version of python being run.

sirosen
  • 1,716
  • 13
  • 17