Merge remote-tracking branch 'upstream/master' into merge-master-into-features
This commit is contained in:
commit
802755ceed
|
@ -25,7 +25,7 @@ env:
|
||||||
- TESTENV=py35-trial
|
- TESTENV=py35-trial
|
||||||
- TESTENV=py27-nobyte
|
- TESTENV=py27-nobyte
|
||||||
- TESTENV=doctesting
|
- TESTENV=doctesting
|
||||||
- TESTENV=py27-cxfreeze
|
- TESTENV=freeze
|
||||||
|
|
||||||
script: tox --recreate -e $TESTENV
|
script: tox --recreate -e $TESTENV
|
||||||
|
|
||||||
|
|
|
@ -254,6 +254,15 @@ time or change existing behaviors in order to make them less surprising/more use
|
||||||
|
|
||||||
* ``optparse`` backward compatibility supports float/complex types (`#457`_).
|
* ``optparse`` backward compatibility supports float/complex types (`#457`_).
|
||||||
|
|
||||||
|
* Refined logic for determining the ``rootdir``, considering only valid
|
||||||
|
paths which fixes a number of issues: `#1594`_, `#1435`_ and `#1471`_.
|
||||||
|
Thanks to `@blueyed`_ and `@davehunt`_ for the PR.
|
||||||
|
|
||||||
|
* Always include full assertion explanation. The previous behaviour was hiding
|
||||||
|
sub-expressions that happened to be False, assuming this was redundant information.
|
||||||
|
Thanks `@bagerard`_ for reporting (`#1503`_). Thanks to `@davehunt`_ and
|
||||||
|
`@tomviner`_ for PR.
|
||||||
|
|
||||||
* Renamed the pytest ``pdb`` module (plugin) into ``debugging``.
|
* Renamed the pytest ``pdb`` module (plugin) into ``debugging``.
|
||||||
|
|
||||||
* Better message in case of not using parametrized variable (see `#1539`_).
|
* Better message in case of not using parametrized variable (see `#1539`_).
|
||||||
|
@ -322,11 +331,13 @@ time or change existing behaviors in order to make them less surprising/more use
|
||||||
.. _#1421: https://github.com/pytest-dev/pytest/issues/1421
|
.. _#1421: https://github.com/pytest-dev/pytest/issues/1421
|
||||||
.. _#1426: https://github.com/pytest-dev/pytest/issues/1426
|
.. _#1426: https://github.com/pytest-dev/pytest/issues/1426
|
||||||
.. _#1428: https://github.com/pytest-dev/pytest/pull/1428
|
.. _#1428: https://github.com/pytest-dev/pytest/pull/1428
|
||||||
|
.. _#1435: https://github.com/pytest-dev/pytest/issues/1435
|
||||||
.. _#1441: https://github.com/pytest-dev/pytest/pull/1441
|
.. _#1441: https://github.com/pytest-dev/pytest/pull/1441
|
||||||
.. _#1444: https://github.com/pytest-dev/pytest/pull/1444
|
.. _#1444: https://github.com/pytest-dev/pytest/pull/1444
|
||||||
.. _#1454: https://github.com/pytest-dev/pytest/pull/1454
|
.. _#1454: https://github.com/pytest-dev/pytest/pull/1454
|
||||||
.. _#1461: https://github.com/pytest-dev/pytest/pull/1461
|
.. _#1461: https://github.com/pytest-dev/pytest/pull/1461
|
||||||
.. _#1468: https://github.com/pytest-dev/pytest/pull/1468
|
.. _#1468: https://github.com/pytest-dev/pytest/pull/1468
|
||||||
|
.. _#1471: https://github.com/pytest-dev/pytest/issues/1471
|
||||||
.. _#1474: https://github.com/pytest-dev/pytest/pull/1474
|
.. _#1474: https://github.com/pytest-dev/pytest/pull/1474
|
||||||
.. _#1479: https://github.com/pytest-dev/pytest/issues/1479
|
.. _#1479: https://github.com/pytest-dev/pytest/issues/1479
|
||||||
.. _#1502: https://github.com/pytest-dev/pytest/pull/1502
|
.. _#1502: https://github.com/pytest-dev/pytest/pull/1502
|
||||||
|
@ -341,6 +352,7 @@ time or change existing behaviors in order to make them less surprising/more use
|
||||||
.. _#1562: https://github.com/pytest-dev/pytest/issues/1562
|
.. _#1562: https://github.com/pytest-dev/pytest/issues/1562
|
||||||
.. _#1579: https://github.com/pytest-dev/pytest/issues/1579
|
.. _#1579: https://github.com/pytest-dev/pytest/issues/1579
|
||||||
.. _#1580: https://github.com/pytest-dev/pytest/pull/1580
|
.. _#1580: https://github.com/pytest-dev/pytest/pull/1580
|
||||||
|
.. _#1594: https://github.com/pytest-dev/pytest/issues/1594
|
||||||
.. _#1597: https://github.com/pytest-dev/pytest/pull/1597
|
.. _#1597: https://github.com/pytest-dev/pytest/pull/1597
|
||||||
.. _#1605: https://github.com/pytest-dev/pytest/issues/1605
|
.. _#1605: https://github.com/pytest-dev/pytest/issues/1605
|
||||||
.. _#1616: https://github.com/pytest-dev/pytest/pull/1616
|
.. _#1616: https://github.com/pytest-dev/pytest/pull/1616
|
||||||
|
@ -367,6 +379,7 @@ time or change existing behaviors in order to make them less surprising/more use
|
||||||
.. _#717: https://github.com/pytest-dev/pytest/issues/717
|
.. _#717: https://github.com/pytest-dev/pytest/issues/717
|
||||||
.. _#925: https://github.com/pytest-dev/pytest/issues/925
|
.. _#925: https://github.com/pytest-dev/pytest/issues/925
|
||||||
|
|
||||||
|
|
||||||
.. _@anntzer: https://github.com/anntzer
|
.. _@anntzer: https://github.com/anntzer
|
||||||
.. _@bagerard: https://github.com/bagerard
|
.. _@bagerard: https://github.com/bagerard
|
||||||
.. _@BeyondEvil: https://github.com/BeyondEvil
|
.. _@BeyondEvil: https://github.com/BeyondEvil
|
||||||
|
@ -397,8 +410,8 @@ time or change existing behaviors in order to make them less surprising/more use
|
||||||
.. _@RedBeardCode: https://github.com/RedBeardCode
|
.. _@RedBeardCode: https://github.com/RedBeardCode
|
||||||
.. _@sallner: https://github.com/sallner
|
.. _@sallner: https://github.com/sallner
|
||||||
.. _@sober7: https://github.com/sober7
|
.. _@sober7: https://github.com/sober7
|
||||||
.. _@suzaku: https://github.com/suzaku
|
|
||||||
.. _@Stranger6667: https://github.com/Stranger6667
|
.. _@Stranger6667: https://github.com/Stranger6667
|
||||||
|
.. _@suzaku: https://github.com/suzaku
|
||||||
.. _@tareqalayan: https://github.com/tareqalayan
|
.. _@tareqalayan: https://github.com/tareqalayan
|
||||||
.. _@taschini: https://github.com/taschini
|
.. _@taschini: https://github.com/taschini
|
||||||
.. _@tramwaj29: https://github.com/tramwaj29
|
.. _@tramwaj29: https://github.com/tramwaj29
|
||||||
|
|
23
README.rst
23
README.rst
|
@ -1,5 +1,5 @@
|
||||||
.. image:: http://pytest.org/latest/_static/pytest1.png
|
.. image:: http://docs.pytest.org/en/latest/_static/pytest1.png
|
||||||
:target: http://pytest.org
|
:target: http://docs.pytest.org
|
||||||
:align: center
|
:align: center
|
||||||
:alt: pytest
|
:alt: pytest
|
||||||
|
|
||||||
|
@ -51,33 +51,34 @@ To execute it::
|
||||||
test_sample.py:5: AssertionError
|
test_sample.py:5: AssertionError
|
||||||
======= 1 failed in 0.12 seconds ========
|
======= 1 failed in 0.12 seconds ========
|
||||||
|
|
||||||
Due to ``pytest``'s detailed assertion introspection, only plain ``assert`` statements are used. See `getting-started <http://pytest.org/latest/getting-started.html#our-first-test-run>`_ for more examples.
|
|
||||||
|
Due to ``py.test``'s detailed assertion introspection, only plain ``assert`` statements are used. See `getting-started <http://docs.pytest.org/en/latest/getting-started.html#our-first-test-run>`_ for more examples.
|
||||||
|
|
||||||
|
|
||||||
Features
|
Features
|
||||||
--------
|
--------
|
||||||
|
|
||||||
- Detailed info on failing `assert statements <http://pytest.org/latest/assert.html>`_ (no need to remember ``self.assert*`` names);
|
- Detailed info on failing `assert statements <http://docs.pytest.org/en/latest/assert.html>`_ (no need to remember ``self.assert*`` names);
|
||||||
|
|
||||||
- `Auto-discovery
|
- `Auto-discovery
|
||||||
<http://pytest.org/latest/goodpractices.html#python-test-discovery>`_
|
<http://docs.pytest.org/en/latest/goodpractices.html#python-test-discovery>`_
|
||||||
of test modules and functions;
|
of test modules and functions;
|
||||||
|
|
||||||
- `Modular fixtures <http://pytest.org/latest/fixture.html>`_ for
|
- `Modular fixtures <http://docs.pytest.org/en/latest/fixture.html>`_ for
|
||||||
managing small or parametrized long-lived test resources;
|
managing small or parametrized long-lived test resources;
|
||||||
|
|
||||||
- Can run `unittest <http://pytest.org/latest/unittest.html>`_ (or trial),
|
- Can run `unittest <http://docs.pytest.org/en/latest/unittest.html>`_ (or trial),
|
||||||
`nose <http://pytest.org/latest/nose.html>`_ test suites out of the box;
|
`nose <http://docs.pytest.org/en/latest/nose.html>`_ test suites out of the box;
|
||||||
|
|
||||||
- Python2.6+, Python3.3+, PyPy-2.3, Jython-2.5 (untested);
|
- Python2.6+, Python3.3+, PyPy-2.3, Jython-2.5 (untested);
|
||||||
|
|
||||||
- Rich plugin architecture, with over 150+ `external plugins <http://pytest.org/latest/plugins.html#installing-external-plugins-searching>`_ and thriving community;
|
- Rich plugin architecture, with over 150+ `external plugins <http://docs.pytest.org/en/latest/plugins.html#installing-external-plugins-searching>`_ and thriving community;
|
||||||
|
|
||||||
|
|
||||||
Documentation
|
Documentation
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
For full documentation, including installation, tutorials and PDF documents, please see http://pytest.org.
|
For full documentation, including installation, tutorials and PDF documents, please see http://docs.pytest.org.
|
||||||
|
|
||||||
|
|
||||||
Bugs/Requests
|
Bugs/Requests
|
||||||
|
@ -89,7 +90,7 @@ Please use the `GitHub issue tracker <https://github.com/pytest-dev/pytest/issue
|
||||||
Changelog
|
Changelog
|
||||||
---------
|
---------
|
||||||
|
|
||||||
Consult the `Changelog <http://pytest.org/latest/changelog.html>`_ page for fixes and enhancements of each version.
|
Consult the `Changelog <http://docs.pytest.org/en/latest/changelog.html>`_ page for fixes and enhancements of each version.
|
||||||
|
|
||||||
|
|
||||||
License
|
License
|
||||||
|
|
|
@ -1181,6 +1181,8 @@ def get_common_ancestor(args):
|
||||||
if str(arg)[0] == "-":
|
if str(arg)[0] == "-":
|
||||||
continue
|
continue
|
||||||
p = py.path.local(arg)
|
p = py.path.local(arg)
|
||||||
|
if not p.exists():
|
||||||
|
continue
|
||||||
if common_ancestor is None:
|
if common_ancestor is None:
|
||||||
common_ancestor = p
|
common_ancestor = p
|
||||||
else:
|
else:
|
||||||
|
@ -1194,21 +1196,28 @@ def get_common_ancestor(args):
|
||||||
common_ancestor = shared
|
common_ancestor = shared
|
||||||
if common_ancestor is None:
|
if common_ancestor is None:
|
||||||
common_ancestor = py.path.local()
|
common_ancestor = py.path.local()
|
||||||
elif not common_ancestor.isdir():
|
elif common_ancestor.isfile():
|
||||||
common_ancestor = common_ancestor.dirpath()
|
common_ancestor = common_ancestor.dirpath()
|
||||||
return common_ancestor
|
return common_ancestor
|
||||||
|
|
||||||
|
|
||||||
|
def get_dirs_from_args(args):
|
||||||
|
return [d for d in (py.path.local(x) for x in args
|
||||||
|
if not str(x).startswith("-"))
|
||||||
|
if d.exists()]
|
||||||
|
|
||||||
|
|
||||||
def determine_setup(inifile, args):
|
def determine_setup(inifile, args):
|
||||||
|
dirs = get_dirs_from_args(args)
|
||||||
if inifile:
|
if inifile:
|
||||||
iniconfig = py.iniconfig.IniConfig(inifile)
|
iniconfig = py.iniconfig.IniConfig(inifile)
|
||||||
try:
|
try:
|
||||||
inicfg = iniconfig["pytest"]
|
inicfg = iniconfig["pytest"]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
inicfg = None
|
inicfg = None
|
||||||
rootdir = get_common_ancestor(args)
|
rootdir = get_common_ancestor(dirs)
|
||||||
else:
|
else:
|
||||||
ancestor = get_common_ancestor(args)
|
ancestor = get_common_ancestor(dirs)
|
||||||
rootdir, inifile, inicfg = getcfg(
|
rootdir, inifile, inicfg = getcfg(
|
||||||
[ancestor], ["pytest.ini", "tox.ini", "setup.cfg"])
|
[ancestor], ["pytest.ini", "tox.ini", "setup.cfg"])
|
||||||
if rootdir is None:
|
if rootdir is None:
|
||||||
|
@ -1216,7 +1225,13 @@ def determine_setup(inifile, args):
|
||||||
if rootdir.join("setup.py").exists():
|
if rootdir.join("setup.py").exists():
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
rootdir = ancestor
|
rootdir, inifile, inicfg = getcfg(
|
||||||
|
dirs, ["pytest.ini", "tox.ini", "setup.cfg"])
|
||||||
|
if rootdir is None:
|
||||||
|
rootdir = get_common_ancestor([py.path.local(), ancestor])
|
||||||
|
is_fs_root = os.path.splitdrive(str(rootdir))[1] == os.sep
|
||||||
|
if is_fs_root:
|
||||||
|
rootdir = ancestor
|
||||||
return rootdir, inifile, inicfg or {}
|
return rootdir, inifile, inicfg or {}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ environment:
|
||||||
# builds timing out in AppVeyor
|
# builds timing out in AppVeyor
|
||||||
- TOXENV: "linting,py26,py27,py33,py34,py35,pypy"
|
- TOXENV: "linting,py26,py27,py33,py34,py35,pypy"
|
||||||
- TOXENV: "py27-pexpect,py27-xdist,py27-trial,py35-pexpect,py35-xdist,py35-trial"
|
- 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:
|
install:
|
||||||
- echo Installed Pythons
|
- echo Installed Pythons
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
Full pytest documentation
|
Full pytest documentation
|
||||||
===========================
|
===========================
|
||||||
|
|
||||||
`Download latest version as PDF <pytest.pdf>`_
|
`Download latest version as PDF <https://media.readthedocs.org/pdf/pytest/latest/pytest.pdf>`_
|
||||||
|
|
||||||
.. `Download latest version as EPUB <http://media.readthedocs.org/epub/pytest/latest/pytest.epub>`_
|
.. `Download latest version as EPUB <http://media.readthedocs.org/epub/pytest/latest/pytest.epub>`_
|
||||||
|
|
||||||
|
|
|
@ -29,25 +29,29 @@ project/testrun-specific information.
|
||||||
|
|
||||||
Here is the algorithm which finds the rootdir from ``args``:
|
Here is the algorithm which finds the rootdir from ``args``:
|
||||||
|
|
||||||
- determine the common ancestor directory for the specified ``args``.
|
- determine the common ancestor directory for the specified ``args`` that are
|
||||||
|
recognised as paths that exist in the file system. If no such paths are
|
||||||
|
found, the common ancestor directory is set to the current working directory.
|
||||||
|
|
||||||
- look for ``pytest.ini``, ``tox.ini`` and ``setup.cfg`` files in the
|
- look for ``pytest.ini``, ``tox.ini`` and ``setup.cfg`` files in the ancestor
|
||||||
ancestor directory and upwards. If one is matched, it becomes the
|
directory and upwards. If one is matched, it becomes the ini-file and its
|
||||||
ini-file and its directory becomes the rootdir. An existing
|
directory becomes the rootdir.
|
||||||
``pytest.ini`` file will always be considered a match whereas
|
|
||||||
``tox.ini`` and ``setup.cfg`` will only match if they contain
|
|
||||||
a ``[pytest]`` section.
|
|
||||||
|
|
||||||
- if no ini-file was found, look for ``setup.py`` upwards from
|
- if no ini-file was found, look for ``setup.py`` upwards from the common
|
||||||
the common ancestor directory to determine the ``rootdir``.
|
ancestor directory to determine the ``rootdir``.
|
||||||
|
|
||||||
- if no ini-file and no ``setup.py`` was found, use the already
|
- if no ``setup.py`` was found, look for ``pytest.ini``, ``tox.ini`` and
|
||||||
determined common ancestor as root directory. This allows to
|
``setup.cfg`` in each of the specified ``args`` and upwards. If one is
|
||||||
work with pytest in structures that are not part of a package
|
matched, it becomes the ini-file and its directory becomes the rootdir.
|
||||||
and don't have any particular ini-file configuration.
|
|
||||||
|
|
||||||
Note that options from multiple ini-files candidates are never merged,
|
- if no ini-file was found, use the already determined common ancestor as root
|
||||||
the first one wins (``pytest.ini`` always wins even if it does not
|
directory. This allows to work with pytest in structures that are not part of
|
||||||
|
a package and don't have any particular ini-file configuration.
|
||||||
|
|
||||||
|
Note that an existing ``pytest.ini`` file will always be considered a match,
|
||||||
|
whereas ``tox.ini`` and ``setup.cfg`` will only match if they contain a
|
||||||
|
``[pytest]`` section. Options from multiple ini-files candidates are never
|
||||||
|
merged - the first one wins (``pytest.ini`` always wins, even if it does not
|
||||||
contain a ``[pytest]`` section).
|
contain a ``[pytest]`` section).
|
||||||
|
|
||||||
The ``config`` object will subsequently carry these attributes:
|
The ``config`` object will subsequently carry these attributes:
|
||||||
|
|
|
@ -720,40 +720,29 @@ and run it::
|
||||||
You'll see that the fixture finalizers could use the precise reporting
|
You'll see that the fixture finalizers could use the precise reporting
|
||||||
information.
|
information.
|
||||||
|
|
||||||
Integrating pytest runner and cx_freeze
|
Freezing pytest
|
||||||
-----------------------------------------------------------
|
---------------
|
||||||
|
|
||||||
If you freeze your application using a tool like
|
If you freeze your application using a tool like
|
||||||
`cx_freeze <https://cx-freeze.readthedocs.io>`_ in order to distribute it
|
`PyInstaller <https://pyinstaller.readthedocs.io>`_
|
||||||
to your end-users, it is a good idea to also package your test runner and run
|
in order to distribute it to your end-users, it is a good idea to also package
|
||||||
your tests using the frozen application.
|
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.
|
||||||
|
|
||||||
This way packaging errors such as dependencies not being
|
Fortunately recent ``PyInstaller`` releases already have a custom hook
|
||||||
included into the executable can be detected early while also allowing you to
|
for pytest, but if you are using another tool to freeze executables
|
||||||
send test files to users so they can run them in their machines, which can be
|
such as ``cx_freeze`` or ``py2exe``, you can use ``pytest.freeze_includes()``
|
||||||
invaluable to obtain more information about a hard to reproduce bug.
|
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.
|
||||||
|
|
||||||
Unfortunately ``cx_freeze`` can't discover them
|
Instead of freezing the pytest runner as a separate executable, you can make
|
||||||
automatically because of ``pytest``'s use of dynamic module loading, so you
|
your frozen program work as the pytest runner by some clever
|
||||||
must declare them explicitly by using ``pytest.freeze_includes()``::
|
argument handling during program startup. This allows you to
|
||||||
|
have a single executable, which is usually more convenient.
|
||||||
|
|
||||||
# contents of setup.py
|
.. code-block:: python
|
||||||
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::
|
|
||||||
|
|
||||||
# contents of app_main.py
|
# contents of app_main.py
|
||||||
import sys
|
import sys
|
||||||
|
@ -766,7 +755,8 @@ over to ``pytest`` instead. For example::
|
||||||
# by your argument-parsing library of choice as usual
|
# by your argument-parsing library of choice as usual
|
||||||
...
|
...
|
||||||
|
|
||||||
This makes it convenient to execute your tests from within your frozen
|
|
||||||
application, using standard ``pytest`` command-line options::
|
This allows you to execute tests using the frozen
|
||||||
|
application with standard ``pytest`` command-line options::
|
||||||
|
|
||||||
./app_main --pytest --verbose --tb=long --junitxml=results.xml test-suite/
|
./app_main --pytest --verbose --tb=long --junitxml=results.xml test-suite/
|
||||||
|
|
|
@ -11,7 +11,7 @@ Installation and Getting Started
|
||||||
`colorama (Windows) <http://pypi.python.org/pypi/colorama>`_,
|
`colorama (Windows) <http://pypi.python.org/pypi/colorama>`_,
|
||||||
`argparse (py26) <http://pypi.python.org/pypi/argparse>`_.
|
`argparse (py26) <http://pypi.python.org/pypi/argparse>`_.
|
||||||
|
|
||||||
**documentation as PDF**: `download latest <http://pytest.org/latest/pytest.pdf>`_
|
**documentation as PDF**: `download latest <https://media.readthedocs.org/pdf/pytest/latest/pytest.pdf>`_
|
||||||
|
|
||||||
.. _`getstarted`:
|
.. _`getstarted`:
|
||||||
.. _installation:
|
.. _installation:
|
||||||
|
|
|
@ -10,7 +10,7 @@ pytest: helps you write better programs
|
||||||
- free and open source software, distributed under the terms of the :ref:`MIT license <license>`
|
- free and open source software, distributed under the terms of the :ref:`MIT license <license>`
|
||||||
- **well tested** with more than a thousand tests against itself
|
- **well tested** with more than a thousand tests against itself
|
||||||
- **strict backward compatibility policy** for safe pytest upgrades
|
- **strict backward compatibility policy** for safe pytest upgrades
|
||||||
- :ref:`comprehensive online <toc>` and `PDF documentation <pytest.pdf>`_
|
- :ref:`comprehensive online <toc>` and `PDF documentation <https://media.readthedocs.org/pdf/pytest/latest/pytest.pdf>`_
|
||||||
- many :ref:`third party plugins <extplugins>` and :ref:`builtin helpers <pytest helpers>`,
|
- many :ref:`third party plugins <extplugins>` and :ref:`builtin helpers <pytest helpers>`,
|
||||||
- used in :ref:`many small and large projects and organisations <projects>`
|
- used in :ref:`many small and large projects and organisations <projects>`
|
||||||
- comes with many :ref:`tested examples <examples>`
|
- comes with many :ref:`tested examples <examples>`
|
||||||
|
|
|
@ -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 pytest 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
|
This is the script that is actually frozen into an executable: simply executes
|
||||||
pytest main().
|
py.test main().
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
import sys
|
import sys
|
||||||
import pytest
|
import pytest
|
||||||
sys.exit(pytest.main())
|
sys.exit(pytest.main())
|
|
@ -1,6 +1,6 @@
|
||||||
|
|
||||||
def test_upper():
|
def test_upper():
|
||||||
assert 'foo'.upper() == 'FOO'
|
assert 'foo'.upper() == 'FOO'
|
||||||
|
|
||||||
def test_lower():
|
def test_lower():
|
||||||
assert 'FOO'.lower() == 'foo'
|
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))
|
|
@ -493,7 +493,8 @@ class TestSession:
|
||||||
class Test_getinitialnodes:
|
class Test_getinitialnodes:
|
||||||
def test_global_file(self, testdir, tmpdir):
|
def test_global_file(self, testdir, tmpdir):
|
||||||
x = tmpdir.ensure("x.py")
|
x = tmpdir.ensure("x.py")
|
||||||
config = testdir.parseconfigure(x)
|
with tmpdir.as_cwd():
|
||||||
|
config = testdir.parseconfigure(x)
|
||||||
col = testdir.getnode(config, x)
|
col = testdir.getnode(config, x)
|
||||||
assert isinstance(col, pytest.Module)
|
assert isinstance(col, pytest.Module)
|
||||||
assert col.name == 'x.py'
|
assert col.name == 'x.py'
|
||||||
|
@ -507,7 +508,8 @@ class Test_getinitialnodes:
|
||||||
subdir = tmpdir.join("subdir")
|
subdir = tmpdir.join("subdir")
|
||||||
x = subdir.ensure("x.py")
|
x = subdir.ensure("x.py")
|
||||||
subdir.ensure("__init__.py")
|
subdir.ensure("__init__.py")
|
||||||
config = testdir.parseconfigure(x)
|
with subdir.as_cwd():
|
||||||
|
config = testdir.parseconfigure(x)
|
||||||
col = testdir.getnode(config, x)
|
col = testdir.getnode(config, x)
|
||||||
assert isinstance(col, pytest.Module)
|
assert isinstance(col, pytest.Module)
|
||||||
assert col.name == 'x.py'
|
assert col.name == 'x.py'
|
||||||
|
|
|
@ -483,7 +483,8 @@ def test_consider_args_after_options_for_rootdir_and_inifile(testdir, args):
|
||||||
args[i] = d1
|
args[i] = d1
|
||||||
elif arg == 'dir2':
|
elif arg == 'dir2':
|
||||||
args[i] = d2
|
args[i] = d2
|
||||||
result = testdir.runpytest(*args)
|
with root.as_cwd():
|
||||||
|
result = testdir.runpytest(*args)
|
||||||
result.stdout.fnmatch_lines(['*rootdir: *myroot, inifile: '])
|
result.stdout.fnmatch_lines(['*rootdir: *myroot, inifile: '])
|
||||||
|
|
||||||
|
|
||||||
|
@ -566,10 +567,14 @@ class TestWarning:
|
||||||
class TestRootdir:
|
class TestRootdir:
|
||||||
def test_simple_noini(self, tmpdir):
|
def test_simple_noini(self, tmpdir):
|
||||||
assert get_common_ancestor([tmpdir]) == tmpdir
|
assert get_common_ancestor([tmpdir]) == tmpdir
|
||||||
assert get_common_ancestor([tmpdir.mkdir("a"), tmpdir]) == tmpdir
|
a = tmpdir.mkdir("a")
|
||||||
assert get_common_ancestor([tmpdir, tmpdir.join("a")]) == tmpdir
|
assert get_common_ancestor([a, tmpdir]) == tmpdir
|
||||||
|
assert get_common_ancestor([tmpdir, a]) == tmpdir
|
||||||
with tmpdir.as_cwd():
|
with tmpdir.as_cwd():
|
||||||
assert get_common_ancestor([]) == tmpdir
|
assert get_common_ancestor([]) == tmpdir
|
||||||
|
no_path = tmpdir.join('does-not-exist')
|
||||||
|
assert get_common_ancestor([no_path]) == tmpdir
|
||||||
|
assert get_common_ancestor([no_path.join('a')]) == tmpdir
|
||||||
|
|
||||||
@pytest.mark.parametrize("name", "setup.cfg tox.ini pytest.ini".split())
|
@pytest.mark.parametrize("name", "setup.cfg tox.ini pytest.ini".split())
|
||||||
def test_with_ini(self, tmpdir, name):
|
def test_with_ini(self, tmpdir, name):
|
||||||
|
@ -604,7 +609,8 @@ class TestRootdir:
|
||||||
assert inifile is None
|
assert inifile is None
|
||||||
assert inicfg == {}
|
assert inicfg == {}
|
||||||
|
|
||||||
def test_nothing(self, tmpdir):
|
def test_nothing(self, tmpdir, monkeypatch):
|
||||||
|
monkeypatch.chdir(str(tmpdir))
|
||||||
rootdir, inifile, inicfg = determine_setup(None, [tmpdir])
|
rootdir, inifile, inicfg = determine_setup(None, [tmpdir])
|
||||||
assert rootdir == tmpdir
|
assert rootdir == tmpdir
|
||||||
assert inifile is None
|
assert inifile is None
|
||||||
|
@ -685,3 +691,36 @@ class TestOverrideIniArgs:
|
||||||
"ini2:url=/tmp/user2?a=b&d=e",
|
"ini2:url=/tmp/user2?a=b&d=e",
|
||||||
"ini3:True",
|
"ini3:True",
|
||||||
"ini4:False"])
|
"ini4:False"])
|
||||||
|
|
||||||
|
def test_with_arg_outside_cwd_without_inifile(self, tmpdir, monkeypatch):
|
||||||
|
monkeypatch.chdir(str(tmpdir))
|
||||||
|
a = tmpdir.mkdir("a")
|
||||||
|
b = tmpdir.mkdir("b")
|
||||||
|
rootdir, inifile, inicfg = determine_setup(None, [a, b])
|
||||||
|
assert rootdir == tmpdir
|
||||||
|
assert inifile is None
|
||||||
|
|
||||||
|
def test_with_arg_outside_cwd_with_inifile(self, tmpdir):
|
||||||
|
a = tmpdir.mkdir("a")
|
||||||
|
b = tmpdir.mkdir("b")
|
||||||
|
inifile = a.ensure("pytest.ini")
|
||||||
|
rootdir, parsed_inifile, inicfg = determine_setup(None, [a, b])
|
||||||
|
assert rootdir == a
|
||||||
|
assert inifile == parsed_inifile
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('dirs', ([], ['does-not-exist'],
|
||||||
|
['a/does-not-exist']))
|
||||||
|
def test_with_non_dir_arg(self, dirs, tmpdir):
|
||||||
|
with tmpdir.ensure(dir=True).as_cwd():
|
||||||
|
rootdir, inifile, inicfg = determine_setup(None, dirs)
|
||||||
|
assert rootdir == tmpdir
|
||||||
|
assert inifile is None
|
||||||
|
|
||||||
|
def test_with_existing_file_in_subdir(self, tmpdir):
|
||||||
|
a = tmpdir.mkdir("a")
|
||||||
|
a.ensure("exist")
|
||||||
|
with tmpdir.as_cwd():
|
||||||
|
rootdir, inifile, inicfg = determine_setup(None, ['a/exist'])
|
||||||
|
assert rootdir == tmpdir
|
||||||
|
assert inifile is None
|
||||||
|
|
||||||
|
|
11
tox.ini
11
tox.ini
|
@ -5,7 +5,7 @@ distshare={homedir}/.tox/distshare
|
||||||
envlist=
|
envlist=
|
||||||
linting,py26,py27,py33,py34,py35,pypy,
|
linting,py26,py27,py33,py34,py35,pypy,
|
||||||
{py27,py35}-{pexpect,xdist,trial},
|
{py27,py35}-{pexpect,xdist,trial},
|
||||||
py27-nobyte,doctesting,py27-cxfreeze
|
py27-nobyte,doctesting,freeze
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
commands= pytest --lsof -rfsxX {posargs:testing}
|
commands= pytest --lsof -rfsxX {posargs:testing}
|
||||||
|
@ -128,12 +128,11 @@ changedir=testing
|
||||||
commands=
|
commands=
|
||||||
{envpython} {envbindir}/py.test-jython -rfsxX {posargs}
|
{envpython} {envbindir}/py.test-jython -rfsxX {posargs}
|
||||||
|
|
||||||
[testenv:py27-cxfreeze]
|
[testenv:freeze]
|
||||||
changedir=testing/cx_freeze
|
changedir=testing/freeze
|
||||||
platform=linux|darwin
|
deps=pyinstaller
|
||||||
commands=
|
commands=
|
||||||
{envpython} install_cx_freeze.py
|
{envpython} create_executable.py
|
||||||
{envpython} runtests_setup.py build --build-exe build
|
|
||||||
{envpython} tox_run.py
|
{envpython} tox_run.py
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue