Search code examples
objective-cpyobjc

Embedded Python 3 not creating .pyc files when using importlib


I'm trying to embed a Python 3 interpreter in an Objective C Cocoa app on a Mac, following instructions in this answer (which extends this article) and building Python and PyObjC by hand.

I'd like to be able to run Python code as plugins. I specifically don't want to rely on the stock Apple Python (v2.7). I have most of it working but can't seem to reliably load the plugin scripts. It looks like the embedded Python interpreter is unable to create the __pycache__/*.pyc files. This may be a symptom, or a cause. If I import the plugin file manually from the Python3 REPL (via import or the imp or importlib modules) the .pyc is generated and the plugin then loads correctly. If I don't do this manually the .pyc is not created and I receive a ValueError "Unmarshallable object".

I've tried loosening permissions on the script directory to no avail. The cache_tag looks OK, both from the REPL and from within the bouncer script:

>>> sys.implementation.cache_tag
'cpython-35'

py_compile raises a Cocoa exception if I try and compile the plugin file manually (I'm still digging into that).

I'm using the following:

  • OS X 10.11.5 (El Capitan)
  • XCode 7.2.1
  • Python v3.5.2
  • PyObjC v3.11

I had to make a couple of necessary tweaks to the process outlined in the linked SO answer:

Compiling Python 3 required Homebrew versions of OpenSSL and zlib, and appropriate LDFLAGS and CPPFLAGS:

export CPPFLAGS="-I$(brew --prefix openssl)/include -I$(brew --prefix zlib)/include"
export LDFLAGS="-L$(brew --prefix openssl)/lib -L$(brew --prefix zlib)/lib"

I also ensure pip is installed OK when configuring Python to build:

./configure --prefix="/path/to/python/devbuild/python3.5.2" --with-ensurepip=install

There is a fork of the original article source (which uses the stock Python2) that works fine here, so I suspect I'm not too far off the mark. Any idea what I've missed? Do I need to sign, or otherwise give permission to, the embedded Python? Are there complilation/configuration options I've neglected to set?

TIA


Solution

  • Typical. It's always the last thing you try, isn't it? Adding the directory containing the plugin scripts to sys.path seems to do the trick, although I'm not sure why importlib needs this (I thought the point was to allow you to circumvent the normal import mechanism). Perhaps it's to do with the way the default importlib.machinery.SourceFileLoader is implemented?

    Something like:

    sys.path.append(os.path.abspath("/path/to/plugin/scripts"))
    

    makes the "Unmarshallable object" problem go away. The cache directory and .pyc files are created correctly.