diff options
-rw-r--r-- | .github/FUNDING.yml | 1 | ||||
-rw-r--r-- | CHANGES.rst | 230 | ||||
-rw-r--r-- | README.rst | 143 | ||||
-rw-r--r-- | docs/_templates/tidelift-sidebar.html | 6 | ||||
-rw-r--r-- | docs/conf.py | 9 | ||||
-rw-r--r-- | docs/index.rst | 7 | ||||
-rw-r--r-- | ptr.py | 222 | ||||
-rw-r--r-- | setup.cfg | 13 | ||||
-rw-r--r-- | setup.py | 17 | ||||
-rw-r--r-- | tests/test_ptr.py | 173 |
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. @@ -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 ================== @@ -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) @@ -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 @@ -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) |