Use PyInstaller for freeze test env
cx_freeze doesn't seem to be very well supported in Python 3.5. Using pyinstaller instead and rename environment to "freeze" which is a more generic term for freezing python code into standalone executables. Fix #1769
This commit is contained in:
parent
d911bfcb8a
commit
ed36d627e4
|
@ -25,7 +25,7 @@ env:
|
|||
- TESTENV=py35-trial
|
||||
- TESTENV=py27-nobyte
|
||||
- TESTENV=doctesting
|
||||
- TESTENV=py27-cxfreeze
|
||||
- TESTENV=freeze
|
||||
|
||||
script: tox --recreate -e $TESTENV
|
||||
|
||||
|
|
|
@ -100,7 +100,8 @@ def pytest_namespace():
|
|||
def freeze_includes():
|
||||
"""
|
||||
Returns a list of module names used by py.test that should be
|
||||
included by cx_freeze.
|
||||
included by cx_freeze/pyinstaller to generate a standalone
|
||||
pytest executable.
|
||||
"""
|
||||
result = list(_iter_all_modules(py))
|
||||
result += list(_iter_all_modules(_pytest))
|
||||
|
|
|
@ -10,7 +10,7 @@ environment:
|
|||
# builds timing out in AppVeyor
|
||||
- TOXENV: "linting,py26,py27,py33,py34,py35,pypy"
|
||||
- TOXENV: "py27-pexpect,py27-xdist,py27-trial,py35-pexpect,py35-xdist,py35-trial"
|
||||
- TOXENV: "py27-nobyte,doctesting,py27-cxfreeze"
|
||||
- TOXENV: "py27-nobyte,doctesting,freeze"
|
||||
|
||||
install:
|
||||
- echo Installed Pythons
|
||||
|
|
|
@ -699,36 +699,33 @@ and run it::
|
|||
You'll see that the fixture finalizers could use the precise reporting
|
||||
information.
|
||||
|
||||
Integrating pytest runner and cx_freeze
|
||||
-----------------------------------------------------------
|
||||
Integrating pytest runner and PyInstaller
|
||||
-----------------------------------------
|
||||
|
||||
If you freeze your application using a tool like
|
||||
`cx_freeze <https://cx-freeze.readthedocs.io>`_ in order to distribute it
|
||||
to your end-users, it is a good idea to also package your test runner and run
|
||||
your tests using the frozen application.
|
||||
`PyInstaller <https://pyinstaller.readthedocs.io>`_
|
||||
in order to distribute it to your end-users, it is a good idea to also package
|
||||
your test runner and run your tests using the frozen application. This way packaging
|
||||
errors such as dependencies not being included into the executable can be detected early
|
||||
while also allowing you to send test files to users so they can run them in their
|
||||
machines, which can be invaluable to obtain more information about a hard to reproduce bug.
|
||||
|
||||
This way packaging errors such as dependencies not being
|
||||
included into the executable can be detected early while also allowing you to
|
||||
send test files to users so they can run them in their machines, which can be
|
||||
invaluable to obtain more information about a hard to reproduce bug.
|
||||
|
||||
Unfortunately ``cx_freeze`` can't discover them
|
||||
Unfortunately ``PyInstaller`` can't discover them
|
||||
automatically because of ``pytest``'s use of dynamic module loading, so you
|
||||
must declare them explicitly by using ``pytest.freeze_includes()``::
|
||||
must declare them explicitly by using ``pytest.freeze_includes()`` and an
|
||||
auxiliary script:
|
||||
|
||||
# contents of setup.py
|
||||
from cx_Freeze import setup, Executable
|
||||
.. code-block:: python
|
||||
|
||||
# contents of create_executable.py
|
||||
import pytest
|
||||
import subprocess
|
||||
|
||||
setup(
|
||||
name="app_main",
|
||||
executables=[Executable("app_main.py")],
|
||||
options={"build_exe":
|
||||
{
|
||||
'includes': pytest.freeze_includes()}
|
||||
},
|
||||
# ... other options
|
||||
)
|
||||
hidden = []
|
||||
for x in pytest.freeze_includes():
|
||||
hidden.extend(['--hidden-import', x])
|
||||
args = ['pyinstaller'] + hidden + ['runtests_script.py']
|
||||
subprocess.check_call(' '.join(args), shell=True)
|
||||
|
||||
If you don't want to ship a different executable just in order to run your tests,
|
||||
you can make your program check for a certain flag and pass control
|
||||
|
|
|
@ -1,64 +0,0 @@
|
|||
"""
|
||||
Installs cx_freeze from source, but first patching
|
||||
setup.py as described here:
|
||||
|
||||
http://stackoverflow.com/questions/25107697/compiling-cx-freeze-under-ubuntu
|
||||
"""
|
||||
import glob
|
||||
import tarfile
|
||||
import os
|
||||
import sys
|
||||
import platform
|
||||
import py
|
||||
|
||||
if __name__ == '__main__':
|
||||
if 'ubuntu' not in platform.version().lower():
|
||||
|
||||
print('Not Ubuntu, installing using pip. (platform.version() is %r)' %
|
||||
platform.version())
|
||||
res = os.system('pip install cx_freeze')
|
||||
if res != 0:
|
||||
sys.exit(res)
|
||||
sys.exit(0)
|
||||
|
||||
rootdir = py.path.local.make_numbered_dir(prefix='cx_freeze')
|
||||
|
||||
res = os.system('pip install --download %s --no-use-wheel '
|
||||
'cx_freeze' % rootdir)
|
||||
if res != 0:
|
||||
sys.exit(res)
|
||||
|
||||
packages = glob.glob('%s/*.tar.gz' % rootdir)
|
||||
assert len(packages) == 1
|
||||
tar_filename = packages[0]
|
||||
|
||||
tar_file = tarfile.open(tar_filename)
|
||||
try:
|
||||
tar_file.extractall(path=str(rootdir))
|
||||
finally:
|
||||
tar_file.close()
|
||||
|
||||
basename = os.path.basename(tar_filename).replace('.tar.gz', '')
|
||||
setup_py_filename = '%s/%s/setup.py' % (rootdir, basename)
|
||||
with open(setup_py_filename) as f:
|
||||
lines = f.readlines()
|
||||
|
||||
line_to_patch = 'if not vars.get("Py_ENABLE_SHARED", 0):'
|
||||
for index, line in enumerate(lines):
|
||||
if line_to_patch in line:
|
||||
indent = line[:line.index(line_to_patch)]
|
||||
lines[index] = indent + 'if True:\n'
|
||||
print('Patched line %d' % (index + 1))
|
||||
break
|
||||
else:
|
||||
sys.exit('Could not find line in setup.py to patch!')
|
||||
|
||||
with open(setup_py_filename, 'w') as f:
|
||||
f.writelines(lines)
|
||||
|
||||
os.chdir('%s/%s' % (rootdir, basename))
|
||||
res = os.system('python setup.py install')
|
||||
if res != 0:
|
||||
sys.exit(res)
|
||||
|
||||
sys.exit(0)
|
|
@ -1,15 +0,0 @@
|
|||
"""
|
||||
Sample setup.py script that generates an executable with pytest runner embedded.
|
||||
"""
|
||||
if __name__ == '__main__':
|
||||
from cx_Freeze import setup, Executable
|
||||
import pytest
|
||||
|
||||
setup(
|
||||
name="runtests",
|
||||
version="0.1",
|
||||
description="example of how embedding py.test into an executable using cx_freeze",
|
||||
executables=[Executable("runtests_script.py")],
|
||||
options={"build_exe": {'includes': pytest.freeze_includes()}},
|
||||
)
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
"""
|
||||
Called by tox.ini: uses the generated executable to run the tests in ./tests/
|
||||
directory.
|
||||
|
||||
.. note:: somehow calling "build/runtests_script" directly from tox doesn't
|
||||
seem to work (at least on Windows).
|
||||
"""
|
||||
if __name__ == '__main__':
|
||||
import os
|
||||
import sys
|
||||
|
||||
executable = os.path.join(os.getcwd(), 'build', 'runtests_script')
|
||||
if sys.platform.startswith('win'):
|
||||
executable += '.exe'
|
||||
sys.exit(os.system('%s tests' % executable))
|
|
@ -0,0 +1,3 @@
|
|||
build/
|
||||
dist/
|
||||
*.spec
|
|
@ -0,0 +1,13 @@
|
|||
"""
|
||||
Generates an executable with pytest runner embedded using PyInstaller.
|
||||
"""
|
||||
if __name__ == '__main__':
|
||||
import pytest
|
||||
import subprocess
|
||||
|
||||
hidden = []
|
||||
for x in pytest.freeze_includes():
|
||||
hidden.extend(['--hidden-import', x])
|
||||
args = ['pyinstaller', '--noconfirm'] + hidden + ['runtests_script.py']
|
||||
subprocess.check_call(' '.join(args), shell=True)
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
"""
|
||||
This is the script that is actually frozen into an executable: simply executes
|
||||
py.test main().
|
||||
"""
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
import pytest
|
||||
"""
|
||||
This is the script that is actually frozen into an executable: simply executes
|
||||
py.test main().
|
||||
"""
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
import pytest
|
||||
sys.exit(pytest.main())
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
def test_upper():
|
||||
assert 'foo'.upper() == 'FOO'
|
||||
|
||||
def test_lower():
|
||||
|
||||
def test_upper():
|
||||
assert 'foo'.upper() == 'FOO'
|
||||
|
||||
def test_lower():
|
||||
assert 'FOO'.lower() == 'foo'
|
|
@ -0,0 +1,12 @@
|
|||
"""
|
||||
Called by tox.ini: uses the generated executable to run the tests in ./tests/
|
||||
directory.
|
||||
"""
|
||||
if __name__ == '__main__':
|
||||
import os
|
||||
import sys
|
||||
|
||||
executable = os.path.join(os.getcwd(), 'dist', 'runtests_script', 'runtests_script')
|
||||
if sys.platform.startswith('win'):
|
||||
executable += '.exe'
|
||||
sys.exit(os.system('%s tests' % executable))
|
11
tox.ini
11
tox.ini
|
@ -5,7 +5,7 @@ distshare={homedir}/.tox/distshare
|
|||
envlist=
|
||||
linting,py26,py27,py33,py34,py35,pypy,
|
||||
{py27,py35}-{pexpect,xdist,trial},
|
||||
py27-nobyte,doctesting,py27-cxfreeze
|
||||
py27-nobyte,doctesting,freeze
|
||||
|
||||
[testenv]
|
||||
commands= py.test --lsof -rfsxX {posargs:testing}
|
||||
|
@ -124,12 +124,11 @@ changedir=testing
|
|||
commands=
|
||||
{envpython} {envbindir}/py.test-jython -rfsxX {posargs}
|
||||
|
||||
[testenv:py27-cxfreeze]
|
||||
changedir=testing/cx_freeze
|
||||
platform=linux|darwin
|
||||
[testenv:freeze]
|
||||
changedir=testing/freeze
|
||||
deps=pyinstaller
|
||||
commands=
|
||||
{envpython} install_cx_freeze.py
|
||||
{envpython} runtests_setup.py build --build-exe build
|
||||
{envpython} create_executable.py
|
||||
{envpython} tox_run.py
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue