Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
28761c8
Have AssertionRewritingHook derive from importlib.abc.MetaPathFinder
bluetech Jul 17, 2019
7259c45
Fix some check_untyped_defs = True mypy warnings
bluetech Jul 14, 2019
2a6a1ca
Inject width via pylib to argparse formatter
blueyed Apr 5, 2019
f05ca74
Merge pull request #5056 from blueyed/argparsing-width
blueyed Aug 17, 2019
c2f7624
Merge pull request #5673 from bluetech/type-annotations-3
bluetech Aug 20, 2019
c28e428
Merge remote-tracking branch 'upstream/master' into mm
nicoddemus Aug 26, 2019
5bf9f9a
Merge pull request #5788 from nicoddemus/mm
asottile Aug 27, 2019
d47b9d0
Gracefully handle HTTP errors from pastebin
goerz Aug 18, 2019
404cf0c
Merge pull request #5764 from goerz/pastebin
nicoddemus Aug 30, 2019
f8dd634
Fix "lexer" being used when uploading to bpaste.net
goerz Aug 30, 2019
10bf6aa
Implemented the dynamic scope feature.
aklajnert Aug 21, 2019
1675048
Merge pull request #5808 from goerz/pastebin
nicoddemus Aug 31, 2019
f2f3ced
Fixed the fixture function signature.
aklajnert Sep 10, 2019
450409d
Merge master into features
blueyed Sep 17, 2019
c997c32
Merge pull request #5856 from blueyed/mm
blueyed Sep 17, 2019
6918d07
Merge remote-tracking branch 'upstream/features' into aklajnert/1682-…
nicoddemus Sep 18, 2019
df46afc
Change fixture argument handling tests to unit-tests
nicoddemus Sep 18, 2019
e2382e9
Minor cleanup in tests.
aklajnert Sep 19, 2019
9669413
Merge pull request #5776 from aklajnert/1682-dynamic-scope
nicoddemus Sep 19, 2019
409cc29
Merge master into features
blueyed Sep 21, 2019
c1361b4
Merge pull request #5868 from blueyed/mm
blueyed Sep 21, 2019
d3d9f9f
Merge remote-tracking branch 'upstream/master' into mm
nicoddemus Sep 23, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ mbyt
Michael Aquilina
Michael Birtwell
Michael Droettboom
Michael Goerz
Michael Seifert
Michal Wajszczuk
Mihai Capotă
Expand Down
2 changes: 2 additions & 0 deletions changelog/1682.deprecation.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Passing arguments to pytest.fixture() as positional arguments is deprecated - pass them
as a keyword argument instead.
3 changes: 3 additions & 0 deletions changelog/1682.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
The ``scope`` parameter of ``@pytest.fixture`` can now be a callable that receives
the fixture name and the ``config`` object as keyword-only parameters.
See `the docs <https://docs.pytest.org/en/fixture.html#dynamic-scope>`__ for more information.
1 change: 1 addition & 0 deletions changelog/5056.trivial.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The HelpFormatter uses ``py.io.get_terminal_width`` for better width detection.
1 change: 1 addition & 0 deletions changelog/5764.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
New behavior of the ``--pastebin`` option: failures to connect to the pastebin server are reported, without failing the pytest run
1 change: 1 addition & 0 deletions changelog/5806.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix "lexer" being used when uploading to bpaste.net from ``--pastebin`` to "text".
2 changes: 1 addition & 1 deletion doc/en/example/costlysetup/conftest.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import pytest


@pytest.fixture("session")
@pytest.fixture(scope="session")
def setup(request):
setup = CostlySetup()
yield setup
Expand Down
26 changes: 26 additions & 0 deletions doc/en/fixture.rst
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,32 @@ are finalized when the last test of a *package* finishes.
Use this new feature sparingly and please make sure to report any issues you find.


Dynamic scope
^^^^^^^^^^^^^

In some cases, you might want to change the scope of the fixture without changing the code.
To do that, pass a callable to ``scope``. The callable must return a string with a valid scope
and will be executed only once - during the fixture definition. It will be called with two
keyword arguments - ``fixture_name`` as a string and ``config`` with a configuration object.

This can be especially useful when dealing with fixtures that need time for setup, like spawning
a docker container. You can use the command-line argument to control the scope of the spawned
containers for different environments. See the example below.

.. code-block:: python

def determine_scope(fixture_name, config):
if config.getoption("--keep-containers"):
return "session"
return "function"


@pytest.fixture(scope=determine_scope)
def docker_container():
yield spawn_container()



Order: Higher-scoped fixtures are instantiated first
----------------------------------------------------

Expand Down
27 changes: 17 additions & 10 deletions src/_pytest/_code/code.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,15 @@
from inspect import CO_VARARGS
from inspect import CO_VARKEYWORDS
from traceback import format_exception_only
from types import CodeType
from types import TracebackType
from typing import Any
from typing import Dict
from typing import Generic
from typing import List
from typing import Optional
from typing import Pattern
from typing import Set
from typing import Tuple
from typing import TypeVar
from typing import Union
Expand All @@ -29,7 +34,7 @@
class Code:
""" wrapper around Python code objects """

def __init__(self, rawcode):
def __init__(self, rawcode) -> None:
if not hasattr(rawcode, "co_filename"):
rawcode = getrawcode(rawcode)
try:
Expand All @@ -38,7 +43,7 @@ def __init__(self, rawcode):
self.name = rawcode.co_name
except AttributeError:
raise TypeError("not a code object: {!r}".format(rawcode))
self.raw = rawcode
self.raw = rawcode # type: CodeType

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

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

Expand Down Expand Up @@ -722,7 +727,7 @@ def repr_traceback_entry(self, entry, excinfo=None):
else:
line_index = entry.lineno - entry.getfirstlinesource()

lines = []
lines = [] # type: List[str]
style = entry._repr_style
if style is None:
style = self.style
Expand Down Expand Up @@ -799,7 +804,7 @@ def _truncate_recursive_traceback(self, traceback):
exc_msg=str(e),
max_frames=max_frames,
total=len(traceback),
)
) # type: Optional[str]
traceback = traceback[:max_frames] + traceback[-max_frames:]
else:
if recursionindex is not None:
Expand All @@ -812,10 +817,12 @@ def _truncate_recursive_traceback(self, traceback):

def repr_excinfo(self, excinfo):

repr_chain = []
repr_chain = (
[]
) # type: List[Tuple[ReprTraceback, Optional[ReprFileLocation], Optional[str]]]
e = excinfo.value
descr = None
seen = set()
seen = set() # type: Set[int]
while e is not None and id(e) not in seen:
seen.add(id(e))
if excinfo:
Expand Down Expand Up @@ -868,8 +875,8 @@ def __repr__(self):


class ExceptionRepr(TerminalRepr):
def __init__(self):
self.sections = []
def __init__(self) -> None:
self.sections = [] # type: List[Tuple[str, str, str]]

def addsection(self, name, content, sep="-"):
self.sections.append((name, content, sep))
Expand Down
13 changes: 7 additions & 6 deletions src/_pytest/_code/source.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import warnings
from ast import PyCF_ONLY_AST as _AST_FLAG
from bisect import bisect_right
from typing import List

import py

Expand All @@ -19,11 +20,11 @@ class Source:
_compilecounter = 0

def __init__(self, *parts, **kwargs):
self.lines = lines = []
self.lines = lines = [] # type: List[str]
de = kwargs.get("deindent", True)
for part in parts:
if not part:
partlines = []
partlines = [] # type: List[str]
elif isinstance(part, Source):
partlines = part.lines
elif isinstance(part, (tuple, list)):
Expand Down Expand Up @@ -157,8 +158,7 @@ def compile(
source = "\n".join(self.lines) + "\n"
try:
co = compile(source, filename, mode, flag)
except SyntaxError:
ex = sys.exc_info()[1]
except SyntaxError as ex:
# re-represent syntax errors from parsing python strings
msglines = self.lines[: ex.lineno]
if ex.offset:
Expand All @@ -173,7 +173,8 @@ def compile(
if flag & _AST_FLAG:
return co
lines = [(x + "\n") for x in self.lines]
linecache.cache[filename] = (1, None, lines, filename)
# Type ignored because linecache.cache is private.
linecache.cache[filename] = (1, None, lines, filename) # type: ignore
return co


Expand Down Expand Up @@ -282,7 +283,7 @@ def get_statement_startend2(lineno, node):
return start, end


def getstatementrange_ast(lineno, source, assertion=False, astnode=None):
def getstatementrange_ast(lineno, source: Source, assertion=False, astnode=None):
if astnode is None:
content = str(source)
# See #4260:
Expand Down
11 changes: 8 additions & 3 deletions src/_pytest/assertion/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
support for presenting detailed information in failing assertions.
"""
import sys
from typing import Optional

from _pytest.assertion import rewrite
from _pytest.assertion import truncate
Expand Down Expand Up @@ -52,7 +53,9 @@ def register_assert_rewrite(*names):
importhook = hook
break
else:
importhook = DummyRewriteHook()
# TODO(typing): Add a protocol for mark_rewrite() and use it
# for importhook and for PytestPluginManager.rewrite_hook.
importhook = DummyRewriteHook() # type: ignore
importhook.mark_rewrite(*names)


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


def install_importhook(config):
Expand Down Expand Up @@ -108,6 +111,7 @@ def pytest_runtest_setup(item):
"""

def callbinrepr(op, left, right):
# type: (str, object, object) -> Optional[str]
"""Call the pytest_assertrepr_compare hook and prepare the result

This uses the first result from the hook and then ensures the
Expand All @@ -133,12 +137,13 @@ def callbinrepr(op, left, right):
if item.config.getvalue("assertmode") == "rewrite":
res = res.replace("%", "%%")
return res
return None

util._reprcompare = callbinrepr

if item.ihook.pytest_assertion_pass.get_hookimpls():

def call_assertion_pass_hook(lineno, expl, orig):
def call_assertion_pass_hook(lineno, orig, expl):
item.ihook.pytest_assertion_pass(
item=item, lineno=lineno, orig=orig, expl=expl
)
Expand Down
Loading