Skip to content

Commit fa211fe

Browse files
authored
Merge pytest-dev/pytest@master (#165)
2 parents 68f17e2 + 2719eb0 commit fa211fe

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+386
-87
lines changed

.coveragerc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,5 @@ exclude_lines =
2424
\#\s*pragma: no cover
2525
^\s*raise NotImplementedError\b
2626
^\s*return NotImplemented\b
27+
28+
^\s*if TYPE_CHECKING:

.github/workflows/main.yml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ on:
1010
push:
1111
branches:
1212
- master
13+
tags:
14+
- "*"
15+
1316
pull_request:
1417
branches:
1518
- master
@@ -154,3 +157,35 @@ jobs:
154157
flags: ${{ runner.os }}
155158
fail_ci_if_error: false
156159
name: ${{ matrix.name }}
160+
161+
deploy:
162+
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') && github.repository == 'pytest-dev/pytest'
163+
164+
runs-on: ubuntu-latest
165+
166+
needs: [build]
167+
168+
steps:
169+
- uses: actions/checkout@v1
170+
- name: Set up Python
171+
uses: actions/setup-python@v1
172+
with:
173+
python-version: "3.7"
174+
- name: Install dependencies
175+
run: |
176+
python -m pip install --upgrade pip
177+
pip install --upgrade wheel setuptools tox
178+
- name: Build package
179+
run: |
180+
python setup.py sdist bdist_wheel
181+
- name: Publish package to PyPI
182+
uses: pypa/gh-action-pypi-publish@master
183+
with:
184+
user: __token__
185+
password: ${{ secrets.pypi_token }}
186+
- name: Publish GitHub release notes
187+
env:
188+
GH_RELEASE_NOTES_TOKEN: ${{ secrets.release_notes }}
189+
run: |
190+
sudo apt-get install pandoc
191+
tox -e publish-gh-release-notes

AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ Ceridwen
5454
Charles Cloud
5555
Charnjit SiNGH (CCSJ)
5656
Chris Lamb
57+
Chris NeJame
5758
Christian Boelsen
5859
Christian Fetzer
5960
Christian Neumüller

changelog/2780.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Captured output during teardown is shown with ``-rP``.

changelog/6436.bugfix.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
:class:`FixtureDef <_pytest.fixtures.FixtureDef>` objects now properly register their finalizers with autouse and
2+
parameterized fixtures that execute before them in the fixture stack so they are torn
3+
down at the right times, and in the right order.

doc/en/conf.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@
1919
import sys
2020

2121
from _pytest import __version__ as version
22+
from _pytest.compat import TYPE_CHECKING
2223

23-
if False: # TYPE_CHECKING
24+
if TYPE_CHECKING:
2425
import sphinx.application
2526

2627

doc/en/customize.rst

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@ which were registered by installed plugins.
2020
Initialization: determining rootdir and inifile
2121
-----------------------------------------------
2222

23-
24-
2523
pytest determines a ``rootdir`` for each test run which depends on
2624
the command line arguments (specified test files, paths) and on
2725
the existence of *ini-files*. The determined ``rootdir`` and *ini-file* are
@@ -30,17 +28,17 @@ printed as part of the pytest header during startup.
3028
Here's a summary what ``pytest`` uses ``rootdir`` for:
3129

3230
* Construct *nodeids* during collection; each test is assigned
33-
a unique *nodeid* which is rooted at the ``rootdir`` and takes in account full path,
34-
class name, function name and parametrization (if any).
31+
a unique *nodeid* which is rooted at the ``rootdir`` and takes into account
32+
the full path, class name, function name and parametrization (if any).
3533

3634
* Is used by plugins as a stable location to store project/test run specific information;
3735
for example, the internal :ref:`cache <cache>` plugin creates a ``.pytest_cache`` subdirectory
3836
in ``rootdir`` to store its cross-test run state.
3937

40-
Important to emphasize that ``rootdir`` is **NOT** used to modify ``sys.path``/``PYTHONPATH`` or
38+
``rootdir`` is **NOT** used to modify ``sys.path``/``PYTHONPATH`` or
4139
influence how modules are imported. See :ref:`pythonpath` for more details.
4240

43-
``--rootdir=path`` command-line option can be used to force a specific directory.
41+
The ``--rootdir=path`` command-line option can be used to force a specific directory.
4442
The directory passed may contain environment variables when it is used in conjunction
4543
with ``addopts`` in a ``pytest.ini`` file.
4644

scripts/publish-gh-release-notes.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,19 +68,21 @@ def main(argv):
6868
if len(argv) > 1:
6969
tag_name = argv[1]
7070
else:
71-
tag_name = os.environ.get("TRAVIS_TAG")
71+
tag_name = os.environ.get("GITHUB_REF")
7272
if not tag_name:
73-
print("tag_name not given and $TRAVIS_TAG not set", file=sys.stderr)
73+
print("tag_name not given and $GITHUB_REF not set", file=sys.stderr)
7474
return 1
75+
if tag_name.startswith("refs/tags/"):
76+
tag_name = tag_name[len("refs/tags/") :]
7577

7678
token = os.environ.get("GH_RELEASE_NOTES_TOKEN")
7779
if not token:
7880
print("GH_RELEASE_NOTES_TOKEN not set", file=sys.stderr)
7981
return 1
8082

81-
slug = os.environ.get("TRAVIS_REPO_SLUG")
83+
slug = os.environ.get("GITHUB_REPOSITORY")
8284
if not slug:
83-
print("TRAVIS_REPO_SLUG not set", file=sys.stderr)
85+
print("GITHUB_REPOSITORY not set", file=sys.stderr)
8486
return 1
8587

8688
rst_body = parse_changelog(tag_name)

src/_pytest/_code/code.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,10 @@
3232
from _pytest._io.saferepr import safeformat
3333
from _pytest._io.saferepr import saferepr
3434
from _pytest.compat import overload
35+
from _pytest.compat import TYPE_CHECKING
3536
from _pytest.outcomes import OutcomeException
3637

37-
if False: # TYPE_CHECKING
38+
if TYPE_CHECKING:
3839
from typing import Type
3940
from typing_extensions import Literal
4041
from weakref import ReferenceType # noqa: F401

src/_pytest/compat.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,13 @@
2727
from _pytest.outcomes import fail
2828
from _pytest.outcomes import TEST_OUTCOME
2929

30-
if False: # TYPE_CHECKING
30+
if sys.version_info < (3, 5, 2):
31+
TYPE_CHECKING = False # type: bool
32+
else:
33+
from typing import TYPE_CHECKING
34+
35+
36+
if TYPE_CHECKING:
3137
from typing import Type # noqa: F401 (used in type string)
3238

3339

src/_pytest/config/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,13 @@
3737
from _pytest._code import ExceptionInfo
3838
from _pytest._code import filter_traceback
3939
from _pytest.compat import importlib_metadata
40+
from _pytest.compat import TYPE_CHECKING
4041
from _pytest.outcomes import fail
4142
from _pytest.outcomes import Skipped
4243
from _pytest.pathlib import Path
4344
from _pytest.warning_types import PytestConfigWarning
4445

45-
if False: # TYPE_CHECKING
46+
if TYPE_CHECKING:
4647
from typing import Type
4748

4849
from .argparsing import Argument

src/_pytest/config/findpaths.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@
55
import py
66

77
from .exceptions import UsageError
8+
from _pytest.compat import TYPE_CHECKING
89
from _pytest.outcomes import fail
910

10-
if False:
11+
if TYPE_CHECKING:
1112
from . import Config # noqa: F401
1213

1314

src/_pytest/doctest.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,13 @@
1818
from _pytest._code.code import ReprFileLocation
1919
from _pytest._code.code import TerminalRepr
2020
from _pytest.compat import safe_getattr
21+
from _pytest.compat import TYPE_CHECKING
2122
from _pytest.fixtures import FixtureRequest
2223
from _pytest.outcomes import Skipped
2324
from _pytest.python_api import approx
2425
from _pytest.warning_types import PytestWarning
2526

26-
if False: # TYPE_CHECKING
27+
if TYPE_CHECKING:
2728
import doctest
2829
from typing import Type
2930

src/_pytest/fixtures.py

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,13 @@
2727
from _pytest.compat import is_generator
2828
from _pytest.compat import NOTSET
2929
from _pytest.compat import safe_getattr
30+
from _pytest.compat import TYPE_CHECKING
3031
from _pytest.deprecated import FIXTURE_POSITIONAL_ARGUMENTS
3132
from _pytest.deprecated import FUNCARGNAMES
3233
from _pytest.outcomes import fail
3334
from _pytest.outcomes import TEST_OUTCOME
3435

35-
if False: # TYPE_CHECKING
36+
if TYPE_CHECKING:
3637
from typing import Type
3738

3839
from _pytest import nodes
@@ -880,9 +881,7 @@ def finish(self, request):
880881
self._finalizers = []
881882

882883
def execute(self, request):
883-
# get required arguments and register our own finish()
884-
# with their finalization
885-
for argname in self.argnames:
884+
for argname in self._dependee_fixture_argnames(request):
886885
fixturedef = request._get_active_fixturedef(argname)
887886
if argname != "request":
888887
fixturedef.addfinalizer(functools.partial(self.finish, request=request))
@@ -905,6 +904,61 @@ def execute(self, request):
905904
hook = self._fixturemanager.session.gethookproxy(request.node.fspath)
906905
return hook.pytest_fixture_setup(fixturedef=self, request=request)
907906

907+
def _dependee_fixture_argnames(self, request):
908+
"""A list of argnames for fixtures that this fixture depends on.
909+
910+
Given a request, this looks at the currently known list of fixture argnames, and
911+
attempts to determine what slice of the list contains fixtures that it can know
912+
should execute before it. This information is necessary so that this fixture can
913+
know what fixtures to register its finalizer with to make sure that if they
914+
would be torn down, they would tear down this fixture before themselves. It's
915+
crucial for fixtures to be torn down in the inverse order that they were set up
916+
in so that they don't try to clean up something that another fixture is still
917+
depending on.
918+
919+
When autouse fixtures are involved, it can be tricky to figure out when fixtures
920+
should be torn down. To solve this, this method leverages the ``fixturenames``
921+
list provided by the ``request`` object, as this list is at least somewhat
922+
sorted (in terms of the order fixtures are set up in) by the time this method is
923+
reached. It's sorted enough that the starting point of fixtures that depend on
924+
this one can be found using the ``self._parent_request`` stack.
925+
926+
If a request in the ``self._parent_request`` stack has a ``:class:FixtureDef``
927+
associated with it, then that fixture is dependent on this one, so any fixture
928+
names that appear in the list of fixture argnames that come after it can also be
929+
ruled out. The argnames of all fixtures associated with a request in the
930+
``self._parent_request`` stack are found, and the lowest index argname is
931+
considered the earliest point in the list of fixture argnames where everything
932+
from that point onward can be considered to execute after this fixture.
933+
Everything before this point can be considered fixtures that this fixture
934+
depends on, and so this fixture should register its finalizer with all of them
935+
to ensure that if any of them are to be torn down, they will tear this fixture
936+
down first.
937+
938+
This is the first part of the list of fixture argnames that is returned. The last
939+
part of the list is everything in ``self.argnames`` as those are explicit
940+
dependees of this fixture, so this fixture should definitely register its
941+
finalizer with them.
942+
"""
943+
all_fix_names = request.fixturenames
944+
try:
945+
current_fix_index = all_fix_names.index(self.argname)
946+
except ValueError:
947+
current_fix_index = len(request.fixturenames)
948+
parent_fixture_indexes = set()
949+
950+
parent_request = request._parent_request
951+
while hasattr(parent_request, "_parent_request"):
952+
if hasattr(parent_request, "_fixturedef"):
953+
parent_fix_name = parent_request._fixturedef.argname
954+
if parent_fix_name in all_fix_names:
955+
parent_fixture_indexes.add(all_fix_names.index(parent_fix_name))
956+
parent_request = parent_request._parent_request
957+
958+
stack_slice_index = min([current_fix_index, *parent_fixture_indexes])
959+
active_fixture_argnames = all_fix_names[:stack_slice_index]
960+
return {*active_fixture_argnames, *self.argnames}
961+
908962
def cache_key(self, request):
909963
return request.param_index if not hasattr(request, "param") else request.param
910964

src/_pytest/nodes.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from _pytest._code.code import ReprExceptionInfo
1818
from _pytest.compat import cached_property
1919
from _pytest.compat import getfslineno
20+
from _pytest.compat import TYPE_CHECKING
2021
from _pytest.config import Config
2122
from _pytest.deprecated import NODE_USE_FROM_PARENT
2223
from _pytest.fixtures import FixtureDef
@@ -27,7 +28,7 @@
2728
from _pytest.mark.structures import NodeKeywords
2829
from _pytest.outcomes import Failed
2930

30-
if False: # TYPE_CHECKING
31+
if TYPE_CHECKING:
3132
# Imported here due to circular import.
3233
from _pytest.main import Session # noqa: F401
3334

src/_pytest/outcomes.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88

99
from packaging.version import Version
1010

11-
if False: # TYPE_CHECKING
11+
TYPE_CHECKING = False # avoid circular import through compat
12+
13+
if TYPE_CHECKING:
1214
from typing import NoReturn
1315

1416

src/_pytest/pytester.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,19 @@
3030
from _pytest._io.saferepr import saferepr
3131
from _pytest.capture import MultiCapture
3232
from _pytest.capture import SysCapture
33+
from _pytest.compat import TYPE_CHECKING
3334
from _pytest.fixtures import FixtureRequest
3435
from _pytest.main import ExitCode
3536
from _pytest.main import Session
3637
from _pytest.monkeypatch import MonkeyPatch
3738
from _pytest.pathlib import Path
3839
from _pytest.reports import TestReport
3940

40-
if False: # TYPE_CHECKING
41+
if TYPE_CHECKING:
4142
from typing import Type
4243

44+
import pexpect
45+
4346

4447
IGNORE_PAM = [ # filenames added when obtaining details about the current user
4548
"/var/lib/sss/mc/passwd"
@@ -193,7 +196,7 @@ def __repr__(self):
193196
self._name, ", ".join(("{}={!r}".format(k, v)) for k, v in d.items())
194197
)
195198

196-
if False: # TYPE_CHECKING
199+
if TYPE_CHECKING:
197200
# The class has undetermined attributes, this tells mypy about it.
198201
def __getattr__(self, key):
199202
raise NotImplementedError()
@@ -1281,7 +1284,7 @@ def runpytest_subprocess(self, *args, timeout=None) -> RunResult:
12811284
args = self._getpytestargs() + args
12821285
return self.run(*args, timeout=timeout)
12831286

1284-
def spawn_pytest(self, *args, **kwargs):
1287+
def spawn_pytest(self, *args: str, **kwargs) -> "pexpect.spawn":
12851288
"""Run pytest using pexpect.
12861289
12871290
This makes sure to use the right pytest and sets up the temporary
@@ -1304,7 +1307,7 @@ def spawn_pytest(self, *args, **kwargs):
13041307
args = self._getpytestargs() + ("--basetemp={}".format(basetemp),) + args
13051308
return self.spawn(args[0], *args[1:], **kwargs)
13061309

1307-
def spawn(self, *args: str, **kwargs):
1310+
def spawn(self, *args: str, **kwargs) -> "pexpect.spawn":
13081311
"""Run a command using pexpect.
13091312
13101313
The pexpect child is returned.

src/_pytest/python_api.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,10 @@
2323
import _pytest._code
2424
from _pytest.compat import overload
2525
from _pytest.compat import STRING_TYPES
26+
from _pytest.compat import TYPE_CHECKING
2627
from _pytest.outcomes import fail
2728

28-
if False: # TYPE_CHECKING
29+
if TYPE_CHECKING:
2930
from typing import Type # noqa: F401 (used in type string)
3031

3132

src/_pytest/recwarn.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@
1212
from typing import Union
1313

1414
from _pytest.compat import overload
15+
from _pytest.compat import TYPE_CHECKING
1516
from _pytest.fixtures import yield_fixture
1617
from _pytest.outcomes import fail
1718

18-
if False: # TYPE_CHECKING
19+
if TYPE_CHECKING:
1920
from typing import Type
2021

2122

0 commit comments

Comments
 (0)