diff --git a/.travis.yml b/.travis.yml index 3a8f36e95f8..74483452cf2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,7 +25,7 @@ env: - TESTENV=py35-trial - TESTENV=py27-nobyte - TESTENV=doctesting - - TESTENV=py27-cxfreeze + - TESTENV=freeze script: tox --recreate -e $TESTENV diff --git a/_pytest/genscript.py b/_pytest/genscript.py index d2962d8fc82..f070d88162c 100755 --- a/_pytest/genscript.py +++ b/_pytest/genscript.py @@ -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)) diff --git a/appveyor.yml b/appveyor.yml index 2bd72db451b..9c28366a0d9 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -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 diff --git a/doc/en/example/simple.rst b/doc/en/example/simple.rst index 1007ee1e1c5..5275c52c87d 100644 --- a/doc/en/example/simple.rst +++ b/doc/en/example/simple.rst @@ -699,40 +699,29 @@ and run it:: You'll see that the fixture finalizers could use the precise reporting information. -Integrating pytest runner and cx_freeze ------------------------------------------------------------ +Freezing pytest +--------------- If you freeze your application using a tool like -`cx_freeze `_ 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. - -Unfortunately ``cx_freeze`` 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()``:: - - # contents of setup.py - from cx_Freeze import setup, Executable - import pytest - - setup( - name="app_main", - executables=[Executable("app_main.py")], - options={"build_exe": - { - 'includes': pytest.freeze_includes()} - }, - # ... other options - ) - -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 -over to ``pytest`` instead. For example:: +`PyInstaller `_ +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 useful to obtain more information about a hard to reproduce bug. + +Fortunately recent ``PyInstaller`` releases already have a custom hook +for pytest, but if you are using another tool to freeze executables +such as ``cx_freeze`` or ``py2exe``, you can use ``pytest.freeze_includes()`` +to obtain the full list of internal pytest modules. How to configure the tools +to find the internal modules varies from tool to tool, however. + +Instead of freezing the pytest runner as a separate executable, you can make +your frozen program work as the pytest runner by some clever +argument handling during program startup. This allows you to +have a single executable, which is usually more convenient. + +.. code-block:: python # contents of app_main.py import sys @@ -745,7 +734,7 @@ over to ``pytest`` instead. For example:: # by your argument-parsing library of choice as usual ... -This makes it convenient to execute your tests from within your frozen -application, using standard ``py.test`` command-line options:: +This allows you to execute tests using the frozen +application with standard ``py.test`` command-line options:: ./app_main --pytest --verbose --tb=long --junitxml=results.xml test-suite/ diff --git a/testing/cx_freeze/install_cx_freeze.py b/testing/cx_freeze/install_cx_freeze.py deleted file mode 100644 index 83dce87aa56..00000000000 --- a/testing/cx_freeze/install_cx_freeze.py +++ /dev/null @@ -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) diff --git a/testing/cx_freeze/runtests_setup.py b/testing/cx_freeze/runtests_setup.py deleted file mode 100644 index 01e8a8a8929..00000000000 --- a/testing/cx_freeze/runtests_setup.py +++ /dev/null @@ -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()}}, - ) - diff --git a/testing/freeze/.gitignore b/testing/freeze/.gitignore new file mode 100644 index 00000000000..490310b6c11 --- /dev/null +++ b/testing/freeze/.gitignore @@ -0,0 +1,3 @@ +build/ +dist/ +*.spec \ No newline at end of file diff --git a/testing/freeze/create_executable.py b/testing/freeze/create_executable.py new file mode 100644 index 00000000000..8cf259c4024 --- /dev/null +++ b/testing/freeze/create_executable.py @@ -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) + diff --git a/testing/cx_freeze/runtests_script.py b/testing/freeze/runtests_script.py similarity index 95% rename from testing/cx_freeze/runtests_script.py rename to testing/freeze/runtests_script.py index f2b032d7655..cb961fc6caf 100644 --- a/testing/cx_freeze/runtests_script.py +++ b/testing/freeze/runtests_script.py @@ -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()) \ No newline at end of file diff --git a/testing/cx_freeze/tests/test_doctest.txt b/testing/freeze/tests/test_doctest.txt similarity index 100% rename from testing/cx_freeze/tests/test_doctest.txt rename to testing/freeze/tests/test_doctest.txt diff --git a/testing/cx_freeze/tests/test_trivial.py b/testing/freeze/tests/test_trivial.py similarity index 95% rename from testing/cx_freeze/tests/test_trivial.py rename to testing/freeze/tests/test_trivial.py index d8a572baaed..6cf6b05adb2 100644 --- a/testing/cx_freeze/tests/test_trivial.py +++ b/testing/freeze/tests/test_trivial.py @@ -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' \ No newline at end of file diff --git a/testing/cx_freeze/tox_run.py b/testing/freeze/tox_run.py similarity index 56% rename from testing/cx_freeze/tox_run.py rename to testing/freeze/tox_run.py index e8df2684bb1..5310ac1b722 100644 --- a/testing/cx_freeze/tox_run.py +++ b/testing/freeze/tox_run.py @@ -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') - if sys.platform.startswith('win'): - executable += '.exe' +""" +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)) \ No newline at end of file diff --git a/tox.ini b/tox.ini index 5fa9ef21e6e..b16a06887cb 100644 --- a/tox.ini +++ b/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