Skip to content

Commit c28b631

Browse files
authored
Merge master into features (#5874)
Merge master into features
2 parents 7c64d5d + d3d9f9f commit c28b631

27 files changed

+530
-119
lines changed

AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ mbyt
176176
Michael Aquilina
177177
Michael Birtwell
178178
Michael Droettboom
179+
Michael Goerz
179180
Michael Seifert
180181
Michal Wajszczuk
181182
Mihai Capotă

changelog/1682.deprecation.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Passing arguments to pytest.fixture() as positional arguments is deprecated - pass them
2+
as a keyword argument instead.

changelog/1682.feature.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
The ``scope`` parameter of ``@pytest.fixture`` can now be a callable that receives
2+
the fixture name and the ``config`` object as keyword-only parameters.
3+
See `the docs <https://docs.pytest.org/en/fixture.html#dynamic-scope>`__ for more information.

changelog/5056.trivial.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
The HelpFormatter uses ``py.io.get_terminal_width`` for better width detection.

changelog/5764.feature.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
New behavior of the ``--pastebin`` option: failures to connect to the pastebin server are reported, without failing the pytest run

changelog/5806.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix "lexer" being used when uploading to bpaste.net from ``--pastebin`` to "text".

doc/en/example/costlysetup/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import pytest
22

33

4-
@pytest.fixture("session")
4+
@pytest.fixture(scope="session")
55
def setup(request):
66
setup = CostlySetup()
77
yield setup

doc/en/fixture.rst

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,32 @@ are finalized when the last test of a *package* finishes.
301301
Use this new feature sparingly and please make sure to report any issues you find.
302302

303303

304+
Dynamic scope
305+
^^^^^^^^^^^^^
306+
307+
In some cases, you might want to change the scope of the fixture without changing the code.
308+
To do that, pass a callable to ``scope``. The callable must return a string with a valid scope
309+
and will be executed only once - during the fixture definition. It will be called with two
310+
keyword arguments - ``fixture_name`` as a string and ``config`` with a configuration object.
311+
312+
This can be especially useful when dealing with fixtures that need time for setup, like spawning
313+
a docker container. You can use the command-line argument to control the scope of the spawned
314+
containers for different environments. See the example below.
315+
316+
.. code-block:: python
317+
318+
def determine_scope(fixture_name, config):
319+
if config.getoption("--keep-containers"):
320+
return "session"
321+
return "function"
322+
323+
324+
@pytest.fixture(scope=determine_scope)
325+
def docker_container():
326+
yield spawn_container()
327+
328+
329+
304330
Order: Higher-scoped fixtures are instantiated first
305331
----------------------------------------------------
306332

src/_pytest/_code/code.py

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,15 @@
55
from inspect import CO_VARARGS
66
from inspect import CO_VARKEYWORDS
77
from traceback import format_exception_only
8+
from types import CodeType
89
from types import TracebackType
10+
from typing import Any
11+
from typing import Dict
912
from typing import Generic
13+
from typing import List
1014
from typing import Optional
1115
from typing import Pattern
16+
from typing import Set
1217
from typing import Tuple
1318
from typing import TypeVar
1419
from typing import Union
@@ -29,7 +34,7 @@
2934
class Code:
3035
""" wrapper around Python code objects """
3136

32-
def __init__(self, rawcode):
37+
def __init__(self, rawcode) -> None:
3338
if not hasattr(rawcode, "co_filename"):
3439
rawcode = getrawcode(rawcode)
3540
try:
@@ -38,7 +43,7 @@ def __init__(self, rawcode):
3843
self.name = rawcode.co_name
3944
except AttributeError:
4045
raise TypeError("not a code object: {!r}".format(rawcode))
41-
self.raw = rawcode
46+
self.raw = rawcode # type: CodeType
4247

4348
def __eq__(self, other):
4449
return self.raw == other.raw
@@ -351,7 +356,7 @@ def recursionindex(self):
351356
""" return the index of the frame/TracebackEntry where recursion
352357
originates if appropriate, None if no recursion occurred
353358
"""
354-
cache = {}
359+
cache = {} # type: Dict[Tuple[Any, int, int], List[Dict[str, Any]]]
355360
for i, entry in enumerate(self):
356361
# id for the code.raw is needed to work around
357362
# the strange metaprogramming in the decorator lib from pypi
@@ -650,7 +655,7 @@ def repr_args(self, entry):
650655
args.append((argname, saferepr(argvalue)))
651656
return ReprFuncArgs(args)
652657

653-
def get_source(self, source, line_index=-1, excinfo=None, short=False):
658+
def get_source(self, source, line_index=-1, excinfo=None, short=False) -> List[str]:
654659
""" return formatted and marked up source lines. """
655660
import _pytest._code
656661

@@ -722,7 +727,7 @@ def repr_traceback_entry(self, entry, excinfo=None):
722727
else:
723728
line_index = entry.lineno - entry.getfirstlinesource()
724729

725-
lines = []
730+
lines = [] # type: List[str]
726731
style = entry._repr_style
727732
if style is None:
728733
style = self.style
@@ -799,7 +804,7 @@ def _truncate_recursive_traceback(self, traceback):
799804
exc_msg=str(e),
800805
max_frames=max_frames,
801806
total=len(traceback),
802-
)
807+
) # type: Optional[str]
803808
traceback = traceback[:max_frames] + traceback[-max_frames:]
804809
else:
805810
if recursionindex is not None:
@@ -812,10 +817,12 @@ def _truncate_recursive_traceback(self, traceback):
812817

813818
def repr_excinfo(self, excinfo):
814819

815-
repr_chain = []
820+
repr_chain = (
821+
[]
822+
) # type: List[Tuple[ReprTraceback, Optional[ReprFileLocation], Optional[str]]]
816823
e = excinfo.value
817824
descr = None
818-
seen = set()
825+
seen = set() # type: Set[int]
819826
while e is not None and id(e) not in seen:
820827
seen.add(id(e))
821828
if excinfo:
@@ -868,8 +875,8 @@ def __repr__(self):
868875

869876

870877
class ExceptionRepr(TerminalRepr):
871-
def __init__(self):
872-
self.sections = []
878+
def __init__(self) -> None:
879+
self.sections = [] # type: List[Tuple[str, str, str]]
873880

874881
def addsection(self, name, content, sep="-"):
875882
self.sections.append((name, content, sep))

src/_pytest/_code/source.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import warnings
88
from ast import PyCF_ONLY_AST as _AST_FLAG
99
from bisect import bisect_right
10+
from typing import List
1011

1112
import py
1213

@@ -19,11 +20,11 @@ class Source:
1920
_compilecounter = 0
2021

2122
def __init__(self, *parts, **kwargs):
22-
self.lines = lines = []
23+
self.lines = lines = [] # type: List[str]
2324
de = kwargs.get("deindent", True)
2425
for part in parts:
2526
if not part:
26-
partlines = []
27+
partlines = [] # type: List[str]
2728
elif isinstance(part, Source):
2829
partlines = part.lines
2930
elif isinstance(part, (tuple, list)):
@@ -157,8 +158,7 @@ def compile(
157158
source = "\n".join(self.lines) + "\n"
158159
try:
159160
co = compile(source, filename, mode, flag)
160-
except SyntaxError:
161-
ex = sys.exc_info()[1]
161+
except SyntaxError as ex:
162162
# re-represent syntax errors from parsing python strings
163163
msglines = self.lines[: ex.lineno]
164164
if ex.offset:
@@ -173,7 +173,8 @@ def compile(
173173
if flag & _AST_FLAG:
174174
return co
175175
lines = [(x + "\n") for x in self.lines]
176-
linecache.cache[filename] = (1, None, lines, filename)
176+
# Type ignored because linecache.cache is private.
177+
linecache.cache[filename] = (1, None, lines, filename) # type: ignore
177178
return co
178179

179180

@@ -282,7 +283,7 @@ def get_statement_startend2(lineno, node):
282283
return start, end
283284

284285

285-
def getstatementrange_ast(lineno, source, assertion=False, astnode=None):
286+
def getstatementrange_ast(lineno, source: Source, assertion=False, astnode=None):
286287
if astnode is None:
287288
content = str(source)
288289
# See #4260:

src/_pytest/assertion/__init__.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
support for presenting detailed information in failing assertions.
33
"""
44
import sys
5+
from typing import Optional
56

67
from _pytest.assertion import rewrite
78
from _pytest.assertion import truncate
@@ -52,7 +53,9 @@ def register_assert_rewrite(*names):
5253
importhook = hook
5354
break
5455
else:
55-
importhook = DummyRewriteHook()
56+
# TODO(typing): Add a protocol for mark_rewrite() and use it
57+
# for importhook and for PytestPluginManager.rewrite_hook.
58+
importhook = DummyRewriteHook() # type: ignore
5659
importhook.mark_rewrite(*names)
5760

5861

@@ -69,7 +72,7 @@ class AssertionState:
6972
def __init__(self, config, mode):
7073
self.mode = mode
7174
self.trace = config.trace.root.get("assertion")
72-
self.hook = None
75+
self.hook = None # type: Optional[rewrite.AssertionRewritingHook]
7376

7477

7578
def install_importhook(config):
@@ -108,6 +111,7 @@ def pytest_runtest_setup(item):
108111
"""
109112

110113
def callbinrepr(op, left, right):
114+
# type: (str, object, object) -> Optional[str]
111115
"""Call the pytest_assertrepr_compare hook and prepare the result
112116
113117
This uses the first result from the hook and then ensures the
@@ -133,12 +137,13 @@ def callbinrepr(op, left, right):
133137
if item.config.getvalue("assertmode") == "rewrite":
134138
res = res.replace("%", "%%")
135139
return res
140+
return None
136141

137142
util._reprcompare = callbinrepr
138143

139144
if item.ihook.pytest_assertion_pass.get_hookimpls():
140145

141-
def call_assertion_pass_hook(lineno, expl, orig):
146+
def call_assertion_pass_hook(lineno, orig, expl):
142147
item.ihook.pytest_assertion_pass(
143148
item=item, lineno=lineno, orig=orig, expl=expl
144149
)

0 commit comments

Comments
 (0)