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()}}, | ||||
|     ) | ||||
| 
 | ||||
|  | @ -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,15 +1,12 @@ | |||
| """ | ||||
| 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') | ||||
|     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