Merge remote-tracking branch 'upstream/master' into merge-master-into-features

This commit is contained in:
Bruno Oliveira 2018-09-22 18:14:36 -03:00
commit 56d0b5a7e2
40 changed files with 816 additions and 138 deletions

View File

@ -1,4 +1,9 @@
[run]
source = _pytest,testing
source = pytest,_pytest,testing/
parallel = 1
branch = 1
[paths]
source = src/
.tox/*/lib/python*/site-packages/
.tox\*\Lib\site-packages\

1
.gitignore vendored
View File

@ -24,6 +24,7 @@ src/_pytest/_version.py
.eggs/
doc/*/_build
doc/*/.doctrees
build/
dist/
*.egg-info

View File

@ -38,7 +38,8 @@ repos:
language: python
additional_dependencies: [pygments, restructuredtext_lint]
- id: changelogs-rst
name: changelog files must end in .rst
entry: ./scripts/fail
language: script
files: 'changelog/.*(?<!\.rst)$'
name: changelog filenames
language: fail
entry: 'changelog files must be named ####.(feature|bugfix|doc|removal|vendor|trivial).rst'
exclude: changelog/(\d+\.(feature|bugfix|doc|removal|vendor|trivial).rst|README.rst|_template.rst)
files: ^changelog/

View File

@ -18,12 +18,12 @@ env:
- TOXENV=py27-xdist
- TOXENV=py27-trial
- TOXENV=py27-numpy
- TOXENV=py27-pluggymaster
- TOXENV=py27-pluggymaster PYTEST_NO_COVERAGE=1
- TOXENV=py36-pexpect
- TOXENV=py36-xdist
- TOXENV=py36-trial
- TOXENV=py36-numpy
- TOXENV=py36-pluggymaster
- TOXENV=py36-pluggymaster PYTEST_NO_COVERAGE=1
- TOXENV=py27-nobyte
- TOXENV=doctesting
- TOXENV=docs PYTEST_NO_COVERAGE=1
@ -84,7 +84,7 @@ before_script:
if [[ "$PYTEST_NO_COVERAGE" != 1 ]]; then
export COVERAGE_FILE="$PWD/.coverage"
export COVERAGE_PROCESS_START="$PWD/.coveragerc"
export _PYTEST_TOX_COVERAGE_RUN="coverage run --source {envsitepackagesdir}/_pytest/,{toxinidir}/testing -m"
export _PYTEST_TOX_COVERAGE_RUN="coverage run -m"
export _PYTEST_TOX_EXTRA_DEP=coverage-enable-subprocess
fi
@ -94,11 +94,11 @@ after_success:
- |
if [[ "$PYTEST_NO_COVERAGE" != 1 ]]; then
set -e
pip install codecov
pip install coverage
coverage combine
coverage xml --ignore-errors
coverage report -m --ignore-errors
codecov --required -X gcov pycov search -f coverage.xml --flags ${TOXENV//-/ } linux
bash <(curl -s https://codecov.io/bash) -Z -X gcov -X coveragepy -X search -X xcode -X gcovout -X fix -f coverage.xml -F "${TOXENV//-/,},linux"
# Coveralls does not support merged reports.
if [[ "$TOXENV" = py37 ]]; then

View File

@ -18,25 +18,60 @@ with advance notice in the **Deprecations** section of releases.
.. towncrier release notes start
pytest 3.8.1 (2018-09-22)
=========================
Bug Fixes
---------
- `#3286 <https://github.com/pytest-dev/pytest/issues/3286>`_: ``.pytest_cache`` directory is now automatically ignored by Git. Users who would like to contribute a solution for other SCMs please consult/comment on this issue.
- `#3749 <https://github.com/pytest-dev/pytest/issues/3749>`_: Fix the following error during collection of tests inside packages::
TypeError: object of type 'Package' has no len()
- `#3941 <https://github.com/pytest-dev/pytest/issues/3941>`_: Fix bug where indirect parametrization would consider the scope of all fixtures used by the test function to determine the parametrization scope, and not only the scope of the fixtures being parametrized.
- `#3973 <https://github.com/pytest-dev/pytest/issues/3973>`_: Fix crash of the assertion rewriter if a test changed the current working directory without restoring it afterwards.
- `#3998 <https://github.com/pytest-dev/pytest/issues/3998>`_: Fix issue that prevented some caplog properties (for example ``record_tuples``) from being available when entering the debugger with ``--pdb``.
- `#3999 <https://github.com/pytest-dev/pytest/issues/3999>`_: Fix ``UnicodeDecodeError`` in python2.x when a class returns a non-ascii binary ``__repr__`` in an assertion which also contains non-ascii text.
Improved Documentation
----------------------
- `#3996 <https://github.com/pytest-dev/pytest/issues/3996>`_: New `Deprecations and Removals <https://docs.pytest.org/en/latest/deprecations.html>`_ page shows all currently
deprecated features, the rationale to do so, and alternatives to update your code. It also list features removed
from pytest in past major releases to help those with ancient pytest versions to upgrade.
Trivial/Internal Changes
------------------------
- `#3955 <https://github.com/pytest-dev/pytest/issues/3955>`_: Improve pre-commit detection for changelog filenames
- `#3975 <https://github.com/pytest-dev/pytest/issues/3975>`_: Remove legacy code around im_func as that was python2 only
pytest 3.8.0 (2018-09-05)
=========================
Deprecations and Removals
-------------------------
- `#2452 <https://github.com/pytest-dev/pytest/issues/2452>`_: ``Config.warn`` has been deprecated, it should be replaced by calls to the standard ``warnings.warn``.
``Node.warn`` now supports two signatures:
* ``node.warn(PytestWarning("some message"))``: is now the recommended way to call this function. The warning
instance must be a ``PytestWarning`` or subclass instance.
* ``node.warn("CI", "some message")``: this code/message form is now deprecated and should be converted to
the warning instance form above.
``RemovedInPytest4Warning`` and ``PytestExperimentalApiWarning`` are now part of the public API and should be accessed
using ``pytest.RemovedInPytest4Warning`` and ``pytest.PytestExperimentalApiWarning``.
- `#2452 <https://github.com/pytest-dev/pytest/issues/2452>`_: ``Config.warn`` and ``Node.warn`` have been
deprecated, see `<https://docs.pytest.org/en/latest/deprecations.html#config-warn-and-node-warn>`_ for rationale and
examples.
- `#3936 <https://github.com/pytest-dev/pytest/issues/3936>`_: ``@pytest.mark.filterwarnings`` second parameter is no longer regex-escaped,
making it possible to actually use regular expressions to check the warning message.
@ -63,7 +98,10 @@ Features
more info.
- `#3784 <https://github.com/pytest-dev/pytest/issues/3784>`_: Add option to disable plugin auto-loading.
- `#3251 <https://github.com/pytest-dev/pytest/issues/3251>`_: Warnings are now captured and displayed during test collection.
- `#3784 <https://github.com/pytest-dev/pytest/issues/3784>`_: ``PYTEST_DISABLE_PLUGIN_AUTOLOAD`` environment variable disables plugin auto-loading when set.
- `#3829 <https://github.com/pytest-dev/pytest/issues/3829>`_: Added the ``count`` option to ``console_output_style`` to enable displaying the progress as a count instead of a percentage.
@ -250,15 +288,10 @@ pytest 3.7.0 (2018-07-30)
Deprecations and Removals
-------------------------
- `#2639 <https://github.com/pytest-dev/pytest/issues/2639>`_: ``pytest_namespace`` has been deprecated.
See the documentation for ``pytest_namespace`` hook for suggestions on how to deal
with this in plugins which use this functionality.
- `#2639 <https://github.com/pytest-dev/pytest/issues/2639>`_: ``pytest_namespace`` has been `deprecated <https://docs.pytest.org/en/latest/deprecations.html#pytest-namespace>`_.
- `#3661 <https://github.com/pytest-dev/pytest/issues/3661>`_: Calling a fixture function directly, as opposed to request them in a test function, now issues a ``RemovedInPytest4Warning``. It will be changed into an error in pytest ``4.0``.
This is a great source of confusion to new users, which will often call the fixture functions and request them from test functions interchangeably, which breaks the fixture resolution model.
- `#3661 <https://github.com/pytest-dev/pytest/issues/3661>`_: Calling a fixture function directly, as opposed to request them in a test function, now issues a ``RemovedInPytest4Warning``. See `the documentation for rationale and examples <https://docs.pytest.org/en/latest/deprecations.html#calling-fixtures-directly>`_.
@ -620,7 +653,7 @@ Deprecations and Removals
<https://github.com/pytest-dev/pytest/issues/2770>`_)
- Defining ``pytest_plugins`` is now deprecated in non-top-level conftest.py
files, because they "leak" to the entire directory tree. (`#3084
files, because they "leak" to the entire directory tree. `See the docs <https://docs.pytest.org/en/latest/deprecations.html#pytest-plugins-in-non-top-level-conftest-files>`_ for the rationale behind this decision (`#3084
<https://github.com/pytest-dev/pytest/issues/3084>`_)

View File

@ -13,10 +13,12 @@ environment:
- TOXENV: "py27-trial"
- TOXENV: "py27-numpy"
- TOXENV: "py27-pluggymaster"
PYTEST_NO_COVERAGE: "1"
- TOXENV: "py36-xdist"
- TOXENV: "py36-trial"
- TOXENV: "py36-numpy"
- TOXENV: "py36-pluggymaster"
PYTEST_NO_COVERAGE: "1"
- TOXENV: "py27-nobyte"
- TOXENV: "doctesting"
- TOXENV: "py36-freeze"

View File

@ -1 +0,0 @@
Warnings are now captured and displayed during test collection.

View File

@ -6,6 +6,7 @@ Release announcements
:maxdepth: 2
release-3.8.1
release-3.8.0
release-3.7.4
release-3.7.3

View File

@ -0,0 +1,25 @@
pytest-3.8.1
=======================================
pytest 3.8.1 has just been released to PyPI.
This is a bug-fix release, being a drop-in replacement. To upgrade::
pip install --upgrade pytest
The full changelog is available at https://docs.pytest.org/en/latest/changelog.html.
Thanks to all who contributed to this release, among them:
* Ankit Goel
* Anthony Sottile
* Bruno Oliveira
* Daniel Hahler
* Maximilian Albert
* Ronny Pfannschmidt
* William Jamir Silva
* wim glenn
Happy testing,
The pytest Development Team

View File

@ -7,14 +7,16 @@ Keeping backwards compatibility has a very high priority in the pytest project.
With the pytest 3.0 release we introduced a clear communication scheme for when we will actually remove the old busted joint and politely ask you to use the new hotness instead, while giving you enough time to adjust your tests or raise concerns if there are valid reasons to keep deprecated functionality around.
To communicate changes we are already issuing deprecation warnings, but they are not displayed by default. In pytest 3.0 we changed the default setting so that pytest deprecation warnings are displayed if not explicitly silenced (with ``--disable-pytest-warnings``).
To communicate changes we issue deprecation warnings using a custom warning hierarchy (see :ref:`internal-warnings`). These warnings may be suppressed using the standard means: ``-W`` command-line flag or ``filterwarnings`` ini options (see :ref:`warnings`), but we suggest to use these sparingly and temporarily, and heed the warnings when possible.
We will only remove deprecated functionality in major releases (e.g. if we deprecate something in 3.0 we will remove it in 4.0), and keep it around for at least two minor releases (e.g. if we deprecate something in 3.9 and 4.0 is the next release, we will not remove it in 4.0 but in 5.0).
We will only start the removal of deprecated functionality in major releases (e.g. if we deprecate something in 3.0 we will start to remove it in 4.0), and keep it around for at least two minor releases (e.g. if we deprecate something in 3.9 and 4.0 is the next release, we start to remove it in 5.0, not in 4.0).
When the deprecation expires (e.g. 4.0 is released), we won't remove the deprecated functionality immediately, but will use the standard warning filters to turn them into **errors** by default. This approach makes it explicit that removal is imminent, and still gives you time to turn the deprecated feature into a warning instead of an error so it can be dealt with in your own time. In the next minor release (e.g. 4.1), the feature will be effectively removed.
Deprecation Roadmap
-------------------
We track deprecation and removal of features using milestones and the `deprecation <https://github.com/pytest-dev/pytest/issues?q=label%3A%22type%3A+deprecation%22>`_ and `removal <https://github.com/pytest-dev/pytest/labels/type%3A%20removal>`_ labels on GitHub.
Features currently deprecated and removed in previous releases can be found in :ref:`deprecations`.
Following our deprecation policy, after starting issuing deprecation warnings we keep features for *at least* two minor versions before considering removal.
We track future deprecation and removal of features using milestones and the `deprecation <https://github.com/pytest-dev/pytest/issues?q=label%3A%22type%3A+deprecation%22>`_ and `removal <https://github.com/pytest-dev/pytest/labels/type%3A%20removal>`_ labels on GitHub.

View File

@ -39,6 +39,7 @@ Full pytest documentation
bash-completion
backwards-compatibility
deprecations
historical-notes
license
contributing

View File

@ -32,7 +32,7 @@ Here's a summary what ``pytest`` uses ``rootdir`` for:
class name, function name and parametrization (if any).
* Is used by plugins as a stable location to store project/test run specific information;
for example, the internal :ref:`cache <cache>` plugin creates a ``.cache`` subdirectory
for example, the internal :ref:`cache <cache>` plugin creates a ``.pytest_cache`` subdirectory
in ``rootdir`` to store its cross-test run state.
Important to emphasize that ``rootdir`` is **NOT** used to modify ``sys.path``/``PYTHONPATH`` or

315
doc/en/deprecations.rst Normal file
View File

@ -0,0 +1,315 @@
.. _deprecations:
Deprecations and Removals
=========================
This page lists all pytest features that are currently deprecated or have been removed in past major releases.
The objective is to give users a clear rationale why a certain feature has been removed, and what alternatives
should be used instead.
Deprecated Features
-------------------
Below is a complete list of all pytest features which are considered deprecated. Using those features will issue
:class:`_pytest.warning_types.PytestWarning` or subclasses, which can be filtered using
:ref:`standard warning filters <warnings>`.
``Config.warn`` and ``Node.warn``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 3.8
Those methods were part of the internal pytest warnings system, but since ``3.8`` pytest is using the builtin warning
system for its own warnings, so those two functions are now deprecated.
``Config.warn`` should be replaced by calls to the standard ``warnings.warn``.
``Node.warn`` now supports two signatures:
* ``node.warn(PytestWarning("some message"))``: is now the recommended way to call this function.
The warning instance must be a PytestWarning or subclass.
* ``node.warn("CI", "some message")``: this code/message form is now deprecated and should be converted to the warning instance form above.
``pytest_namespace``
~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 3.7
This hook is deprecated because it greatly complicates the pytest internals regarding configuration and initialization, making some
bug fixes and refactorings impossible.
Example of usage:
.. code-block:: python
class MySymbol:
...
def pytest_namespace():
return {"my_symbol": MySymbol()}
Plugin authors relying on this hook should instead require that users now import the plugin modules directly (with an appropriate public API).
As a stopgap measure, plugin authors may still inject their names into pytest's namespace, usually during ``pytest_configure``:
.. code-block:: python
import pytest
def pytest_configure():
pytest.my_symbol = MySymbol()
Calling fixtures directly
~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 3.7
Calling a fixture function directly, as opposed to request them in a test function, is deprecated.
For example:
.. code-block:: python
@pytest.fixture
def cell():
return ...
@pytest.fixture
def full_cell():
cell = cell()
cell.make_full()
return cell
This is a great source of confusion to new users, which will often call the fixture functions and request them from test functions interchangeably, which breaks the fixture resolution model.
In those cases just request the function directly in the dependent fixture:
.. code-block:: python
@pytest.fixture
def cell():
return ...
@pytest.fixture
def full_cell(cell):
cell.make_full()
return cell
``Node.get_marker``
~~~~~~~~~~~~~~~~~~~
.. deprecated:: 3.6
As part of a large :ref:`marker-revamp`, :meth:`_pytest.nodes.Node.get_marker` is deprecated. See
:ref:`the documentation <update marker code>` on tips on how to update your code.
record_xml_property
~~~~~~~~~~~~~~~~~~~
.. deprecated:: 3.5
The ``record_xml_property`` fixture is now deprecated in favor of the more generic ``record_property``, which
can be used by other consumers (for example ``pytest-html``) to obtain custom information about the test run.
This is just a matter of renaming the fixture as the API is the same:
.. code-block:: python
def test_foo(record_xml_property):
...
Change to:
.. code-block:: python
def test_foo(record_property):
...
pytest_plugins in non-top-level conftest files
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 3.5
Defining ``pytest_plugins`` is now deprecated in non-top-level conftest.py
files because they will activate referenced plugins *globally*, which is surprising because for all other pytest
features ``conftest.py`` files are only *active* for tests at or below it.
Metafunc.addcall
~~~~~~~~~~~~~~~~
.. deprecated:: 3.3
:meth:`_pytest.python.Metafunc.addcall` was a precursor to the current parametrized mechanism. Users should use
:meth:`_pytest.python.Metafunc.parametrize` instead.
marks in ``pytest.mark.parametrize``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 3.2
Applying marks to values of a ``pytest.mark.parametrize`` call is now deprecated. For example:
.. code-block:: python
@pytest.mark.parametrize(
"a, b", [(3, 9), pytest.mark.xfail(reason="flaky")(6, 36), (10, 100)]
)
def test_foo(a, b):
...
This code applies the ``pytest.mark.xfail(reason="flaky")`` mark to the ``(6, 36)`` value of the above parametrization
call.
This was considered hard to read and understand, and also its implementation presented problems to the code preventing
further internal improvements in the marks architecture.
To update the code, use ``pytest.param``:
.. code-block:: python
@pytest.mark.parametrize(
"a, b",
[(3, 9), pytest.param((6, 36), marks=pytest.mark.xfail(reason="flaky")), (10, 100)],
)
def test_foo(a, b):
...
Passing command-line string to ``pytest.main()``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 3.0
Passing a command-line string to ``pytest.main()`` is deprecated:
.. code-block:: python
pytest.main("-v -s")
Pass a list instead:
.. code-block:: python
pytest.main(["-v", "-s"])
By passing a string, users expect that pytest will interpret that command-line using the shell rules they are working
on (for example ``bash`` or ``Powershell``), but this is very hard/impossible to do in a portable way.
``yield`` tests
~~~~~~~~~~~~~~~
.. deprecated:: 3.0
pytest supports ``yield``-style tests, where a test function actually ``yield`` functions and values
that are then turned into proper test methods. Example:
.. code-block:: python
def check(x, y):
assert x ** x == y
def test_squared():
yield check, 2, 4
yield check, 3, 9
This would result into two actual test functions being generated.
This form of test function doesn't support fixtures properly, and users should switch to ``pytest.mark.parametrize``:
.. code-block:: python
@pytest.mark.parametrize("x, y", [(2, 4), (3, 9)])
def test_squared():
assert x ** x == y
``pytest_funcarg__`` prefix
~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 3.0
In very early pytest versions fixtures could be defined using the ``pytest_funcarg__`` prefix:
.. code-block:: python
def pytest_funcarg__data():
return SomeData()
Switch over to the ``@pytest.fixture`` decorator:
.. code-block:: python
@pytest.fixture
def data():
return SomeData()
[pytest] section in setup.cfg files
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 3.0
``[pytest]`` sections in ``setup.cfg`` files should now be named ``[tool:pytest]``
to avoid conflicts with other distutils commands.
Result log (``--result-log``)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 3.0
The ``--resultlog`` command line option has been deprecated: it is little used
and there are more modern and better alternatives, for example `pytest-tap <https://tappy.readthedocs.io/en/latest/>`_.
Removed Features
----------------
As stated in our :ref:`backwards-compatibility` policy, deprecated features are removed only in major releases after
an appropriate period of deprecation has passed.
Reinterpretation mode (``--assert=reinterp``)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*Removed in version 3.0.*
Reinterpretation mode has now been removed and only plain and rewrite
mode are available, consequently the ``--assert=reinterp`` option is
no longer available. This also means files imported from plugins or
``conftest.py`` will not benefit from improved assertions by
default, you should use ``pytest.register_assert_rewrite()`` to
explicitly turn on assertion rewriting for those files.
Removed command-line options
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*Removed in version 3.0.*
The following deprecated commandline options were removed:
* ``--genscript``: no longer supported;
* ``--no-assert``: use ``--assert=plain`` instead;
* ``--nomagic``: use ``--assert=plain`` instead;
* ``--report``: use ``-r`` instead;
py.test-X* entry points
~~~~~~~~~~~~~~~~~~~~~~~
*Removed in version 3.0.*
Removed all ``py.test-X*`` entry points. The versioned, suffixed entry points
were never documented and a leftover from a pre-virtualenv era. These entry
points also created broken entry points in wheels, so removing them also
removes a source of confusion for users.

View File

@ -411,10 +411,11 @@ is to be run with different sets of arguments for its three arguments:
Running it results in some skips if we don't have all the python interpreters installed and otherwise runs all combinations (5 interpreters times 5 interpreters times 3 objects to serialize/deserialize)::
. $ pytest -rs -q multipython.py
...sss...sssssssss...sss... [100%]
...ssssssssssssssssssssssss [100%]
========================= short test summary info ==========================
SKIP [15] $REGENDOC_TMPDIR/CWD/multipython.py:29: 'python3.4' not found
12 passed, 15 skipped in 0.12 seconds
SKIP [12] $REGENDOC_TMPDIR/CWD/multipython.py:29: 'python3.4' not found
SKIP [12] $REGENDOC_TMPDIR/CWD/multipython.py:29: 'python3.5' not found
3 passed, 24 skipped in 0.12 seconds
Indirect parametrization of optional implementations/imports
--------------------------------------------------------------------

View File

@ -1,7 +1,7 @@
Installation and Getting Started
===================================
**Pythons**: Python 2.7, 3.4, 3.5, 3.6, Jython, PyPy-2.3
**Pythons**: Python 2.7, 3.4, 3.5, 3.6, 3.7, Jython, PyPy-2.3
**Platforms**: Unix/Posix and Windows

View File

@ -52,6 +52,8 @@ should add ``--strict`` to ``addopts``:
serial
.. _marker-revamp:
Marker revamp and iteration
---------------------------

View File

@ -1257,15 +1257,25 @@ passed multiple times. The expected format is ``name=value``. For example::
One or more Glob-style file patterns determining which python files
are considered as test modules. Search for multiple glob patterns by
adding a space between patterns::
adding a space between patterns:
.. code-block:: ini
[pytest]
python_files = test_*.py check_*.py example_*.py
By default, pytest will consider any file matching with ``test_*.py``
and ``*_test.py`` globs as a test module.
Or one per line:
.. code-block:: ini
[pytest]
python_files =
test_*.py
check_*.py
example_*.py
By default, files matching ``test_*.py`` and ``*_test.py`` will be considered
test modules.
.. confval:: python_functions

View File

@ -330,6 +330,9 @@ You can also use it as a contextmanager::
myobject.deprecated_method()
.. _internal-warnings:
Internal pytest warnings
------------------------
@ -363,9 +366,8 @@ defines an ``__init__`` constructor, as this prevents the class from being insta
These warnings might be filtered using the same builtin mechanisms used to filter other types of warnings.
Following our :ref:`backwards-compatibility`, deprecated features will be kept *at least* two minor releases. After that,
they will changed so they by default raise errors instead of just warnings, so users can adapt to it on their own time
if not having done so until now. In a later release the deprecated feature will be removed completely.
Please read our :ref:`backwards-compatibility` to learn how we proceed about deprecating and eventually removing
features.
The following warning types ares used by pytest and are part of the public API:

View File

@ -1,7 +0,0 @@
#!/usr/bin/env python
"""Used by .pre-commit-config.yaml"""
import sys
if __name__ == "__main__":
print(" ".join(sys.argv[1:]))
sys.exit(1)

View File

@ -2,7 +2,7 @@ REM scripts called by AppVeyor to setup the environment variables to enable cove
if not defined PYTEST_NO_COVERAGE (
set "COVERAGE_FILE=%CD%\.coverage"
set "COVERAGE_PROCESS_START=%CD%\.coveragerc"
set "_PYTEST_TOX_COVERAGE_RUN=coverage run --source {envsitepackagesdir}/_pytest/,{toxinidir}/testing -m"
set "_PYTEST_TOX_COVERAGE_RUN=coverage run -m"
set "_PYTEST_TOX_EXTRA_DEP=coverage-enable-subprocess"
echo Coverage setup completed
) else (

View File

@ -8,6 +8,7 @@ import marshal
import os
import re
import six
import string
import struct
import sys
import types
@ -16,7 +17,8 @@ import atomicwrites
import py
from _pytest.assertion import util
from _pytest.compat import PurePath, spec_from_file_location
from _pytest.paths import fnmatch_ex
# pytest caches rewritten pycs in __pycache__.
if hasattr(imp, "get_tag"):
@ -45,14 +47,6 @@ else:
return ast.Call(a, b, c, None, None)
if sys.version_info >= (3, 4):
from importlib.util import spec_from_file_location
else:
def spec_from_file_location(*_, **__):
return None
class AssertionRewritingHook(object):
"""PEP302 Import hook which rewrites asserts."""
@ -198,14 +192,14 @@ class AssertionRewritingHook(object):
return False
# For matching the name it must be as if it was a filename.
parts[-1] = parts[-1] + ".py"
fn_pypath = py.path.local(os.path.sep.join(parts))
path = PurePath(os.path.sep.join(parts) + ".py")
for pat in self.fnpats:
# if the pattern contains subdirectories ("tests/**.py" for example) we can't bail out based
# on the name alone because we need to match against the full path
if os.path.dirname(pat):
return False
if fn_pypath.fnmatch(pat):
if fnmatch_ex(pat, path):
return False
if self._is_marked_for_rewrite(name, state):
@ -473,10 +467,14 @@ def _saferepr(obj):
"""
r = py.io.saferepr(obj)
if isinstance(r, six.text_type):
return r.replace(u"\n", u"\\n")
else:
return r.replace(b"\n", b"\\n")
# only occurs in python2.x, repr must return text in python3+
if isinstance(r, bytes):
# Represent unprintable bytes as `\x##`
r = u"".join(
u"\\x{:x}".format(ord(c)) if c not in string.printable else c.decode()
for c in r
)
return r.replace(u"\n", u"\\n")
from _pytest.assertion.util import format_explanation as _format_explanation # noqa

View File

@ -115,15 +115,18 @@ class Cache(object):
else:
with f:
json.dump(value, f, indent=2, sort_keys=True)
self._ensure_readme()
def _ensure_readme(self):
self._ensure_supporting_files()
def _ensure_supporting_files(self):
"""Create supporting files in the cache dir that are not really part of the cache."""
if self._cachedir.is_dir():
readme_path = self._cachedir / "README.md"
if not readme_path.is_file():
readme_path.write_text(README_CONTENT)
msg = u"# created by pytest automatically, do not change\n*"
self._cachedir.joinpath(".gitignore").write_text(msg, encoding="UTF-8")
class LFPlugin(object):
""" Plugin which implements the --lf (run last-failing) option """

View File

@ -23,7 +23,7 @@ except ImportError: # pragma: no cover
# Only available in Python 3.4+ or as a backport
enum = None
__all__ = ["Path"]
__all__ = ["Path", "PurePath"]
_PY3 = sys.version_info > (3, 0)
_PY2 = not _PY3
@ -42,9 +42,9 @@ PY36 = sys.version_info[:2] >= (3, 6)
MODULE_NOT_FOUND_ERROR = "ModuleNotFoundError" if PY36 else "ImportError"
if PY36:
from pathlib import Path
from pathlib import Path, PurePath
else:
from pathlib2 import Path
from pathlib2 import Path, PurePath
if _PY3:
@ -56,6 +56,14 @@ else:
from collections import Mapping, Sequence # noqa
if sys.version_info >= (3, 4):
from importlib.util import spec_from_file_location
else:
def spec_from_file_location(*_, **__):
return None
def _format_args(func):
return str(signature(func))

View File

@ -460,8 +460,8 @@ class LoggingPlugin(object):
try:
yield # run test
finally:
del item.catch_log_handler
if when == "teardown":
del item.catch_log_handler
del item.catch_log_handlers
if self.print_logs:

View File

@ -504,13 +504,14 @@ class Session(nodes.FSCollector):
pkginit = parent.join("__init__.py")
if pkginit.isfile():
if pkginit in self._node_cache:
root = self._node_cache[pkginit]
root = self._node_cache[pkginit][0]
else:
col = root._collectfile(pkginit)
if col:
if isinstance(col[0], Package):
root = col[0]
self._node_cache[root.fspath] = root
# always store a list in the cache, matchnodes expects it
self._node_cache[root.fspath] = [root]
# If it's a directory argument, recurse and look for any Subpackages.
# Let the Package collector deal with subnodes, don't collect here.
@ -530,8 +531,8 @@ class Session(nodes.FSCollector):
if (type(x), x.fspath) in self._node_cache:
yield self._node_cache[(type(x), x.fspath)]
else:
yield x
self._node_cache[(type(x), x.fspath)] = x
yield x
else:
assert argpath.check(file=1)

View File

@ -219,8 +219,8 @@ class MonkeyPatch(object):
self.setitem(os.environ, name, value)
def delenv(self, name, raising=True):
""" Delete ``name`` from the environment. Raise KeyError it does not
exist.
""" Delete ``name`` from the environment. Raise KeyError if it does
not exist.
If ``raising`` is set to False, no exception will be raised if the
environment variable is missing.

View File

@ -1,5 +1,11 @@
from .compat import Path
from os.path import expanduser, expandvars, isabs
from os.path import expanduser, expandvars, isabs, sep
from posixpath import sep as posix_sep
import fnmatch
import sys
import six
from .compat import Path, PurePath
def resolve_from_str(input, root):
@ -11,3 +17,36 @@ def resolve_from_str(input, root):
return Path(input)
else:
return root.joinpath(input)
def fnmatch_ex(pattern, path):
"""FNMatcher port from py.path.common which works with PurePath() instances.
The difference between this algorithm and PurePath.match() is that the latter matches "**" glob expressions
for each part of the path, while this algorithm uses the whole path instead.
For example:
"tests/foo/bar/doc/test_foo.py" matches pattern "tests/**/doc/test*.py" with this algorithm, but not with
PurePath.match().
This algorithm was ported to keep backward-compatibility with existing settings which assume paths match according
this logic.
References:
* https://bugs.python.org/issue29249
* https://bugs.python.org/issue34731
"""
path = PurePath(path)
iswin32 = sys.platform.startswith("win")
if iswin32 and sep not in pattern and posix_sep in pattern:
# Running on Windows, the pattern has no Windows path separators,
# and the pattern has one or more Posix path separators. Replace
# the Posix path separators with the Windows path separator.
pattern = pattern.replace(posix_sep, sep)
if sep not in pattern:
name = path.name
else:
name = six.text_type(path)
return fnmatch.fnmatch(name, pattern)

View File

@ -37,6 +37,7 @@ from _pytest.compat import (
getlocation,
enum,
get_default_arg_names,
getimfunc,
)
from _pytest.outcomes import fail
from _pytest.mark.structures import (
@ -681,14 +682,12 @@ class Class(PyCollector):
def setup(self):
setup_class = _get_xunit_func(self.obj, "setup_class")
if setup_class is not None:
setup_class = getattr(setup_class, "im_func", setup_class)
setup_class = getattr(setup_class, "__func__", setup_class)
setup_class = getimfunc(setup_class)
setup_class(self.obj)
fin_class = getattr(self.obj, "teardown_class", None)
if fin_class is not None:
fin_class = getattr(fin_class, "im_func", fin_class)
fin_class = getattr(fin_class, "__func__", fin_class)
fin_class = getimfunc(fin_class)
self.addfinalizer(lambda: fin_class(self.obj))
@ -1145,13 +1144,18 @@ def _find_parametrized_scope(argnames, arg2fixturedefs, indirect):
"""
from _pytest.fixtures import scopes
indirect_as_list = isinstance(indirect, (list, tuple))
all_arguments_are_fixtures = (
indirect is True or indirect_as_list and len(indirect) == argnames
)
if isinstance(indirect, (list, tuple)):
all_arguments_are_fixtures = len(indirect) == len(argnames)
else:
all_arguments_are_fixtures = bool(indirect)
if all_arguments_are_fixtures:
fixturedefs = arg2fixturedefs or {}
used_scopes = [fixturedef[0].scope for name, fixturedef in fixturedefs.items()]
used_scopes = [
fixturedef[0].scope
for name, fixturedef in fixturedefs.items()
if name in argnames
]
if used_scopes:
# Takes the most narrow scope from used fixtures
for scope in reversed(scopes):
@ -1436,7 +1440,7 @@ class Function(FunctionMixin, nodes.Item, fixtures.FuncargnamesCompatAttr):
@property
def function(self):
"underlying python 'function' object"
return getattr(self.obj, "im_func", self.obj)
return getimfunc(self.obj)
def _getobj(self):
name = self.name

View File

@ -9,6 +9,7 @@ import _pytest._code
from _pytest.config import hookimpl
from _pytest.outcomes import fail, skip, xfail
from _pytest.python import transfer_markers, Class, Module, Function
from _pytest.compat import getimfunc
def pytest_pycollect_makeitem(collector, name, obj):
@ -53,7 +54,7 @@ class UnitTestCase(Class):
x = getattr(self.obj, name)
if not getattr(x, "__test__", True):
continue
funcobj = getattr(x, "im_func", x)
funcobj = getimfunc(x)
transfer_markers(funcobj, cls, module)
yield TestCaseFunction(name, parent=self, callobj=funcobj)
foundsomething = True

View File

@ -1,13 +1,19 @@
# coding: utf-8
from __future__ import absolute_import, division, print_function
import sys
import _pytest._code
import pytest
import mock
from test_excinfo import TWMock
from six import text_type
from test_excinfo import TWMock
try:
import mock
except ImportError:
import unittest.mock as mock
def test_ne():
code1 = _pytest._code.Code(compile('foo = "bar"', "", "exec"))

View File

@ -0,0 +1,2 @@
def test():
pass

View File

@ -1597,6 +1597,13 @@ def test_package_collection_infinite_recursion(testdir):
result.stdout.fnmatch_lines("*1 passed*")
def test_package_collection_init_given_as_argument(testdir):
"""Regression test for #3749"""
p = testdir.copy_example("collect/package_init_given_as_arg")
result = testdir.runpytest(p / "pkg" / "__init__.py")
result.stdout.fnmatch_lines("*1 passed*")
def test_package_with_modules(testdir):
"""
.

View File

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
import sys
import textwrap
import pytest
@ -488,6 +489,10 @@ class TestRequestBasic(object):
assert len(arg2fixturedefs) == 1
assert arg2fixturedefs["something"][0].argname == "something"
@pytest.mark.skipif(
hasattr(sys, "pypy_version_info"),
reason="this method of test doesn't work on pypy",
)
def test_request_garbage(self, testdir):
testdir.makepyfile(
"""
@ -498,33 +503,32 @@ class TestRequestBasic(object):
@pytest.fixture(autouse=True)
def something(request):
# this method of test doesn't work on pypy
if hasattr(sys, "pypy_version_info"):
yield
else:
original = gc.get_debug()
gc.set_debug(gc.DEBUG_SAVEALL)
gc.collect()
original = gc.get_debug()
gc.set_debug(gc.DEBUG_SAVEALL)
gc.collect()
yield
yield
try:
gc.collect()
leaked_types = sum(1 for _ in gc.garbage
if isinstance(_, PseudoFixtureDef))
# debug leaked types if the test fails
print(leaked_types)
gc.garbage[:] = []
try:
assert leaked_types == 0
finally:
gc.set_debug(original)
assert leaked_types == 0
finally:
gc.set_debug(original)
def test_func():
pass
"""
)
reprec = testdir.inline_run()
reprec.assertoutcome(passed=1)
result = testdir.runpytest()
result.stdout.fnmatch_lines("* 1 passed in *")
def test_getfixturevalue_recursive(self, testdir):
testdir.makeconftest(

View File

@ -132,6 +132,52 @@ class TestMetafunc(object):
except ValueError as ve:
assert "has an unsupported scope value 'doggy'" in str(ve)
def test_find_parametrized_scope(self):
"""unittest for _find_parametrized_scope (#3941)"""
from _pytest.python import _find_parametrized_scope
@attr.s
class DummyFixtureDef(object):
scope = attr.ib()
fixtures_defs = dict(
session_fix=[DummyFixtureDef("session")],
package_fix=[DummyFixtureDef("package")],
module_fix=[DummyFixtureDef("module")],
class_fix=[DummyFixtureDef("class")],
func_fix=[DummyFixtureDef("function")],
)
# use arguments to determine narrow scope; the cause of the bug is that it would look on all
# fixture defs given to the method
def find_scope(argnames, indirect):
return _find_parametrized_scope(argnames, fixtures_defs, indirect=indirect)
assert find_scope(["func_fix"], indirect=True) == "function"
assert find_scope(["class_fix"], indirect=True) == "class"
assert find_scope(["module_fix"], indirect=True) == "module"
assert find_scope(["package_fix"], indirect=True) == "package"
assert find_scope(["session_fix"], indirect=True) == "session"
assert find_scope(["class_fix", "func_fix"], indirect=True) == "function"
assert find_scope(["func_fix", "session_fix"], indirect=True) == "function"
assert find_scope(["session_fix", "class_fix"], indirect=True) == "class"
assert find_scope(["package_fix", "session_fix"], indirect=True) == "package"
assert find_scope(["module_fix", "session_fix"], indirect=True) == "module"
# when indirect is False or is not for all scopes, always use function
assert find_scope(["session_fix", "module_fix"], indirect=False) == "function"
assert (
find_scope(["session_fix", "module_fix"], indirect=["module_fix"])
== "function"
)
assert (
find_scope(
["session_fix", "module_fix"], indirect=["session_fix", "module_fix"]
)
== "module"
)
def test_parametrize_and_id(self):
def func(x, y):
pass
@ -796,7 +842,7 @@ class TestMetafuncFunctional(object):
p = testdir.makepyfile(
"""
# assumes that generate/provide runs in the same process
import sys, pytest
import sys, pytest, six
def pytest_generate_tests(metafunc):
metafunc.addcall(param=metafunc)
@ -815,11 +861,7 @@ class TestMetafuncFunctional(object):
def test_method(self, metafunc, pytestconfig):
assert metafunc.config == pytestconfig
assert metafunc.module.__name__ == __name__
if sys.version_info > (3, 0):
unbound = TestClass.test_method
else:
unbound = TestClass.test_method.im_func
# XXX actually have an unbound test function here?
unbound = six.get_unbound_function(TestClass.test_method)
assert metafunc.function == unbound
assert metafunc.cls == TestClass
"""
@ -1387,6 +1429,39 @@ class TestMetafuncFunctionalAuto(object):
result = testdir.runpytest()
result.stdout.fnmatch_lines(["* 3 passed *"])
def test_parametrize_some_arguments_auto_scope(self, testdir, monkeypatch):
"""Integration test for (#3941)"""
class_fix_setup = []
monkeypatch.setattr(sys, "class_fix_setup", class_fix_setup, raising=False)
func_fix_setup = []
monkeypatch.setattr(sys, "func_fix_setup", func_fix_setup, raising=False)
testdir.makepyfile(
"""
import pytest
import sys
@pytest.fixture(scope='class', autouse=True)
def class_fix(request):
sys.class_fix_setup.append(request.param)
@pytest.fixture(autouse=True)
def func_fix():
sys.func_fix_setup.append(True)
@pytest.mark.parametrize('class_fix', [10, 20], indirect=True)
class Test:
def test_foo(self):
pass
def test_bar(self):
pass
"""
)
result = testdir.runpytest_inprocess()
result.stdout.fnmatch_lines(["* 4 passed in *"])
assert func_fix_setup == [True] * 4
assert class_fix_setup == [10, 20]
def test_parametrize_issue634(self, testdir):
testdir.makepyfile(
"""

View File

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, print_function
import glob
@ -57,7 +58,7 @@ def getmsg(f, extra_ns=None, must_pass=False):
except AssertionError:
if must_pass:
pytest.fail("shouldn't have raised")
s = str(sys.exc_info()[1])
s = six.text_type(sys.exc_info()[1])
if not s.startswith("assert"):
return "AssertionError: " + s
return s
@ -608,6 +609,21 @@ class TestAssertionRewrite(object):
assert r"where 1 = \n{ \n~ \n}.a" in util._format_lines([getmsg(f)])[0]
def test_custom_repr_non_ascii(self):
def f():
class A(object):
name = u"ä"
def __repr__(self):
return self.name.encode("UTF-8") # only legal in python2
a = A()
assert not a.name
msg = getmsg(f)
assert "UnicodeDecodeError" not in msg
assert "UnicodeEncodeError" not in msg
class TestRewriteOnImport(object):
def test_pycache_is_a_file(self, testdir):
@ -1232,3 +1248,27 @@ class TestEarlyRewriteBailout(object):
hook.fnpats[:] = ["tests/**.py"]
assert hook.find_module("file") is not None
assert self.find_module_calls == ["file"]
@pytest.mark.skipif(
sys.platform.startswith("win32"), reason="cannot remove cwd on Windows"
)
def test_cwd_changed(self, testdir):
testdir.makepyfile(
**{
"test_bar.py": """
import os
import shutil
import tempfile
d = tempfile.mkdtemp()
os.chdir(d)
shutil.rmtree(d)
""",
"test_foo.py": """
def test():
pass
""",
}
)
result = testdir.runpytest()
result.stdout.fnmatch_lines("* 1 passed in *")

View File

@ -884,3 +884,14 @@ class TestReadme(object):
)
testdir.runpytest()
assert self.check_readme(testdir) is True
def test_gitignore(testdir):
"""Ensure we automatically create .gitignore file in the pytest_cache directory (#3286)."""
from _pytest.cacheprovider import Cache
config = testdir.parseconfig()
cache = Cache.for_config(config)
cache.set("foo", "bar")
msg = "# created by pytest automatically, do not change\n*"
assert cache._cachedir.joinpath(".gitignore").read_text(encoding="UTF-8") == msg

69
testing/test_paths.py Normal file
View File

@ -0,0 +1,69 @@
import sys
import py
import pytest
from _pytest.paths import fnmatch_ex
class TestPort:
"""Test that our port of py.common.FNMatcher (fnmatch_ex) produces the same results as the
original py.path.local.fnmatch method.
"""
@pytest.fixture(params=["pathlib", "py.path"])
def match(self, request):
if request.param == "py.path":
def match_(pattern, path):
return py.path.local(path).fnmatch(pattern)
else:
assert request.param == "pathlib"
def match_(pattern, path):
return fnmatch_ex(pattern, path)
return match_
if sys.platform == "win32":
drv1 = "c:"
drv2 = "d:"
else:
drv1 = "/c"
drv2 = "/d"
@pytest.mark.parametrize(
"pattern, path",
[
("*.py", "foo.py"),
("*.py", "bar/foo.py"),
("test_*.py", "foo/test_foo.py"),
("tests/*.py", "tests/foo.py"),
(drv1 + "/*.py", drv1 + "/foo.py"),
(drv1 + "/foo/*.py", drv1 + "/foo/foo.py"),
("tests/**/test*.py", "tests/foo/test_foo.py"),
("tests/**/doc/test*.py", "tests/foo/bar/doc/test_foo.py"),
("tests/**/doc/**/test*.py", "tests/foo/doc/bar/test_foo.py"),
],
)
def test_matching(self, match, pattern, path):
assert match(pattern, path)
@pytest.mark.parametrize(
"pattern, path",
[
("*.py", "foo.pyc"),
("*.py", "foo/foo.pyc"),
("tests/*.py", "foo/foo.py"),
(drv1 + "/*.py", drv2 + "/foo.py"),
(drv1 + "/foo/*.py", drv2 + "/foo/foo.py"),
("tests/**/test*.py", "tests/foo.py"),
("tests/**/test*.py", "foo/test_foo.py"),
("tests/**/doc/test*.py", "tests/foo/bar/doc/foo.py"),
("tests/**/doc/test*.py", "tests/foo/bar/test_foo.py"),
],
)
def test_not_matching(self, match, pattern, path):
assert not match(pattern, path)

View File

@ -397,6 +397,24 @@ class TestPDB(object):
child.read()
self.flush(child)
def test_pdb_with_caplog_on_pdb_invocation(self, testdir):
p1 = testdir.makepyfile(
"""
def test_1(capsys, caplog):
import logging
logging.getLogger(__name__).warning("some_warning")
assert 0
"""
)
child = testdir.spawn_pytest("--pdb %s" % str(p1))
child.send("caplog.record_tuples\n")
child.expect_exact(
"[('test_pdb_with_caplog_on_pdb_invocation', 30, 'some_warning')]"
)
child.sendeof()
child.read()
self.flush(child)
def test_set_trace_capturing_afterwards(self, testdir):
p1 = testdir.makepyfile(
"""

35
tox.ini
View File

@ -17,9 +17,10 @@ envlist =
docs
[testenv]
whitelist_externals = env
commands =
{env:_PYTEST_TOX_COVERAGE_RUN:} pytest --lsof -ra {posargs:testing}
coverage: coverage combine
coverage: coverage report
passenv = USER USERNAME
setenv =
# configuration if a user runs tox with a "coverage" factor, for example "tox -e py36-coverage"
@ -30,7 +31,7 @@ setenv =
deps =
hypothesis>=3.56
nose
mock
{py27,pypy}: mock
requests
{env:_PYTEST_TOX_EXTRA_DEP:}
@ -38,7 +39,7 @@ deps =
changedir = .
deps =
pytest-xdist>=1.13
mock
py27: mock
nose
passenv = USER USERNAME TRAVIS
commands =
@ -48,23 +49,28 @@ commands =
[testenv:linting]
skip_install = True
basepython = python3.6
deps = pre-commit
deps = pre-commit>=1.11.0
commands = pre-commit run --all-files --show-diff-on-failure
[testenv:py27-xdist]
deps =
pytest-xdist>=1.13
mock
{py27,pypy}: mock
nose
hypothesis>=3.56
{env:_PYTEST_TOX_EXTRA_DEP:}
whitelist_externals = env
passenv = USER USERNAME TRAVIS
commands =
{env:_PYTEST_TOX_COVERAGE_RUN:} pytest -n auto -ra {posargs:testing}
[testenv:py36-xdist]
deps = {[testenv:py27-xdist]deps}
# NOTE: copied from above due to https://github.com/tox-dev/tox/issues/706.
deps =
pytest-xdist>=1.13
{py27,pypy}: mock
nose
hypothesis>=3.56
{env:_PYTEST_TOX_EXTRA_DEP:}
commands = {[testenv:py27-xdist]commands}
[testenv:py27-pexpect]
@ -73,7 +79,6 @@ platform = linux|darwin
deps =
pexpect
{env:_PYTEST_TOX_EXTRA_DEP:}
whitelist_externals = env
commands =
{env:_PYTEST_TOX_COVERAGE_RUN:} pytest -ra test_pdb.py test_terminal.py test_unittest.py
@ -87,14 +92,13 @@ commands = {[testenv:py27-pexpect]commands}
deps =
pytest-xdist>=1.13
hypothesis>=3.56
mock
py27: mock
{env:_PYTEST_TOX_EXTRA_DEP:}
distribute = true
changedir=testing
setenv =
{[testenv]setenv}
PYTHONDONTWRITEBYTECODE=1
whitelist_externals = env
passenv = USER USERNAME TRAVIS
commands =
{env:_PYTEST_TOX_COVERAGE_RUN:} pytest -n auto -ra {posargs:.}
@ -103,7 +107,6 @@ commands =
deps =
twisted
{env:_PYTEST_TOX_EXTRA_DEP:}
whitelist_externals = env
commands =
{env:_PYTEST_TOX_COVERAGE_RUN:} pytest -ra {posargs:testing/test_unittest.py}
@ -115,7 +118,6 @@ commands = {[testenv:py27-trial]commands}
deps =
numpy
{env:_PYTEST_TOX_EXTRA_DEP:}
whitelist_externals = env
commands=
{env:_PYTEST_TOX_COVERAGE_RUN:} pytest -ra {posargs:testing/python/approx.py}
@ -127,13 +129,11 @@ commands = {[testenv:py27-numpy]commands}
setenv=
{[testenv]setenv}
_PYTEST_SETUP_SKIP_PLUGGY_DEP=1
deps =
{[testenv]deps}
git+https://github.com/pytest-dev/pluggy.git@master
# NOTE: using env instead of "{[testenv]deps}", because of https://github.com/tox-dev/tox/issues/706.
_PYTEST_TOX_EXTRA_DEP=git+https://github.com/pytest-dev/pluggy.git@master
[testenv:py36-pluggymaster]
setenv = {[testenv:py27-pluggymaster]setenv}
deps = {[testenv:py27-pluggymaster]deps}
[testenv:docs]
skipsdist = True
@ -153,7 +153,6 @@ skipsdist = True
deps =
PyYAML
{env:_PYTEST_TOX_EXTRA_DEP:}
whitelist_externals = env
commands =
{env:_PYTEST_TOX_COVERAGE_RUN:} pytest -ra doc/en
{env:_PYTEST_TOX_COVERAGE_RUN:} pytest --doctest-modules --pyargs _pytest
@ -195,7 +194,7 @@ passenv = *
deps =
colorama
gitpython
pre-commit
pre-commit>=1.11.0
towncrier
wheel
commands = python scripts/release.py {posargs}