summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.hgtags15
-rw-r--r--CHANGES.txt36
-rw-r--r--README.txt95
-rw-r--r--ptr.py105
-rw-r--r--setup.py13
5 files changed, 260 insertions, 4 deletions
diff --git a/.hgtags b/.hgtags
new file mode 100644
index 0000000..0e840ac
--- /dev/null
+++ b/.hgtags
@@ -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.
diff --git a/README.txt b/README.txt
index d09d934..86da93a 100644
--- a/README.txt
+++ b/README.txt
@@ -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,
+ )
diff --git a/ptr.py b/ptr.py
new file mode 100644
index 0000000..5ff2995
--- /dev/null
+++ b/ptr.py
@@ -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()
diff --git a/setup.py b/setup.py
index ac40a89..81e7211 100644
--- a/setup.py
+++ b/setup.py
@@ -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",
],