diff options
-rw-r--r-- | .hgtags | 15 | ||||
-rw-r--r-- | CHANGES.txt | 36 | ||||
-rw-r--r-- | README.txt | 95 | ||||
-rw-r--r-- | ptr.py | 105 | ||||
-rw-r--r-- | setup.py | 13 |
5 files changed, 260 insertions, 4 deletions
@@ -0,0 +1,15 @@ +a41b62bd27ddab9ce0dda1f586a91eaed1d6a28c 1.0a1 +329b93ea52567ef21c0e96bc78c7dce1d1091607 1.0a2 +9c26b0f31e3033de73e81d84c3140ad22c4c6e14 1.0b1 +b66744dba81a3d674d83420d56d7abfcd132f32a 1.0b2 +fc47a169b30fddce2e37259cf9f853b3fc58cf83 1.0 +5073ad862016aed2c9bd01c06105d505139f754a 1.1b1 +1a37e4e32c68807b5a84ce96244ac285336d0bff 1.1 +fff2c07f64587d73505cdcac4880ecd652f8b308 1.2 +2759a927af554bf3dc7a8feb79353b88bb3eec97 2.0 +339be8b7dc911dd3cd3e2095d7fe1de47fb6c956 2.1 +b4e3fd6cf0e3559f71dcecbf321dbc92723f3df5 2.1.1 +ad7ce5b4257c47bbfc6ff49271ef3a675deb156a 2.1.2 +b0c0ff130ab962a5eea998c91b077296952e4465 2.2 +cc63a4576386eaade57b16590fe7c0053f4533c2 2.2.1 +b71c2b4ed8f735500eabe3fabc5048a36a6a3420 2.3 diff --git a/CHANGES.txt b/CHANGES.txt index e69de29..6b0b044 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -0,0 +1,36 @@ +2.3 +~~~ + +* Use hgdistver for version management and file discovery. + +2.2 +~~~ + +* Honor ``.eggs`` directory for transient downloads as introduced in Setuptools + 7.0. + +2.1 +~~~ + +* The preferred invocation is now the 'pytest' command. + +2.0 +~~~ + +* Removed support for the alternate usage. The recommended usage (as a + distutils command) is now the only supported usage. +* Removed support for the --junitxml parameter to the ptr command. Clients + should pass the same parameter (and all other py.test arguments) to py.test + via the --addopts parameter. + +1.1 +~~~ + +* Added support for --addopts to pass any arguments through to py.test. +* Deprecated support for --junitxml. Use --addopts instead. --junitxml will be + removed in 2.0. + +1.0 +~~~ + +Initial implementation. @@ -1,2 +1,95 @@ -`Documentation <https://pythonhosted.org/pytest-runner>`_pytest-runner
+pytest-runner
=============
+
+Setup scripts can use pytest-runner to add setup.py test support for pytest
+runner.
+
+Usage
+-----
+
+- Add 'pytest-runner' to your 'setup_requires'. Pin to '>=2.0,<3dev' (or
+ similar) to avoid pulling in incompatible versions.
+- Include 'pytest' and any other testing requirements to 'tests_require'.
+- Invoke tests with ``setup.py pytest``.
+- Pass ``--index-url`` to have test requirements downloaded from an alternate
+ index URL.
+- Pass additional py.test command-line options using ``--addopts``.
+- Set permanent options for the pytest distutils command in the ``[pytest]``
+ section of setup.cfg.
+- Set permanent options for the pytest run itself in the ``[pytest]``
+ section of pytest.ini or tox.ini. See `pytest 567
+ <https://bitbucket.org/hpk42/pytest/issue/567>`_ for details on
+ why setup.cfg is inadequate.
+- Optionally, set ``test=pytest`` in the ``[aliases]`` section of setup.cfg
+ to cause ``setup.py test`` to invoke pytest.
+
+Example
+-------
+
+The most simple usage looks like this in setup.py::
+
+ setup(
+ setup_requires=[
+ 'pytest-runner',
+ ],
+ tests_require=[
+ 'pytest',
+ ],
+ )
+
+Additional dependencies require to run the tests (e.g. mock or pytest
+plugins) may be added to tests_require and will be downloaded and
+required by the session before invoking pytest.
+
+See the `jaraco.collections
+<https://bitbucket.org/jaraco/jaraco.collections/>`_ project
+for real-world usage.
+
+Standalone Example
+------------------
+
+Although ``pytest-runner`` is typically used to add pytest test
+runner support to maintained packages, ``pytest-runner`` may
+also be used to create standalone tests. Consider `this example
+failure <https://gist.github.com/jaraco/d979a558bc0bf2194c23>`_,
+reported in `jsonpickle #117
+<https://github.com/jsonpickle/jsonpickle/issues/117>`_.
+
+That single file may be cloned or downloaded and simply run on
+any system with Python and Setuptools. It will download the
+specified dependencies and run the tests. Afterward, the the
+cloned directory can be removed and with it all trace of
+invoking the test. No other dependencies are needed and no
+system configuration is altered.
+
+Then, anyone trying to replicate the failure can do so easily
+and with all the power of pytest (rewritten assertions,
+rich comparisons, interactive debugging, extensibility through
+plugins, etc).
+
+As a result, the communication barrier for describing and
+replicating failures is made almost trivially low.
+
+Considerations
+--------------
+
+Conditional Requirement
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Because it uses Setuptools setup_requires, pytest-runner will install itself
+on every invocation of setup.py. In some cases, this causes delays for
+invocations of setup.py that will never invoke pytest-runner. To help avoid
+this contingency, consider requiring pytest-runner only when pytest
+is invoked::
+
+ needs_pytest = {'pytest', 'test', 'ptr'}.intersection(sys.argv)
+ pytest_runner = ['pytest_runner'] if needs_pytest else []
+
+ # ...
+
+ setup(
+ #...
+ setup_requires=[
+ #... (other setup requirements)
+ ] + pytest_runner,
+ )
@@ -0,0 +1,105 @@ +""" +Implementation +""" + +import os as _os +import shlex as _shlex +import contextlib as _contextlib +import sys as _sys + +import setuptools.command.test as orig + +@_contextlib.contextmanager +def _save_argv(repl=None): + saved = _sys.argv[:] + if repl is not None: + _sys.argv[:] = repl + try: + yield saved + finally: + _sys.argv[:] = saved + + +class PyTest(orig.test): + user_options = [ + ('extras', None, "Install (all) setuptools extras when running tests"), + ('index-url=', None, "Specify an index url from which to retrieve " + "dependencies"), + ('allow-hosts=', None, "Whitelist of comma-separated hosts to allow " + "when retrieving dependencies"), + ('addopts=', None, "Additional options to be passed verbatim to the " + "pytest runner") + ] + + def initialize_options(self): + self.extras = False + self.index_url = None + self.allow_hosts = None + self.addopts = [] + + def finalize_options(self): + if self.addopts: + self.addopts = _shlex.split(self.addopts) + + def run(self): + """ + Override run to ensure requirements are available in this session (but + don't install them anywhere). + """ + self._build_egg_fetcher() + if self.distribution.install_requires: + self.distribution.fetch_build_eggs(self.distribution.install_requires) + if self.distribution.tests_require: + self.distribution.fetch_build_eggs(self.distribution.tests_require) + if self.distribution.extras_require and self.extras: + list(map(self.distribution.fetch_build_eggs, + self.distribution.extras_require.values())) + if self.dry_run: + self.announce('skipping tests (dry run)') + return + self.with_project_on_sys_path(self.run_tests) + if self.result_code: + raise SystemExit(self.result_code) + return self.result_code + + def _build_egg_fetcher(self): + """Build an egg fetcher that respects index_url and allow_hosts""" + # modified from setuptools.dist:Distribution.fetch_build_egg + from setuptools.command.easy_install import easy_install + main_dist = self.distribution + # construct a fake distribution to store the args for easy_install + dist = main_dist.__class__({'script_args': ['easy_install']}) + dist.parse_config_files() + opts = dist.get_option_dict('easy_install') + keep = ( + 'find_links', 'site_dirs', 'index_url', 'optimize', + 'site_dirs', 'allow_hosts' + ) + for key in opts.keys(): + if key not in keep: + del opts[key] # don't use any other settings + if main_dist.dependency_links: + links = main_dist.dependency_links[:] + if 'find_links' in opts: + links = opts['find_links'][1].split() + links + opts['find_links'] = ('setup', links) + if self.allow_hosts: + opts['allow_hosts'] = ('test', self.allow_hosts) + if self.index_url: + opts['index_url'] = ('test', self.index_url) + install_dir_func = getattr(dist, 'get_egg_cache_dir', _os.getcwd) + install_dir = install_dir_func() + cmd = easy_install( + dist, args=["x"], install_dir=install_dir, exclude_scripts=True, + always_copy=False, build_directory=None, editable=False, + upgrade=False, multi_version=True, no_report = True + ) + cmd.ensure_finalized() + main_dist._egg_fetcher = cmd + + def run_tests(self): + """ + Invoke pytest, replacing argv. + """ + with _save_argv(_sys.argv[:1] + self.addopts): + self.result_code = __import__('pytest').main() @@ -20,11 +20,17 @@ setup_params = dict( use_scm_version=True,
author="Jason R. Coombs",
author_email="jaraco@jaraco.com",
- description="pytest-runner",
+ description="Invoke py.test as distutils command with dependency "
+ "resolution.",
long_description=long_description,
url="https://bitbucket.org/jaraco/pytest-runner",
- packages=setuptools.find_packages(),
- namespace_packages=[''],
+ py_modules=['ptr'],
+ entry_points = {
+ 'distutils.commands': [
+ 'ptr = ptr:PyTest',
+ 'pytest = ptr:PyTest',
+ ],
+ },
install_requires=[
],
setup_requires=[
@@ -38,6 +44,7 @@ setup_params = dict( "Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
+ "Programming Language :: Python :: 2.6",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
],
|