Search code examples
pythonobjective-cmacospyobjc

'if' statement, not protecting against AttributeError: 'NoneType' object has no attribute


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!


Solution

  • 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.