-
-
Notifications
You must be signed in to change notification settings - Fork 474
Description
Context
As a Mu Editor contributor, and having worked a bit on improving its packaging, I joined the sprint today in order to understand how easy/difficult it will be for Mu to be packaged with the newer briefcase 0.3 release.
Facts about Mu packaging:
- macOS Application Bundle produced with
briefcase0.2.x. - Windows installer produced with
pynsist. - PyPI wheel produced with
wheel+setuptools. - Also included in Debian, Ubuntu, and Raspbian package repositories. Maybe Fedora, too. Not 100% sure because Linux distribution packaging has been, AFAICT, handled outside of the development tree (or maybe it hasn't had much love, in the last 12 months or so).
Challenges about Mu packaging:
- Having a single source of truth WRT to dependency specification and meta data.
- The current solution is
setuptoolsbased and all of the information sourced fromsetup.py+setup.cfg(from there, we use a non-trivial script to produce the necessarypynsistinput such that it can do its thing, on Windows).
The Issue
Packaging Mu on Windows leads to a partially ok Mu installation for various motives. In this issue, I'm focusing on a failure that results when trying to bring up its Python REPL -- it leads to a crash (Mu's fault) because a particular module fails to import, resulting in an unhandled exception.
Specifics:
- Mu's REPL is based on
qtconsolethat, on Windows, ends up requiringpywin32. pywin32uses.pthfiles to guidesite.pyin populatingsys.pathin a specific way.
Bottom line:
- Briefcase packaged Windows applications that depend on
pywin32fail to importwin32api, on of its modules.
Investigation
After a few hints from @freakboy3742 and @dgelessus at Gitter, here's what's going on:
- The Python support package being used is the default one: one of the "embeddable zip file" packages from https://www.python.org/downloads/windows/. (FTR, all if this was explored with Python 3.6.8 64 bit).
- In particular it includes a
pythonXX._pththat:- Makes Python run in isolated mode...
- ...and prevents site.py from being loaded...
- ...per these docs here.
- Such
pythonXX._pthfile is actually overwritten bybriefcasein order to:- Add the
src\appandsrc\app_packagestosys.pathsuch that both the application and its dependencies can be successfully imported.
- Add the
- However, the presence of the
._pthfile:- Prevents the
sitemodule from being loaded at startup... - ...which would be responsible for populating
sys.pathfrom any.pthfiles that are found on thesite-packagesdirectories.
- Prevents the
A Successful Hack
With this information, I invested some time fiddling with these things to see if, at least, I could make it work. Here's what I did that resulted in a working Mu REPL (thus, its underlying import win32api working!):
-
Hacked the cookiecutter template's
briefcase.tomlfile (took me quite figuring out where this file was coming from!!!):- Set
app_packages_pathtosrc\python\lib\site-packages, instead.
- Set
-
Then, hacked
briefcase's overwriting ofpythonXX._pthto produce:pythonXX.zip . lib\site-packages import site ..\app- This lets Python find the Standard Library with the first 2 lines...
- ...find application dependencies with the 3rd line...
- ...has
siteimported such that.pthfiles in the application dependencies are handled... - ...lastly adds the application package path such that it can be imported and run.
-
Lastly, I observed that having
siteimported lead to an over-populated, non-safesys.path. For some reason, my local Python installation'ssite-packageswas being added, and then maybe some more. -
With that, the last step of the hack, was creating a
sitecustomize.py, which is automatically picked up whensiteis imported per the revampedpythonXX._pth. Here's my take:import re import sys _IS_LOCAL = re.compile(r'(src\\python|src\\app)', re.IGNORECASE) sys.path = [path for path in sys.path if _IS_LOCAL.search(path)]
With these three changes, the import win32api in Mu succeeds and, ultimately Mu works as intended WRT to providing a Python REPL.
Thoughts
- Handling
.pthfiles in dependencies is a must, I'd venture saying. - My approach is the result of fiddling and exploring and I don't like it very much (but it works!). It feels unnecessarily complicated and, thus, brittle.
- Somewhat orthogonal, but related, maybe having a
venvto which dependencies arepip installed instead ofpip install --targeted will be more robust, at least for the platforms where that is feasible. No worries about playing with import PATHs in three distinct places (well, maybe some PATH cleaning up could be in order, to guarantee isolation -- see mysitecustomize.py, above).
All in all, I leave this issue here in the hope that some useful discussion comes out of it, while serving as a hack-guide to someone facing a similar failures. I'm not sure yet about what could be a solid, sustainable, simple, future-proof solution.
Shall we discuss? :)
PS: I suppose similar failures will happen on any platform, as long as .pth files are brought in by dependencies.