summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/FUNDING.yml1
-rw-r--r--CHANGES.rst230
-rw-r--r--README.rst143
-rw-r--r--docs/_templates/tidelift-sidebar.html6
-rw-r--r--docs/conf.py9
-rw-r--r--docs/index.rst7
-rw-r--r--ptr.py222
-rw-r--r--setup.cfg13
-rw-r--r--setup.py17
-rw-r--r--tests/test_ptr.py173
10 files changed, 806 insertions, 15 deletions
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 0000000..3b4273a
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1 @@
+tidelift: pypi/pytest-runner
diff --git a/CHANGES.rst b/CHANGES.rst
index e69de29..2d225f7 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -0,0 +1,230 @@
+v5.3.1
+======
+
+* Refreshed package metadata.
+
+v5.3.0
+======
+
+* Require Python 3.6 or later.
+* Refreshed package metadata.
+
+5.2
+===
+
+* #50: This project is deprecated.
+
+5.1
+===
+
+* #49: Surgically restore support for older setuptools versions.
+
+5.0
+===
+
+* #42: Prefer pyproject.toml
+* Refresh package metadata.
+* This release now intentionally introduces the changes
+ unintionally brought about in 4.5 and 4.3, where the
+ adoption of declarative config adds a new requirement
+ on setuptools 30.4 or later. On systems running older
+ setuptools, installation of pytest-runner via
+ ``easy_install`` (or ``setup_requires``), will result
+ in a ``DistributionNotFound`` exception.
+
+ All projects should pin to ``pytest-runner < 5``
+ or upgrade the environment to ``setuptools >= 30.4``
+ (prior to invoking setup.py).
+
+4.5.1
+=====
+
+* #48: Revert changes from 4.5 - restoring project to the
+ state at 4.4.
+
+4.5
+===
+
+(Pulled from PyPI due to #43 and #48)
+
+* Packaging (skeleton) refresh, including adoption of
+ `black <https://pypi.org/project/black>`_ for style.
+
+4.4
+===
+
+* #43: Detect condition where declarative config will cause
+ errors and emit a UserWarning with guidance on necessary
+ actions.
+
+4.3.1
+=====
+
+* #43: Re-release of 4.2 to supersede the 4.3 release which
+ proved to be backward-incompatible in that it requires
+ setuptools 30.4 or possibly later (to install). In the future, a
+ backward-incompatible release will re-release these changes.
+ For projects including pytest-runner, particularly as
+ ``setup_requires``, if support for older setuptools is required,
+ please pin to ``pytest-runner < 5``.
+
+4.3
+===
+
+(Pulled from PyPI due to #43)
+
+* #42: Update project metadata, including pyproject.toml declaration.
+
+4.2
+===
+
+* #40: Remove declared dependency and instead assert it at
+ run time.
+
+4.1
+===
+
+* #40: Declare dependency on Setuptools in package metadata.
+
+4.0
+===
+
+* Drop support for Setuptools before Setuptools 27.3.0.
+
+3.0.1
+=====
+
+* #38: Fixed AttributeError when running with ``--dry-run``.
+ ``PyTest.run()`` no longer stores nor returns the result code.
+ Based on the commit message for `840ff4c <
+ https://github.com/pytest-dev/pytest-runner/commit/840ff4c2bf6c752d9770f0dd8d64a841060cf9bc>`_,
+ nothing has ever relied on that value.
+
+3.0
+===
+
+* Dropped support for Python 2.6 and 3.1.
+
+2.12.2
+======
+
+* #33: Packaging refresh.
+
+2.12.1
+======
+
+* #32: Fix support for ``dependency_links``.
+
+2.12
+====
+
+* #30: Rework support for ``--allow-hosts`` and
+ ``--index-url``, removing dependence on
+ ``setuptools.Distribution``'s private member.
+ Additionally corrects logic in marker evaluation
+ along with unit tests!
+
+2.11.1
+======
+
+* #28: Fix logic in marker evaluation.
+
+2.11
+====
+
+* #27: Improved wording in the README around configuration
+ for the distutils command and pytest proper.
+
+2.10.1
+======
+
+* #21: Avoid mutating dictionary keys during iteration.
+
+2.10
+====
+
+* #20: Leverage technique in `setuptools 794
+ <https://github.com/pypa/setuptools/issues/794>`_
+ to populate PYTHONPATH during test runs such that
+ Python subprocesses will have a dependency context
+ comparable to the test runner.
+
+2.9
+===
+
+* Added Trove Classifier indicating this package is part
+ of the pytest framework.
+
+2.8
+===
+
+* #16: Added a license file, required for membership to
+ pytest-dev.
+* Releases are now made automatically by pushing a
+ tagged release that passes tests on Python 3.5.
+
+2.7
+===
+
+* Moved hosting to Github.
+
+2.6
+===
+
+* Add support for un-named, environment-specific extras.
+
+2.5.1
+=====
+
+* Restore Python 2.6 compatibility.
+
+2.5
+===
+
+* Moved hosting to `pytest-dev
+ <https://bitbucket.org/pytest-dev/pytest-runner>`_.
+
+2.4
+===
+
+* Added `documentation <https://pythonhosted.org/pytest-runner>`_.
+* Use setuptools_scm for version management and file discovery.
+* Updated internal packaging technique. README is now included
+ in the package metadata.
+
+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.rst b/README.rst
index a3e1b74..b964e5a 100644
--- a/README.rst
+++ b/README.rst
@@ -1,13 +1,13 @@
-.. image:: https://img.shields.io/pypi/v/skeleton.svg
+.. image:: https://img.shields.io/pypi/v/pytest-runner.svg
:target: `PyPI link`_
-.. image:: https://img.shields.io/pypi/pyversions/skeleton.svg
+.. image:: https://img.shields.io/pypi/pyversions/pytest-runner.svg
:target: `PyPI link`_
-.. _PyPI link: https://pypi.org/project/skeleton
+.. _PyPI link: https://pypi.org/project/pytest-runner
-.. image:: https://github.com/jaraco/skeleton/workflows/tests/badge.svg
- :target: https://github.com/jaraco/skeleton/actions?query=workflow%3A%22tests%22
+.. image:: https://github.com/pytest-dev/pytest-runner/workflows/tests/badge.svg
+ :target: https://github.com/pytest-dev/pytest-runner/actions?query=workflow%3A%22tests%22
:alt: tests
.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
@@ -19,3 +19,136 @@
.. image:: https://img.shields.io/badge/skeleton-2021-informational
:target: https://blog.jaraco.com/skeleton
+
+.. image:: https://tidelift.com/badges/package/pypi/pytest-runner
+ :target: https://tidelift.com/subscription/pkg/pypi-pytest-runner?utm_source=pypi-pytest-runner&utm_medium=readme
+
+Setup scripts can use pytest-runner to add setup.py test support for pytest
+runner.
+
+Deprecation Notice
+==================
+
+pytest-runner depends on deprecated features of setuptools and relies on features that break security
+mechanisms in pip. For example 'setup_requires' and 'tests_require' bypass ``pip --require-hashes``.
+See also `pypa/setuptools#1684 <https://github.com/pypa/setuptools/issues/1684>`_.
+
+It is recommended that you:
+
+- Remove ``'pytest-runner'`` from your ``setup_requires``, preferably removing the ``setup_requires`` option.
+- Remove ``'pytest'`` and any other testing requirements from ``tests_require``, preferably removing the ``tests_requires`` option.
+- Select a tool to bootstrap and then run tests such as tox.
+
+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 (unnecessary if specified for easy_install in setup.cfg).
+- Pass additional py.test command-line options using ``--addopts``.
+- Set permanent options for the ``python setup.py pytest`` command (like ``index-url``)
+ in the ``[pytest]`` section of ``setup.cfg``.
+- Set permanent options for the ``py.test`` run (like ``addopts`` or ``pep8ignore``) in the ``[pytest]``
+ section of ``pytest.ini`` or ``tox.ini`` or put them in the ``[tool:pytest]``
+ section of ``setup.cfg``. See `pytest issue 567
+ <https://github.com/pytest-dev/pytest/issues/567>`_.
+- Optionally, set ``test=pytest`` in the ``[aliases]`` section of ``setup.cfg``
+ to cause ``python 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.
+
+Follow `this search on github
+<https://github.com/search?utf8=%E2%9C%93&q=filename%3Asetup.py+pytest-runner&type=Code&ref=searchresults>`_
+for examples of real-world usage.
+
+Standalone Example
+==================
+
+This technique is deprecated - if you have standalone scripts
+you wish to invoke with dependencies, `use pip-run
+<https://pypi.org/project/pip-run>`_.
+
+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>`_
+or `this MongoDB test
+<https://gist.github.com/jaraco/0b9e482f5c0a1300dc9a>`_
+demonstrating a technique that works even when dependencies
+are required in the test.
+
+Either example 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,
+ )
+
+For Enterprise
+==============
+
+Available as part of the Tidelift Subscription.
+
+This project and the maintainers of thousands of other packages are working with Tidelift to deliver one enterprise subscription that covers all of the open source you use.
+
+`Learn more <https://tidelift.com/subscription/pkg/pypi-PROJECT?utm_source=pypi-PROJECT&utm_medium=referral&utm_campaign=github>`_.
+
+Security Contact
+================
+
+To report a security vulnerability, please use the
+`Tidelift security contact <https://tidelift.com/security>`_.
+Tidelift will coordinate the fix and disclosure.
diff --git a/docs/_templates/tidelift-sidebar.html b/docs/_templates/tidelift-sidebar.html
new file mode 100644
index 0000000..ce48f46
--- /dev/null
+++ b/docs/_templates/tidelift-sidebar.html
@@ -0,0 +1,6 @@
+<h3 class="donation">For Enterprise</h3>
+
+<p>
+Professionally-supported {{ project }} is available with the
+<a href="https://tidelift.com/subscription/pkg/pypi-{{ project }}?utm_source=pypi-{{ project }}&utm_medium=referral">Tidelift Subscription</a>.
+</p>
diff --git a/docs/conf.py b/docs/conf.py
index f65d1fa..2ac23dd 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -21,9 +21,18 @@ link_files = {
pattern=r'PEP[- ](?P<pep_number>\d+)',
url='https://www.python.org/dev/peps/pep-{pep_number:0>4}/',
),
+ dict(
+ pattern=r'Setuptools #(?P<setuptools_issue>\d+)',
+ url='https://github.com/pypa/setuptools' '/issues/{setuptools_issue}/',
+ ),
],
)
}
# Be strict about any broken references:
nitpicky = True
+
+# Custom sidebar templates, maps document names to template names.
+html_theme = 'alabaster'
+templates_path = ['_templates']
+html_sidebars = {'index': ['tidelift-sidebar.html']}
diff --git a/docs/index.rst b/docs/index.rst
index 325842b..c43329a 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -6,12 +6,7 @@ Welcome to |project| documentation!
history
-
-.. automodule:: skeleton
- :members:
- :undoc-members:
- :show-inheritance:
-
+.. include:: ../README.rst
Indices and tables
==================
diff --git a/ptr.py b/ptr.py
new file mode 100644
index 0000000..cf45e68
--- /dev/null
+++ b/ptr.py
@@ -0,0 +1,222 @@
+"""
+Implementation
+"""
+
+import os as _os
+import shlex as _shlex
+import contextlib as _contextlib
+import sys as _sys
+import operator as _operator
+import itertools as _itertools
+import warnings as _warnings
+
+try:
+ # ensure that map has the same meaning on Python 2
+ from future_builtins import map
+except ImportError:
+ pass
+
+import pkg_resources
+import setuptools.command.test as orig
+from setuptools import Distribution
+
+
+@_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 CustomizedDist(Distribution):
+
+ allow_hosts = None
+ index_url = None
+
+ def fetch_build_egg(self, req):
+ """Specialized version of Distribution.fetch_build_egg
+ that respects respects allow_hosts and index_url."""
+ from setuptools.command.easy_install import easy_install
+
+ dist = Distribution({'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 list(opts):
+ if key not in keep:
+ del opts[key] # don't use any other settings
+ if self.dependency_links:
+ links = self.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(self, '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,
+ user=False,
+ )
+ cmd.ensure_finalized()
+ return cmd.easy_install(req)
+
+
+class PyTest(orig.test):
+ """
+ >>> import setuptools
+ >>> dist = setuptools.Distribution()
+ >>> cmd = PyTest(dist)
+ """
+
+ 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 = []
+ self.ensure_setuptools_version()
+
+ @staticmethod
+ def ensure_setuptools_version():
+ """
+ Due to the fact that pytest-runner is often required (via
+ setup-requires directive) by toolchains that never invoke
+ it (i.e. they're only installing the package, not testing it),
+ instead of declaring the dependency in the package
+ metadata, assert the requirement at run time.
+ """
+ pkg_resources.require('setuptools>=27.3')
+
+ def finalize_options(self):
+ if self.addopts:
+ self.addopts = _shlex.split(self.addopts)
+
+ @staticmethod
+ def marker_passes(marker):
+ """
+ Given an environment marker, return True if the marker is valid
+ and matches this environment.
+ """
+ return (
+ not marker
+ or not pkg_resources.invalid_marker(marker)
+ and pkg_resources.evaluate_marker(marker)
+ )
+
+ def install_dists(self, dist):
+ """
+ Extend install_dists to include extras support
+ """
+ return _itertools.chain(
+ orig.test.install_dists(dist), self.install_extra_dists(dist)
+ )
+
+ def install_extra_dists(self, dist):
+ """
+ Install extras that are indicated by markers or
+ install all extras if '--extras' is indicated.
+ """
+ extras_require = dist.extras_require or {}
+
+ spec_extras = (
+ (spec.partition(':'), reqs) for spec, reqs in extras_require.items()
+ )
+ matching_extras = (
+ reqs
+ for (name, sep, marker), reqs in spec_extras
+ # include unnamed extras or all if self.extras indicated
+ if (not name or self.extras)
+ # never include extras that fail to pass marker eval
+ and self.marker_passes(marker)
+ )
+ results = list(map(dist.fetch_build_eggs, matching_extras))
+ return _itertools.chain.from_iterable(results)
+
+ @staticmethod
+ def _warn_old_setuptools():
+ msg = (
+ "pytest-runner will stop working on this version of setuptools; "
+ "please upgrade to setuptools 30.4 or later or pin to "
+ "pytest-runner < 5."
+ )
+ ver_str = pkg_resources.get_distribution('setuptools').version
+ ver = pkg_resources.parse_version(ver_str)
+ if ver < pkg_resources.parse_version('30.4'):
+ _warnings.warn(msg)
+
+ def run(self):
+ """
+ Override run to ensure requirements are available in this session (but
+ don't install them anywhere).
+ """
+ self._warn_old_setuptools()
+ dist = CustomizedDist()
+ for attr in 'allow_hosts index_url'.split():
+ setattr(dist, attr, getattr(self, attr))
+ for attr in (
+ 'dependency_links install_requires ' 'tests_require extras_require '
+ ).split():
+ setattr(dist, attr, getattr(self.distribution, attr))
+ installed_dists = self.install_dists(dist)
+ if self.dry_run:
+ self.announce('skipping tests (dry run)')
+ return
+ paths = map(_operator.attrgetter('location'), installed_dists)
+ with self.paths_on_pythonpath(paths):
+ with self.project_on_sys_path():
+ return self.run_tests()
+
+ @property
+ def _argv(self):
+ return ['pytest'] + self.addopts
+
+ def run_tests(self):
+ """
+ Invoke pytest, replacing argv. Return result code.
+ """
+ with _save_argv(_sys.argv[:1] + self.addopts):
+ result_code = __import__('pytest').main()
+ if result_code:
+ raise SystemExit(result_code)
diff --git a/setup.cfg b/setup.cfg
index 69eb0ee..14bff40 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,22 +1,25 @@
[metadata]
-name = skeleton
+name = pytest-runner
author = Jason R. Coombs
author_email = jaraco@jaraco.com
-description = skeleton
+description = Invoke py.test as distutils command with dependency resolution
long_description = file:README.rst
-url = https://github.com/jaraco/skeleton
+url = https://github.com/pytest-dev/pytest-runner/
classifiers =
Development Status :: 5 - Production/Stable
Intended Audience :: Developers
License :: OSI Approved :: MIT License
Programming Language :: Python :: 3
Programming Language :: Python :: 3 :: Only
+ Framework :: Pytest
[options]
packages = find_namespace:
+py_modules = ptr
include_package_data = true
python_requires = >=3.6
install_requires =
+ # setuptools 27.3 is required at run time
[options.packages.find]
exclude =
@@ -41,6 +44,7 @@ testing =
pytest-enabler >= 1.0.1
# local
+ pytest-virtualenv
docs =
# upstream
@@ -51,3 +55,6 @@ docs =
# local
[options.entry_points]
+distutils.commands =
+ ptr = ptr:PyTest
+ pytest = ptr:PyTest
diff --git a/setup.py b/setup.py
index bac24a4..e58e686 100644
--- a/setup.py
+++ b/setup.py
@@ -2,5 +2,20 @@
import setuptools
+
+compat = dict(
+ name='pytest-runner',
+ py_modules=['ptr'],
+ setup_requires=['setuptools_scm >= 1.15.0'],
+ entry_points={'distutils.commands': ['ptr = ptr:PyTest', 'pytest = ptr:PyTest']},
+)
+"""
+Because pytest-runner is frequently installed by
+setup_requires and thus easy_install, and because
+many systems still run with setuptools prior to
+30.4 in which support for declarative config was
+added, supply the basic metadata here. Ref #49.
+"""
+
if __name__ == "__main__":
- setuptools.setup()
+ setuptools.setup(**compat)
diff --git a/tests/test_ptr.py b/tests/test_ptr.py
new file mode 100644
index 0000000..f6eac16
--- /dev/null
+++ b/tests/test_ptr.py
@@ -0,0 +1,173 @@
+from __future__ import unicode_literals
+
+import io
+import os
+import shutil
+import sys
+import tarfile
+import textwrap
+import time
+import itertools
+
+import pytest
+
+
+def DALS(s):
+ "dedent and left-strip"
+ return textwrap.dedent(s).lstrip()
+
+
+def make_sdist(dist_path, files):
+ """
+ Create a simple sdist tarball at dist_path, containing the files
+ listed in ``files`` as ``(filename, content)`` tuples.
+ """
+
+ with tarfile.open(dist_path, 'w:gz') as dist:
+ for filename, content in files:
+ file_bytes = io.BytesIO(content.encode('utf-8'))
+ file_info = tarfile.TarInfo(name=filename)
+ file_info.size = len(file_bytes.getvalue())
+ file_info.mtime = int(time.time())
+ dist.addfile(file_info, fileobj=file_bytes)
+
+
+@pytest.fixture
+def venv(virtualenv):
+ yield virtualenv
+ # Workaround virtualenv not cleaning itself as it should...
+ virtualenv.delete = True
+ virtualenv.teardown()
+
+
+setuptools_reqs = (
+ ['setuptools', 'setuptools==27.3.0', 'setuptools==32.3.1', 'setuptools==36.3.0']
+ if sys.version_info < (3, 7)
+ else ['setuptools', 'setuptools==38.4.1']
+)
+args_variants = ['', '--extras']
+
+
+@pytest.mark.xfail('platform.system() == "Windows"')
+@pytest.mark.parametrize(
+ 'setuptools_req, test_args', itertools.product(setuptools_reqs, args_variants)
+)
+def test_egg_fetcher(venv, setuptools_req, test_args):
+ test_args = test_args.split()
+ # Install pytest & pytest-runner.
+ venv.run('python setup.py develop', cwd=os.getcwd())
+ venv.run('pip install pytest')
+ # Install setuptools version.
+ venv.run('pip install -U'.split() + [setuptools_req])
+ # For debugging purposes.
+ venv.run('pip freeze --all')
+ # Prepare fake index.
+ index_dir = (venv.workspace / 'index').mkdir()
+ for n in range(5):
+ dist_name = 'barbazquux' + str(n + 1)
+ dist_version = '0.1'
+ dist_sdist = '%s-%s.tar.gz' % (dist_name, dist_version)
+ dist_dir = (index_dir / dist_name).mkdir()
+ make_sdist(
+ dist_dir / dist_sdist,
+ (
+ (
+ 'setup.py',
+ textwrap.dedent(
+ '''
+ from setuptools import setup
+ setup(
+ name={dist_name!r},
+ version={dist_version!r},
+ py_modules=[{dist_name!r}],
+ )
+ '''
+ ).format(dist_name=dist_name, dist_version=dist_version),
+ ),
+ (dist_name + '.py', ''),
+ ),
+ )
+ with (dist_dir / 'index.html').open('w') as fp:
+ fp.write(
+ DALS(
+ '''
+ <!DOCTYPE html><html><body>
+ <a href="{dist_sdist}" rel="internal">{dist_sdist}</a><br/>
+ </body></html>
+ '''
+ ).format(dist_sdist=dist_sdist)
+ )
+ # Move barbazquux1 out of the index.
+ shutil.move(index_dir / 'barbazquux1', venv.workspace)
+ barbazquux1_link = (
+ 'file://'
+ + str(venv.workspace.abspath())
+ + '/barbazquux1/barbazquux1-0.1.tar.gz'
+ + '#egg=barbazquux1-0.1'
+ )
+ # Prepare fake project.
+ project_dir = (venv.workspace / 'project-0.1').mkdir()
+ with open(project_dir / 'setup.py', 'w') as fp:
+ fp.write(
+ DALS(
+ '''
+ from setuptools import setup
+ setup(
+ name='project',
+ version='0.1',
+ dependency_links = [
+ {barbazquux1_link!r},
+ ],
+ setup_requires=[
+ 'pytest-runner',
+ ],
+ install_requires=[
+ 'barbazquux1',
+ ],
+ tests_require=[
+ 'pytest',
+ 'barbazquux2',
+ ],
+ extras_require={{
+ ':"{sys_platform}" in sys_platform': 'barbazquux3',
+ ':"barbazquux" in sys_platform': 'barbazquux4',
+ 'extra': 'barbazquux5',
+ }}
+ )
+ '''
+ ).format(sys_platform=sys.platform, barbazquux1_link=barbazquux1_link)
+ )
+ with open(project_dir / 'setup.cfg', 'w') as fp:
+ fp.write(
+ DALS(
+ '''
+ [easy_install]
+ index_url = .
+ '''
+ )
+ )
+ with open(project_dir / 'test_stuff.py', 'w') as fp:
+ fp.write(
+ DALS(
+ '''
+ import pytest
+
+ def test_stuff():
+ import barbazquux1
+ import barbazquux2
+ import barbazquux3
+ with pytest.raises(ImportError):
+ import barbazquux4
+ if {importable_barbazquux5}:
+ import barbazquux5
+ else:
+ with pytest.raises(ImportError):
+ import barbazquux5
+ '''
+ ).format(importable_barbazquux5=('--extras' in test_args))
+ )
+ # Run fake project tests.
+ cmd = 'python setup.py pytest'.split()
+ cmd += ['--index-url=' + index_dir.abspath()]
+ cmd += test_args
+ venv.run(cmd, cwd=project_dir)