Merged in nocoddemus/pytest/cx_freeze-support (pull request #189)

This commit is contained in:
Floris Bruynooghe 2014-09-03 21:55:46 +01:00
commit eae1055fb0
10 changed files with 135 additions and 24 deletions

View File

@ -1,6 +1,10 @@
NEXT
-----------
- Added function pytest.freeze_includes(), which makes it easy to embed
pytest into executables using tools like cx_freeze.
See docs for examples and rationale. Thanks Bruno Oliveira.
- Improve assertion rewriting cache invalidation precision.
- fixed issue561: adapt autouse fixture example for python3.

View File

@ -1,6 +1,12 @@
""" generate a single-file self-contained version of pytest """
import py
import os
import sys
import pkgutil
import py
import _pytest
def find_toplevel(name):
@ -80,3 +86,42 @@ def pytest_cmdline_main(config):
tw.line("generated pytest standalone script: %s" % genscript,
bold=True)
return 0
def pytest_namespace():
return {'freeze_includes': freeze_includes}
def freeze_includes():
"""
Returns a list of module names used by py.test that should be
included by cx_freeze.
"""
result = list(_iter_all_modules(py))
result += list(_iter_all_modules(_pytest))
return result
def _iter_all_modules(package, prefix=''):
"""
Iterates over the names of all modules that can be found in the given
package, recursively.
Example:
_iter_all_modules(_pytest) ->
['_pytest.assertion.newinterpret',
'_pytest.capture',
'_pytest.core',
...
]
"""
if type(package) is not str:
path, prefix = package.__path__[0], package.__name__ + '.'
else:
path = package
for _, name, is_package in pkgutil.iter_modules([path]):
if is_package:
for m in _iter_all_modules(os.path.join(path, name), prefix=name + '.'):
yield prefix + m
else:
yield prefix + name

View File

@ -682,33 +682,27 @@ 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 embedding the ``pytest`` runner into a frozen executable using
``cx_freeze`` is not as straightforward as one would like,
because ``pytest`` makes heavy use of dynamic module loading which
``cx_freeze`` can't resolve by itself.
To solve this, you have to manually include ``pytest`` and ``py``
modules by using the ``build_exe`` option in your ``setup.py`` script, like this::
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
includes = [
'_pytest.doctest',
'_pytest.unittest',
# ... lots more
]
setup(
name="runtests",
options={"build_exe": {'includes': includes}},
name="app_main",
executables=[Executable("app_main.py")],
options={"build_exe":
{
'includes': pytest.freeze_includes()}
},
# ... other options
)
(For the complete list, check out the modules under ``_pytest`` in your
site-packages).
With that, you can make your program check for a certain flag and pass control
over to ``pytest``::
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::
# contents of app_main.py
import sys
@ -722,7 +716,6 @@ over to ``pytest``::
...
This makes it convenient to execute your tests from within your frozen
application, using standard ``py.test`` command-line::
application, using standard ``py.test`` command-line options::
$ ./app_main --pytest --verbose --tb=long --junit-xml=results.xml test-suite/ /bin/sh: 1: ./app_main: not found
/bin/sh: 1: ./app_main: not found
$ ./app_main --pytest --verbose --tb=long --junit-xml=results.xml test-suite/

View File

@ -0,0 +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
sys.exit(pytest.main())

View File

@ -0,0 +1,15 @@
"""
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="exemple of how embedding py.test into an executable using cx_freeze",
executables=[Executable("runtests_script.py")],
options={"build_exe": {'includes': pytest.freeze_includes()}},
)

View File

@ -0,0 +1,6 @@
Testing doctest::
>>> 1 + 1
2

View File

@ -0,0 +1,6 @@
def test_upper():
assert 'foo'.upper() == 'FOO'
def test_lower():
assert 'FOO'.lower() == 'foo'

View File

@ -0,0 +1,15 @@
"""
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))

View File

@ -36,3 +36,13 @@ def test_gen(testdir, anypython, standalone):
result = standalone.run(anypython, testdir, p)
assert result.ret != 0
def test_freeze_includes():
"""
Smoke test for freeze_includes(), to ensure that it works across all
supported python versions.
"""
includes = pytest.freeze_includes()
assert len(includes) > 1
assert '_pytest.genscript' in includes

10
tox.ini
View File

@ -1,6 +1,6 @@
[tox]
distshare={homedir}/.tox/distshare
envlist=flakes,py26,py27,py34,pypy,py27-pexpect,py33-pexpect,py27-nobyte,py32,py33,py27-xdist,py33-xdist,py27-trial,py33-trial,doctesting
envlist=flakes,py26,py27,py34,pypy,py27-pexpect,py33-pexpect,py27-nobyte,py32,py33,py27-xdist,py33-xdist,py27-trial,py33-trial,doctesting,py27-cxfreeze
[testenv]
changedir=testing
@ -123,6 +123,14 @@ commands=
{envpython} {envbindir}/py.test-jython \
-rfsxX --junitxml={envlogdir}/junit-{envname}2.xml []
[testenv:py27-cxfreeze]
deps=cx_freeze
changedir=testing/cx_freeze
basepython=python2.7
commands=
{envpython} runtests_setup.py build --build-exe build
{envpython} tox_run.py
[pytest]
minversion=2.0
plugins=pytester