Merge remote-tracking branch 'upstream/master' into mm
Conflicts: src/_pytest/main.py src/_pytest/mark/structures.py src/_pytest/python.py testing/test_main.py testing/test_parseopt.py
This commit is contained in:
commit
78baa7b575
|
@ -7,6 +7,7 @@ Here is a quick checklist that should be present in PRs.
|
|||
- [ ] Target the `features` branch for new features, improvements, and removals/deprecations.
|
||||
- [ ] Include documentation when adding new features.
|
||||
- [ ] Include new tests or update existing tests when applicable.
|
||||
- [X] Allow maintainers to push and squash when merging my commits. Please uncheck this if you prefer to squash the commits yourself.
|
||||
|
||||
Unless your change is trivial or a small documentation fix (e.g., a typo or reword of a small section) please:
|
||||
|
||||
|
|
35
.travis.yml
35
.travis.yml
|
@ -1,6 +1,6 @@
|
|||
language: python
|
||||
dist: xenial
|
||||
python: '3.7'
|
||||
dist: trusty
|
||||
python: '3.5.1'
|
||||
cache: false
|
||||
|
||||
env:
|
||||
|
@ -16,36 +16,11 @@ install:
|
|||
|
||||
jobs:
|
||||
include:
|
||||
# OSX tests - first (in test stage), since they are the slower ones.
|
||||
# Coverage for:
|
||||
# - osx
|
||||
# - verbose=1
|
||||
- os: osx
|
||||
osx_image: xcode10.1
|
||||
language: generic
|
||||
env: TOXENV=py37-xdist PYTEST_COVERAGE=1 PYTEST_ADDOPTS=-v
|
||||
before_install:
|
||||
- which python3
|
||||
- python3 -V
|
||||
- ln -sfn "$(which python3)" /usr/local/bin/python
|
||||
- python -V
|
||||
- test $(python -c 'import sys; print("%d%d" % sys.version_info[0:2])') = 37
|
||||
|
||||
# Full run of latest supported version, without xdist.
|
||||
# Coverage for:
|
||||
# - pytester's LsofFdLeakChecker
|
||||
# - TestArgComplete (linux only)
|
||||
# - numpy
|
||||
# - old attrs
|
||||
# - verbose=0
|
||||
# - test_sys_breakpoint_interception (via pexpect).
|
||||
- env: TOXENV=py37-lsof-numpy-oldattrs-pexpect-twisted PYTEST_COVERAGE=1 PYTEST_ADDOPTS=
|
||||
python: '3.7'
|
||||
|
||||
# Coverage for Python 3.5.{0,1} specific code, mostly typing related.
|
||||
- env: TOXENV=py35 PYTEST_COVERAGE=1 PYTEST_ADDOPTS="-k test_raises_cyclic_reference"
|
||||
python: '3.5.1'
|
||||
dist: trusty
|
||||
before_install:
|
||||
# Work around https://github.com/jaraco/zipp/issues/40.
|
||||
- python -m pip install -U pip 'setuptools>=34.4.0' virtualenv
|
||||
|
||||
before_script:
|
||||
- |
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Assertion rewriting hooks are (re)stored for the current item, which fixes them being still used after e.g. pytester's :func:`testdir.runpytest <_pytest.pytester.Testdir.runpytest>` etc.
|
|
@ -0,0 +1 @@
|
|||
:func:`pytest.exit() <_pytest.outcomes.exit>` is handled when emitted from the :func:`pytest_sessionfinish <_pytest.hookspec.pytest_sessionfinish>` hook. This includes quitting from a debugger.
|
|
@ -162,7 +162,7 @@ html_logo = "img/pytest1.png"
|
|||
# The name of an image file (within the static path) to use as favicon of the
|
||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
html_favicon = "img/pytest1favi.ico"
|
||||
html_favicon = "img/favicon.png"
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
Binary file not shown.
Before Width: | Height: | Size: 3.7 KiB |
|
@ -901,8 +901,8 @@ Can be either a ``str`` or ``Sequence[str]``.
|
|||
pytest_plugins = ("myapp.testsupport.tools", "myapp.testsupport.regression")
|
||||
|
||||
|
||||
pytest_mark
|
||||
~~~~~~~~~~~
|
||||
pytestmark
|
||||
~~~~~~~~~~
|
||||
|
||||
**Tutorial**: :ref:`scoped-marking`
|
||||
|
||||
|
|
|
@ -72,6 +72,8 @@ class Code:
|
|||
""" return a path object pointing to source code (or a str in case
|
||||
of OSError / non-existing file).
|
||||
"""
|
||||
if not self.raw.co_filename:
|
||||
return ""
|
||||
try:
|
||||
p = py.path.local(self.raw.co_filename)
|
||||
# maybe don't try this checking
|
||||
|
|
|
@ -8,6 +8,7 @@ import warnings
|
|||
from bisect import bisect_right
|
||||
from types import CodeType
|
||||
from types import FrameType
|
||||
from typing import Any
|
||||
from typing import Iterator
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
|
@ -17,6 +18,7 @@ from typing import Union
|
|||
|
||||
import py
|
||||
|
||||
from _pytest.compat import get_real_func
|
||||
from _pytest.compat import overload
|
||||
from _pytest.compat import TYPE_CHECKING
|
||||
|
||||
|
@ -277,7 +279,7 @@ def compile_( # noqa: F811
|
|||
return s.compile(filename, mode, flags, _genframe=_genframe)
|
||||
|
||||
|
||||
def getfslineno(obj) -> Tuple[Optional[Union["Literal['']", py.path.local]], int]:
|
||||
def getfslineno(obj: Any) -> Tuple[Union[str, py.path.local], int]:
|
||||
""" Return source location (path, lineno) for the given object.
|
||||
If the source cannot be determined return ("", -1).
|
||||
|
||||
|
@ -285,6 +287,13 @@ def getfslineno(obj) -> Tuple[Optional[Union["Literal['']", py.path.local]], int
|
|||
"""
|
||||
from .code import Code
|
||||
|
||||
# xxx let decorators etc specify a sane ordering
|
||||
# NOTE: this used to be done in _pytest.compat.getfslineno, initially added
|
||||
# in 6ec13a2b9. It ("place_as") appears to be something very custom.
|
||||
obj = get_real_func(obj)
|
||||
if hasattr(obj, "place_as"):
|
||||
obj = obj.place_as
|
||||
|
||||
try:
|
||||
code = Code(obj)
|
||||
except TypeError:
|
||||
|
@ -293,18 +302,16 @@ def getfslineno(obj) -> Tuple[Optional[Union["Literal['']", py.path.local]], int
|
|||
except TypeError:
|
||||
return "", -1
|
||||
|
||||
fspath = fn and py.path.local(fn) or None
|
||||
fspath = fn and py.path.local(fn) or ""
|
||||
lineno = -1
|
||||
if fspath:
|
||||
try:
|
||||
_, lineno = findsource(obj)
|
||||
except IOError:
|
||||
pass
|
||||
return fspath, lineno
|
||||
else:
|
||||
fspath = code.path
|
||||
lineno = code.firstlineno
|
||||
assert isinstance(lineno, int)
|
||||
return fspath, lineno
|
||||
return code.path, code.firstlineno
|
||||
|
||||
|
||||
#
|
||||
|
|
|
@ -8,6 +8,7 @@ from _pytest.assertion import rewrite
|
|||
from _pytest.assertion import truncate
|
||||
from _pytest.assertion import util
|
||||
from _pytest.compat import TYPE_CHECKING
|
||||
from _pytest.config import hookimpl
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from _pytest.main import Session
|
||||
|
@ -105,7 +106,8 @@ def pytest_collection(session: "Session") -> None:
|
|||
assertstate.hook.set_session(session)
|
||||
|
||||
|
||||
def pytest_runtest_setup(item):
|
||||
@hookimpl(tryfirst=True, hookwrapper=True)
|
||||
def pytest_runtest_protocol(item):
|
||||
"""Setup the pytest_assertrepr_compare and pytest_assertion_pass hooks
|
||||
|
||||
The newinterpret and rewrite modules will use util._reprcompare if
|
||||
|
@ -143,6 +145,7 @@ def pytest_runtest_setup(item):
|
|||
return res
|
||||
return None
|
||||
|
||||
saved_assert_hooks = util._reprcompare, util._assertion_pass
|
||||
util._reprcompare = callbinrepr
|
||||
|
||||
if item.ihook.pytest_assertion_pass.get_hookimpls():
|
||||
|
@ -154,10 +157,9 @@ def pytest_runtest_setup(item):
|
|||
|
||||
util._assertion_pass = call_assertion_pass_hook
|
||||
|
||||
yield
|
||||
|
||||
def pytest_runtest_teardown(item):
|
||||
util._reprcompare = None
|
||||
util._assertion_pass = None
|
||||
util._reprcompare, util._assertion_pass = saved_assert_hooks
|
||||
|
||||
|
||||
def pytest_sessionfinish(session):
|
||||
|
|
|
@ -23,7 +23,6 @@ from typing import Union
|
|||
import attr
|
||||
import py
|
||||
|
||||
import _pytest
|
||||
from _pytest._io.saferepr import saferepr
|
||||
from _pytest.outcomes import fail
|
||||
from _pytest.outcomes import TEST_OUTCOME
|
||||
|
@ -308,16 +307,6 @@ def get_real_method(obj, holder):
|
|||
return obj
|
||||
|
||||
|
||||
def getfslineno(obj) -> Tuple[Union[str, py.path.local], int]:
|
||||
# xxx let decorators etc specify a sane ordering
|
||||
obj = get_real_func(obj)
|
||||
if hasattr(obj, "place_as"):
|
||||
obj = obj.place_as
|
||||
fslineno = _pytest._code.getfslineno(obj)
|
||||
assert isinstance(fslineno[1], int), obj
|
||||
return fslineno
|
||||
|
||||
|
||||
def getimfunc(func):
|
||||
try:
|
||||
return func.__func__
|
||||
|
|
|
@ -28,7 +28,6 @@ from pluggy import HookspecMarker
|
|||
from pluggy import PluginManager
|
||||
|
||||
import _pytest._code
|
||||
import _pytest.assertion
|
||||
import _pytest.deprecated
|
||||
import _pytest.hookspec # the extension point definitions
|
||||
from .exceptions import PrintHelp
|
||||
|
@ -284,6 +283,8 @@ class PytestPluginManager(PluginManager):
|
|||
"""
|
||||
|
||||
def __init__(self):
|
||||
import _pytest.assertion
|
||||
|
||||
super().__init__("pytest")
|
||||
# The objects are module objects, only used generically.
|
||||
self._conftest_plugins = set() # type: Set[object]
|
||||
|
@ -917,6 +918,8 @@ class Config:
|
|||
ns, unknown_args = self._parser.parse_known_and_unknown_args(args)
|
||||
mode = getattr(ns, "assertmode", "plain")
|
||||
if mode == "rewrite":
|
||||
import _pytest.assertion
|
||||
|
||||
try:
|
||||
hook = _pytest.assertion.install_importhook(self)
|
||||
except SystemError:
|
||||
|
|
|
@ -15,12 +15,12 @@ import py
|
|||
import _pytest
|
||||
from _pytest._code.code import FormattedExcinfo
|
||||
from _pytest._code.code import TerminalRepr
|
||||
from _pytest._code.source import getfslineno
|
||||
from _pytest._io import TerminalWriter
|
||||
from _pytest.compat import _format_args
|
||||
from _pytest.compat import _PytestWrapper
|
||||
from _pytest.compat import get_real_func
|
||||
from _pytest.compat import get_real_method
|
||||
from _pytest.compat import getfslineno
|
||||
from _pytest.compat import getfuncargnames
|
||||
from _pytest.compat import getimfunc
|
||||
from _pytest.compat import getlocation
|
||||
|
|
|
@ -10,6 +10,7 @@ from typing import List
|
|||
from typing import Mapping
|
||||
|
||||
import pytest
|
||||
from _pytest import nodes
|
||||
from _pytest.compat import nullcontext
|
||||
from _pytest.config import _strtobool
|
||||
from _pytest.config import create_terminal_writer
|
||||
|
@ -326,13 +327,13 @@ class LogCaptureFixture:
|
|||
logger.setLevel(level)
|
||||
|
||||
@property
|
||||
def handler(self):
|
||||
def handler(self) -> LogCaptureHandler:
|
||||
"""
|
||||
:rtype: LogCaptureHandler
|
||||
"""
|
||||
return self._item.catch_log_handler
|
||||
return self._item.catch_log_handler # type: ignore[no-any-return] # noqa: F723
|
||||
|
||||
def get_records(self, when):
|
||||
def get_records(self, when: str) -> List[logging.LogRecord]:
|
||||
"""
|
||||
Get the logging records for one of the possible test phases.
|
||||
|
||||
|
@ -346,7 +347,7 @@ class LogCaptureFixture:
|
|||
"""
|
||||
handler = self._item.catch_log_handlers.get(when)
|
||||
if handler:
|
||||
return handler.records
|
||||
return handler.records # type: ignore[no-any-return] # noqa: F723
|
||||
else:
|
||||
return []
|
||||
|
||||
|
@ -619,7 +620,9 @@ class LoggingPlugin:
|
|||
yield
|
||||
|
||||
@contextmanager
|
||||
def _runtest_for_main(self, item, when):
|
||||
def _runtest_for_main(
|
||||
self, item: nodes.Item, when: str
|
||||
) -> Generator[None, None, None]:
|
||||
"""Implements the internals of pytest_runtest_xxx() hook."""
|
||||
with catching_logs(
|
||||
LogCaptureHandler(), formatter=self.formatter, level=self.log_level
|
||||
|
@ -632,15 +635,15 @@ class LoggingPlugin:
|
|||
return
|
||||
|
||||
if not hasattr(item, "catch_log_handlers"):
|
||||
item.catch_log_handlers = {}
|
||||
item.catch_log_handlers[when] = log_handler
|
||||
item.catch_log_handler = log_handler
|
||||
item.catch_log_handlers = {} # type: ignore[attr-defined] # noqa: F821
|
||||
item.catch_log_handlers[when] = log_handler # type: ignore[attr-defined] # noqa: F821
|
||||
item.catch_log_handler = log_handler # type: ignore[attr-defined] # noqa: F821
|
||||
try:
|
||||
yield # run test
|
||||
finally:
|
||||
if when == "teardown":
|
||||
del item.catch_log_handler
|
||||
del item.catch_log_handlers
|
||||
del item.catch_log_handler # type: ignore[attr-defined] # noqa: F821
|
||||
del item.catch_log_handlers # type: ignore[attr-defined] # noqa: F821
|
||||
|
||||
if self.print_logs:
|
||||
# Add a captured log section to the report.
|
||||
|
|
|
@ -4,6 +4,7 @@ import functools
|
|||
import importlib
|
||||
import os
|
||||
import sys
|
||||
from typing import Callable
|
||||
from typing import Dict
|
||||
from typing import FrozenSet
|
||||
from typing import List
|
||||
|
@ -24,7 +25,7 @@ from _pytest.config import ExitCode
|
|||
from _pytest.config import hookimpl
|
||||
from _pytest.config import UsageError
|
||||
from _pytest.fixtures import FixtureManager
|
||||
from _pytest.outcomes import exit
|
||||
from _pytest.outcomes import Exit
|
||||
from _pytest.reports import CollectReport
|
||||
from _pytest.runner import collect_one_node
|
||||
from _pytest.runner import SetupState
|
||||
|
@ -175,7 +176,9 @@ def pytest_addoption(parser):
|
|||
)
|
||||
|
||||
|
||||
def wrap_session(config, doit):
|
||||
def wrap_session(
|
||||
config: Config, doit: Callable[[Config, "Session"], Optional[Union[int, ExitCode]]]
|
||||
) -> Union[int, ExitCode]:
|
||||
"""Skeleton command line program"""
|
||||
session = Session.from_config(config)
|
||||
session.exitstatus = ExitCode.OK
|
||||
|
@ -192,10 +195,10 @@ def wrap_session(config, doit):
|
|||
raise
|
||||
except Failed:
|
||||
session.exitstatus = ExitCode.TESTS_FAILED
|
||||
except (KeyboardInterrupt, exit.Exception):
|
||||
except (KeyboardInterrupt, Exit):
|
||||
excinfo = _pytest._code.ExceptionInfo.from_current()
|
||||
exitstatus = ExitCode.INTERRUPTED
|
||||
if isinstance(excinfo.value, exit.Exception):
|
||||
exitstatus = ExitCode.INTERRUPTED # type: Union[int, ExitCode]
|
||||
if isinstance(excinfo.value, Exit):
|
||||
if excinfo.value.returncode is not None:
|
||||
exitstatus = excinfo.value.returncode
|
||||
if initstate < 2:
|
||||
|
@ -209,7 +212,7 @@ def wrap_session(config, doit):
|
|||
excinfo = _pytest._code.ExceptionInfo.from_current()
|
||||
try:
|
||||
config.notify_exception(excinfo, config.option)
|
||||
except exit.Exception as exc:
|
||||
except Exit as exc:
|
||||
if exc.returncode is not None:
|
||||
session.exitstatus = exc.returncode
|
||||
sys.stderr.write("{}: {}\n".format(type(exc).__name__, exc))
|
||||
|
@ -218,12 +221,18 @@ def wrap_session(config, doit):
|
|||
sys.stderr.write("mainloop: caught unexpected SystemExit!\n")
|
||||
|
||||
finally:
|
||||
excinfo = None # Explicitly break reference cycle.
|
||||
# Explicitly break reference cycle.
|
||||
excinfo = None # type: ignore
|
||||
session.startdir.chdir()
|
||||
if initstate >= 2:
|
||||
config.hook.pytest_sessionfinish(
|
||||
session=session, exitstatus=session.exitstatus
|
||||
)
|
||||
try:
|
||||
config.hook.pytest_sessionfinish(
|
||||
session=session, exitstatus=session.exitstatus
|
||||
)
|
||||
except Exit as exc:
|
||||
if exc.returncode is not None:
|
||||
session.exitstatus = exc.returncode
|
||||
sys.stderr.write("{}: {}\n".format(type(exc).__name__, exc))
|
||||
config._ensure_unconfigure()
|
||||
return session.exitstatus
|
||||
|
||||
|
@ -363,6 +372,7 @@ class Session(nodes.FSCollector):
|
|||
_setupstate = None # type: SetupState
|
||||
# Set on the session by fixtures.pytest_sessionstart.
|
||||
_fixturemanager = None # type: FixtureManager
|
||||
exitstatus = None # type: Union[int, ExitCode]
|
||||
|
||||
def __init__(self, config: Config) -> None:
|
||||
nodes.FSCollector.__init__(
|
||||
|
|
|
@ -2,14 +2,16 @@ import inspect
|
|||
import warnings
|
||||
from collections import namedtuple
|
||||
from collections.abc import MutableMapping
|
||||
from typing import Iterable
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
from typing import Set
|
||||
from typing import Union
|
||||
|
||||
import attr
|
||||
|
||||
from .._code.source import getfslineno
|
||||
from ..compat import ascii_escaped
|
||||
from ..compat import getfslineno
|
||||
from ..compat import NOTSET
|
||||
from _pytest.outcomes import fail
|
||||
from _pytest.warning_types import PytestUnknownMarkWarning
|
||||
|
@ -270,7 +272,7 @@ def get_unpacked_marks(obj):
|
|||
return normalize_mark_list(mark_list)
|
||||
|
||||
|
||||
def normalize_mark_list(mark_list):
|
||||
def normalize_mark_list(mark_list: Iterable[Union[Mark, MarkDecorator]]) -> List[Mark]:
|
||||
"""
|
||||
normalizes marker decorating helpers to mark objects
|
||||
|
||||
|
|
|
@ -15,8 +15,8 @@ import _pytest._code
|
|||
from _pytest._code.code import ExceptionChainRepr
|
||||
from _pytest._code.code import ExceptionInfo
|
||||
from _pytest._code.code import ReprExceptionInfo
|
||||
from _pytest._code.source import getfslineno
|
||||
from _pytest.compat import cached_property
|
||||
from _pytest.compat import getfslineno
|
||||
from _pytest.compat import TYPE_CHECKING
|
||||
from _pytest.config import Config
|
||||
from _pytest.config import PytestPluginManager
|
||||
|
@ -361,7 +361,9 @@ class Node(metaclass=NodeMeta):
|
|||
return self._repr_failure_py(excinfo, style)
|
||||
|
||||
|
||||
def get_fslocation_from_item(item):
|
||||
def get_fslocation_from_item(
|
||||
item: "Item",
|
||||
) -> Tuple[Union[str, py.path.local], Optional[int]]:
|
||||
"""Tries to extract the actual location from an item, depending on available attributes:
|
||||
|
||||
* "fslocation": a pair (path, lineno)
|
||||
|
@ -370,9 +372,10 @@ def get_fslocation_from_item(item):
|
|||
|
||||
:rtype: a tuple of (str|LocalPath, int) with filename and line number.
|
||||
"""
|
||||
result = getattr(item, "location", None)
|
||||
if result is not None:
|
||||
return result[:2]
|
||||
try:
|
||||
return item.location[:2]
|
||||
except AttributeError:
|
||||
pass
|
||||
obj = getattr(item, "obj", None)
|
||||
if obj is not None:
|
||||
return getfslineno(obj)
|
||||
|
|
|
@ -610,14 +610,14 @@ class Testdir:
|
|||
"""
|
||||
self.tmpdir.chdir()
|
||||
|
||||
def _makefile(self, ext, args, kwargs, encoding="utf-8"):
|
||||
items = list(kwargs.items())
|
||||
def _makefile(self, ext, lines, files, encoding="utf-8"):
|
||||
items = list(files.items())
|
||||
|
||||
def to_text(s):
|
||||
return s.decode(encoding) if isinstance(s, bytes) else str(s)
|
||||
|
||||
if args:
|
||||
source = "\n".join(to_text(x) for x in args)
|
||||
if lines:
|
||||
source = "\n".join(to_text(x) for x in lines)
|
||||
basename = self.request.function.__name__
|
||||
items.insert(0, (basename, source))
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ from collections import Counter
|
|||
from collections import defaultdict
|
||||
from collections.abc import Sequence
|
||||
from functools import partial
|
||||
from typing import Dict
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
from typing import Tuple
|
||||
|
@ -21,10 +22,10 @@ from _pytest import fixtures
|
|||
from _pytest import nodes
|
||||
from _pytest._code import filter_traceback
|
||||
from _pytest._code.code import ExceptionInfo
|
||||
from _pytest._code.source import getfslineno
|
||||
from _pytest.compat import ascii_escaped
|
||||
from _pytest.compat import get_default_arg_names
|
||||
from _pytest.compat import get_real_func
|
||||
from _pytest.compat import getfslineno
|
||||
from _pytest.compat import getimfunc
|
||||
from _pytest.compat import getlocation
|
||||
from _pytest.compat import is_generator
|
||||
|
@ -37,6 +38,7 @@ from _pytest.compat import STRING_TYPES
|
|||
from _pytest.config import hookimpl
|
||||
from _pytest.deprecated import FUNCARGNAMES
|
||||
from _pytest.mark import MARK_GEN
|
||||
from _pytest.mark import ParameterSet
|
||||
from _pytest.mark.structures import get_unpacked_marks
|
||||
from _pytest.mark.structures import Mark
|
||||
from _pytest.mark.structures import normalize_mark_list
|
||||
|
@ -392,7 +394,7 @@ class PyCollector(PyobjMixin, nodes.Collector):
|
|||
fm = self.session._fixturemanager
|
||||
|
||||
definition = FunctionDefinition.from_parent(self, name=name, callobj=funcobj)
|
||||
fixtureinfo = fm.getfixtureinfo(definition, funcobj, cls)
|
||||
fixtureinfo = definition._fixtureinfo
|
||||
|
||||
metafunc = Metafunc(
|
||||
definition, fixtureinfo, self.config, cls=cls, module=module
|
||||
|
@ -931,7 +933,6 @@ class Metafunc:
|
|||
to set a dynamic scope using test context or configuration.
|
||||
"""
|
||||
from _pytest.fixtures import scope2index
|
||||
from _pytest.mark import ParameterSet
|
||||
|
||||
argnames, parameters = ParameterSet._for_parametrize(
|
||||
argnames,
|
||||
|
@ -992,7 +993,9 @@ class Metafunc:
|
|||
newcalls.append(newcallspec)
|
||||
self._calls = newcalls
|
||||
|
||||
def _resolve_arg_ids(self, argnames, ids, parameters, item):
|
||||
def _resolve_arg_ids(
|
||||
self, argnames: List[str], ids, parameters: List[ParameterSet], item: nodes.Item
|
||||
):
|
||||
"""Resolves the actual ids for the given argnames, based on the ``ids`` parameter given
|
||||
to ``parametrize``.
|
||||
|
||||
|
@ -1045,7 +1048,7 @@ class Metafunc:
|
|||
)
|
||||
return new_ids
|
||||
|
||||
def _resolve_arg_value_types(self, argnames, indirect):
|
||||
def _resolve_arg_value_types(self, argnames: List[str], indirect) -> Dict[str, str]:
|
||||
"""Resolves if each parametrized argument must be considered a parameter to a fixture or a "funcarg"
|
||||
to the function, based on the ``indirect`` parameter of the parametrized() call.
|
||||
|
||||
|
|
|
@ -104,6 +104,8 @@ class TestGeneralUsage:
|
|||
|
||||
@pytest.mark.parametrize("load_cov_early", [True, False])
|
||||
def test_early_load_setuptools_name(self, testdir, monkeypatch, load_cov_early):
|
||||
monkeypatch.delenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD")
|
||||
|
||||
testdir.makepyfile(mytestplugin1_module="")
|
||||
testdir.makepyfile(mytestplugin2_module="")
|
||||
testdir.makepyfile(mycov_module="")
|
||||
|
|
|
@ -524,6 +524,14 @@ def test_getfslineno() -> None:
|
|||
B.__name__ = "B2"
|
||||
assert getfslineno(B)[1] == -1
|
||||
|
||||
co = compile("...", "", "eval")
|
||||
assert co.co_filename == ""
|
||||
|
||||
if hasattr(sys, "pypy_version_info"):
|
||||
assert getfslineno(co) == ("", -1)
|
||||
else:
|
||||
assert getfslineno(co) == ("", 0)
|
||||
|
||||
|
||||
def test_code_of_object_instance_with_call() -> None:
|
||||
class A:
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import sys
|
||||
|
||||
import pytest
|
||||
from _pytest.pytester import Testdir
|
||||
|
||||
if sys.gettrace():
|
||||
|
||||
|
@ -118,3 +119,9 @@ def dummy_yaml_custom_test(testdir):
|
|||
"""
|
||||
)
|
||||
testdir.makefile(".yaml", test1="")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def testdir(testdir: Testdir) -> Testdir:
|
||||
testdir.monkeypatch.setenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD", "1")
|
||||
return testdir
|
||||
|
|
|
@ -72,10 +72,19 @@ class TestImportHookInstallation:
|
|||
result = testdir.runpytest_subprocess()
|
||||
result.stdout.fnmatch_lines(
|
||||
[
|
||||
"E * AssertionError: ([[][]], [[][]], [[]<TestReport *>[]])*",
|
||||
"E * assert"
|
||||
" {'failed': 1, 'passed': 0, 'skipped': 0} =="
|
||||
" {'failed': 0, 'passed': 1, 'skipped': 0}",
|
||||
"> r.assertoutcome(passed=1)",
|
||||
"E AssertionError: ([[][]], [[][]], [[]<TestReport *>[]])*",
|
||||
"E assert {'failed': 1,... 'skipped': 0} == {'failed': 0,... 'skipped': 0}",
|
||||
"E Omitting 1 identical items, use -vv to show",
|
||||
"E Differing items:",
|
||||
"E Use -v to get the full diff",
|
||||
]
|
||||
)
|
||||
# XXX: unstable output.
|
||||
result.stdout.fnmatch_lines_random(
|
||||
[
|
||||
"E {'failed': 1} != {'failed': 0}",
|
||||
"E {'passed': 0} != {'passed': 1}",
|
||||
]
|
||||
)
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ from _pytest.config import ExitCode
|
|||
|
||||
|
||||
def test_version(testdir, pytestconfig):
|
||||
testdir.monkeypatch.delenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD")
|
||||
result = testdir.runpytest("--version")
|
||||
assert result.ret == 0
|
||||
# p = py.path.local(py.__file__).dirpath()
|
||||
|
|
|
@ -1300,6 +1300,7 @@ def test_runs_twice(testdir, run_and_parse):
|
|||
|
||||
def test_runs_twice_xdist(testdir, run_and_parse):
|
||||
pytest.importorskip("xdist")
|
||||
testdir.monkeypatch.delenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD")
|
||||
f = testdir.makepyfile(
|
||||
"""
|
||||
def test_pass():
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
from typing import Optional
|
||||
|
||||
import pytest
|
||||
from _pytest.config import ExitCode
|
||||
from _pytest.pytester import Testdir
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
@ -50,3 +53,25 @@ def test_wrap_session_notify_exception(ret_exc, testdir):
|
|||
assert result.stderr.lines == ["mainloop: caught unexpected SystemExit!"]
|
||||
else:
|
||||
assert result.stderr.lines == ["Exit: exiting after {}...".format(exc.__name__)]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("returncode", (None, 42))
|
||||
def test_wrap_session_exit_sessionfinish(
|
||||
returncode: Optional[int], testdir: Testdir
|
||||
) -> None:
|
||||
testdir.makeconftest(
|
||||
"""
|
||||
import pytest
|
||||
def pytest_sessionfinish():
|
||||
pytest.exit(msg="exit_pytest_sessionfinish", returncode={returncode})
|
||||
""".format(
|
||||
returncode=returncode
|
||||
)
|
||||
)
|
||||
result = testdir.runpytest()
|
||||
if returncode:
|
||||
assert result.ret == returncode
|
||||
else:
|
||||
assert result.ret == ExitCode.NO_TESTS_COLLECTED
|
||||
assert result.stdout.lines[-1] == "collected 0 items"
|
||||
assert result.stderr.lines == ["Exit: exit_pytest_sessionfinish"]
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
"""
|
||||
Test importing of all internal packages and modules.
|
||||
|
||||
This ensures all internal packages can be imported without needing the pytest
|
||||
namespace being set, which is critical for the initialization of xdist.
|
||||
"""
|
||||
import pkgutil
|
||||
import subprocess
|
||||
import sys
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
import subprocess
|
||||
import sys
|
||||
|
||||
import py
|
||||
|
||||
import _pytest
|
||||
import pytest
|
||||
|
||||
pytestmark = pytest.mark.slow
|
||||
|
||||
MODSET = [
|
||||
x
|
||||
for x in py.path.local(_pytest.__file__).dirpath().visit("*.py")
|
||||
if x.purebasename != "__init__"
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("modfile", MODSET, ids=lambda x: x.purebasename)
|
||||
def test_fileimport(modfile):
|
||||
# this test ensures all internal packages can import
|
||||
# without needing the pytest namespace being set
|
||||
# this is critical for the initialization of xdist
|
||||
|
||||
p = subprocess.Popen(
|
||||
[
|
||||
sys.executable,
|
||||
"-c",
|
||||
"import sys, py; py.path.local(sys.argv[1]).pyimport()",
|
||||
modfile.strpath,
|
||||
],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
)
|
||||
(out, err) = p.communicate()
|
||||
assert p.returncode == 0, "importing %s failed (exitcode %d): out=%r, err=%r" % (
|
||||
modfile,
|
||||
p.returncode,
|
||||
out,
|
||||
err,
|
||||
)
|
|
@ -1,7 +1,7 @@
|
|||
import argparse
|
||||
import distutils.spawn
|
||||
import os
|
||||
import shlex
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
import py
|
||||
|
@ -288,7 +288,7 @@ class TestParser:
|
|||
|
||||
|
||||
def test_argcomplete(testdir, monkeypatch) -> None:
|
||||
if not distutils.spawn.find_executable("bash"):
|
||||
if not shutil.which("bash"):
|
||||
pytest.skip("bash not available")
|
||||
script = str(testdir.tmpdir.join("test_argcomplete"))
|
||||
|
||||
|
|
|
@ -606,6 +606,7 @@ class TestTerminalFunctional:
|
|||
assert result.ret == 0
|
||||
|
||||
def test_header_trailer_info(self, testdir, request):
|
||||
testdir.monkeypatch.delenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD")
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
def test_passes():
|
||||
|
@ -736,6 +737,7 @@ class TestTerminalFunctional:
|
|||
if not pytestconfig.pluginmanager.get_plugin("xdist"):
|
||||
pytest.skip("xdist plugin not installed")
|
||||
|
||||
testdir.monkeypatch.delenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD")
|
||||
result = testdir.runpytest(
|
||||
verbose_testfile, "-v", "-n 1", "-Walways::pytest.PytestWarning"
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue