summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml4
-rw-r--r--CHANGES.rst113
-rw-r--r--LICENSE21
-rw-r--r--README.rst113
-rw-r--r--docs/conf.py4
-rw-r--r--docs/index.rst11
-rw-r--r--ptr.py205
-rw-r--r--setup.py22
-rw-r--r--tests/test_ptr.py157
9 files changed, 629 insertions, 21 deletions
diff --git a/.travis.yml b/.travis.yml
index 91ba39a..03d4e22 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,6 +3,7 @@ language: python
python:
- 2.7
- 3.6
+- nightly
install:
- pip install tox "setuptools>=28.2"
script:
@@ -18,6 +19,7 @@ deploy:
all_branches: true
python: 3.6
user: jaraco
- # supply password with `travis encrypt --add deploy.password`
distributions: dists
skip_upload_docs: true
+ password:
+ secure: lZfYQx0ZrCf2FJ+348etKWfzTySB3BZYGd0ce5RFHN2BppcdkONyJfTs4rgdrFEn/WtOaKV3SkJYR09xvlr+4kbLibg7fXhueqZt0ZkhRBnoDE4SxCjICyFCmisG6O3zkrVosizch70/0MqseNanhgXOPhd5llCfQHIqLsa145BG4hM5kxAHPO3Rz2/HCObOTPe4HKj93RAK7lPIMZVN6omcWoG6ZB0QqK+i3LTUtmJ3gE6q/iHk3VF9cJs8xtn3hdo++Lhrboa2NIqf6fl8oxR1C24Wh8vBQ69uTNjmVnDFYMulTs5475jjgDBXPPgVz3CAqYAy/PI+NPw59ebK8MzlaMRK/h/xSrdhxW6K3WbBL71Dn2UGuejXHFC3IuCI832xwkuEupOcGLWz4r2uBnhbgXF63vZ2gYPqrCGHxvDpbtllTVyEeebP8BnFzZttxSn9rbhTP1O9Dn/9tRko8WskyXIR+/2JOA9KP5uT47yeHoFBBUS0GB1XdkGhBrJfQx2LhfHuI6bpzosMiMfJrVNZEg8k0I9XA0uwxDgZrdqKGMKBR6233MXiJg8NyUPRInCRGth4rufcq6kn+wsT9RvuSt9XOhGiYLwChjvHexUXrLGBgupu0Y80Oqcp3YkDG3WHUX++Z2zJSQovrb4BMdKS6Y4usQ8Df5SklIpMCf0=
diff --git a/CHANGES.rst b/CHANGES.rst
index e69de29..21132ef 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -0,0 +1,113 @@
+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/LICENSE b/LICENSE
new file mode 100644
index 0000000..49c95bf
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2011-2016 Jason R. Coombs
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.rst b/README.rst
index 0e0e26e..d9c9288 100644
--- a/README.rst
+++ b/README.rst
@@ -1,12 +1,15 @@
-.. image:: https://img.shields.io/pypi/v/skeleton.svg
- :target: https://pypi.org/project/skeleton
+.. image:: https://img.shields.io/pypi/v/pytest-runner.svg
+ :target: https://pypi.org/project/pytest-runner
-.. image:: https://img.shields.io/pypi/pyversions/skeleton.svg
+.. image:: https://img.shields.io/pypi/pyversions/pytest-runner.svg
-.. image:: https://img.shields.io/pypi/dm/skeleton.svg
+.. image:: https://img.shields.io/pypi/dm/pytest-runner.svg
-.. image:: https://img.shields.io/travis/jaraco/skeleton/master.svg
- :target: http://travis-ci.org/jaraco/skeleton
+.. image:: https://img.shields.io/travis/pytest-dev/pytest-runner/master.svg
+ :target: http://travis-ci.org/pytest-dev/pytest-runner
+
+Setup scripts can use pytest-runner to add setup.py test support for pytest
+runner.
License
@@ -15,3 +18,101 @@ License
License is indicated in the project metadata (typically one or more
of the Trove classifiers). For more details, see `this explanation
<https://github.com/jaraco/skeleton/issues/1>`_.
+
+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 rwt
+<https://pypi.org/project/rwt>`_.
+
+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,
+ )
diff --git a/docs/conf.py b/docs/conf.py
index 8bc8298..5d50361 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -27,6 +27,10 @@ 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}/',
+ ),
],
),
}
diff --git a/docs/index.rst b/docs/index.rst
index d14131b..ae93273 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -1,17 +1,12 @@
-Welcome to skeleton documentation!
-========================================
+Welcome to pytest-runner documentation!
+=======================================
.. toctree::
:maxdepth: 1
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..ecba60c
--- /dev/null
+++ b/ptr.py
@@ -0,0 +1,205 @@
+"""
+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
+
+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
+
+
+@_contextlib.contextmanager
+def null():
+ yield
+
+
+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 = []
+
+ 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)
+ )
+
+ @staticmethod
+ def _install_dists_compat(dist):
+ """
+ Copy of install_dists from setuptools 27.3.0.
+ """
+ ir_d = dist.fetch_build_eggs(dist.install_requires or [])
+ tr_d = dist.fetch_build_eggs(dist.tests_require or [])
+ return _itertools.chain(ir_d, tr_d)
+
+ def install_dists(self, dist):
+ """
+ Extend install_dists to include extras support
+ """
+ i_d = getattr(orig.test, 'install_dists', self._install_dists_compat)
+ return _itertools.chain(i_d(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 paths_on_pythonpath(paths):
+ """
+ Backward compatibility for paths_on_pythonpath;
+ Returns a null context if paths_on_pythonpath is
+ not implemented in orig.test.
+ Note that this also means that the paths iterable
+ is never consumed, which incidentally means that
+ the None values from dist.fetch_build_eggs in
+ older Setuptools will be disregarded.
+ """
+ try:
+ return orig.test.paths_on_pythonpath(paths)
+ except AttributeError:
+ return null()
+
+ def _super_run(self):
+ dist = CustomizedDist()
+ for attr in 'allow_hosts index_url'.split():
+ setattr(dist, attr, getattr(self, attr))
+ for attr in '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):
+ self.with_project_on_sys_path(self.run_tests)
+
+ def run(self):
+ """
+ Override run to ensure requirements are available in this session (but
+ don't install them anywhere).
+ """
+ self._super_run()
+ if self.result_code:
+ raise SystemExit(self.result_code)
+ return self.result_code
+
+ @property
+ def _argv(self):
+ return ['pytest'] + self.addopts
+
+ 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 72d901c..1e95a0e 100644
--- a/setup.py
+++ b/setup.py
@@ -9,8 +9,8 @@ import setuptools
with io.open('README.rst', encoding='utf-8') as readme:
long_description = readme.read()
-name = 'skeleton'
-description = ''
+name = 'pytest-runner'
+description = 'Invoke py.test as distutils command with dependency resolution'
nspkg_technique = 'native'
"""
Does this package use "native" namespace packages or
@@ -24,20 +24,24 @@ params = dict(
author_email="jaraco@jaraco.com",
description=description or name,
long_description=long_description,
- url="https://github.com/jaraco/" + name,
- packages=setuptools.find_packages(),
- include_package_data=True,
+ url="https://github.com/pytest-dev/" + name,
namespace_packages=(
name.split('.')[:-1] if nspkg_technique == 'managed'
else []
),
- python_requires='>=2.7',
+ py_modules=['ptr'],
+ python_requires='>=2.6',
install_requires=[
],
extras_require={
'testing': [
'pytest>=2.8',
'pytest-sugar',
+ 'pytest-virtualenv',
+ ],
+ 'testing:python_version=="2.6"': [
+ # undeclared dependency of pytest-virtualenv
+ 'importlib',
],
'docs': [
'sphinx',
@@ -52,10 +56,16 @@ 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",
+ "Framework :: Pytest",
],
entry_points={
+ 'distutils.commands': [
+ 'ptr = ptr:PyTest',
+ 'pytest = ptr:PyTest',
+ ],
},
)
if __name__ == '__main__':
diff --git a/tests/test_ptr.py b/tests/test_ptr.py
new file mode 100644
index 0000000..aeb0f8d
--- /dev/null
+++ b/tests/test_ptr.py
@@ -0,0 +1,157 @@
+from __future__ import unicode_literals
+
+import contextlib
+import io
+import os
+import sys
+import tarfile
+import textwrap
+import time
+
+import pytest
+
+
+def DALS(s):
+ "dedent and left-strip"
+ return textwrap.dedent(s).lstrip()
+
+
+def _tarfile_open_ex(*args, **kwargs):
+ """
+ Extend result as a context manager.
+ """
+ return contextlib.closing(tarfile.open(*args, **kwargs))
+
+
+if sys.version_info[:2] < (2, 7) or (3, 0) <= sys.version_info[:2] < (3, 2):
+ tarfile_open = _tarfile_open_ex
+else:
+ tarfile_open = tarfile.open
+
+
+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()
+
+
+@pytest.mark.parametrize('setuptools_req, test_args', (
+ ('setuptools==27.2.0', ''),
+ ('setuptools==27.2.0', '--extras'),
+ ('setuptools==27.3.0', ''),
+ ('setuptools==27.3.0', '--extras'),
+ ('setuptools==32.3.1', ''),
+ ('setuptools==32.3.1', '--extras'),
+ ('setuptools==36.3.0', ''),
+ ('setuptools==36.3.0', '--extras'),
+ ('setuptools' , ''),
+ ('setuptools' , '--extras'),
+))
+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))
+ # 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',
+ 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))
+ 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)