maarten-dp/arcade-curtains

Can't use PyInstaller to bundle a script that uses arcade-curtains with Python 3.7

Closed this issue · 6 comments

I've hit a bit of an esoteric issue here...

I'm trying to bundle Python scripts that use Arcade into a an .exe with PyInstaller. Works great. But, if I have a script that also includes arcade-curtains, PyInstaller has a problem. I worked on getting a pretty simple reproduction case together, though it still is unfortunately lengthy. I've only tested this on Windows 10 with Python 3.7:

Create a directory to hold an app that relies on arcade-curtains

mkdir curtains_app
cd curtains_app

Create two files in curtains_app/ directory

setup.py:

import setuptools
setuptools.setup(
    name="curtains_app",
    version="0.0.0",
    packages=setuptools.find_packages(),
    py_modules=['myapp'],
    install_requires=[
        "arcade-curtains>=0.2.1",
        "pyinstaller==4.0",
    ],
)

myapp.py:

    import arcade
    if __name__ == "__main__":
        print("running myapp.py")

Note: Notice the app doesn't even import arcade-curtains, but arcade-curtains is installed into the venv because it is in the list of dependencies in install_requires.

Create directory (myenv/) to hold virtual environment and PyInstaller artifacts

Create and activate fresh venv

cd ..
mkdir myenv
cd myenv
py -3.7 -m venv venv  # could probably use `python -m venv venv`
venv\Scripts\activate

Use pip to install curtains_app project into active venv

pip install ..\curtains_app

Run manually to ensure app executes:

python -m myapp

(it runs fine)

Bundle app with PyInstaller and run it

pyinstaller venv\Lib\site-packages\myapp.py
dist\myapp\myapp.exe  

When the app is run, it fails with:

  <...snip...>
  File "arcade\sprite.py", line 1058, in <module>
  File "dataclasses.py", line 958, in dataclass
  File "dataclasses.py", line 950, in wrap
  File "dataclasses.py", line 801, in _process_class
  File "dataclasses.py", line 801, in <listcomp>
  File "dataclasses.py", line 659, in _get_field
  File "dataclasses.py", line 550, in _is_classvar
AttributeError: module 'typing' has no attribute '_ClassVar'
[16064] Failed to execute script myapp

Clear out PyInstaller artifacts and manually uninstall dataclasses package to try again

rmdir /s /q build dist
del *.spec
pip uninstall dataclasses
pyinstaller venv\Lib\site-packages\myapp.py
dist\myapp\myapp.exe

With dataclasses uninstalled, the PyInstaller bundle of the app now runs fine.

Summary

I'm not an expert on PyInstaller, but I'm guessing that the explicit dependency arcade-curtains has on dataclasses (https://github.com/maarten-dp/arcade-curtains/blob/master/requirements.txt#L2) is what is causing this issue with Python 3.7. I think this extra dependency was a backport necessary for Python 3.6 (https://pypi.org/project/dataclasses/), but it seems to get in the way with Python 3.7? Odd that Python itself runs the script, but when bundled with PyInstaller it has an issue.

Any way to make this dependency conditional on what version of Python a client is using?

Hey SirGnip,

I can't tell you how happy I am someone is taking curtains for a thorough spin :)

Actually, curtains doesn't need a dependency on dataclasses, I've only added it because it's not a requirement in arcade itself. I've removed the dependency, as it's clearly doing more harm than good.

Thank you for bringing this to my attention as well as building a testcase to reproduce.

I'll try to take some time in the near future to actually try to reproduce and validate the removal of the dependency fixes the issue.

EDIT: I spoke too soon, will have to look into this a bit deeper :)

0.2.2 removes dataclasses as a dependency, and adds it to the dev dependencies only for python versions lower than 3.7

Thanks for the fix. I'll give it a whirl and report back.
Side question... I've not seen a repo before that has a requirements.txt file that is used at install time (when doing a pip install of arcacde-curtains). Is this standard setuptools behavior? I always thought dependencies had to be in install_requires of setup.py (or use a special build tool). Is there any special config you had to do to set this up?

I'm using pbr. Packaging in python is still a painpoint in the language, but pbr comes very close to providing something sustainable.

I tried re-running my repro steps with 0.2.2 and the pyinstaller bundle ran properly! Thanks for the fix!

Regarding pbr, I recognized or googled everything in your setup.cfg, but just blew past "pbr" in setup.py somehow... and of course that was the important bit. Thanks for the pointer! Looks interesting...

Thank you for all your efforts!