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