From afb8a4e35d0bf3393668a90bb4a30e55091543a2 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sat, 6 Jan 2018 13:31:38 -0200 Subject: [PATCH 01/23] Document bootstrap and initialization hooks Fix #2616 --- _pytest/hookspec.py | 15 +++++++++++++-- doc/en/writing_plugins.rst | 13 ++++++++++++- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/_pytest/hookspec.py b/_pytest/hookspec.py index f004dd097..c3d550189 100644 --- a/_pytest/hookspec.py +++ b/_pytest/hookspec.py @@ -85,8 +85,7 @@ def pytest_configure(config): # ------------------------------------------------------------------------- # Bootstrapping hooks called for plugins registered early enough: -# internal and 3rd party plugins as well as directly -# discoverable conftest.py local plugins. +# internal and 3rd party plugins. # ------------------------------------------------------------------------- @@ -96,6 +95,9 @@ def pytest_cmdline_parse(pluginmanager, args): Stops at first non-None result, see :ref:`firstresult` + .. note:: + This hook will not be called for ``conftest.py`` files, only for setuptools plugins. + :param _pytest.config.PytestPluginManager pluginmanager: pytest plugin manager :param list[str] args: list of arguments passed on the command line """ @@ -107,6 +109,9 @@ def pytest_cmdline_preparse(config, args): This hook is considered deprecated and will be removed in a future pytest version. Consider using :func:`pytest_load_initial_conftests` instead. + .. note:: + This hook will not be called for ``conftest.py`` files, only for setuptools plugins. + :param _pytest.config.Config config: pytest config object :param list[str] args: list of arguments passed on the command line """ @@ -117,6 +122,9 @@ def pytest_cmdline_main(config): """ called for performing the main command line action. The default implementation will invoke the configure hooks and runtest_mainloop. + .. note:: + This hook will not be called for ``conftest.py`` files, only for setuptools plugins. + Stops at first non-None result, see :ref:`firstresult` :param _pytest.config.Config config: pytest config object @@ -127,6 +135,9 @@ def pytest_load_initial_conftests(early_config, parser, args): """ implements the loading of initial conftest files ahead of command line option parsing. + .. note:: + This hook will not be called for ``conftest.py`` files, only for setuptools plugins. + :param _pytest.config.Config early_config: pytest config object :param list[str] args: list of arguments passed on the command line :param _pytest.config.Parser parser: to add command line options diff --git a/doc/en/writing_plugins.rst b/doc/en/writing_plugins.rst index 35fc7010d..753b300cd 100644 --- a/doc/en/writing_plugins.rst +++ b/doc/en/writing_plugins.rst @@ -583,11 +583,22 @@ pytest hook reference Initialization, command line and configuration hooks ---------------------------------------------------- +Bootstrapping hooks +~~~~~~~~~~~~~~~~~~~ + +Bootstrapping hooks called for plugins registered early enough (internal and setuptools plugins). + .. autofunction:: pytest_load_initial_conftests .. autofunction:: pytest_cmdline_preparse .. autofunction:: pytest_cmdline_parse -.. autofunction:: pytest_addoption .. autofunction:: pytest_cmdline_main + +Initialization hooks +~~~~~~~~~~~~~~~~~~~~ + +Initialization hooks called for plugins and ``conftest.py`` files. + +.. autofunction:: pytest_addoption .. autofunction:: pytest_configure .. autofunction:: pytest_unconfigure From cb6b851780ac010990c1404776c44841d01a8b84 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 4 Jan 2018 22:48:41 -0200 Subject: [PATCH 02/23] Update deprecation/removal docs to point to labels/milestones instead Using milestones and proper issues are a much saner way to handle these topics than keeping them in sync in a separate document --- doc/en/backwards-compatibility.rst | 89 +----------------------------- 1 file changed, 2 insertions(+), 87 deletions(-) diff --git a/doc/en/backwards-compatibility.rst b/doc/en/backwards-compatibility.rst index 84f2c43ed..55506e7c3 100644 --- a/doc/en/backwards-compatibility.rst +++ b/doc/en/backwards-compatibility.rst @@ -15,91 +15,6 @@ We will only remove deprecated functionality in major releases (e.g. if we depre Deprecation Roadmap ------------------- -This page lists deprecated features and when we plan to remove them. It is important to list the feature, the version where it got deprecated and the version we plan to remove it. +We track deprecation and removal of features using milestones and the `deprecation `_ and `removal `_ labels on GitHub. -Following our deprecation policy, we should aim to keep features for *at least* two minor versions after it was considered deprecated. - - -Future Releases -~~~~~~~~~~~~~~~ - -3.4 -^^^ - -**Old style classes** - -Issue: `#2147 `_. - -Deprecated in ``3.2``. - -4.0 -^^^ - -**Yield tests** - -Deprecated in ``3.0``. - -**pytest-namespace hook** - -deprecated in ``3.2``. - -**Marks in parameter sets** - -Deprecated in ``3.2``. - -**--result-log** - -Deprecated in ``3.0``. - -See `#830 `_ for more information. Suggested alternative: `pytest-tap `_. - -**metafunc.addcall** - -Issue: `#2876 `_. - -Deprecated in ``3.3``. - -**pytest_plugins in non-toplevel conftests** - -There is a deep conceptual confusion as ``conftest.py`` files themselves are activated/deactivated based on path, but the plugins they depend on aren't. - -Issue: `#2639 `_. - -Not yet officially deprecated. - -**passing a single string to pytest.main()** - -Pass a list of strings to ``pytest.main()`` instead. - -Deprecated in ``3.1``. - -**[pytest] section in setup.cfg** - -Use ``[tool:pytest]`` instead for compatibility with other tools. - -Deprecated in ``3.0``. - -Past Releases -~~~~~~~~~~~~~ - -3.0 -^^^ - -* The following deprecated commandline options were removed: - - * ``--genscript``: no longer supported; - * ``--no-assert``: use ``--assert=plain`` instead; - * ``--nomagic``: use ``--assert=plain`` instead; - * ``--report``: use ``-r`` instead; - -* Removed all ``py.test-X*`` entry points. The versioned, suffixed entry points - were never documented and a leftover from a pre-virtualenv era. These entry - points also created broken entry points in wheels, so removing them also - removes a source of confusion for users. - - - -3.3 -^^^ - -* Dropped support for EOL Python 2.6 and 3.3. \ No newline at end of file +Following our deprecation policy, after starting issuing deprecation warnings we keep features for *at least* two minor versions before considering removal. From bd1d17e8de30f6d52f9c4b61df4c6403f8f4f61c Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Tue, 26 Dec 2017 19:47:26 -0800 Subject: [PATCH 03/23] Replace py.std with stdlib imports --- _pytest/_code/code.py | 10 ++++---- _pytest/_code/source.py | 8 +++---- changelog/3067.trivial | 1 + doc/en/example/assertion/failure_demo.py | 4 ++-- doc/en/example/reportingdemo.rst | 4 ++-- extra/get_issues.py | 1 - testing/acceptance_test.py | 13 ++++++----- testing/code/test_excinfo.py | 18 ++++++++------- testing/code/test_source.py | 8 +++---- testing/python/collect.py | 5 ++-- testing/python/metafunc.py | 4 ++-- testing/test_argcomplete.py | 19 ++++++++-------- testing/test_assertrewrite.py | 5 ++-- testing/test_cache.py | 12 +++++----- testing/test_collection.py | 19 ++++++++-------- testing/test_config.py | 10 ++++---- testing/test_conftest.py | 6 ++--- testing/test_parseopt.py | 3 ++- testing/test_pluginmanager.py | 20 ++++++++-------- testing/test_recwarn.py | 29 ++++++++++++------------ testing/test_runner.py | 18 ++++++++------- testing/test_terminal.py | 6 ++--- 22 files changed, 117 insertions(+), 106 deletions(-) create mode 100644 changelog/3067.trivial diff --git a/_pytest/_code/code.py b/_pytest/_code/code.py index 3fb232bd4..84627a435 100644 --- a/_pytest/_code/code.py +++ b/_pytest/_code/code.py @@ -1,5 +1,7 @@ from __future__ import absolute_import, division, print_function +import inspect import sys +import traceback from inspect import CO_VARARGS, CO_VARKEYWORDS import re from weakref import ref @@ -422,7 +424,7 @@ class ExceptionInfo(object): """ if style == 'native': return ReprExceptionInfo(ReprTracebackNative( - py.std.traceback.format_exception( + traceback.format_exception( self.type, self.value, self.traceback[0]._rawentry, @@ -556,7 +558,7 @@ class FormattedExcinfo(object): # else: # self._line("%-10s =\\" % (name,)) # # XXX - # py.std.pprint.pprint(value, stream=self.excinfowriter) + # pprint.pprint(value, stream=self.excinfowriter) return ReprLocals(lines) def repr_traceback_entry(self, entry, excinfo=None): @@ -669,7 +671,7 @@ class FormattedExcinfo(object): else: # fallback to native repr if the exception doesn't have a traceback: # ExceptionInfo objects require a full traceback to work - reprtraceback = ReprTracebackNative(py.std.traceback.format_exception(type(e), e, None)) + reprtraceback = ReprTracebackNative(traceback.format_exception(type(e), e, None)) reprcrash = None repr_chain += [(reprtraceback, reprcrash, descr)] @@ -886,7 +888,7 @@ def getrawcode(obj, trycall=True): obj = getattr(obj, 'f_code', obj) obj = getattr(obj, '__code__', obj) if trycall and not hasattr(obj, 'co_firstlineno'): - if hasattr(obj, '__call__') and not py.std.inspect.isclass(obj): + if hasattr(obj, '__call__') and not inspect.isclass(obj): x = getrawcode(obj.__call__, trycall=False) if hasattr(x, 'co_firstlineno'): return x diff --git a/_pytest/_code/source.py b/_pytest/_code/source.py index 322c72afb..409961d9a 100644 --- a/_pytest/_code/source.py +++ b/_pytest/_code/source.py @@ -3,6 +3,7 @@ from __future__ import absolute_import, division, generators, print_function import ast from ast import PyCF_ONLY_AST as _AST_FLAG from bisect import bisect_right +import linecache import sys import six import inspect @@ -191,7 +192,7 @@ class Source(object): if flag & _AST_FLAG: return co lines = [(x + "\n") for x in self.lines] - py.std.linecache.cache[filename] = (1, None, lines, filename) + linecache.cache[filename] = (1, None, lines, filename) return co # @@ -223,8 +224,7 @@ def getfslineno(obj): code = _pytest._code.Code(obj) except TypeError: try: - fn = (py.std.inspect.getsourcefile(obj) or - py.std.inspect.getfile(obj)) + fn = inspect.getsourcefile(obj) or inspect.getfile(obj) except TypeError: return "", -1 @@ -248,7 +248,7 @@ def getfslineno(obj): def findsource(obj): try: - sourcelines, lineno = py.std.inspect.findsource(obj) + sourcelines, lineno = inspect.findsource(obj) except py.builtin._sysex: raise except: # noqa diff --git a/changelog/3067.trivial b/changelog/3067.trivial new file mode 100644 index 000000000..2b7185100 --- /dev/null +++ b/changelog/3067.trivial @@ -0,0 +1 @@ +Replace py.std with stdlib imports. diff --git a/doc/en/example/assertion/failure_demo.py b/doc/en/example/assertion/failure_demo.py index d31fba2ad..423bbeb93 100644 --- a/doc/en/example/assertion/failure_demo.py +++ b/doc/en/example/assertion/failure_demo.py @@ -159,10 +159,10 @@ class TestRaises(object): def test_dynamic_compile_shows_nicely(): src = 'def foo():\n assert 1 == 0\n' name = 'abc-123' - module = py.std.imp.new_module(name) + module = imp.new_module(name) code = _pytest._code.compile(src, name, 'exec') py.builtin.exec_(code, module.__dict__) - py.std.sys.modules[name] = module + sys.modules[name] = module module.foo() diff --git a/doc/en/example/reportingdemo.rst b/doc/en/example/reportingdemo.rst index 9edc02b3c..9964d67f2 100644 --- a/doc/en/example/reportingdemo.rst +++ b/doc/en/example/reportingdemo.rst @@ -413,10 +413,10 @@ get on the terminal - we are working on that):: def test_dynamic_compile_shows_nicely(): src = 'def foo():\n assert 1 == 0\n' name = 'abc-123' - module = py.std.imp.new_module(name) + module = imp.new_module(name) code = _pytest._code.compile(src, name, 'exec') py.builtin.exec_(code, module.__dict__) - py.std.sys.modules[name] = module + sys.modules[name] = module > module.foo() failure_demo.py:166: diff --git a/extra/get_issues.py b/extra/get_issues.py index 2a8f8c316..99378b2f5 100644 --- a/extra/get_issues.py +++ b/extra/get_issues.py @@ -1,6 +1,5 @@ import json import py -import textwrap issues_url = "https://api.github.com/repos/pytest-dev/pytest/issues" diff --git a/testing/acceptance_test.py b/testing/acceptance_test.py index 661aaa09b..e80049d5c 100644 --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -2,6 +2,7 @@ from __future__ import absolute_import, division, print_function import os import sys +import types import six @@ -398,7 +399,7 @@ class TestGeneralUsage(object): p = tmpdir.join('test_test_plugins_given_as_strings.py') p.write('def test_foo(): pass') - mod = py.std.types.ModuleType("myplugin") + mod = types.ModuleType("myplugin") monkeypatch.setitem(sys.modules, 'myplugin', mod) assert pytest.main(args=[str(tmpdir)], plugins=['myplugin']) == 0 @@ -492,17 +493,17 @@ class TestInvocationVariants(object): def test_python_minus_m_invocation_ok(self, testdir): p1 = testdir.makepyfile("def test_hello(): pass") - res = testdir.run(py.std.sys.executable, "-m", "pytest", str(p1)) + res = testdir.run(sys.executable, "-m", "pytest", str(p1)) assert res.ret == 0 def test_python_minus_m_invocation_fail(self, testdir): p1 = testdir.makepyfile("def test_fail(): 0/0") - res = testdir.run(py.std.sys.executable, "-m", "pytest", str(p1)) + res = testdir.run(sys.executable, "-m", "pytest", str(p1)) assert res.ret == 1 def test_python_pytest_package(self, testdir): p1 = testdir.makepyfile("def test_pass(): pass") - res = testdir.run(py.std.sys.executable, "-m", "pytest", str(p1)) + res = testdir.run(sys.executable, "-m", "pytest", str(p1)) assert res.ret == 0 res.stdout.fnmatch_lines(["*1 passed*"]) @@ -560,7 +561,7 @@ class TestInvocationVariants(object): ]) def join_pythonpath(what): - cur = py.std.os.environ.get('PYTHONPATH') + cur = os.environ.get('PYTHONPATH') if cur: return str(what) + os.pathsep + cur return what @@ -618,7 +619,7 @@ class TestInvocationVariants(object): # └── test_world.py def join_pythonpath(*dirs): - cur = py.std.os.environ.get('PYTHONPATH') + cur = os.environ.get('PYTHONPATH') if cur: dirs += (cur,) return os.pathsep.join(str(p) for p in dirs) diff --git a/testing/code/test_excinfo.py b/testing/code/test_excinfo.py index 58b1b1b67..6b4adf001 100644 --- a/testing/code/test_excinfo.py +++ b/testing/code/test_excinfo.py @@ -2,6 +2,8 @@ from __future__ import absolute_import, division, print_function import operator +import os +import sys import _pytest import py import pytest @@ -472,7 +474,7 @@ class TestFormattedExcinfo(object): excinfo = _pytest._code.ExceptionInfo() repr = pr.repr_excinfo(excinfo) assert repr.reprtraceback.reprentries[1].lines[0] == "> ???" - if py.std.sys.version_info[0] >= 3: + if sys.version_info[0] >= 3: assert repr.chain[0][0].reprentries[1].lines[0] == "> ???" def test_repr_many_line_source_not_existing(self): @@ -487,7 +489,7 @@ raise ValueError() excinfo = _pytest._code.ExceptionInfo() repr = pr.repr_excinfo(excinfo) assert repr.reprtraceback.reprentries[1].lines[0] == "> ???" - if py.std.sys.version_info[0] >= 3: + if sys.version_info[0] >= 3: assert repr.chain[0][0].reprentries[1].lines[0] == "> ???" def test_repr_source_failing_fullsource(self): @@ -545,13 +547,13 @@ raise ValueError() fail = IOError() repr = pr.repr_excinfo(excinfo) assert repr.reprtraceback.reprentries[0].lines[0] == "> ???" - if py.std.sys.version_info[0] >= 3: + if sys.version_info[0] >= 3: assert repr.chain[0][0].reprentries[0].lines[0] == "> ???" fail = py.error.ENOENT # noqa repr = pr.repr_excinfo(excinfo) assert repr.reprtraceback.reprentries[0].lines[0] == "> ???" - if py.std.sys.version_info[0] >= 3: + if sys.version_info[0] >= 3: assert repr.chain[0][0].reprentries[0].lines[0] == "> ???" def test_repr_local(self): @@ -738,7 +740,7 @@ raise ValueError() repr = p.repr_excinfo(excinfo) assert repr.reprtraceback assert len(repr.reprtraceback.reprentries) == len(reprtb.reprentries) - if py.std.sys.version_info[0] >= 3: + if sys.version_info[0] >= 3: assert repr.chain[0][0] assert len(repr.chain[0][0].reprentries) == len(reprtb.reprentries) assert repr.reprcrash.path.endswith("mod.py") @@ -758,7 +760,7 @@ raise ValueError() def raiseos(): raise OSError(2) - monkeypatch.setattr(py.std.os, 'getcwd', raiseos) + monkeypatch.setattr(os, 'getcwd', raiseos) assert p._makepath(__file__) == __file__ p.repr_traceback(excinfo) @@ -816,10 +818,10 @@ raise ValueError() for style in ("short", "long", "no"): for showlocals in (True, False): repr = excinfo.getrepr(style=style, showlocals=showlocals) - if py.std.sys.version_info[0] < 3: + if sys.version_info[0] < 3: assert isinstance(repr, ReprExceptionInfo) assert repr.reprtraceback.style == style - if py.std.sys.version_info[0] >= 3: + if sys.version_info[0] >= 3: assert isinstance(repr, ExceptionChainRepr) for repr in repr.chain: assert repr[0].style == style diff --git a/testing/code/test_source.py b/testing/code/test_source.py index fcce3fa96..ee731ed4f 100644 --- a/testing/code/test_source.py +++ b/testing/code/test_source.py @@ -2,6 +2,7 @@ # disable flake check on this file because some constructs are strange # or redundant on purpose and can't be disable on a line-by-line basis from __future__ import absolute_import, division, print_function +import inspect import sys import _pytest._code @@ -187,9 +188,9 @@ class TestSourceParsingAndCompiling(object): def f(): raise ValueError() """) - source1 = py.std.inspect.getsource(co1) + source1 = inspect.getsource(co1) assert 'KeyError' in source1 - source2 = py.std.inspect.getsource(co2) + source2 = inspect.getsource(co2) assert 'ValueError' in source2 def test_getstatement(self): @@ -373,7 +374,6 @@ def test_deindent(): c = '''while True: pass ''' - import inspect lines = deindent(inspect.getsource(f).splitlines()) assert lines == ["def f():", " c = '''while True:", " pass", "'''"] @@ -461,7 +461,7 @@ def test_getfslineno(): fspath, lineno = getfslineno(A) - _, A_lineno = py.std.inspect.findsource(A) + _, A_lineno = inspect.findsource(A) assert fspath.basename == "test_source.py" assert lineno == A_lineno diff --git a/testing/python/collect.py b/testing/python/collect.py index 16c2154b8..3e00ae961 100644 --- a/testing/python/collect.py +++ b/testing/python/collect.py @@ -4,7 +4,6 @@ import sys from textwrap import dedent import _pytest._code -import py import pytest from _pytest.main import ( Collector, @@ -25,7 +24,7 @@ class TestModule(object): b = testdir.mkdir("b") p = a.ensure("test_whatever.py") p.pyimport() - del py.std.sys.modules['test_whatever'] + del sys.modules['test_whatever'] b.ensure("test_whatever.py") result = testdir.runpytest() result.stdout.fnmatch_lines([ @@ -754,7 +753,7 @@ class TestSorting(object): assert fn1 == fn2 assert fn1 != modcol - if py.std.sys.version_info < (3, 0): + if sys.version_info < (3, 0): assert cmp(fn1, fn2) == 0 assert hash(fn1) == hash(fn2) diff --git a/testing/python/metafunc.py b/testing/python/metafunc.py index 2ffb7bb5d..a173f7739 100644 --- a/testing/python/metafunc.py +++ b/testing/python/metafunc.py @@ -730,7 +730,7 @@ class TestMetafuncFunctional(object): def test_attributes(self, testdir): p = testdir.makepyfile(""" # assumes that generate/provide runs in the same process - import py, pytest + import sys, pytest def pytest_generate_tests(metafunc): metafunc.addcall(param=metafunc) @@ -749,7 +749,7 @@ class TestMetafuncFunctional(object): def test_method(self, metafunc, pytestconfig): assert metafunc.config == pytestconfig assert metafunc.module.__name__ == __name__ - if py.std.sys.version_info > (3, 0): + if sys.version_info > (3, 0): unbound = TestClass.test_method else: unbound = TestClass.test_method.im_func diff --git a/testing/test_argcomplete.py b/testing/test_argcomplete.py index c92612577..7a5e25d69 100644 --- a/testing/test_argcomplete.py +++ b/testing/test_argcomplete.py @@ -1,5 +1,6 @@ from __future__ import absolute_import, division, print_function -import py +import subprocess +import sys import pytest # test for _argcomplete but not specific for any application @@ -23,21 +24,21 @@ def equal_with_bash(prefix, ffc, fc, out=None): def _wrapcall(*args, **kargs): try: - if py.std.sys.version_info > (2, 7): - return py.std.subprocess.check_output(*args, **kargs).decode().splitlines() + if sys.version_info > (2, 7): + return subprocess.check_output(*args, **kargs).decode().splitlines() if 'stdout' in kargs: raise ValueError('stdout argument not allowed, it will be overridden.') - process = py.std.subprocess.Popen( - stdout=py.std.subprocess.PIPE, *args, **kargs) + process = subprocess.Popen( + stdout=subprocess.PIPE, *args, **kargs) output, unused_err = process.communicate() retcode = process.poll() if retcode: cmd = kargs.get("args") if cmd is None: cmd = args[0] - raise py.std.subprocess.CalledProcessError(retcode, cmd) + raise subprocess.CalledProcessError(retcode, cmd) return output.decode().splitlines() - except py.std.subprocess.CalledProcessError: + except subprocess.CalledProcessError: return [] @@ -83,7 +84,7 @@ class TestArgComplete(object): ffc = FastFilesCompleter() fc = FilesCompleter() for x in ['/', '/d', '/data', 'qqq', '']: - assert equal_with_bash(x, ffc, fc, out=py.std.sys.stdout) + assert equal_with_bash(x, ffc, fc, out=sys.stdout) @pytest.mark.skipif("sys.platform in ('win32', 'darwin')") def test_remove_dir_prefix(self): @@ -94,4 +95,4 @@ class TestArgComplete(object): ffc = FastFilesCompleter() fc = FilesCompleter() for x in '/usr/'.split(): - assert not equal_with_bash(x, ffc, fc, out=py.std.sys.stdout) + assert not equal_with_bash(x, ffc, fc, out=sys.stdout) diff --git a/testing/test_assertrewrite.py b/testing/test_assertrewrite.py index 77cfd2900..2a3dbc2ec 100644 --- a/testing/test_assertrewrite.py +++ b/testing/test_assertrewrite.py @@ -5,6 +5,7 @@ import os import py_compile import stat import sys +import textwrap import zipfile import py import pytest @@ -911,7 +912,7 @@ class TestAssertionRewriteHookDetails(object): def test_reload_is_same(self, testdir): # A file that will be picked up during collecting. testdir.tmpdir.join("file.py").ensure() - testdir.tmpdir.join("pytest.ini").write(py.std.textwrap.dedent(""" + testdir.tmpdir.join("pytest.ini").write(textwrap.dedent(""" [pytest] python_files = *.py """)) @@ -997,7 +998,7 @@ class TestIssue2121(): def test_simple_failure(): assert 1 + 1 == 3 """) - testdir.tmpdir.join("pytest.ini").write(py.std.textwrap.dedent(""" + testdir.tmpdir.join("pytest.ini").write(textwrap.dedent(""" [pytest] python_files = tests/**.py """)) diff --git a/testing/test_cache.py b/testing/test_cache.py index a37170cdd..e3fa2ebb1 100755 --- a/testing/test_cache.py +++ b/testing/test_cache.py @@ -410,8 +410,8 @@ class TestLastFailed(object): def test_lastfailed_collectfailure(self, testdir, monkeypatch): testdir.makepyfile(test_maybe=""" - import py - env = py.std.os.environ + import os + env = os.environ if '1' == env['FAILIMPORT']: raise ImportError('fail') def test_hello(): @@ -439,8 +439,8 @@ class TestLastFailed(object): def test_lastfailed_failure_subset(self, testdir, monkeypatch): testdir.makepyfile(test_maybe=""" - import py - env = py.std.os.environ + import os + env = os.environ if '1' == env['FAILIMPORT']: raise ImportError('fail') def test_hello(): @@ -448,8 +448,8 @@ class TestLastFailed(object): """) testdir.makepyfile(test_maybe2=""" - import py - env = py.std.os.environ + import os + env = os.environ if '1' == env['FAILIMPORT']: raise ImportError('fail') def test_hello(): diff --git a/testing/test_collection.py b/testing/test_collection.py index 563ed0439..f2d542c62 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -1,6 +1,7 @@ from __future__ import absolute_import, division, print_function +import pprint +import sys import pytest -import py import _pytest._code from _pytest.main import Session, EXIT_NOTESTSCOLLECTED, _in_venv @@ -36,7 +37,7 @@ class TestCollector(object): assert fn1 == fn2 assert fn1 != modcol - if py.std.sys.version_info < (3, 0): + if sys.version_info < (3, 0): assert cmp(fn1, fn2) == 0 assert hash(fn1) == hash(fn2) @@ -128,7 +129,7 @@ class TestCollectFS(object): ("activate", "activate.csh", "activate.fish", "Activate", "Activate.bat", "Activate.ps1")) def test_ignored_virtualenvs(self, testdir, fname): - bindir = "Scripts" if py.std.sys.platform.startswith("win") else "bin" + bindir = "Scripts" if sys.platform.startswith("win") else "bin" testdir.tmpdir.ensure("virtual", bindir, fname) testfile = testdir.tmpdir.ensure("virtual", "test_invenv.py") testfile.write("def test_hello(): pass") @@ -147,7 +148,7 @@ class TestCollectFS(object): ("activate", "activate.csh", "activate.fish", "Activate", "Activate.bat", "Activate.ps1")) def test_ignored_virtualenvs_norecursedirs_precedence(self, testdir, fname): - bindir = "Scripts" if py.std.sys.platform.startswith("win") else "bin" + bindir = "Scripts" if sys.platform.startswith("win") else "bin" # norecursedirs takes priority testdir.tmpdir.ensure(".virtual", bindir, fname) testfile = testdir.tmpdir.ensure(".virtual", "test_invenv.py") @@ -163,7 +164,7 @@ class TestCollectFS(object): "Activate", "Activate.bat", "Activate.ps1")) def test__in_venv(self, testdir, fname): """Directly test the virtual env detection function""" - bindir = "Scripts" if py.std.sys.platform.startswith("win") else "bin" + bindir = "Scripts" if sys.platform.startswith("win") else "bin" # no bin/activate, not a virtualenv base_path = testdir.tmpdir.mkdir('venv') assert _in_venv(base_path) is False @@ -436,7 +437,7 @@ class TestSession(object): assert item.name == "test_func" newid = item.nodeid assert newid == id - py.std.pprint.pprint(hookrec.calls) + pprint.pprint(hookrec.calls) topdir = testdir.tmpdir # noqa hookrec.assert_contains([ ("pytest_collectstart", "collector.fspath == topdir"), @@ -486,7 +487,7 @@ class TestSession(object): id = p.basename items, hookrec = testdir.inline_genitems(id) - py.std.pprint.pprint(hookrec.calls) + pprint.pprint(hookrec.calls) assert len(items) == 2 hookrec.assert_contains([ ("pytest_collectstart", @@ -508,7 +509,7 @@ class TestSession(object): items, hookrec = testdir.inline_genitems() assert len(items) == 1 - py.std.pprint.pprint(hookrec.calls) + pprint.pprint(hookrec.calls) hookrec.assert_contains([ ("pytest_collectstart", "collector.fspath == test_aaa"), ("pytest_pycollect_makeitem", "name == 'test_func'"), @@ -529,7 +530,7 @@ class TestSession(object): items, hookrec = testdir.inline_genitems(id) assert len(items) == 2 - py.std.pprint.pprint(hookrec.calls) + pprint.pprint(hookrec.calls) hookrec.assert_contains([ ("pytest_collectstart", "collector.fspath == test_aaa"), ("pytest_pycollect_makeitem", "name == 'test_func'"), diff --git a/testing/test_config.py b/testing/test_config.py index 44b8c317a..5202b21b9 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -1,6 +1,6 @@ from __future__ import absolute_import, division, print_function import sys -import py +import textwrap import pytest import _pytest._code @@ -57,7 +57,7 @@ class TestParseIni(object): ('pytest', 'pytest.ini')], ) def test_ini_names(self, testdir, name, section): - testdir.tmpdir.join(name).write(py.std.textwrap.dedent(""" + testdir.tmpdir.join(name).write(textwrap.dedent(""" [{section}] minversion = 1.0 """.format(section=section))) @@ -66,11 +66,11 @@ class TestParseIni(object): def test_toxini_before_lower_pytestini(self, testdir): sub = testdir.tmpdir.mkdir("sub") - sub.join("tox.ini").write(py.std.textwrap.dedent(""" + sub.join("tox.ini").write(textwrap.dedent(""" [pytest] minversion = 2.0 """)) - testdir.tmpdir.join("pytest.ini").write(py.std.textwrap.dedent(""" + testdir.tmpdir.join("pytest.ini").write(textwrap.dedent(""" [pytest] minversion = 1.5 """)) @@ -731,7 +731,7 @@ class TestRootdir(object): class TestOverrideIniArgs(object): @pytest.mark.parametrize("name", "setup.cfg tox.ini pytest.ini".split()) def test_override_ini_names(self, testdir, name): - testdir.tmpdir.join(name).write(py.std.textwrap.dedent(""" + testdir.tmpdir.join(name).write(textwrap.dedent(""" [pytest] custom = 1.0""")) testdir.makeconftest(""" diff --git a/testing/test_conftest.py b/testing/test_conftest.py index f0f187606..6566f752a 100644 --- a/testing/test_conftest.py +++ b/testing/test_conftest.py @@ -232,7 +232,7 @@ def test_fixture_dependency(testdir, monkeypatch): ct1.write("") sub = testdir.mkdir("sub") sub.join("__init__.py").write("") - sub.join("conftest.py").write(py.std.textwrap.dedent(""" + sub.join("conftest.py").write(dedent(""" import pytest @pytest.fixture @@ -249,7 +249,7 @@ def test_fixture_dependency(testdir, monkeypatch): """)) subsub = sub.mkdir("subsub") subsub.join("__init__.py").write("") - subsub.join("test_bar.py").write(py.std.textwrap.dedent(""" + subsub.join("test_bar.py").write(dedent(""" import pytest @pytest.fixture @@ -265,7 +265,7 @@ def test_fixture_dependency(testdir, monkeypatch): def test_conftest_found_with_double_dash(testdir): sub = testdir.mkdir("sub") - sub.join("conftest.py").write(py.std.textwrap.dedent(""" + sub.join("conftest.py").write(dedent(""" def pytest_addoption(parser): parser.addoption("--hello-world", action="store_true") """)) diff --git a/testing/test_parseopt.py b/testing/test_parseopt.py index 921592570..55983bbb1 100644 --- a/testing/test_parseopt.py +++ b/testing/test_parseopt.py @@ -1,4 +1,5 @@ from __future__ import absolute_import, division, print_function +import argparse import sys import os import py @@ -189,7 +190,7 @@ class TestParser(object): assert option.no is False def test_drop_short_helper(self): - parser = py.std.argparse.ArgumentParser(formatter_class=parseopt.DropShorterLongHelpFormatter) + parser = argparse.ArgumentParser(formatter_class=parseopt.DropShorterLongHelpFormatter) parser.add_argument('-t', '--twoword', '--duo', '--two-word', '--two', help='foo').map_long_option = {'two': 'two-word'} # throws error on --deux only! diff --git a/testing/test_pluginmanager.py b/testing/test_pluginmanager.py index 6192176e8..503ba8454 100644 --- a/testing/test_pluginmanager.py +++ b/testing/test_pluginmanager.py @@ -1,8 +1,10 @@ # encoding: UTF-8 from __future__ import absolute_import, division, print_function import pytest -import py import os +import re +import sys +import types from _pytest.config import get_config, PytestPluginManager from _pytest.main import EXIT_NOTESTSCOLLECTED, Session @@ -208,14 +210,14 @@ def test_importplugin_error_message(testdir, pytestpm): expected_message = '.*Error importing plugin "qwe": Not possible to import: .' expected_traceback = ".*in test_traceback" - assert py.std.re.match(expected_message, str(excinfo.value)) - assert py.std.re.match(expected_traceback, str(excinfo.traceback[-1])) + assert re.match(expected_message, str(excinfo.value)) + assert re.match(expected_traceback, str(excinfo.traceback[-1])) class TestPytestPluginManager(object): def test_register_imported_modules(self): pm = PytestPluginManager() - mod = py.std.types.ModuleType("x.y.pytest_hello") + mod = types.ModuleType("x.y.pytest_hello") pm.register(mod) assert pm.is_registered(mod) values = pm.get_plugins() @@ -226,8 +228,8 @@ class TestPytestPluginManager(object): assert pm.get_plugins() == values def test_canonical_import(self, monkeypatch): - mod = py.std.types.ModuleType("pytest_xyz") - monkeypatch.setitem(py.std.sys.modules, 'pytest_xyz', mod) + mod = types.ModuleType("pytest_xyz") + monkeypatch.setitem(sys.modules, 'pytest_xyz', mod) pm = PytestPluginManager() pm.import_plugin('pytest_xyz') assert pm.get_plugin('pytest_xyz') == mod @@ -237,7 +239,7 @@ class TestPytestPluginManager(object): testdir.syspathinsert() testdir.makepyfile(pytest_p1="#") testdir.makepyfile(pytest_p2="#") - mod = py.std.types.ModuleType("temp") + mod = types.ModuleType("temp") mod.pytest_plugins = ["pytest_p1", "pytest_p2"] pytestpm.consider_module(mod) assert pytestpm.get_plugin("pytest_p1").__name__ == "pytest_p1" @@ -245,12 +247,12 @@ class TestPytestPluginManager(object): def test_consider_module_import_module(self, testdir): pytestpm = get_config().pluginmanager - mod = py.std.types.ModuleType("x") + mod = types.ModuleType("x") mod.pytest_plugins = "pytest_a" aplugin = testdir.makepyfile(pytest_a="#") reprec = testdir.make_hook_recorder(pytestpm) # syspath.prepend(aplugin.dirpath()) - py.std.sys.path.insert(0, str(aplugin.dirpath())) + sys.path.insert(0, str(aplugin.dirpath())) pytestpm.consider_module(mod) call = reprec.getcall(pytestpm.hook.pytest_plugin_registered.name) assert call.plugin.__name__ == "pytest_a" diff --git a/testing/test_recwarn.py b/testing/test_recwarn.py index 31e70460f..0c80c169e 100644 --- a/testing/test_recwarn.py +++ b/testing/test_recwarn.py @@ -1,7 +1,6 @@ from __future__ import absolute_import, division, print_function import warnings import re -import py import pytest from _pytest.recwarn import WarningsRecorder @@ -24,9 +23,9 @@ class TestWarningsRecorderChecker(object): rec = WarningsRecorder() with rec: assert not rec.list - py.std.warnings.warn_explicit("hello", UserWarning, "xyz", 13) + warnings.warn_explicit("hello", UserWarning, "xyz", 13) assert len(rec.list) == 1 - py.std.warnings.warn(DeprecationWarning("hello")) + warnings.warn(DeprecationWarning("hello")) assert len(rec.list) == 2 warn = rec.pop() assert str(warn.message) == "hello" @@ -64,14 +63,14 @@ class TestDeprecatedCall(object): def dep(self, i, j=None): if i == 0: - py.std.warnings.warn("is deprecated", DeprecationWarning, - stacklevel=1) + warnings.warn("is deprecated", DeprecationWarning, + stacklevel=1) return 42 def dep_explicit(self, i): if i == 0: - py.std.warnings.warn_explicit("dep_explicit", category=DeprecationWarning, - filename="hello", lineno=3) + warnings.warn_explicit("dep_explicit", category=DeprecationWarning, + filename="hello", lineno=3) def test_deprecated_call_raises(self): with pytest.raises(AssertionError) as excinfo: @@ -86,16 +85,16 @@ class TestDeprecatedCall(object): assert ret == 42 def test_deprecated_call_preserves(self): - onceregistry = py.std.warnings.onceregistry.copy() - filters = py.std.warnings.filters[:] - warn = py.std.warnings.warn - warn_explicit = py.std.warnings.warn_explicit + onceregistry = warnings.onceregistry.copy() + filters = warnings.filters[:] + warn = warnings.warn + warn_explicit = warnings.warn_explicit self.test_deprecated_call_raises() self.test_deprecated_call() - assert onceregistry == py.std.warnings.onceregistry - assert filters == py.std.warnings.filters - assert warn is py.std.warnings.warn - assert warn_explicit is py.std.warnings.warn_explicit + assert onceregistry == warnings.onceregistry + assert filters == warnings.filters + assert warn is warnings.warn + assert warn_explicit is warnings.warn_explicit def test_deprecated_explicit_call_raises(self): with pytest.raises(AssertionError): diff --git a/testing/test_runner.py b/testing/test_runner.py index a8dc8dfbd..0c82cd474 100644 --- a/testing/test_runner.py +++ b/testing/test_runner.py @@ -2,10 +2,12 @@ from __future__ import absolute_import, division, print_function import _pytest._code +import inspect import os import py import pytest import sys +import types from _pytest import runner, main, outcomes @@ -392,10 +394,10 @@ reporttypes = [ @pytest.mark.parametrize('reporttype', reporttypes, ids=[x.__name__ for x in reporttypes]) def test_report_extra_parameters(reporttype): - if hasattr(py.std.inspect, 'signature'): - args = list(py.std.inspect.signature(reporttype.__init__).parameters.keys())[1:] + if hasattr(inspect, 'signature'): + args = list(inspect.signature(reporttype.__init__).parameters.keys())[1:] else: - args = py.std.inspect.getargspec(reporttype.__init__)[0][1:] + args = inspect.getargspec(reporttype.__init__)[0][1:] basekw = dict.fromkeys(args, []) report = reporttype(newthing=1, **basekw) assert report.newthing == 1 @@ -564,10 +566,10 @@ def test_importorskip(monkeypatch): importorskip("asdlkj") try: - sys = importorskip("sys") - assert sys == py.std.sys + sysmod = importorskip("sys") + assert sysmod is sys # path = pytest.importorskip("os.path") - # assert path == py.std.os.path + # assert path == os.path excinfo = pytest.raises(pytest.skip.Exception, f) path = py.path.local(excinfo.getrepr().reprcrash.path) # check that importorskip reports the actual call @@ -575,7 +577,7 @@ def test_importorskip(monkeypatch): assert path.purebasename == "test_runner" pytest.raises(SyntaxError, "pytest.importorskip('x y z')") pytest.raises(SyntaxError, "pytest.importorskip('x=y')") - mod = py.std.types.ModuleType("hello123") + mod = types.ModuleType("hello123") mod.__version__ = "1.3" monkeypatch.setitem(sys.modules, "hello123", mod) pytest.raises(pytest.skip.Exception, """ @@ -595,7 +597,7 @@ def test_importorskip_imports_last_module_part(): def test_importorskip_dev_module(monkeypatch): try: - mod = py.std.types.ModuleType("mockmodule") + mod = types.ModuleType("mockmodule") mod.__version__ = '0.13.0.dev-43290' monkeypatch.setitem(sys.modules, 'mockmodule', mod) mod2 = pytest.importorskip('mockmodule', minversion='0.12.0') diff --git a/testing/test_terminal.py b/testing/test_terminal.py index 97c2f71fb..a515fc703 100644 --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -326,7 +326,7 @@ def test_repr_python_version(monkeypatch): try: monkeypatch.setattr(sys, 'version_info', (2, 5, 1, 'final', 0)) assert repr_pythonversion() == "2.5.1-final-0" - py.std.sys.version_info = x = (2, 3) + sys.version_info = x = (2, 3) assert repr_pythonversion() == str(x) finally: monkeypatch.undo() # do this early as pytest can get confused @@ -475,11 +475,11 @@ class TestTerminalFunctional(object): pass """) result = testdir.runpytest() - verinfo = ".".join(map(str, py.std.sys.version_info[:3])) + verinfo = ".".join(map(str, sys.version_info[:3])) result.stdout.fnmatch_lines([ "*===== test session starts ====*", "platform %s -- Python %s*pytest-%s*py-%s*pluggy-%s" % ( - py.std.sys.platform, verinfo, + sys.platform, verinfo, pytest.__version__, py.__version__, pluggy.__version__), "*test_header_trailer_info.py .*", "=* 1 passed*in *.[0-9][0-9] seconds *=", From 820ea6d68f96ed320af5df14ecedbf20a86ed5b2 Mon Sep 17 00:00:00 2001 From: "Per A. Brodtkorb" Date: Wed, 10 Jan 2018 16:44:26 +0100 Subject: [PATCH 04/23] Update Freezing pytest description in simple.rst I have trouble using third party plugins in my frozen program and discovered that other people have experienced it as well: https://mail.python.org/pipermail//pytest-dev/2015-June/003015.html The problem is that the mechanism for plugin discovery used by pytest (setupttools entry points) doesn't work with frozen executables so pytest can't find any plugins. The solution seems to be to import the third party plugins explicitly as shown here: https://mail.python.org/pipermail//pytest-dev/2015-June/003018.html This is not mentioned anywhere in the documentaion. --- doc/en/example/simple.rst | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/doc/en/example/simple.rst b/doc/en/example/simple.rst index 9c773aaed..5dbf0a519 100644 --- a/doc/en/example/simple.rst +++ b/doc/en/example/simple.rst @@ -826,15 +826,20 @@ Instead of freezing the pytest runner as a separate executable, you can make your frozen program work as the pytest runner by some clever argument handling during program startup. This allows you to have a single executable, which is usually more convenient. +Please note that the mechanism for plugin discovery used by pytest +(setupttools entry points) doesn't work with frozen executables so pytest +can't find any third party plugins automatically. To include third party plugins +like ``pytest-timeout`` they must be imported explicitly and passed on to pytest.main. .. code-block:: python # contents of app_main.py import sys + import pytest_timeout # Third party plugin if len(sys.argv) > 1 and sys.argv[1] == '--pytest': import pytest - sys.exit(pytest.main(sys.argv[2:])) + sys.exit(pytest.main(sys.argv[2:], plugins=[pytest_timeout])) else: # normal application execution: at this point argv can be parsed # by your argument-parsing library of choice as usual @@ -845,3 +850,4 @@ This allows you to execute tests using the frozen application with standard ``pytest`` command-line options:: ./app_main --pytest --verbose --tb=long --junitxml=results.xml test-suite/ + From 4a3863c2e28df69b59315a14b3a2ae4767d07ef9 Mon Sep 17 00:00:00 2001 From: Brian Maissy Date: Sun, 14 Jan 2018 23:00:23 +0200 Subject: [PATCH 05/23] use flush in order to avoid hanging on mac --- testing/test_pdb.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/testing/test_pdb.py b/testing/test_pdb.py index a565cbd0d..8618473bb 100644 --- a/testing/test_pdb.py +++ b/testing/test_pdb.py @@ -402,5 +402,4 @@ class TestPDB(object): child = testdir.spawn_pytest("--pdbcls=custom_pdb:CustomPdb %s" % str(p1)) child.expect('custom set_trace>') - if child.isalive(): - child.wait() + self.flush(child) From 0a0d97aeb5eac43101b6f3f8a4b7d93f27464568 Mon Sep 17 00:00:00 2001 From: Brian Maissy Date: Sun, 14 Jan 2018 23:14:59 +0200 Subject: [PATCH 06/23] added changelog news fragment --- changelog/2022.bugfix | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/2022.bugfix diff --git a/changelog/2022.bugfix b/changelog/2022.bugfix new file mode 100644 index 000000000..67c9c8f77 --- /dev/null +++ b/changelog/2022.bugfix @@ -0,0 +1 @@ +Fixed hanging pexpect test on MacOS by using flush() instead of wait(). \ No newline at end of file From 1f4831a23fdfab060006f65bb4fa891167374f2f Mon Sep 17 00:00:00 2001 From: Cyrus Maden Date: Mon, 15 Jan 2018 12:28:21 -0800 Subject: [PATCH 07/23] Update getting-started.rst --- doc/en/getting-started.rst | 86 ++++++++++++++++---------------------- 1 file changed, 36 insertions(+), 50 deletions(-) diff --git a/doc/en/getting-started.rst b/doc/en/getting-started.rst index 64b010826..d4847d59b 100644 --- a/doc/en/getting-started.rst +++ b/doc/en/getting-started.rst @@ -7,32 +7,34 @@ Installation and Getting Started **PyPI package name**: `pytest `_ -**dependencies**: `py `_, +**Dependencies**: `py `_, `colorama (Windows) `_, -**documentation as PDF**: `download latest `_ +**Documentation as PDF**: `download latest `_ + +``pytest`` is a framework that makes building simple and scalable tests easy. Tests are expressive and readable—no boilerplate code required. Get started in minutes with a small unit test or complex functional test for your application or library. .. _`getstarted`: -.. _installation: +.. _`installpytest`: -Installation +Install ``pytest`` ---------------------------------------- -Installation:: +1. Run the following command in your command line:: pip install -U pytest -To check your installation has installed the correct version:: +2. Check that you installed the correct version:: $ pytest --version This is pytest version 3.x.y, imported from $PYTHON_PREFIX/lib/python3.5/site-packages/pytest.py -.. _`simpletest`: +.. _`createyourfirsttest`: -Our first test run +Create your first test ---------------------------------------------------------- -Let's create a first test file with a simple test function:: +Create a simple test function with just four lines of code:: # content of test_sample.py def func(x): @@ -41,7 +43,7 @@ Let's create a first test file with a simple test function:: def test_answer(): assert func(3) == 5 -That's it. You can execute the test function now:: +That’s it. You can now execute the test function:: $ pytest =========================== test session starts ============================ @@ -62,30 +64,26 @@ That's it. You can execute the test function now:: test_sample.py:5: AssertionError ========================= 1 failed in 0.12 seconds ========================= -We got a failure report because our little ``func(3)`` call did not return ``5``. +This test returns a failure report because ``func(3)`` does not return ``5``. .. note:: - You can simply use the ``assert`` statement for asserting test - expectations. pytest's :ref:`assert introspection` will intelligently - report intermediate values of the assert expression freeing - you from the need to learn the many names of `JUnit legacy methods`_. + You can use the ``assert`` statement to verify test expectations. pytest’s :ref:`Advanced assertion introspection` will intelligently report intermediate values of the assert expression so you can avoid the many names `of JUnit legacy methods`_. .. _`JUnit legacy methods`: http://docs.python.org/library/unittest.html#test-cases -.. _`assert statement`: http://docs.python.org/reference/simple_stmts.html#the-assert-statement +.. _`Advanced assertion introspection`: http://docs.python.org/reference/simple_stmts.html#the-assert-statement -Running multiple tests +Run multiple tests ---------------------------------------------------------- -``pytest`` will run all files in the current directory and its subdirectories of the form test_*.py or \*_test.py. More generally, it follows :ref:`standard test discovery rules `. +``pytest`` will run all files of the form test_*.py or \*_test.py in the current directory and its subdirectories. More generally, it follows :ref:`standard test discovery rules `. -Asserting that a certain exception is raised +Assert that a certain exception is raised -------------------------------------------------------------- -If you want to assert that some code raises an exception you can -use the ``raises`` helper:: +Use the ``raises`` helper to assert that some code raises an exception:: # content of test_sysexit.py import pytest @@ -96,18 +94,16 @@ use the ``raises`` helper:: with pytest.raises(SystemExit): f() -Running it with, this time in "quiet" reporting mode:: +Execute the test function with “quiet” reporting mode:: $ pytest -q test_sysexit.py . [100%] 1 passed in 0.12 seconds -Grouping multiple tests in a class +Group multiple tests in a class -------------------------------------------------------------- -Once you start to have more than a few tests it often makes sense -to group tests logically, in classes and modules. Let's write a class -containing two tests:: +Once you develop multiple tests, you may want to group them into a class. pytest makes it easy to create a class containing more than one test:: # content of test_class.py class TestClass(object): @@ -119,9 +115,7 @@ containing two tests:: x = "hello" assert hasattr(x, 'check') -The two tests are found because of the standard :ref:`test discovery`. -There is no need to subclass anything. We can simply -run the module by passing its filename:: +``pytest`` discovers all tests following its :ref:`Conventions for Python test discovery `, so it finds both ``test_`` prefixed functions. There is no need to subclass anything. We can simply run the module by passing its filename: $ pytest -q test_class.py .F [100%] @@ -139,26 +133,19 @@ run the module by passing its filename:: test_class.py:8: AssertionError 1 failed, 1 passed in 0.12 seconds -The first test passed, the second failed. Again we can easily see -the intermediate values used in the assertion, helping us to -understand the reason for the failure. +The first test passed and the second failed. You can easily see the intermediate values in the assertion to help you understand the reason for the failure. -Going functional: requesting a unique temporary directory +Request a unique temporary directory for functional tests -------------------------------------------------------------- -For functional tests one often needs to create some files -and pass them to application objects. pytest provides -:ref:`builtinfixtures` which allow to request arbitrary -resources, for example a unique temporary directory:: +``pytest`` provides `Builtin fixtures/function arguments `_ to request arbitrary resources, like a unique temporary directory: # content of test_tmpdir.py def test_needsfiles(tmpdir): print (tmpdir) assert 0 -We list the name ``tmpdir`` in the test function signature and -``pytest`` will lookup and call a fixture factory to create the resource -before performing the test function call. Let's just run it:: +List the name ``tmpdir`` in the test function signature and ``pytest`` will lookup and call a fixture factory to create the resource before performing the test function call. Before the test runs, ``pytest`` creates a unique-per-test-invocation temporary directory:: $ pytest -q test_tmpdir.py F [100%] @@ -177,22 +164,21 @@ before performing the test function call. Let's just run it:: PYTEST_TMPDIR/test_needsfiles0 1 failed in 0.12 seconds -Before the test runs, a unique-per-test-invocation temporary directory -was created. More info at :ref:`tmpdir handling`. +More info on tmpdir handeling is available at `Temporary directories and files `_. -You can find out what kind of builtin :ref:`fixtures` exist by typing:: +Find out what kind of builtin ```pytest`` fixtures `_ exist with the command:: pytest --fixtures # shows builtin and custom fixtures -Where to go next +Continue reading ------------------------------------- -Here are a few suggestions where to go next: +Check out additional pytest resources to help you customize tests for your unique workflow: -* :ref:`cmdline` for command line invocation examples -* :ref:`good practices ` for virtualenv, test layout -* :ref:`existingtestsuite` for working with pre-existing tests -* :ref:`fixtures` for providing a functional baseline to your tests -* :ref:`plugins` managing and writing plugins +* ":ref:`cmdline`" for command line invocation examples +* ":ref:`goodpractices`" for virtualenv and test layouts +* ":ref:`existingtestsuite`" for working with pre-existing tests +* ":ref:`fixtures`" for providing a functional baseline to your tests +* ":ref:`plugins`" for managing and writing plugins .. include:: links.inc From f555a3a76cc205b0f0d2b253d0146290e7ca34cf Mon Sep 17 00:00:00 2001 From: Cyrus Maden Date: Mon, 15 Jan 2018 13:27:10 -0800 Subject: [PATCH 08/23] Update getting started guide Proofread; added intro paragraph under first header to orient new users; fixed grammar errors (switched to active voice, actionable directions, etc) to improve readability --- .DS_Store | Bin 0 -> 6148 bytes doc/.DS_Store | Bin 0 -> 6148 bytes doc/en/.DS_Store | Bin 0 -> 6148 bytes doc/en/getting-started.rst | 14 +++++--------- 4 files changed, 5 insertions(+), 9 deletions(-) create mode 100644 .DS_Store create mode 100644 doc/.DS_Store create mode 100644 doc/en/.DS_Store diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..7cbe275046a42350e05301fa3e51caa9dcc81710 GIT binary patch literal 6148 zcmeH~Jr2S!425ml0g0s}V-^m;4I%_5-~tFbB6UFQIXcfj3xyd~=vlJA*s0a_4NWZ~ zdU%d|kwHY(aHH%j%uJDYGRh>k@o~Ffr|E7bw^h;#@Ihw#xlK?3DnJFO02QDD3sN8t z^7Upx&%{Te0#slb3fT9dz>PK8g8u11@DTv)QFg=HX9=)a0j$XuLtMpWxr-|5*!DDnJGPOabkW z=i>n{m1pb6>skGhRa-YW=$9kB{RAMfqj(EA!;KrJ4LH~3h_y_>bP3Z5S>vG!N$@uSMUZw^aLJ2(QSkU3$fqIb9pr1d;e&iX;6Vd)od{|=!lohtBGA;&_%QP(7ai*Ls7pS=NC^Gt$`e=02O#vU>M7p)&Dj8 zP5=K);))7Tfxl8fN2|?hi6>=k?LE$FZGrFLmUDxfVeS+RUXFoYjlb| WP3!`lj=0l-{24G^XjI_a3fut+K^1ra literal 0 HcmV?d00001 diff --git a/doc/en/getting-started.rst b/doc/en/getting-started.rst index d4847d59b..3c6391c6a 100644 --- a/doc/en/getting-started.rst +++ b/doc/en/getting-started.rst @@ -15,7 +15,7 @@ Installation and Getting Started ``pytest`` is a framework that makes building simple and scalable tests easy. Tests are expressive and readable—no boilerplate code required. Get started in minutes with a small unit test or complex functional test for your application or library. .. _`getstarted`: -.. _`installpytest`: +.. _`installation`: Install ``pytest`` ---------------------------------------- @@ -29,7 +29,7 @@ Install ``pytest`` $ pytest --version This is pytest version 3.x.y, imported from $PYTHON_PREFIX/lib/python3.5/site-packages/pytest.py -.. _`createyourfirsttest`: +.. _`simpletest`: Create your first test ---------------------------------------------------------- @@ -68,11 +68,7 @@ This test returns a failure report because ``func(3)`` does not return ``5``. .. note:: - You can use the ``assert`` statement to verify test expectations. pytest’s :ref:`Advanced assertion introspection` will intelligently report intermediate values of the assert expression so you can avoid the many names `of JUnit legacy methods`_. - -.. _`JUnit legacy methods`: http://docs.python.org/library/unittest.html#test-cases - -.. _`Advanced assertion introspection`: http://docs.python.org/reference/simple_stmts.html#the-assert-statement + You can use the ``assert`` statement to verify test expectations. pytest’s `Advanced assertion introspection `_ will intelligently report intermediate values of the assert expression so you can avoid the many names `of JUnit legacy methods `_. Run multiple tests ---------------------------------------------------------- @@ -115,7 +111,7 @@ Once you develop multiple tests, you may want to group them into a class. pytest x = "hello" assert hasattr(x, 'check') -``pytest`` discovers all tests following its :ref:`Conventions for Python test discovery `, so it finds both ``test_`` prefixed functions. There is no need to subclass anything. We can simply run the module by passing its filename: +``pytest`` discovers all tests following its :ref:`Conventions for Python test discovery `, so it finds both ``test_`` prefixed functions. There is no need to subclass anything. We can simply run the module by passing its filename:: $ pytest -q test_class.py .F [100%] @@ -138,7 +134,7 @@ The first test passed and the second failed. You can easily see the intermediate Request a unique temporary directory for functional tests -------------------------------------------------------------- -``pytest`` provides `Builtin fixtures/function arguments `_ to request arbitrary resources, like a unique temporary directory: +``pytest`` provides `Builtin fixtures/function arguments `_ to request arbitrary resources, like a unique temporary directory:: # content of test_tmpdir.py def test_needsfiles(tmpdir): From b7485763581ba0f8936fdc451d1c9fa5b08007c0 Mon Sep 17 00:00:00 2001 From: Cyrus Maden Date: Mon, 15 Jan 2018 13:36:44 -0800 Subject: [PATCH 09/23] Add name --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 862378be9..557729978 100644 --- a/AUTHORS +++ b/AUTHORS @@ -38,6 +38,7 @@ Christian Boelsen Christian Theunert Christian Tismer Christopher Gilling +Cyrus Maden Daniel Grana Daniel Hahler Daniel Nuri From d8c23fd39bbe3e6d3f44ef44ffe58bee81fccaee Mon Sep 17 00:00:00 2001 From: Kate Date: Tue, 16 Jan 2018 12:36:28 +0300 Subject: [PATCH 10/23] Fix wrong formatting --- doc/en/parametrize.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/en/parametrize.rst b/doc/en/parametrize.rst index 7bc37ae38..991dd4b1e 100644 --- a/doc/en/parametrize.rst +++ b/doc/en/parametrize.rst @@ -123,7 +123,7 @@ To get all combinations of multiple parametrized arguments you can stack def test_foo(x, y): pass -This will run the test with the arguments set to ``x=0/y=2``,``x=1/y=2``, +This will run the test with the arguments set to ``x=0/y=2``, ``x=1/y=2``, ``x=0/y=3``, and ``x=1/y=3`` exhausting parameters in the order of the decorators. .. _`pytest_generate_tests`: From e0b63e34fa2ad6c5cb6d607b3f276d3724c7986f Mon Sep 17 00:00:00 2001 From: Kimberly Date: Thu, 18 Jan 2018 10:22:04 -0700 Subject: [PATCH 11/23] fixed typo in logging doc and added fix to changelog --- changelog/3129.trivial | 1 + doc/en/logging.rst | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 changelog/3129.trivial diff --git a/changelog/3129.trivial b/changelog/3129.trivial new file mode 100644 index 000000000..96c6e1e25 --- /dev/null +++ b/changelog/3129.trivial @@ -0,0 +1 @@ +corrected 'you' to 'your' in logging doc diff --git a/doc/en/logging.rst b/doc/en/logging.rst index e3bf56038..f87e1329b 100644 --- a/doc/en/logging.rst +++ b/doc/en/logging.rst @@ -69,7 +69,7 @@ with:: pytest --no-print-logs -Or in you ``pytest.ini``: +Or in your ``pytest.ini``: .. code-block:: ini From 8be1136d039465d8e69ebc4ede35956f40aa6ea3 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 18 Jan 2018 16:40:00 -0200 Subject: [PATCH 12/23] Small changelog formatting --- changelog/3129.trivial | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog/3129.trivial b/changelog/3129.trivial index 96c6e1e25..65958660c 100644 --- a/changelog/3129.trivial +++ b/changelog/3129.trivial @@ -1 +1 @@ -corrected 'you' to 'your' in logging doc +Corrected 'you' to 'your' in logging docs. From 931e8830ba62a54c0158fd9a60af6b5f28fa1f11 Mon Sep 17 00:00:00 2001 From: Cyrus Maden Date: Thu, 18 Jan 2018 15:54:31 -0800 Subject: [PATCH 13/23] Update changelog Not issue ID. Will update with pr ID after submitting pr --- changelog/pr.doc | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/pr.doc diff --git a/changelog/pr.doc b/changelog/pr.doc new file mode 100644 index 000000000..28e61c1d8 --- /dev/null +++ b/changelog/pr.doc @@ -0,0 +1 @@ +Improve readability (wording, grammar) of Getting Started guide From cd76366d87eaaec6d04234c3d625c4506c058eaf Mon Sep 17 00:00:00 2001 From: Cyrus Maden Date: Thu, 18 Jan 2018 15:59:37 -0800 Subject: [PATCH 14/23] Rename pr.doc to 3131.doc --- changelog/{pr.doc => 3131.doc} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename changelog/{pr.doc => 3131.doc} (100%) diff --git a/changelog/pr.doc b/changelog/3131.doc similarity index 100% rename from changelog/pr.doc rename to changelog/3131.doc From ebb4c4715560bbf86b7cd328bef90b0c981bedc7 Mon Sep 17 00:00:00 2001 From: Cyrus Maden Date: Fri, 19 Jan 2018 18:44:33 -0800 Subject: [PATCH 15/23] Delete .DS_Store --- doc/.DS_Store | Bin 6148 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 doc/.DS_Store diff --git a/doc/.DS_Store b/doc/.DS_Store deleted file mode 100644 index f72d921e6ac404f4ad650e931a05c7f1f695bc1d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeH~Jr2S!425lAKw|00n1usyg9yP1xB!B(As7&Qj?VKB1BDq?=vlJA*p1cp4NWZ~ zx_R_#kxoSBaHFg&3`~(vA!;KrJ4LH~3h_y_>bP Date: Fri, 19 Jan 2018 18:44:46 -0800 Subject: [PATCH 16/23] Delete .DS_Store --- doc/en/.DS_Store | Bin 6148 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 doc/en/.DS_Store diff --git a/doc/en/.DS_Store b/doc/en/.DS_Store deleted file mode 100644 index f940bed6e384f5f76cd23313e2f8236eb972bc2c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKI|>3Z5S>vG!N$@uSMUZw^aLJ2(QSkU3$fqIb9pr1d;e&iX;6Vd)od{|=!lohtBGA;&_%QP(7ai*Ls7pS=NC^Gt$`e=02O#vU>M7p)&Dj8 zP5=K);))7Tfxl8fN2|?hi6>=k?LE$FZGrFLmUDxfVeS+RUXFoYjlb| WP3!`lj=0l-{24G^XjI_a3fut+K^1ra From 0b6df94b12a841a3de2fbced9e6d75c65f29761d Mon Sep 17 00:00:00 2001 From: Cyrus Maden Date: Fri, 19 Jan 2018 18:45:02 -0800 Subject: [PATCH 17/23] Delete .DS_Store --- .DS_Store | Bin 6148 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 7cbe275046a42350e05301fa3e51caa9dcc81710..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeH~Jr2S!425ml0g0s}V-^m;4I%_5-~tFbB6UFQIXcfj3xyd~=vlJA*s0a_4NWZ~ zdU%d|kwHY(aHH%j%uJDYGRh>k@o~Ffr|E7bw^h;#@Ihw#xlK?3DnJFO02QDD3sN8t z^7Upx&%{Te0#slb3fT9dz>PK8g8u11@DTv)QFg=HX9=)a0j$XuLtMpWxr-|5*!DDnJGPOabkW z=i>n{m1pb6>skGhRa-YW=$9kB{RAMfqj(E Date: Sat, 20 Jan 2018 11:12:59 -0800 Subject: [PATCH 18/23] Typo fix: "handeling" --> "handling" --- doc/en/getting-started.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/en/getting-started.rst b/doc/en/getting-started.rst index 3c6391c6a..4a7284480 100644 --- a/doc/en/getting-started.rst +++ b/doc/en/getting-started.rst @@ -160,7 +160,7 @@ List the name ``tmpdir`` in the test function signature and ``pytest`` will look PYTEST_TMPDIR/test_needsfiles0 1 failed in 0.12 seconds -More info on tmpdir handeling is available at `Temporary directories and files `_. +More info on tmpdir handling is available at `Temporary directories and files `_. Find out what kind of builtin ```pytest`` fixtures `_ exist with the command:: From 196dcc37a81aa5a0155f10a36a3131338108fa48 Mon Sep 17 00:00:00 2001 From: Brian Maissy Date: Sun, 21 Jan 2018 21:50:26 +0200 Subject: [PATCH 19/23] Clarify a possible confusion when using pytest_fixture_setup with fixture functions that return None --- _pytest/hookspec.py | 10 +++++++++- changelog/2698.doc | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 changelog/2698.doc diff --git a/_pytest/hookspec.py b/_pytest/hookspec.py index c3d550189..595ad6d91 100644 --- a/_pytest/hookspec.py +++ b/_pytest/hookspec.py @@ -358,7 +358,15 @@ def pytest_runtest_logreport(report): def pytest_fixture_setup(fixturedef, request): """ performs fixture setup execution. - Stops at first non-None result, see :ref:`firstresult` """ + :return: The return value of the call to the fixture function + + Stops at first non-None result, see :ref:`firstresult` + + .. note:: + If the fixture function returns None, other implementations of + this hook function will continue to be called, according to the + behavior of the :ref:`firstresult` option. + """ def pytest_fixture_post_finalizer(fixturedef, request): diff --git a/changelog/2698.doc b/changelog/2698.doc new file mode 100644 index 000000000..3088b6efc --- /dev/null +++ b/changelog/2698.doc @@ -0,0 +1 @@ +Clarify a possible confusion when using pytest_fixture_setup with fixture functions that return None. \ No newline at end of file From 5c0b340a4b76e4fa5063dfec25508c31fef2c613 Mon Sep 17 00:00:00 2001 From: Brian Maissy Date: Sun, 21 Jan 2018 22:43:00 +0200 Subject: [PATCH 20/23] Clarify that warning capturing doesn't change the warning filter by default --- changelog/2457.doc | 1 + doc/en/warnings.rst | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 changelog/2457.doc diff --git a/changelog/2457.doc b/changelog/2457.doc new file mode 100644 index 000000000..31d7aa1c2 --- /dev/null +++ b/changelog/2457.doc @@ -0,0 +1 @@ +Clarify that warning capturing doesn't change the warning filter by default. \ No newline at end of file diff --git a/doc/en/warnings.rst b/doc/en/warnings.rst index f249d7e3b..d5a627aea 100644 --- a/doc/en/warnings.rst +++ b/doc/en/warnings.rst @@ -112,6 +112,12 @@ decorator or to all tests in a module by setting the ``pytestmark`` variable: pytestmark = pytest.mark.filterwarnings('error') +.. note:: + + Except for these features, pytest does not change the python warning filter; it only captures + and displays the warnings which are issued with respect to the currently configured filter, + including changes to the filter made by test functions or by the system under test. + .. note:: ``DeprecationWarning`` and ``PendingDeprecationWarning`` are hidden by the standard library From 8994603d46d32abc50f50a8967ed042c764b2172 Mon Sep 17 00:00:00 2001 From: Brian Maissy Date: Sun, 21 Jan 2018 23:17:16 +0200 Subject: [PATCH 21/23] Document hooks (defined with historic=True) which cannot be used with hookwrapper=True --- _pytest/hookspec.py | 21 ++++++++++++++++++++- changelog/2423.doc | 1 + 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 changelog/2423.doc diff --git a/_pytest/hookspec.py b/_pytest/hookspec.py index c3d550189..417a9de56 100644 --- a/_pytest/hookspec.py +++ b/_pytest/hookspec.py @@ -16,6 +16,9 @@ def pytest_addhooks(pluginmanager): :param _pytest.config.PytestPluginManager pluginmanager: pytest plugin manager + + .. note:: + This hook is incompatible with ``hookwrapper=True``. """ @@ -27,6 +30,9 @@ def pytest_namespace(): the pytest namespace. This hook is called at plugin registration time. + + .. note:: + This hook is incompatible with ``hookwrapper=True``. """ @@ -36,6 +42,9 @@ def pytest_plugin_registered(plugin, manager): :param plugin: the plugin module or instance :param _pytest.config.PytestPluginManager manager: pytest plugin manager + + .. note:: + This hook is incompatible with ``hookwrapper=True``. """ @@ -66,6 +75,9 @@ def pytest_addoption(parser): The config object is passed around on many internal objects via the ``.config`` attribute or can be retrieved as the ``pytestconfig`` fixture. + + .. note:: + This hook is incompatible with ``hookwrapper=True``. """ @@ -80,6 +92,9 @@ def pytest_configure(config): After that, the hook is called for other conftest files as they are imported. + .. note:: + This hook is incompatible with ``hookwrapper=True``. + :arg _pytest.config.Config config: pytest config object """ @@ -456,7 +471,11 @@ def pytest_terminal_summary(terminalreporter, exitstatus): def pytest_logwarning(message, code, nodeid, fslocation): """ process a warning specified by a message, a code string, a nodeid and fslocation (both of which may be None - if the warning is not tied to a partilar node/location).""" + if the warning is not tied to a partilar node/location). + + .. note:: + This hook is incompatible with ``hookwrapper=True``. + """ # ------------------------------------------------------------------------- # doctest hooks diff --git a/changelog/2423.doc b/changelog/2423.doc new file mode 100644 index 000000000..a28bf02d4 --- /dev/null +++ b/changelog/2423.doc @@ -0,0 +1 @@ +Document hooks (defined with historic=True) which cannot be used with hookwrapper=True. \ No newline at end of file From 8a8797df8034fb37ced96406429fe6f1b28bbf1b Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sun, 21 Jan 2018 23:21:21 -0200 Subject: [PATCH 22/23] Small changelog formatting --- changelog/2423.doc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog/2423.doc b/changelog/2423.doc index a28bf02d4..96cc68297 100644 --- a/changelog/2423.doc +++ b/changelog/2423.doc @@ -1 +1 @@ -Document hooks (defined with historic=True) which cannot be used with hookwrapper=True. \ No newline at end of file +Document hooks (defined with ``historic=True``) which cannot be used with ``hookwrapper=True``. From 4285325cb88d90ad36724de9d441e51da614f249 Mon Sep 17 00:00:00 2001 From: Brian Maissy Date: Thu, 25 Jan 2018 23:07:34 +0200 Subject: [PATCH 23/23] Added note that calling pytest.main multiple times from the same process is not recommended because of import caching --- changelog/3143.doc | 1 + doc/en/usage.rst | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 changelog/3143.doc diff --git a/changelog/3143.doc b/changelog/3143.doc new file mode 100644 index 000000000..b454708ad --- /dev/null +++ b/changelog/3143.doc @@ -0,0 +1 @@ +Added note that calling pytest.main multiple times from the same process is not recommended because of import caching. \ No newline at end of file diff --git a/doc/en/usage.rst b/doc/en/usage.rst index 6091db8be..18830792b 100644 --- a/doc/en/usage.rst +++ b/doc/en/usage.rst @@ -387,6 +387,15 @@ hook was invoked:: $ python myinvoke.py *** test run reporting finishing - + +.. note:: + + Calling ``pytest.main()`` will result in importing your tests and any modules + that they import. Due to the caching mechanism of python's import system, + making subsequent calls to ``pytest.main()`` from the same process will not + reflect changes to those files between the calls. For this reason, making + multiple calls to ``pytest.main()`` from the same process (in order to re-run + tests, for example) is not recommended. + .. include:: links.inc