From 9342778f0192b705c83ef4462b0fa6d754674eb0 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Wed, 5 Dec 2018 15:04:59 +0100 Subject: [PATCH 01/19] fix #4507 and ensure our modules have no warnigns for general import --- src/_pytest/cacheprovider.py | 11 +++++---- src/_pytest/capture.py | 35 ++++++++++++++++------------- src/_pytest/config/__init__.py | 5 +++-- src/_pytest/doctest.py | 17 ++++++++------ src/_pytest/helpconfig.py | 10 +++++++-- src/_pytest/junitxml.py | 8 +++---- src/_pytest/logging.py | 27 +++++++++++----------- src/_pytest/monkeypatch.py | 6 ++--- src/_pytest/pastebin.py | 4 ++-- src/_pytest/pytester.py | 41 ++++++++++++++++++++-------------- src/_pytest/setuponly.py | 6 ++--- src/_pytest/setupplan.py | 6 ++--- src/_pytest/stepwise.py | 4 ++-- src/_pytest/terminal.py | 15 ++++++++----- src/_pytest/tmpdir.py | 17 +++++++------- src/_pytest/warnings.py | 11 ++++----- 16 files changed, 126 insertions(+), 97 deletions(-) diff --git a/src/_pytest/cacheprovider.py b/src/_pytest/cacheprovider.py index 22ce578fc1d..987af832f68 100755 --- a/src/_pytest/cacheprovider.py +++ b/src/_pytest/cacheprovider.py @@ -16,12 +16,15 @@ import py import six -import pytest from .compat import _PY2 as PY2 +from .config import hookimpl +from .fixtures import fixture +from .nodes import Item from .pathlib import Path from .pathlib import resolve_from_str from .pathlib import rmtree + README_CONTENT = u"""\ # pytest cache directory # @@ -233,7 +236,7 @@ def pytest_collection_modifyitems(self, session, config, items): items[:] = self._get_increasing_order( six.itervalues(new_items) ) + self._get_increasing_order(six.itervalues(other_items)) - self.cached_nodeids = [x.nodeid for x in items if isinstance(x, pytest.Item)] + self.cached_nodeids = [x.nodeid for x in items if isinstance(x, Item)] def _get_increasing_order(self, items): return sorted(items, key=lambda item: item.fspath.mtime(), reverse=True) @@ -308,14 +311,14 @@ def pytest_cmdline_main(config): return wrap_session(config, cacheshow) -@pytest.hookimpl(tryfirst=True) +@hookimpl(tryfirst=True) def pytest_configure(config): config.cache = Cache.for_config(config) config.pluginmanager.register(LFPlugin(config), "lfplugin") config.pluginmanager.register(NFPlugin(config), "nfplugin") -@pytest.fixture +@fixture def cache(request): """ Return a cache object that can persist state between testing sessions. diff --git a/src/_pytest/capture.py b/src/_pytest/capture.py index 376b4f87bcc..b218ffa31bb 100644 --- a/src/_pytest/capture.py +++ b/src/_pytest/capture.py @@ -16,7 +16,10 @@ import six -import pytest +from .config import hookimpl +from .fixtures import fixture +from .nodes import File +from .outcomes import skip from _pytest.compat import CaptureIO patchsysdict = {0: "stdin", 1: "stdout", 2: "stderr"} @@ -41,7 +44,7 @@ def pytest_addoption(parser): ) -@pytest.hookimpl(hookwrapper=True) +@hookimpl(hookwrapper=True) def pytest_load_initial_conftests(early_config, parser, args): ns = early_config.known_args_namespace if ns.capture == "fd": @@ -186,9 +189,9 @@ def item_capture(self, when, item): # Hooks - @pytest.hookimpl(hookwrapper=True) + @hookimpl(hookwrapper=True) def pytest_make_collect_report(self, collector): - if isinstance(collector, pytest.File): + if isinstance(collector, File): self.resume_global_capture() outcome = yield self.suspend_global_capture() @@ -201,32 +204,32 @@ def pytest_make_collect_report(self, collector): else: yield - @pytest.hookimpl(hookwrapper=True) + @hookimpl(hookwrapper=True) def pytest_runtest_protocol(self, item): self._current_item = item yield self._current_item = None - @pytest.hookimpl(hookwrapper=True) + @hookimpl(hookwrapper=True) def pytest_runtest_setup(self, item): with self.item_capture("setup", item): yield - @pytest.hookimpl(hookwrapper=True) + @hookimpl(hookwrapper=True) def pytest_runtest_call(self, item): with self.item_capture("call", item): yield - @pytest.hookimpl(hookwrapper=True) + @hookimpl(hookwrapper=True) def pytest_runtest_teardown(self, item): with self.item_capture("teardown", item): yield - @pytest.hookimpl(tryfirst=True) + @hookimpl(tryfirst=True) def pytest_keyboard_interrupt(self, excinfo): self.stop_global_capturing() - @pytest.hookimpl(tryfirst=True) + @hookimpl(tryfirst=True) def pytest_internalerror(self, excinfo): self.stop_global_capturing() @@ -244,7 +247,7 @@ def _ensure_only_one_capture_fixture(request, name): ) -@pytest.fixture +@fixture def capsys(request): """Enable capturing of writes to ``sys.stdout`` and ``sys.stderr`` and make captured output available via ``capsys.readouterr()`` method calls @@ -256,7 +259,7 @@ def capsys(request): yield fixture -@pytest.fixture +@fixture def capsysbinary(request): """Enable capturing of writes to ``sys.stdout`` and ``sys.stderr`` and make captured output available via ``capsys.readouterr()`` method calls @@ -272,7 +275,7 @@ def capsysbinary(request): yield fixture -@pytest.fixture +@fixture def capfd(request): """Enable capturing of writes to file descriptors ``1`` and ``2`` and make captured output available via ``capfd.readouterr()`` method calls @@ -281,14 +284,14 @@ def capfd(request): """ _ensure_only_one_capture_fixture(request, "capfd") if not hasattr(os, "dup"): - pytest.skip( + skip( "capfd fixture needs os.dup function which is not available in this system" ) with _install_capture_fixture_on_item(request, FDCapture) as fixture: yield fixture -@pytest.fixture +@fixture def capfdbinary(request): """Enable capturing of write to file descriptors 1 and 2 and make captured output available via ``capfdbinary.readouterr`` method calls @@ -297,7 +300,7 @@ def capfdbinary(request): """ _ensure_only_one_capture_fixture(request, "capfdbinary") if not hasattr(os, "dup"): - pytest.skip( + skip( "capfdbinary fixture needs os.dup function which is not available in this system" ) with _install_capture_fixture_on_item(request, FDCaptureBinary) as fixture: diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index 562b50c3892..f76be219581 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -20,7 +20,6 @@ from pluggy import PluginManager import _pytest._code -import _pytest.assertion import _pytest.hookspec # the extension point definitions from .exceptions import PrintHelp from .exceptions import UsageError @@ -234,7 +233,9 @@ def __init__(self): self.enable_tracing() # Config._consider_importhook will set a real object if required. - self.rewrite_hook = _pytest.assertion.DummyRewriteHook() + from _pytest.assertion import DummyRewriteHook + + self.rewrite_hook = DummyRewriteHook() # Used to know when we are importing conftests after the pytest_configure stage self._configured = False diff --git a/src/_pytest/doctest.py b/src/_pytest/doctest.py index dbf7df82365..260b0b5cbae 100644 --- a/src/_pytest/doctest.py +++ b/src/_pytest/doctest.py @@ -7,11 +7,14 @@ import sys import traceback -import pytest from _pytest._code.code import ExceptionInfo from _pytest._code.code import ReprFileLocation from _pytest._code.code import TerminalRepr +from _pytest.fixtures import fixture from _pytest.fixtures import FixtureRequest +from _pytest.nodes import Item +from _pytest.outcomes import skip +from _pytest.python import Module DOCTEST_REPORT_CHOICE_NONE = "none" @@ -173,7 +176,7 @@ def _get_runner(checker=None, verbose=None, optionflags=0, continue_on_failure=T ) -class DoctestItem(pytest.Item): +class DoctestItem(Item): def __init__(self, name, parent, runner=None, dtest=None): super(DoctestItem, self).__init__(name, parent) self.runner = runner @@ -305,7 +308,7 @@ def _get_continue_on_failure(config): return continue_on_failure -class DoctestTextfile(pytest.Module): +class DoctestTextfile(Module): obj = None def collect(self): @@ -343,10 +346,10 @@ def _check_all_skipped(test): all_skipped = all(x.options.get(doctest.SKIP, False) for x in test.examples) if all_skipped: - pytest.skip("all tests skipped by +SKIP option") + skip("all tests skipped by +SKIP option") -class DoctestModule(pytest.Module): +class DoctestModule(Module): def collect(self): import doctest @@ -357,7 +360,7 @@ def collect(self): module = self.fspath.pyimport() except ImportError: if self.config.getvalue("doctest_ignore_import_errors"): - pytest.skip("unable to import module %r" % self.fspath) + skip("unable to import module %r" % self.fspath) else: raise # uses internal doctest module parsing mechanism @@ -508,7 +511,7 @@ def getvalue(self): runner._fakeout = UnicodeSpoof() -@pytest.fixture(scope="session") +@fixture(scope="session") def doctest_namespace(): """ Fixture that returns a :py:class:`dict` that will be injected into the namespace of doctests. diff --git a/src/_pytest/helpconfig.py b/src/_pytest/helpconfig.py index 5e60d2a7f94..53df8b7cc67 100644 --- a/src/_pytest/helpconfig.py +++ b/src/_pytest/helpconfig.py @@ -9,7 +9,7 @@ import py -import pytest +from _pytest.config import hookimpl from _pytest.config import PrintHelp @@ -87,11 +87,13 @@ def pytest_addoption(parser): ) -@pytest.hookimpl(hookwrapper=True) +@hookimpl(hookwrapper=True) def pytest_cmdline_parse(): outcome = yield config = outcome.get_result() if config.option.debug: + import pytest + path = os.path.abspath("pytestdebug.log") debugfile = open(path, "w") debugfile.write( @@ -120,6 +122,8 @@ def unset_tracing(): def pytest_cmdline_main(config): if config.option.version: + import pytest + p = py.path.local(pytest.__file__) sys.stderr.write( "This is pytest version %s, imported from %s\n" % (pytest.__version__, p) @@ -198,6 +202,8 @@ def getpluginversioninfo(config): def pytest_report_header(config): lines = [] + import pytest + if config.option.debug or config.option.traceconfig: lines.append("using: pytest-%s pylib-%s" % (pytest.__version__, py.__version__)) diff --git a/src/_pytest/junitxml.py b/src/_pytest/junitxml.py index 09847c942da..af4e1291cdb 100644 --- a/src/_pytest/junitxml.py +++ b/src/_pytest/junitxml.py @@ -20,9 +20,9 @@ import py -import pytest from _pytest import nodes from _pytest.config import filename_arg +from _pytest.fixtures import fixture # Python 2.X and 3.X compatibility if sys.version_info[0] < 3: @@ -243,7 +243,7 @@ def finalize(self): self.to_xml = lambda: py.xml.raw(data) -@pytest.fixture +@fixture def record_property(request): """Add an extra properties the calling test. User properties become part of the test report and are available to the @@ -263,7 +263,7 @@ def append_property(name, value): return append_property -@pytest.fixture +@fixture def record_xml_property(record_property, request): """(Deprecated) use record_property.""" from _pytest import deprecated @@ -273,7 +273,7 @@ def record_xml_property(record_property, request): return record_property -@pytest.fixture +@fixture def record_xml_attribute(request): """Add extra xml attributes to the tag for the calling test. The fixture is callable with ``(name, value)``, with value being diff --git a/src/_pytest/logging.py b/src/_pytest/logging.py index 45fd5950dec..29a0fb0af93 100644 --- a/src/_pytest/logging.py +++ b/src/_pytest/logging.py @@ -10,10 +10,11 @@ import py import six -import pytest +from .config import hookimpl +from .fixtures import fixture from _pytest.compat import dummy_context_manager from _pytest.config import create_terminal_writer - +from _pytest.config import UsageError DEFAULT_LOG_FORMAT = "%(filename)-25s %(lineno)4d %(levelname)-8s %(message)s" DEFAULT_LOG_DATE_FORMAT = "%H:%M:%S" @@ -329,7 +330,7 @@ def at_level(self, level, logger=None): logger.setLevel(orig_level) -@pytest.fixture +@fixture def caplog(request): """Access and control log capturing. @@ -363,7 +364,7 @@ def get_actual_log_level(config, *setting_names): return int(getattr(logging, log_level, log_level)) except ValueError: # Python logging does not recognise this as a logging level - raise pytest.UsageError( + raise UsageError( "'{}' is not recognized as a logging level name for " "'{}'. Please consider passing the " "logging level num instead.".format(log_level, setting_name) @@ -428,7 +429,7 @@ def _log_cli_enabled(self): "--log-cli-level" ) is not None or self._config.getini("log_cli") - @pytest.hookimpl(hookwrapper=True, tryfirst=True) + @hookimpl(hookwrapper=True, tryfirst=True) def pytest_collection(self): # This has to be called before the first log message is logged, # so we can access the terminal reporter plugin. @@ -473,34 +474,34 @@ def _runtest_for(self, item, when): log = log_handler.stream.getvalue().strip() item.add_report_section(when, "log", log) - @pytest.hookimpl(hookwrapper=True) + @hookimpl(hookwrapper=True) def pytest_runtest_setup(self, item): with self._runtest_for(item, "setup"): yield - @pytest.hookimpl(hookwrapper=True) + @hookimpl(hookwrapper=True) def pytest_runtest_call(self, item): with self._runtest_for(item, "call"): yield - @pytest.hookimpl(hookwrapper=True) + @hookimpl(hookwrapper=True) def pytest_runtest_teardown(self, item): with self._runtest_for(item, "teardown"): yield - @pytest.hookimpl(hookwrapper=True) + @hookimpl(hookwrapper=True) def pytest_runtest_logstart(self): if self.log_cli_handler: self.log_cli_handler.reset() with self._runtest_for(None, "start"): yield - @pytest.hookimpl(hookwrapper=True) + @hookimpl(hookwrapper=True) def pytest_runtest_logfinish(self): with self._runtest_for(None, "finish"): yield - @pytest.hookimpl(hookwrapper=True, tryfirst=True) + @hookimpl(hookwrapper=True, tryfirst=True) def pytest_sessionfinish(self): with self.live_logs_context(): if self.log_cli_handler: @@ -511,7 +512,7 @@ def pytest_sessionfinish(self): else: yield - @pytest.hookimpl(hookwrapper=True, tryfirst=True) + @hookimpl(hookwrapper=True, tryfirst=True) def pytest_sessionstart(self): self._setup_cli_logging() with self.live_logs_context(): @@ -523,7 +524,7 @@ def pytest_sessionstart(self): else: yield - @pytest.hookimpl(hookwrapper=True) + @hookimpl(hookwrapper=True) def pytest_runtestloop(self, session): """Runs all collected test items.""" with self.live_logs_context(): diff --git a/src/_pytest/monkeypatch.py b/src/_pytest/monkeypatch.py index 2c81de177a5..53fdaf31c44 100644 --- a/src/_pytest/monkeypatch.py +++ b/src/_pytest/monkeypatch.py @@ -11,7 +11,7 @@ import six -import pytest +from .warning_types import PytestWarning from _pytest.fixtures import fixture from _pytest.pathlib import Path @@ -219,7 +219,7 @@ def _warn_if_env_name_is_not_str(self, name): """On Python 2, warn if the given environment variable name is not a native str (#4056)""" if six.PY2 and not isinstance(name, str): warnings.warn( - pytest.PytestWarning( + PytestWarning( "Environment variable name {!r} should be str".format(name) ) ) @@ -230,7 +230,7 @@ def setenv(self, name, value, prepend=None): and prepend the ``value`` adjoined with the ``prepend`` character.""" if not isinstance(value, str): warnings.warn( - pytest.PytestWarning( + PytestWarning( "Value of environment variable {name} type should be str, but got " "{value!r} (type: {type}); converted to str implicitly".format( name=name, value=value, type=type(value).__name__ diff --git a/src/_pytest/pastebin.py b/src/_pytest/pastebin.py index 9559e3265a2..160416490a3 100644 --- a/src/_pytest/pastebin.py +++ b/src/_pytest/pastebin.py @@ -8,7 +8,7 @@ import six -import pytest +from _pytest.config import hookimpl def pytest_addoption(parser): @@ -24,7 +24,7 @@ def pytest_addoption(parser): ) -@pytest.hookimpl(trylast=True) +@hookimpl(trylast=True) def pytest_configure(config): if config.option.pastebin == "all": tr = config.pluginmanager.getplugin("terminalreporter") diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py index c5962894845..ffbb0100aa3 100644 --- a/src/_pytest/pytester.py +++ b/src/_pytest/pytester.py @@ -18,16 +18,21 @@ import py import six -import pytest from _pytest._code import Source -from _pytest.assertion.rewrite import AssertionRewritingHook from _pytest.capture import MultiCapture from _pytest.capture import SysCapture from _pytest.compat import safe_str +from _pytest.config import hookimpl +from _pytest.fixtures import fixture from _pytest.main import EXIT_INTERRUPTED from _pytest.main import EXIT_OK from _pytest.main import Session +from _pytest.outcomes import fail +from _pytest.outcomes import importorskip +from _pytest.outcomes import skip +from _pytest.outcomes import xfail from _pytest.pathlib import Path +from _pytest.warning_types import PytestWarning IGNORE_PAM = [ # filenames added when obtaining details about the current user u"/var/lib/sss/mc/passwd" @@ -115,7 +120,7 @@ def matching_platform(self): else: return True - @pytest.hookimpl(hookwrapper=True, tryfirst=True) + @hookimpl(hookwrapper=True, tryfirst=True) def pytest_runtest_protocol(self, item): lines1 = self.get_open_files() yield @@ -136,7 +141,7 @@ def pytest_runtest_protocol(self, item): error.append(error[0]) error.append("*** function %s:%s: %s " % item.location) error.append("See issue #2366") - item.warn(pytest.PytestWarning("\n".join(error))) + item.warn(PytestWarning("\n".join(error))) # XXX copied from execnet's conftest.py - needs to be merged @@ -174,7 +179,7 @@ def getexecutable(name, cache={}): return executable -@pytest.fixture(params=["python2.7", "python3.4", "pypy", "pypy3"]) +@fixture(params=["python2.7", "python3.4", "pypy", "pypy3"]) def anypython(request): name = request.param executable = getexecutable(name) @@ -185,14 +190,14 @@ def anypython(request): executable = py.path.local(executable) if executable.check(): return executable - pytest.skip("no suitable %s found" % (name,)) + skip("no suitable %s found" % (name,)) return executable # used at least by pytest-xdist plugin -@pytest.fixture +@fixture def _pytest(request): """Return a helper which offers a gethookrecorder(hook) method which returns a HookRecorder instance which helps to make assertions about called @@ -275,7 +280,7 @@ def assert_contains(self, entries): break print("NONAMEMATCH", name, "with", call) else: - pytest.fail("could not find %r check %r" % (name, check)) + fail("could not find %r check %r" % (name, check)) def popcall(self, name): __tracebackhide__ = True @@ -285,7 +290,7 @@ def popcall(self, name): return call lines = ["could not find call %r, in:" % (name,)] lines.extend([" %s" % x for x in self.calls]) - pytest.fail("\n".join(lines)) + fail("\n".join(lines)) def getcall(self, name): values = self.getcalls(name) @@ -360,17 +365,17 @@ def clear(self): self.calls[:] = [] -@pytest.fixture +@fixture def linecomp(request): return LineComp() -@pytest.fixture(name="LineMatcher") +@fixture(name="LineMatcher") def LineMatcher_fixture(request): return LineMatcher -@pytest.fixture +@fixture def testdir(request, tmpdir_factory): return Testdir(request, tmpdir_factory) @@ -820,6 +825,8 @@ def inline_run(self, *args, **kwargs): """ finalizers = [] try: + from _pytest.assertion.rewrite import AssertionRewritingHook + # When running pytest inline any plugins active in the main test # process are already imported. So this disables the warning which # will trigger to say they can no longer be rewritten, which is @@ -853,7 +860,7 @@ def pytest_configure(x, config): plugins = kwargs.get("plugins") or [] plugins.append(Collect()) - ret = pytest.main(list(args), plugins=plugins) + ret = _pytest.config.main(list(args), plugins=plugins) if len(rec) == 1: reprec = rec.pop() else: @@ -1209,11 +1216,11 @@ def spawn(self, cmd, expect_timeout=10.0): The pexpect child is returned. """ - pexpect = pytest.importorskip("pexpect", "3.0") + pexpect = importorskip("pexpect", "3.0") if hasattr(sys, "pypy_version_info") and "64" in platform.machine(): - pytest.skip("pypy-64 bit not supported") + skip("pypy-64 bit not supported") if sys.platform.startswith("freebsd"): - pytest.xfail("pexpect does not work reliably on freebsd") + xfail("pexpect does not work reliably on freebsd") logfile = self.tmpdir.join("spawn.out").open("wb") child = pexpect.spawn(cmd, logfile=logfile) self.request.addfinalizer(logfile.close) @@ -1386,4 +1393,4 @@ def _match_lines(self, lines2, match_func, match_nickname): extralines.append(nextline) else: self._log("remains unmatched: %r" % (line,)) - pytest.fail(self._log_text) + fail(self._log_text) diff --git a/src/_pytest/setuponly.py b/src/_pytest/setuponly.py index 4bd4ad6d88f..078856d82d1 100644 --- a/src/_pytest/setuponly.py +++ b/src/_pytest/setuponly.py @@ -4,7 +4,7 @@ import sys -import pytest +from _pytest.config import hookimpl def pytest_addoption(parser): @@ -23,7 +23,7 @@ def pytest_addoption(parser): ) -@pytest.hookimpl(hookwrapper=True) +@hookimpl(hookwrapper=True) def pytest_fixture_setup(fixturedef, request): yield config = request.config @@ -82,7 +82,7 @@ def _show_fixture_action(fixturedef, msg): sys.stderr.write(err) -@pytest.hookimpl(tryfirst=True) +@hookimpl(tryfirst=True) def pytest_cmdline_main(config): if config.option.setuponly: config.option.setupshow = True diff --git a/src/_pytest/setupplan.py b/src/_pytest/setupplan.py index 351e0be650f..d8c6d657a95 100644 --- a/src/_pytest/setupplan.py +++ b/src/_pytest/setupplan.py @@ -2,7 +2,7 @@ from __future__ import division from __future__ import print_function -import pytest +from _pytest.config import hookimpl def pytest_addoption(parser): @@ -16,7 +16,7 @@ def pytest_addoption(parser): ) -@pytest.hookimpl(tryfirst=True) +@hookimpl(tryfirst=True) def pytest_fixture_setup(fixturedef, request): # Will return a dummy fixture if the setuponly option is provided. if request.config.option.setupplan: @@ -24,7 +24,7 @@ def pytest_fixture_setup(fixturedef, request): return fixturedef.cached_result -@pytest.hookimpl(tryfirst=True) +@hookimpl(tryfirst=True) def pytest_cmdline_main(config): if config.option.setupplan: config.option.setuponly = True diff --git a/src/_pytest/stepwise.py b/src/_pytest/stepwise.py index 1efa2e7ca74..0ffc585df0b 100644 --- a/src/_pytest/stepwise.py +++ b/src/_pytest/stepwise.py @@ -1,4 +1,4 @@ -import pytest +from _pytest.config import hookimpl def pytest_addoption(parser): @@ -18,7 +18,7 @@ def pytest_addoption(parser): ) -@pytest.hookimpl +@hookimpl def pytest_configure(config): config.pluginmanager.register(StepwisePlugin(config), "stepwiseplugin") diff --git a/src/_pytest/terminal.py b/src/_pytest/terminal.py index 6f389365382..a2d2450d069 100644 --- a/src/_pytest/terminal.py +++ b/src/_pytest/terminal.py @@ -18,7 +18,8 @@ import six from more_itertools import collapse -import pytest +from .config import hookimpl +from .nodes import Item from _pytest import nodes from _pytest.main import EXIT_INTERRUPTED from _pytest.main import EXIT_NOTESTSCOLLECTED @@ -485,7 +486,7 @@ def pytest_collectreport(self, report): self.stats.setdefault("error", []).append(report) elif report.skipped: self.stats.setdefault("skipped", []).append(report) - items = [x for x in report.result if isinstance(x, pytest.Item)] + items = [x for x in report.result if isinstance(x, Item)] self._numcollected += len(items) if self.isatty: self.report_collect() @@ -527,11 +528,11 @@ def report_collect(self, final=False): else: self.write_line(line) - @pytest.hookimpl(trylast=True) + @hookimpl(trylast=True) def pytest_collection_modifyitems(self): self.report_collect(True) - @pytest.hookimpl(trylast=True) + @hookimpl(trylast=True) def pytest_sessionstart(self, session): self._session = session self._sessionstarttime = time.time() @@ -543,6 +544,8 @@ def pytest_sessionstart(self, session): if hasattr(sys, "pypy_version_info"): verinfo = ".".join(map(str, sys.pypy_version_info[:3])) msg += "[pypy-%s-%s]" % (verinfo, sys.pypy_version_info[3]) + import pytest + msg += ", pytest-%s, py-%s, pluggy-%s" % ( pytest.__version__, py.__version__, @@ -622,7 +625,7 @@ def _printcollecteditems(self, items): indent = (len(stack) - 1) * " " self._tw.line("%s%s" % (indent, col)) - @pytest.hookimpl(hookwrapper=True) + @hookimpl(hookwrapper=True) def pytest_sessionfinish(self, exitstatus): outcome = yield outcome.get_result() @@ -643,7 +646,7 @@ def pytest_sessionfinish(self, exitstatus): del self._keyboardinterrupt_memo self.summary_stats() - @pytest.hookimpl(hookwrapper=True) + @hookimpl(hookwrapper=True) def pytest_terminal_summary(self): self.summary_errors() self.summary_failures() diff --git a/src/_pytest/tmpdir.py b/src/_pytest/tmpdir.py index 860c2d4af06..2448eb47eaf 100644 --- a/src/_pytest/tmpdir.py +++ b/src/_pytest/tmpdir.py @@ -12,13 +12,13 @@ import py import six -import pytest +from .fixtures import fixture +from .monkeypatch import MonkeyPatch from .pathlib import ensure_reset_dir from .pathlib import LOCK_TIMEOUT from .pathlib import make_numbered_dir from .pathlib import make_numbered_dir_with_cleanup from .pathlib import Path -from _pytest.monkeypatch import MonkeyPatch @attr.s @@ -31,7 +31,7 @@ class TempPathFactory(object): # using os.path.abspath() to get absolute path instead of resolve() as it # does not work the same in all platforms (see #4427) # Path.absolute() exists, but it is not public (see https://bugs.python.org/issue25012) - convert=attr.converters.optional( + converter=attr.converters.optional( lambda p: Path(os.path.abspath(six.text_type(p))) ) ) @@ -135,23 +135,24 @@ def pytest_configure(config): available at pytest_configure time, but ideally should be moved entirely to the tmpdir_factory session fixture. """ + mp = MonkeyPatch() tmppath_handler = TempPathFactory.from_config(config) t = TempdirFactory(tmppath_handler) config._cleanup.append(mp.undo) mp.setattr(config, "_tmp_path_factory", tmppath_handler, raising=False) mp.setattr(config, "_tmpdirhandler", t, raising=False) - mp.setattr(pytest, "ensuretemp", t.ensuretemp, raising=False) + mp.setattr("pytest.ensuretemp", t.ensuretemp, raising=False) -@pytest.fixture(scope="session") +@fixture(scope="session") def tmpdir_factory(request): """Return a :class:`_pytest.tmpdir.TempdirFactory` instance for the test session. """ return request.config._tmpdirhandler -@pytest.fixture(scope="session") +@fixture(scope="session") def tmp_path_factory(request): """Return a :class:`_pytest.tmpdir.TempPathFactory` instance for the test session. """ @@ -166,7 +167,7 @@ def _mk_tmp(request, factory): return factory.mktemp(name, numbered=True) -@pytest.fixture +@fixture def tmpdir(request, tmpdir_factory): """Return a temporary directory path object which is unique to each test function invocation, @@ -179,7 +180,7 @@ def tmpdir(request, tmpdir_factory): return _mk_tmp(request, tmpdir_factory) -@pytest.fixture +@fixture def tmp_path(request, tmp_path_factory): """Return a temporary directory path object which is unique to each test function invocation, diff --git a/src/_pytest/warnings.py b/src/_pytest/warnings.py index e3e206933ed..c30cd8cf5e8 100644 --- a/src/_pytest/warnings.py +++ b/src/_pytest/warnings.py @@ -6,8 +6,9 @@ import warnings from contextlib import contextmanager -import pytest from _pytest import compat +from _pytest.config import hookimpl +from _pytest.warning_types import RemovedInPytest4Warning SHOW_PYTEST_WARNINGS_ARG = "-Walways::pytest.RemovedInPytest4Warning" @@ -79,7 +80,7 @@ def catch_warnings_for_item(config, ihook, when, item): warnings.filterwarnings("always", category=DeprecationWarning) warnings.filterwarnings("always", category=PendingDeprecationWarning) - warnings.filterwarnings("error", category=pytest.RemovedInPytest4Warning) + warnings.filterwarnings("error", category=RemovedInPytest4Warning) # filters should have this precedence: mark, cmdline options, ini # filters should be applied in the inverse order of precedence @@ -134,7 +135,7 @@ def warning_record_to_str(warning_message): return msg -@pytest.hookimpl(hookwrapper=True, tryfirst=True) +@hookimpl(hookwrapper=True, tryfirst=True) def pytest_runtest_protocol(item): with catch_warnings_for_item( config=item.config, ihook=item.ihook, when="runtest", item=item @@ -142,7 +143,7 @@ def pytest_runtest_protocol(item): yield -@pytest.hookimpl(hookwrapper=True, tryfirst=True) +@hookimpl(hookwrapper=True, tryfirst=True) def pytest_collection(session): config = session.config with catch_warnings_for_item( @@ -151,7 +152,7 @@ def pytest_collection(session): yield -@pytest.hookimpl(hookwrapper=True) +@hookimpl(hookwrapper=True) def pytest_terminal_summary(terminalreporter): config = terminalreporter.config with catch_warnings_for_item( From ae3e52968e3ae596353f46a19cb99b2f78a6297e Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Wed, 5 Dec 2018 15:09:22 +0100 Subject: [PATCH 02/19] add missed file --- testing/test_own_modules.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 testing/test_own_modules.py diff --git a/testing/test_own_modules.py b/testing/test_own_modules.py new file mode 100644 index 00000000000..889058d77cb --- /dev/null +++ b/testing/test_own_modules.py @@ -0,0 +1,23 @@ +import subprocess +import sys +import types + +import _pytest +import pytest + +KNOWN_BAD = {"_pytest.assertion"} + + +def _get_modules(): + for module in vars(_pytest).values(): + if isinstance(module, types.ModuleType): + name = module.__name__ + marks = [pytest.mark.xfail] if name in KNOWN_BAD else [] + yield pytest.param(name, marks=marks) + + +@pytest.mark.parametrize("module_name", sorted(_get_modules())) +def test_module_warning_free(module_name): + subprocess.check_call( + [sys.executable, "-W", "error", "-c", "import " + module_name] + ) From 4621e28a42f4e145bd6947717c930755970eb1d4 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Wed, 5 Dec 2018 15:10:58 +0100 Subject: [PATCH 03/19] CHANGELOG --- changelog/4507.bugfix.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/4507.bugfix.rst diff --git a/changelog/4507.bugfix.rst b/changelog/4507.bugfix.rst new file mode 100644 index 00000000000..49b4f1c7b87 --- /dev/null +++ b/changelog/4507.bugfix.rst @@ -0,0 +1 @@ +Sort out warnings we get at the import time of the internal modules. From 892e10b578273a3124d4353fe48e78653623e98b Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Wed, 5 Dec 2018 15:33:52 +0100 Subject: [PATCH 04/19] fix references used in pytester --- src/_pytest/pytester.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py index ffbb0100aa3..87106fd02e4 100644 --- a/src/_pytest/pytester.py +++ b/src/_pytest/pytester.py @@ -22,7 +22,9 @@ from _pytest.capture import MultiCapture from _pytest.capture import SysCapture from _pytest.compat import safe_str +from _pytest.config import _prepareconfig from _pytest.config import hookimpl +from _pytest.config import main from _pytest.fixtures import fixture from _pytest.main import EXIT_INTERRUPTED from _pytest.main import EXIT_OK @@ -860,7 +862,7 @@ def pytest_configure(x, config): plugins = kwargs.get("plugins") or [] plugins.append(Collect()) - ret = _pytest.config.main(list(args), plugins=plugins) + ret = main(list(args), plugins=plugins) if len(rec) == 1: reprec = rec.pop() else: @@ -946,9 +948,7 @@ def parseconfig(self, *args): """ args = self._ensure_basetemp(args) - import _pytest.config - - config = _pytest.config._prepareconfig(args, self.plugins) + config = _prepareconfig(args, self.plugins) # we don't know what the test will do with this half-setup config # object and thus we make sure it gets unconfigured properly in any # case (otherwise capturing could still be active, for example) From b17ad8251200f5333a2f8086c52d8912f8dbe1ee Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Wed, 5 Dec 2018 15:59:13 +0100 Subject: [PATCH 05/19] add some specific warning filters for false positives --- testing/test_own_modules.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/testing/test_own_modules.py b/testing/test_own_modules.py index 889058d77cb..3191037714f 100644 --- a/testing/test_own_modules.py +++ b/testing/test_own_modules.py @@ -18,6 +18,13 @@ def _get_modules(): @pytest.mark.parametrize("module_name", sorted(_get_modules())) def test_module_warning_free(module_name): - subprocess.check_call( - [sys.executable, "-W", "error", "-c", "import " + module_name] - ) + # fmt: off + subprocess.check_call([ + sys.executable, + "-W", "error", + # from virtualenv on appveyor + "-W", "ignore:.*mode is deprecated.*:DeprecationWarning", + # from bruno testing in a venv + "-W", "ignore:.*Not importing directory.*:ImportWarning", + "-c", "import " + module_name, + ]) From 1698166221a984c3934fbf8c0a5cd8af06b2f13d Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Wed, 23 Jan 2019 20:42:58 +0100 Subject: [PATCH 06/19] fix merge misstake, return record_xml_attribute --- src/_pytest/junitxml.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/_pytest/junitxml.py b/src/_pytest/junitxml.py index e9bcff34060..2c6ad32d7da 100644 --- a/src/_pytest/junitxml.py +++ b/src/_pytest/junitxml.py @@ -273,6 +273,27 @@ def record_xml_property(record_property, request): return record_property +@fixture +def record_xml_attribute(request): + """Add extra xml attributes to the tag for the calling test. + The fixture is callable with ``(name, value)``, with value being + automatically xml-encoded + """ + from _pytest.warning_types import PytestWarning + + request.node.warn(PytestWarning("record_xml_attribute is an experimental feature")) + xml = getattr(request.config, "_xml", None) + if xml is not None: + node_reporter = xml.node_reporter(request.node.nodeid) + return node_reporter.add_attribute + else: + + def add_attr_noop(name, value): + pass + + return add_attr_noop + + def pytest_addoption(parser): group = parser.getgroup("terminal reporting") group.addoption( From edfcabfad3012202f83ed673c72276ccc5cd8557 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Thu, 24 Jan 2019 14:15:47 +0100 Subject: [PATCH 07/19] silence lsof and account for virtualenv bug --- .travis.yml | 1 + src/_pytest/pytester.py | 2 +- testing/test_own_modules.py | 6 +++++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1c055e66356..0e6dd993da1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,7 @@ python: - '3.7' install: - pip install --upgrade --pre tox + - pip install --upgrade virtualenv>= env: matrix: - TOXENV=py27 diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py index ef575596852..49131da19c4 100644 --- a/src/_pytest/pytester.py +++ b/src/_pytest/pytester.py @@ -115,7 +115,7 @@ def isopen(line): def matching_platform(self): try: - subprocess.check_output(("lsof", "-v")) + subprocess.check_output(("lsof", "-v"), stderr=subprocess.STDOUT) except (OSError, subprocess.CalledProcessError): return False else: diff --git a/testing/test_own_modules.py b/testing/test_own_modules.py index 3191037714f..fe02f9a7d39 100644 --- a/testing/test_own_modules.py +++ b/testing/test_own_modules.py @@ -12,7 +12,9 @@ def _get_modules(): for module in vars(_pytest).values(): if isinstance(module, types.ModuleType): name = module.__name__ - marks = [pytest.mark.xfail] if name in KNOWN_BAD else [] + marks = ( + [pytest.mark.xfail(reason="known failure")] if name in KNOWN_BAD else [] + ) yield pytest.param(name, marks=marks) @@ -26,5 +28,7 @@ def test_module_warning_free(module_name): "-W", "ignore:.*mode is deprecated.*:DeprecationWarning", # from bruno testing in a venv "-W", "ignore:.*Not importing directory.*:ImportWarning", + # virtualenv bug + "-W", "ignore:the imp:DeprecationWarning:distutils", "-c", "import " + module_name, ]) From 2b0707702e15fe053641833142ad90b746757a5a Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Fri, 25 Jan 2019 09:37:55 +0100 Subject: [PATCH 08/19] assertion only warns about imp on python3 --- testing/test_own_modules.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/testing/test_own_modules.py b/testing/test_own_modules.py index fe02f9a7d39..3eaed3c2325 100644 --- a/testing/test_own_modules.py +++ b/testing/test_own_modules.py @@ -5,17 +5,18 @@ import _pytest import pytest -KNOWN_BAD = {"_pytest.assertion"} +KNOWN_BAD = { + "_pytest.assertion": [ + pytest.mark.xfail("sys.version_info[0]==3", reason="assertion uses imp") + ] +} def _get_modules(): for module in vars(_pytest).values(): if isinstance(module, types.ModuleType): name = module.__name__ - marks = ( - [pytest.mark.xfail(reason="known failure")] if name in KNOWN_BAD else [] - ) - yield pytest.param(name, marks=marks) + yield pytest.param(name, marks=KNOWN_BAD.get(name, [])) @pytest.mark.parametrize("module_name", sorted(_get_modules())) From 51b40a3eeb4b9df631abe6c9dd385762f6f299de Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Wed, 30 Jan 2019 19:30:39 +0100 Subject: [PATCH 09/19] fix virtualenv version --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 0e6dd993da1..6a5e929c49a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,7 @@ python: - '3.7' install: - pip install --upgrade --pre tox - - pip install --upgrade virtualenv>= + - pip install --upgrade virtualenv>=16.3.0 env: matrix: - TOXENV=py27 From e1e3e3577a1c0a73535d2479cc806f2b00adda04 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Wed, 5 Dec 2018 15:04:59 +0100 Subject: [PATCH 10/19] fix #4507 and ensure our modules have no warnigns for general import --- changelog/4507.bugfix.rst | 1 + src/_pytest/cacheprovider.py | 10 ++++++---- src/_pytest/capture.py | 35 ++++++++++++++++++---------------- src/_pytest/config/__init__.py | 5 +++-- src/_pytest/doctest.py | 18 +++++++++-------- src/_pytest/helpconfig.py | 10 ++++++++-- src/_pytest/junitxml.py | 15 ++++++++++++--- src/_pytest/logging.py | 22 ++++++++++----------- src/_pytest/monkeypatch.py | 4 ++-- src/_pytest/pastebin.py | 4 ++-- src/_pytest/pytester.py | 33 ++++++++++++++++++-------------- src/_pytest/setuponly.py | 6 +++--- src/_pytest/setupplan.py | 6 +++--- src/_pytest/stepwise.py | 4 ++-- src/_pytest/terminal.py | 11 ++++++----- src/_pytest/tmpdir.py | 15 ++++++++------- src/_pytest/warnings.py | 11 ++++++----- testing/test_own_modules.py | 23 ++++++++++++++++++++++ 18 files changed, 144 insertions(+), 89 deletions(-) create mode 100644 changelog/4507.bugfix.rst create mode 100644 testing/test_own_modules.py diff --git a/changelog/4507.bugfix.rst b/changelog/4507.bugfix.rst new file mode 100644 index 00000000000..49b4f1c7b87 --- /dev/null +++ b/changelog/4507.bugfix.rst @@ -0,0 +1 @@ +Sort out warnings we get at the import time of the internal modules. diff --git a/src/_pytest/cacheprovider.py b/src/_pytest/cacheprovider.py index 17463959fb4..201683cfc70 100755 --- a/src/_pytest/cacheprovider.py +++ b/src/_pytest/cacheprovider.py @@ -11,7 +11,9 @@ import attr import py -import pytest +from .config import hookimpl +from .fixtures import fixture +from .nodes import Item from .pathlib import Path from .pathlib import resolve_from_str from .pathlib import rmtree @@ -276,7 +278,7 @@ def pytest_collection_modifyitems(self, session, config, items): items[:] = self._get_increasing_order( new_items.values() ) + self._get_increasing_order(other_items.values()) - self.cached_nodeids = [x.nodeid for x in items if isinstance(x, pytest.Item)] + self.cached_nodeids = [x.nodeid for x in items if isinstance(x, Item)] def _get_increasing_order(self, items): return sorted(items, key=lambda item: item.fspath.mtime(), reverse=True) @@ -354,14 +356,14 @@ def pytest_cmdline_main(config): return wrap_session(config, cacheshow) -@pytest.hookimpl(tryfirst=True) +@hookimpl(tryfirst=True) def pytest_configure(config): config.cache = Cache.for_config(config) config.pluginmanager.register(LFPlugin(config), "lfplugin") config.pluginmanager.register(NFPlugin(config), "nfplugin") -@pytest.fixture +@fixture def cache(request): """ Return a cache object that can persist state between testing sessions. diff --git a/src/_pytest/capture.py b/src/_pytest/capture.py index 302979ef4be..f3a78669ec3 100644 --- a/src/_pytest/capture.py +++ b/src/_pytest/capture.py @@ -10,7 +10,10 @@ from io import UnsupportedOperation from tempfile import TemporaryFile -import pytest +from .config import hookimpl +from .fixtures import fixture +from .nodes import File +from .outcomes import skip from _pytest.compat import CaptureIO patchsysdict = {0: "stdin", 1: "stdout", 2: "stderr"} @@ -35,7 +38,7 @@ def pytest_addoption(parser): ) -@pytest.hookimpl(hookwrapper=True) +@hookimpl(hookwrapper=True) def pytest_load_initial_conftests(early_config, parser, args): ns = early_config.known_args_namespace if ns.capture == "fd": @@ -193,9 +196,9 @@ def item_capture(self, when, item): # Hooks - @pytest.hookimpl(hookwrapper=True) + @hookimpl(hookwrapper=True) def pytest_make_collect_report(self, collector): - if isinstance(collector, pytest.File): + if isinstance(collector, File): self.resume_global_capture() outcome = yield self.suspend_global_capture() @@ -208,32 +211,32 @@ def pytest_make_collect_report(self, collector): else: yield - @pytest.hookimpl(hookwrapper=True) + @hookimpl(hookwrapper=True) def pytest_runtest_protocol(self, item): self._current_item = item yield self._current_item = None - @pytest.hookimpl(hookwrapper=True) + @hookimpl(hookwrapper=True) def pytest_runtest_setup(self, item): with self.item_capture("setup", item): yield - @pytest.hookimpl(hookwrapper=True) + @hookimpl(hookwrapper=True) def pytest_runtest_call(self, item): with self.item_capture("call", item): yield - @pytest.hookimpl(hookwrapper=True) + @hookimpl(hookwrapper=True) def pytest_runtest_teardown(self, item): with self.item_capture("teardown", item): yield - @pytest.hookimpl(tryfirst=True) + @hookimpl(tryfirst=True) def pytest_keyboard_interrupt(self, excinfo): self.stop_global_capturing() - @pytest.hookimpl(tryfirst=True) + @hookimpl(tryfirst=True) def pytest_internalerror(self, excinfo): self.stop_global_capturing() @@ -251,7 +254,7 @@ def _ensure_only_one_capture_fixture(request, name): ) -@pytest.fixture +@fixture def capsys(request): """Enable text capturing of writes to ``sys.stdout`` and ``sys.stderr``. @@ -264,7 +267,7 @@ def capsys(request): yield fixture -@pytest.fixture +@fixture def capsysbinary(request): """Enable bytes capturing of writes to ``sys.stdout`` and ``sys.stderr``. @@ -277,7 +280,7 @@ def capsysbinary(request): yield fixture -@pytest.fixture +@fixture def capfd(request): """Enable text capturing of writes to file descriptors ``1`` and ``2``. @@ -287,14 +290,14 @@ def capfd(request): """ _ensure_only_one_capture_fixture(request, "capfd") if not hasattr(os, "dup"): - pytest.skip( + skip( "capfd fixture needs os.dup function which is not available in this system" ) with _install_capture_fixture_on_item(request, FDCapture) as fixture: yield fixture -@pytest.fixture +@fixture def capfdbinary(request): """Enable bytes capturing of writes to file descriptors ``1`` and ``2``. @@ -304,7 +307,7 @@ def capfdbinary(request): """ _ensure_only_one_capture_fixture(request, "capfdbinary") if not hasattr(os, "dup"): - pytest.skip( + skip( "capfdbinary fixture needs os.dup function which is not available in this system" ) with _install_capture_fixture_on_item(request, FDCaptureBinary) as fixture: diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index e6de86c3619..c6524c0042b 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -17,7 +17,6 @@ from pluggy import PluginManager import _pytest._code -import _pytest.assertion import _pytest.hookspec # the extension point definitions from .exceptions import PrintHelp from .exceptions import UsageError @@ -237,7 +236,9 @@ def __init__(self): self.enable_tracing() # Config._consider_importhook will set a real object if required. - self.rewrite_hook = _pytest.assertion.DummyRewriteHook() + from _pytest.assertion import DummyRewriteHook + + self.rewrite_hook = DummyRewriteHook() # Used to know when we are importing conftests after the pytest_configure stage self._configured = False diff --git a/src/_pytest/doctest.py b/src/_pytest/doctest.py index 50c81902684..6c74a13af4a 100644 --- a/src/_pytest/doctest.py +++ b/src/_pytest/doctest.py @@ -6,14 +6,17 @@ import warnings from contextlib import contextmanager -import pytest from _pytest._code.code import ExceptionInfo from _pytest._code.code import ReprFileLocation from _pytest._code.code import TerminalRepr -from _pytest.compat import safe_getattr +from _pytest.fixtures import fixture from _pytest.fixtures import FixtureRequest from _pytest.outcomes import Skipped from _pytest.warning_types import PytestWarning +from _pytest.nodes import Item +from _pytest.outcomes import skip +from _pytest.python import Module + DOCTEST_REPORT_CHOICE_NONE = "none" DOCTEST_REPORT_CHOICE_CDIFF = "cdiff" @@ -176,7 +179,7 @@ def _get_runner(checker=None, verbose=None, optionflags=0, continue_on_failure=T ) -class DoctestItem(pytest.Item): +class DoctestItem(Item): def __init__(self, name, parent, runner=None, dtest=None): super().__init__(name, parent) self.runner = runner @@ -308,7 +311,7 @@ def _get_continue_on_failure(config): return continue_on_failure -class DoctestTextfile(pytest.Module): +class DoctestTextfile(Module): obj = None def collect(self): @@ -345,7 +348,7 @@ def _check_all_skipped(test): all_skipped = all(x.options.get(doctest.SKIP, False) for x in test.examples) if all_skipped: - pytest.skip("all tests skipped by +SKIP option") + skip("all tests skipped by +SKIP option") def _is_mocked(obj): @@ -390,7 +393,6 @@ def _mock_aware_unwrap(obj, stop=None): inspect.unwrap = real_unwrap -class DoctestModule(pytest.Module): def collect(self): import doctest @@ -418,7 +420,7 @@ def _find(self, tests, obj, name, module, source_lines, globs, seen): module = self.fspath.pyimport() except ImportError: if self.config.getvalue("doctest_ignore_import_errors"): - pytest.skip("unable to import module %r" % self.fspath) + skip("unable to import module %r" % self.fspath) else: raise # uses internal doctest module parsing mechanism @@ -543,7 +545,7 @@ def _get_report_choice(key): }[key] -@pytest.fixture(scope="session") +@fixture(scope="session") def doctest_namespace(): """ Fixture that returns a :py:class:`dict` that will be injected into the namespace of doctests. diff --git a/src/_pytest/helpconfig.py b/src/_pytest/helpconfig.py index b379fae01dc..fb0c6e133d0 100644 --- a/src/_pytest/helpconfig.py +++ b/src/_pytest/helpconfig.py @@ -5,7 +5,7 @@ import py -import pytest +from _pytest.config import hookimpl from _pytest.config import PrintHelp @@ -83,11 +83,13 @@ def pytest_addoption(parser): ) -@pytest.hookimpl(hookwrapper=True) +@hookimpl(hookwrapper=True) def pytest_cmdline_parse(): outcome = yield config = outcome.get_result() if config.option.debug: + import pytest + path = os.path.abspath("pytestdebug.log") debugfile = open(path, "w") debugfile.write( @@ -115,6 +117,8 @@ def unset_tracing(): def showversion(config): + import pytest + p = py.path.local(pytest.__file__) sys.stderr.write( "This is pytest version {}, imported from {}\n".format(pytest.__version__, p) @@ -223,6 +227,8 @@ def getpluginversioninfo(config): def pytest_report_header(config): lines = [] + import pytest + if config.option.debug or config.option.traceconfig: lines.append( "using: pytest-{} pylib-{}".format(pytest.__version__, py.__version__) diff --git a/src/_pytest/junitxml.py b/src/_pytest/junitxml.py index ea33e606c30..301262bd4dc 100644 --- a/src/_pytest/junitxml.py +++ b/src/_pytest/junitxml.py @@ -16,9 +16,9 @@ import py -import pytest from _pytest import nodes from _pytest.config import filename_arg +from _pytest.fixtures import fixture class Junit(py.xml.Namespace): @@ -290,7 +290,6 @@ def _warn_incompatibility_with_xunit2(request, fixture_name): ) -@pytest.fixture def record_property(request): """Add an extra properties the calling test. User properties become part of the test report and are available to the @@ -311,7 +310,17 @@ def append_property(name, value): return append_property -@pytest.fixture +@fixture +def record_xml_property(record_property, request): + """(Deprecated) use record_property.""" + from _pytest import deprecated + + request.node.warn(deprecated.RECORD_XML_PROPERTY) + + return record_property + + +@fixture def record_xml_attribute(request): """Add extra xml attributes to the tag for the calling test. The fixture is callable with ``(name, value)``, with value being diff --git a/src/_pytest/logging.py b/src/_pytest/logging.py index 2861baefda3..a01347c6741 100644 --- a/src/_pytest/logging.py +++ b/src/_pytest/logging.py @@ -5,7 +5,7 @@ import py -import pytest +from .config import hookimpl from _pytest.compat import nullcontext from _pytest.config import create_terminal_writer from _pytest.pathlib import Path @@ -350,7 +350,7 @@ def at_level(self, level, logger=None): logger.setLevel(orig_level) -@pytest.fixture +@fixture def caplog(request): """Access and control log capturing. @@ -384,7 +384,7 @@ def get_actual_log_level(config, *setting_names): return int(getattr(logging, log_level, log_level)) except ValueError: # Python logging does not recognise this as a logging level - raise pytest.UsageError( + raise UsageError( "'{}' is not recognized as a logging level name for " "'{}'. Please consider passing the " "logging level num instead.".format(log_level, setting_name) @@ -510,7 +510,7 @@ def _log_cli_enabled(self): "--log-cli-level" ) is not None or self._config.getini("log_cli") - @pytest.hookimpl(hookwrapper=True, tryfirst=True) + @hookimpl(hookwrapper=True, tryfirst=True) def pytest_collection(self): with self.live_logs_context(): if self.log_cli_handler: @@ -560,29 +560,29 @@ def _runtest_for_main(self, item, when): log = log_handler.stream.getvalue().strip() item.add_report_section(when, "log", log) - @pytest.hookimpl(hookwrapper=True) + @hookimpl(hookwrapper=True) def pytest_runtest_setup(self, item): with self._runtest_for(item, "setup"): yield - @pytest.hookimpl(hookwrapper=True) + @hookimpl(hookwrapper=True) def pytest_runtest_call(self, item): with self._runtest_for(item, "call"): yield - @pytest.hookimpl(hookwrapper=True) + @hookimpl(hookwrapper=True) def pytest_runtest_teardown(self, item): with self._runtest_for(item, "teardown"): yield - @pytest.hookimpl(hookwrapper=True) + @hookimpl(hookwrapper=True) def pytest_runtest_logstart(self): if self.log_cli_handler: self.log_cli_handler.reset() with self._runtest_for(None, "start"): yield - @pytest.hookimpl(hookwrapper=True) + @hookimpl(hookwrapper=True) def pytest_runtest_logfinish(self): with self._runtest_for(None, "finish"): yield @@ -610,7 +610,7 @@ def pytest_sessionfinish(self): else: yield - @pytest.hookimpl(hookwrapper=True, tryfirst=True) + @hookimpl(hookwrapper=True, tryfirst=True) def pytest_sessionstart(self): with self.live_logs_context(): if self.log_cli_handler: @@ -621,7 +621,7 @@ def pytest_sessionstart(self): else: yield - @pytest.hookimpl(hookwrapper=True) + @hookimpl(hookwrapper=True) def pytest_runtestloop(self, session): """Runs all collected test items.""" diff --git a/src/_pytest/monkeypatch.py b/src/_pytest/monkeypatch.py index 090bf61d6e9..e3e4427801c 100644 --- a/src/_pytest/monkeypatch.py +++ b/src/_pytest/monkeypatch.py @@ -5,7 +5,7 @@ import warnings from contextlib import contextmanager -import pytest +from .warning_types import PytestWarning from _pytest.fixtures import fixture from _pytest.pathlib import Path @@ -225,7 +225,7 @@ def setenv(self, name, value, prepend=None): and prepend the ``value`` adjoined with the ``prepend`` character.""" if not isinstance(value, str): warnings.warn( - pytest.PytestWarning( + PytestWarning( "Value of environment variable {name} type should be str, but got " "{value!r} (type: {type}); converted to str implicitly".format( name=name, value=value, type=type(value).__name__ diff --git a/src/_pytest/pastebin.py b/src/_pytest/pastebin.py index ce0e73accc2..c350adefeb4 100644 --- a/src/_pytest/pastebin.py +++ b/src/_pytest/pastebin.py @@ -1,7 +1,7 @@ """ submit failure or test session information to a pastebin service. """ import tempfile -import pytest +from _pytest.config import hookimpl def pytest_addoption(parser): @@ -17,7 +17,7 @@ def pytest_addoption(parser): ) -@pytest.hookimpl(trylast=True) +@hookimpl(trylast=True) def pytest_configure(config): if config.option.pastebin == "all": tr = config.pluginmanager.getplugin("terminalreporter") diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py index 6b304ad9ff1..0a459410ccc 100644 --- a/src/_pytest/pytester.py +++ b/src/_pytest/pytester.py @@ -13,16 +13,21 @@ import py -import pytest from _pytest._code import Source from _pytest._io.saferepr import saferepr -from _pytest.assertion.rewrite import AssertionRewritingHook from _pytest.capture import MultiCapture from _pytest.capture import SysCapture from _pytest.main import ExitCode +from _pytest.config import hookimpl +from _pytest.fixtures import fixture from _pytest.main import Session from _pytest.monkeypatch import MonkeyPatch +from _pytest.outcomes import fail +from _pytest.outcomes import importorskip +from _pytest.outcomes import skip +from _pytest.outcomes import xfail from _pytest.pathlib import Path +from _pytest.warning_types import PytestWarning IGNORE_PAM = [ # filenames added when obtaining details about the current user "/var/lib/sss/mc/passwd" @@ -112,7 +117,7 @@ def matching_platform(self): else: return True - @pytest.hookimpl(hookwrapper=True, tryfirst=True) + @hookimpl(hookwrapper=True, tryfirst=True) def pytest_runtest_protocol(self, item): lines1 = self.get_open_files() yield @@ -133,13 +138,13 @@ def pytest_runtest_protocol(self, item): error.append(error[0]) error.append("*** function %s:%s: %s " % item.location) error.append("See issue #2366") - item.warn(pytest.PytestWarning("\n".join(error))) + item.warn(PytestWarning("\n".join(error))) # used at least by pytest-xdist plugin -@pytest.fixture +@fixture def _pytest(request): """Return a helper which offers a gethookrecorder(hook) method which returns a HookRecorder instance which helps to make assertions about called @@ -232,7 +237,7 @@ def popcall(self, name): return call lines = ["could not find call {!r}, in:".format(name)] lines.extend([" %s" % x for x in self.calls]) - pytest.fail("\n".join(lines)) + fail("\n".join(lines)) def getcall(self, name): values = self.getcalls(name) @@ -307,17 +312,17 @@ def clear(self): self.calls[:] = [] -@pytest.fixture +@fixture def linecomp(request): return LineComp() -@pytest.fixture(name="LineMatcher") +@fixture(name="LineMatcher") def LineMatcher_fixture(request): return LineMatcher -@pytest.fixture +@fixture def testdir(request, tmpdir_factory): return Testdir(request, tmpdir_factory) @@ -828,7 +833,7 @@ def pytest_configure(x, config): rec.append(self.make_hook_recorder(config.pluginmanager)) plugins.append(Collect()) - ret = pytest.main(list(args), plugins=plugins) + ret = _pytest.config.main(list(args), plugins=plugins) if len(rec) == 1: reprec = rec.pop() else: @@ -1190,11 +1195,11 @@ def spawn(self, cmd, expect_timeout=10.0): The pexpect child is returned. """ - pexpect = pytest.importorskip("pexpect", "3.0") + pexpect = importorskip("pexpect", "3.0") if hasattr(sys, "pypy_version_info") and "64" in platform.machine(): - pytest.skip("pypy-64 bit not supported") + skip("pypy-64 bit not supported") if sys.platform.startswith("freebsd"): - pytest.xfail("pexpect does not work reliably on freebsd") + xfail("pexpect does not work reliably on freebsd") logfile = self.tmpdir.join("spawn.out").open("wb") # Do not load user config. @@ -1373,4 +1378,4 @@ def _match_lines(self, lines2, match_func, match_nickname): extralines.append(nextline) else: self._log("remains unmatched: {!r}".format(line)) - pytest.fail(self._log_text) + fail(self._log_text) diff --git a/src/_pytest/setuponly.py b/src/_pytest/setuponly.py index 70d6ed12f86..769b494213e 100644 --- a/src/_pytest/setuponly.py +++ b/src/_pytest/setuponly.py @@ -1,6 +1,6 @@ import sys -import pytest +from _pytest.config import hookimpl def pytest_addoption(parser): @@ -19,7 +19,7 @@ def pytest_addoption(parser): ) -@pytest.hookimpl(hookwrapper=True) +@hookimpl(hookwrapper=True) def pytest_fixture_setup(fixturedef, request): yield config = request.config @@ -78,7 +78,7 @@ def _show_fixture_action(fixturedef, msg): sys.stderr.write(err) -@pytest.hookimpl(tryfirst=True) +@hookimpl(tryfirst=True) def pytest_cmdline_main(config): if config.option.setuponly: config.option.setupshow = True diff --git a/src/_pytest/setupplan.py b/src/_pytest/setupplan.py index 697746f205a..99f8de44b22 100644 --- a/src/_pytest/setupplan.py +++ b/src/_pytest/setupplan.py @@ -1,4 +1,4 @@ -import pytest +from _pytest.config import hookimpl def pytest_addoption(parser): @@ -12,7 +12,7 @@ def pytest_addoption(parser): ) -@pytest.hookimpl(tryfirst=True) +@hookimpl(tryfirst=True) def pytest_fixture_setup(fixturedef, request): # Will return a dummy fixture if the setuponly option is provided. if request.config.option.setupplan: @@ -20,7 +20,7 @@ def pytest_fixture_setup(fixturedef, request): return fixturedef.cached_result -@pytest.hookimpl(tryfirst=True) +@hookimpl(tryfirst=True) def pytest_cmdline_main(config): if config.option.setupplan: config.option.setuponly = True diff --git a/src/_pytest/stepwise.py b/src/_pytest/stepwise.py index eb45554904a..1dc1f8ed79c 100644 --- a/src/_pytest/stepwise.py +++ b/src/_pytest/stepwise.py @@ -1,4 +1,4 @@ -import pytest +from _pytest.config import hookimpl def pytest_addoption(parser): @@ -18,7 +18,7 @@ def pytest_addoption(parser): ) -@pytest.hookimpl +@hookimpl def pytest_configure(config): config.pluginmanager.register(StepwisePlugin(config), "stepwiseplugin") diff --git a/src/_pytest/terminal.py b/src/_pytest/terminal.py index 91e37385276..b14654e1efa 100644 --- a/src/_pytest/terminal.py +++ b/src/_pytest/terminal.py @@ -14,7 +14,8 @@ import py from more_itertools import collapse -import pytest +from .config import hookimpl +from .nodes import Item from _pytest import nodes from _pytest.main import ExitCode @@ -498,7 +499,7 @@ def pytest_collectreport(self, report): self.stats.setdefault("error", []).append(report) elif report.skipped: self.stats.setdefault("skipped", []).append(report) - items = [x for x in report.result if isinstance(x, pytest.Item)] + items = [x for x in report.result if isinstance(x, Item)] self._numcollected += len(items) if self.isatty: self.report_collect() @@ -543,7 +544,7 @@ def report_collect(self, final=False): else: self.write_line(line) - @pytest.hookimpl(trylast=True) + @hookimpl(trylast=True) def pytest_sessionstart(self, session): self._session = session self._sessionstarttime = time.time() @@ -644,7 +645,7 @@ def _printcollecteditems(self, items): for line in col._obj.__doc__.strip().splitlines(): self._tw.line("{}{}".format(indent + " ", line.strip())) - @pytest.hookimpl(hookwrapper=True) + @hookimpl(hookwrapper=True) def pytest_sessionfinish(self, exitstatus): outcome = yield outcome.get_result() @@ -665,7 +666,7 @@ def pytest_sessionfinish(self, exitstatus): del self._keyboardinterrupt_memo self.summary_stats() - @pytest.hookimpl(hookwrapper=True) + @hookimpl(hookwrapper=True) def pytest_terminal_summary(self): self.summary_errors() self.summary_failures() diff --git a/src/_pytest/tmpdir.py b/src/_pytest/tmpdir.py index f2c4d905cf5..901a89fc83f 100644 --- a/src/_pytest/tmpdir.py +++ b/src/_pytest/tmpdir.py @@ -7,13 +7,13 @@ import attr import py -import pytest +from .fixtures import fixture +from .monkeypatch import MonkeyPatch from .pathlib import ensure_reset_dir from .pathlib import LOCK_TIMEOUT from .pathlib import make_numbered_dir from .pathlib import make_numbered_dir_with_cleanup from .pathlib import Path -from _pytest.monkeypatch import MonkeyPatch @attr.s @@ -129,23 +129,24 @@ def pytest_configure(config): available at pytest_configure time, but ideally should be moved entirely to the tmpdir_factory session fixture. """ + mp = MonkeyPatch() tmppath_handler = TempPathFactory.from_config(config) t = TempdirFactory(tmppath_handler) config._cleanup.append(mp.undo) mp.setattr(config, "_tmp_path_factory", tmppath_handler, raising=False) mp.setattr(config, "_tmpdirhandler", t, raising=False) - mp.setattr(pytest, "ensuretemp", t.ensuretemp, raising=False) + mp.setattr("pytest.ensuretemp", t.ensuretemp, raising=False) -@pytest.fixture(scope="session") +@fixture(scope="session") def tmpdir_factory(request): """Return a :class:`_pytest.tmpdir.TempdirFactory` instance for the test session. """ return request.config._tmpdirhandler -@pytest.fixture(scope="session") +@fixture(scope="session") def tmp_path_factory(request): """Return a :class:`_pytest.tmpdir.TempPathFactory` instance for the test session. """ @@ -160,7 +161,7 @@ def _mk_tmp(request, factory): return factory.mktemp(name, numbered=True) -@pytest.fixture +@fixture def tmpdir(tmp_path): """Return a temporary directory path object which is unique to each test function invocation, @@ -173,7 +174,7 @@ def tmpdir(tmp_path): return py.path.local(tmp_path) -@pytest.fixture +@fixture def tmp_path(request, tmp_path_factory): """Return a temporary directory path object which is unique to each test function invocation, diff --git a/src/_pytest/warnings.py b/src/_pytest/warnings.py index f47eee0d4f0..21361ab4391 100644 --- a/src/_pytest/warnings.py +++ b/src/_pytest/warnings.py @@ -2,7 +2,8 @@ import warnings from contextlib import contextmanager -import pytest +from _pytest.config import hookimpl +from _pytest.warning_types import RemovedInPytest4Warning SHOW_PYTEST_WARNINGS_ARG = "-Walways::pytest.RemovedInPytest4Warning" @@ -74,7 +75,7 @@ def catch_warnings_for_item(config, ihook, when, item): warnings.filterwarnings("always", category=DeprecationWarning) warnings.filterwarnings("always", category=PendingDeprecationWarning) - warnings.filterwarnings("error", category=pytest.RemovedInPytest4Warning) + warnings.filterwarnings("error", category=RemovedInPytest4Warning) warnings.filterwarnings("error", category=pytest.PytestDeprecationWarning) # filters should have this precedence: mark, cmdline options, ini @@ -111,7 +112,7 @@ def warning_record_to_str(warning_message): return msg -@pytest.hookimpl(hookwrapper=True, tryfirst=True) +@hookimpl(hookwrapper=True, tryfirst=True) def pytest_runtest_protocol(item): with catch_warnings_for_item( config=item.config, ihook=item.ihook, when="runtest", item=item @@ -119,7 +120,7 @@ def pytest_runtest_protocol(item): yield -@pytest.hookimpl(hookwrapper=True, tryfirst=True) +@hookimpl(hookwrapper=True, tryfirst=True) def pytest_collection(session): config = session.config with catch_warnings_for_item( @@ -128,7 +129,7 @@ def pytest_collection(session): yield -@pytest.hookimpl(hookwrapper=True) +@hookimpl(hookwrapper=True) def pytest_terminal_summary(terminalreporter): config = terminalreporter.config with catch_warnings_for_item( diff --git a/testing/test_own_modules.py b/testing/test_own_modules.py new file mode 100644 index 00000000000..889058d77cb --- /dev/null +++ b/testing/test_own_modules.py @@ -0,0 +1,23 @@ +import subprocess +import sys +import types + +import _pytest +import pytest + +KNOWN_BAD = {"_pytest.assertion"} + + +def _get_modules(): + for module in vars(_pytest).values(): + if isinstance(module, types.ModuleType): + name = module.__name__ + marks = [pytest.mark.xfail] if name in KNOWN_BAD else [] + yield pytest.param(name, marks=marks) + + +@pytest.mark.parametrize("module_name", sorted(_get_modules())) +def test_module_warning_free(module_name): + subprocess.check_call( + [sys.executable, "-W", "error", "-c", "import " + module_name] + ) From 19fdbee3945c1859e2a84fe2c95caec8f8abf85e Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Wed, 5 Dec 2018 15:33:52 +0100 Subject: [PATCH 11/19] fix references used in pytester --- src/_pytest/pytester.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py index 0a459410ccc..973b59a4a42 100644 --- a/src/_pytest/pytester.py +++ b/src/_pytest/pytester.py @@ -18,7 +18,9 @@ from _pytest.capture import MultiCapture from _pytest.capture import SysCapture from _pytest.main import ExitCode +from _pytest.config import _prepareconfig from _pytest.config import hookimpl +from _pytest.config import main from _pytest.fixtures import fixture from _pytest.main import Session from _pytest.monkeypatch import MonkeyPatch @@ -833,7 +835,7 @@ def pytest_configure(x, config): rec.append(self.make_hook_recorder(config.pluginmanager)) plugins.append(Collect()) - ret = _pytest.config.main(list(args), plugins=plugins) + ret = main(list(args), plugins=plugins) if len(rec) == 1: reprec = rec.pop() else: @@ -920,9 +922,7 @@ def parseconfig(self, *args): """ args = self._ensure_basetemp(args) - import _pytest.config - - config = _pytest.config._prepareconfig(args, self.plugins) + config = _prepareconfig(args, self.plugins) # we don't know what the test will do with this half-setup config # object and thus we make sure it gets unconfigured properly in any # case (otherwise capturing could still be active, for example) From 0eb6b87d96bb729aedadd805f3c268ff5d391c6c Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Wed, 5 Dec 2018 15:59:13 +0100 Subject: [PATCH 12/19] add some specific warning filters for false positives --- testing/test_own_modules.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/testing/test_own_modules.py b/testing/test_own_modules.py index 889058d77cb..3191037714f 100644 --- a/testing/test_own_modules.py +++ b/testing/test_own_modules.py @@ -18,6 +18,13 @@ def _get_modules(): @pytest.mark.parametrize("module_name", sorted(_get_modules())) def test_module_warning_free(module_name): - subprocess.check_call( - [sys.executable, "-W", "error", "-c", "import " + module_name] - ) + # fmt: off + subprocess.check_call([ + sys.executable, + "-W", "error", + # from virtualenv on appveyor + "-W", "ignore:.*mode is deprecated.*:DeprecationWarning", + # from bruno testing in a venv + "-W", "ignore:.*Not importing directory.*:ImportWarning", + "-c", "import " + module_name, + ]) From 12f85a2651fdc0a9016cb39cae5bfbf1058bed38 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Wed, 23 Jan 2019 20:42:58 +0100 Subject: [PATCH 13/19] fix merge misstake, return record_xml_attribute --- src/_pytest/junitxml.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/_pytest/junitxml.py b/src/_pytest/junitxml.py index 301262bd4dc..a246a14a018 100644 --- a/src/_pytest/junitxml.py +++ b/src/_pytest/junitxml.py @@ -326,21 +326,21 @@ def record_xml_attribute(request): The fixture is callable with ``(name, value)``, with value being automatically xml-encoded """ - from _pytest.warning_types import PytestExperimentalApiWarning + from _pytest.warning_types import PytestWarning - request.node.warn( - PytestExperimentalApiWarning("record_xml_attribute is an experimental feature") - ) + request.node.warn(PytestWarning("record_xml_attribute is an experimental feature")) + xml = getattr(request.config, "_xml", None) + if xml is not None: + node_reporter = xml.node_reporter(request.node.nodeid) + return node_reporter.add_attribute + else: - _warn_incompatibility_with_xunit2(request, "record_xml_attribute") + def add_attr_noop(name, value): + pass - # Declare noop - def add_attr_noop(name, value): - pass + return add_attr_noop - attr_func = add_attr_noop - xml = getattr(request.config, "_xml", None) if xml is not None: node_reporter = xml.node_reporter(request.node.nodeid) attr_func = node_reporter.add_attribute From 388357d6cb4df245e63c496b3e3377ca1a95ceed Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Thu, 24 Jan 2019 14:15:47 +0100 Subject: [PATCH 14/19] silence lsof and account for virtualenv bug --- .travis.yml | 1 + src/_pytest/pytester.py | 2 +- testing/test_own_modules.py | 6 +++++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2dd424e58de..7457c46f594 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,7 @@ stages: python: '3.7' cache: false + - pip install --upgrade virtualenv>=16.3.0 env: global: - PYTEST_ADDOPTS=-vv diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py index 973b59a4a42..90a12c6830b 100644 --- a/src/_pytest/pytester.py +++ b/src/_pytest/pytester.py @@ -113,7 +113,7 @@ def isopen(line): def matching_platform(self): try: - subprocess.check_output(("lsof", "-v")) + subprocess.check_output(("lsof", "-v"), stderr=subprocess.STDOUT) except (OSError, subprocess.CalledProcessError): return False else: diff --git a/testing/test_own_modules.py b/testing/test_own_modules.py index 3191037714f..fe02f9a7d39 100644 --- a/testing/test_own_modules.py +++ b/testing/test_own_modules.py @@ -12,7 +12,9 @@ def _get_modules(): for module in vars(_pytest).values(): if isinstance(module, types.ModuleType): name = module.__name__ - marks = [pytest.mark.xfail] if name in KNOWN_BAD else [] + marks = ( + [pytest.mark.xfail(reason="known failure")] if name in KNOWN_BAD else [] + ) yield pytest.param(name, marks=marks) @@ -26,5 +28,7 @@ def test_module_warning_free(module_name): "-W", "ignore:.*mode is deprecated.*:DeprecationWarning", # from bruno testing in a venv "-W", "ignore:.*Not importing directory.*:ImportWarning", + # virtualenv bug + "-W", "ignore:the imp:DeprecationWarning:distutils", "-c", "import " + module_name, ]) From 61ef3e6b5b071e6831ff6e29edd434d201453af2 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Fri, 25 Jan 2019 09:37:55 +0100 Subject: [PATCH 15/19] assertion only warns about imp on python3 --- testing/test_own_modules.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/testing/test_own_modules.py b/testing/test_own_modules.py index fe02f9a7d39..3eaed3c2325 100644 --- a/testing/test_own_modules.py +++ b/testing/test_own_modules.py @@ -5,17 +5,18 @@ import _pytest import pytest -KNOWN_BAD = {"_pytest.assertion"} +KNOWN_BAD = { + "_pytest.assertion": [ + pytest.mark.xfail("sys.version_info[0]==3", reason="assertion uses imp") + ] +} def _get_modules(): for module in vars(_pytest).values(): if isinstance(module, types.ModuleType): name = module.__name__ - marks = ( - [pytest.mark.xfail(reason="known failure")] if name in KNOWN_BAD else [] - ) - yield pytest.param(name, marks=marks) + yield pytest.param(name, marks=KNOWN_BAD.get(name, [])) @pytest.mark.parametrize("module_name", sorted(_get_modules())) From 5113afb0ef166c1acaaa62d63d50f61329bac4c6 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Thu, 8 Aug 2019 21:02:22 +0200 Subject: [PATCH 16/19] more code updates --- src/_pytest/doctest.py | 4 +++- src/_pytest/junitxml.py | 2 +- src/_pytest/logging.py | 2 ++ src/_pytest/pytester.py | 4 ++-- src/_pytest/terminal.py | 5 ++++- src/_pytest/warnings.py | 4 ++-- 6 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/_pytest/doctest.py b/src/_pytest/doctest.py index f5450158137..7b41922ff0d 100644 --- a/src/_pytest/doctest.py +++ b/src/_pytest/doctest.py @@ -9,6 +9,7 @@ from _pytest._code.code import ExceptionInfo from _pytest._code.code import ReprFileLocation from _pytest._code.code import TerminalRepr +from _pytest.compat import safe_getattr from _pytest.fixtures import fixture from _pytest.fixtures import FixtureRequest from _pytest.nodes import Item @@ -17,7 +18,6 @@ from _pytest.python import Module from _pytest.warning_types import PytestWarning - DOCTEST_REPORT_CHOICE_NONE = "none" DOCTEST_REPORT_CHOICE_CDIFF = "cdiff" DOCTEST_REPORT_CHOICE_NDIFF = "ndiff" @@ -389,6 +389,8 @@ def _mock_aware_unwrap(obj, stop=None): finally: inspect.unwrap = real_unwrap + +class DoctestModule(Module): def collect(self): import doctest diff --git a/src/_pytest/junitxml.py b/src/_pytest/junitxml.py index faa70b2064c..5ace326416b 100644 --- a/src/_pytest/junitxml.py +++ b/src/_pytest/junitxml.py @@ -356,7 +356,7 @@ def _check_record_param_type(param, v): raise TypeError(msg.format(param=param, g=type(v).__name__)) -@pytest.fixture(scope="session") +@fixture(scope="session") def record_testsuite_property(request): """ Records a new ```` tag as child of the root ````. This is suitable to diff --git a/src/_pytest/logging.py b/src/_pytest/logging.py index 24943d8d145..cd4b3f8ccf4 100644 --- a/src/_pytest/logging.py +++ b/src/_pytest/logging.py @@ -8,6 +8,8 @@ from .config import hookimpl from _pytest.compat import nullcontext from _pytest.config import create_terminal_writer +from _pytest.config import UsageError +from _pytest.fixtures import fixture from _pytest.pathlib import Path DEFAULT_LOG_FORMAT = "%(levelname)-8s %(name)s:%(filename)s:%(lineno)d %(message)s" diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py index fff7e967e7f..49f4eb6bc62 100644 --- a/src/_pytest/pytester.py +++ b/src/_pytest/pytester.py @@ -330,7 +330,7 @@ def testdir(request, tmpdir_factory): return Testdir(request, tmpdir_factory) -@pytest.fixture +@fixture def _sys_snapshot(): snappaths = SysPathsSnapshot() snapmods = SysModulesSnapshot() @@ -339,7 +339,7 @@ def _sys_snapshot(): snappaths.restore() -@pytest.fixture +@fixture def _config_for_test(): from _pytest.config import get_config diff --git a/src/_pytest/terminal.py b/src/_pytest/terminal.py index 74e426e86a2..163d9b0d09b 100644 --- a/src/_pytest/terminal.py +++ b/src/_pytest/terminal.py @@ -167,7 +167,7 @@ def getreportopt(config): return reportopts -@pytest.hookimpl(trylast=True) # after _pytest.runner +@hookimpl(trylast=True) # after _pytest.runner def pytest_report_teststatus(report): if report.passed: letter = "." @@ -559,6 +559,9 @@ def pytest_sessionstart(self, session): if hasattr(sys, "pypy_version_info"): verinfo = ".".join(map(str, sys.pypy_version_info[:3])) msg += "[pypy-{}-{}]".format(verinfo, sys.pypy_version_info[3]) + import pytest + import py + msg += ", pytest-{}, py-{}, pluggy-{}".format( pytest.__version__, py.__version__, pluggy.__version__ ) diff --git a/src/_pytest/warnings.py b/src/_pytest/warnings.py index e00a20b0641..21ced3fda61 100644 --- a/src/_pytest/warnings.py +++ b/src/_pytest/warnings.py @@ -3,7 +3,7 @@ from contextlib import contextmanager from _pytest.config import hookimpl -from _pytest.warning_types import RemovedInPytest4Warning +from _pytest.warning_types import PytestDeprecationWarning SHOW_PYTEST_WARNINGS_ARG = "-Walways::pytest.RemovedInPytest4Warning" @@ -75,7 +75,7 @@ def catch_warnings_for_item(config, ihook, when, item): warnings.filterwarnings("always", category=DeprecationWarning) warnings.filterwarnings("always", category=PendingDeprecationWarning) - warnings.filterwarnings("error", category=pytest.PytestDeprecationWarning) + warnings.filterwarnings("error", category=PytestDeprecationWarning) # filters should have this precedence: mark, cmdline options, ini # filters should be applied in the inverse order of precedence From d96d573eeb34e8378be353c0ad59d7e3e0b0795e Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Thu, 8 Aug 2019 21:05:52 +0200 Subject: [PATCH 17/19] sync junitxml with master --- src/_pytest/junitxml.py | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/src/_pytest/junitxml.py b/src/_pytest/junitxml.py index 5ace326416b..cfb3cc10bee 100644 --- a/src/_pytest/junitxml.py +++ b/src/_pytest/junitxml.py @@ -290,6 +290,7 @@ def _warn_incompatibility_with_xunit2(request, fixture_name): ) +@fixture def record_property(request): """Add an extra properties the calling test. User properties become part of the test report and are available to the @@ -310,36 +311,27 @@ def append_property(name, value): return append_property -@fixture -def record_xml_property(record_property, request): - """(Deprecated) use record_property.""" - from _pytest import deprecated - - request.node.warn(deprecated.RECORD_XML_PROPERTY) - - return record_property - - @fixture def record_xml_attribute(request): """Add extra xml attributes to the tag for the calling test. The fixture is callable with ``(name, value)``, with value being automatically xml-encoded """ - from _pytest.warning_types import PytestWarning + from _pytest.warning_types import PytestExperimentalApiWarning - request.node.warn(PytestWarning("record_xml_attribute is an experimental feature")) - xml = getattr(request.config, "_xml", None) - if xml is not None: - node_reporter = xml.node_reporter(request.node.nodeid) - return node_reporter.add_attribute - else: + request.node.warn( + PytestExperimentalApiWarning("record_xml_attribute is an experimental feature") + ) - def add_attr_noop(name, value): - pass + _warn_incompatibility_with_xunit2(request, "record_xml_attribute") - return add_attr_noop + # Declare noop + def add_attr_noop(name, value): + pass + + attr_func = add_attr_noop + xml = getattr(request.config, "_xml", None) if xml is not None: node_reporter = xml.node_reporter(request.node.nodeid) attr_func = node_reporter.add_attribute From bb706aac8f32b9bfc02f3d91ce8ddbd008292f4e Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Thu, 8 Aug 2019 21:48:08 +0200 Subject: [PATCH 18/19] assertion no longer uses imp --- testing/test_own_modules.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/testing/test_own_modules.py b/testing/test_own_modules.py index 3eaed3c2325..5d9b8a5da94 100644 --- a/testing/test_own_modules.py +++ b/testing/test_own_modules.py @@ -5,20 +5,22 @@ import _pytest import pytest -KNOWN_BAD = { - "_pytest.assertion": [ - pytest.mark.xfail("sys.version_info[0]==3", reason="assertion uses imp") - ] -} +KNOWN_BAD = {} -def _get_modules(): - for module in vars(_pytest).values(): - if isinstance(module, types.ModuleType): - name = module.__name__ +def _modvalues(module): + for submodule in vars(module).values(): + if isinstance(submodule, types.ModuleType): + name = submodule.__name__ yield pytest.param(name, marks=KNOWN_BAD.get(name, [])) +def _get_modules(): + yield from _modvalues(_pytest) + yield from _modvalues(_pytest.config) + yield from _modvalues(_pytest.mark) + + @pytest.mark.parametrize("module_name", sorted(_get_modules())) def test_module_warning_free(module_name): # fmt: off From 2d1d3139bb24e88f2b0508106ab255c6f7003e95 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Thu, 8 Aug 2019 21:57:42 +0200 Subject: [PATCH 19/19] type annotate KNOWN BAD --- testing/test_own_modules.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/testing/test_own_modules.py b/testing/test_own_modules.py index 5d9b8a5da94..34f96cd7b12 100644 --- a/testing/test_own_modules.py +++ b/testing/test_own_modules.py @@ -1,11 +1,13 @@ import subprocess import sys import types +from typing import Dict import _pytest import pytest +from _pytest.mark import MarkDecorator -KNOWN_BAD = {} +KNOWN_BAD: Dict[str, MarkDecorator] = {} def _modvalues(module):