diff --git a/AUTHORS b/AUTHORS index 2dc79433d..b61969ff4 100644 --- a/AUTHORS +++ b/AUTHORS @@ -202,3 +202,4 @@ Xuan Luong Xuecong Liao Zoltán Máté Roland Puntaier +Allan Feldman diff --git a/README.rst b/README.rst index 3630dd4c6..b2ed1e140 100644 --- a/README.rst +++ b/README.rst @@ -23,6 +23,9 @@ .. image:: https://ci.appveyor.com/api/projects/status/mrgbjaua7t33pg6b?svg=true :target: https://ci.appveyor.com/project/pytestbot/pytest +.. image:: https://www.codetriage.com/pytest-dev/pytest/badges/users.svg + :target: https://www.codetriage.com/pytest-dev/pytest + The ``pytest`` framework makes it easy to write small tests, yet scales to support complex functional testing for applications and libraries. diff --git a/_pytest/config.py b/_pytest/config.py index a9b071b6f..06214e6ca 100644 --- a/_pytest/config.py +++ b/_pytest/config.py @@ -1328,10 +1328,14 @@ def determine_setup(inifile, args, warnfunc=None, rootdir_cmd_arg=None): dirs = get_dirs_from_args(args) if inifile: iniconfig = py.iniconfig.IniConfig(inifile) - try: - inicfg = iniconfig["pytest"] - except KeyError: - inicfg = None + is_cfg_file = str(inifile).endswith('.cfg') + sections = ['tool:pytest', 'pytest'] if is_cfg_file else ['pytest'] + for section in sections: + try: + inicfg = iniconfig[section] + break + except KeyError: + inicfg = None rootdir = get_common_ancestor(dirs) else: ancestor = get_common_ancestor(dirs) diff --git a/_pytest/fixtures.py b/_pytest/fixtures.py index f9f3277f9..df0d1a41a 100644 --- a/_pytest/fixtures.py +++ b/_pytest/fixtures.py @@ -24,6 +24,12 @@ from _pytest.compat import ( from _pytest.outcomes import fail, TEST_OUTCOME +@attr.s(frozen=True) +class PseudoFixtureDef(object): + cached_result = attr.ib() + scope = attr.ib() + + def pytest_sessionstart(session): import _pytest.python import _pytest.nodes @@ -440,10 +446,9 @@ class FixtureRequest(FuncargnamesCompatAttr): fixturedef = self._getnextfixturedef(argname) except FixtureLookupError: if argname == "request": - class PseudoFixtureDef(object): - cached_result = (self, [0], None) - scope = "function" - return PseudoFixtureDef + cached_result = (self, [0], None) + scope = "function" + return PseudoFixtureDef(cached_result, scope) raise # remove indent to prevent the python3 exception # from leaking into the call diff --git a/_pytest/python_api.py b/_pytest/python_api.py index 89ef9e0a9..541371449 100644 --- a/_pytest/python_api.py +++ b/_pytest/python_api.py @@ -153,6 +153,8 @@ class ApproxScalar(ApproxBase): """ Perform approximate comparisons for single numbers only. """ + DEFAULT_ABSOLUTE_TOLERANCE = 1e-12 + DEFAULT_RELATIVE_TOLERANCE = 1e-6 def __repr__(self): """ @@ -223,7 +225,7 @@ class ApproxScalar(ApproxBase): # Figure out what the absolute tolerance should be. ``self.abs`` is # either None or a value specified by the user. - absolute_tolerance = set_default(self.abs, 1e-12) + absolute_tolerance = set_default(self.abs, self.DEFAULT_ABSOLUTE_TOLERANCE) if absolute_tolerance < 0: raise ValueError("absolute tolerance can't be negative: {}".format(absolute_tolerance)) @@ -241,7 +243,7 @@ class ApproxScalar(ApproxBase): # we've made sure the user didn't ask for an absolute tolerance only, # because we don't want to raise errors about the relative tolerance if # we aren't even going to use it. - relative_tolerance = set_default(self.rel, 1e-6) * abs(self.expected) + relative_tolerance = set_default(self.rel, self.DEFAULT_RELATIVE_TOLERANCE) * abs(self.expected) if relative_tolerance < 0: raise ValueError("relative tolerance can't be negative: {}".format(absolute_tolerance)) @@ -252,6 +254,13 @@ class ApproxScalar(ApproxBase): return max(relative_tolerance, absolute_tolerance) +class ApproxDecimal(ApproxScalar): + from decimal import Decimal + + DEFAULT_ABSOLUTE_TOLERANCE = Decimal('1e-12') + DEFAULT_RELATIVE_TOLERANCE = Decimal('1e-6') + + def approx(expected, rel=None, abs=None, nan_ok=False): """ Assert that two numbers (or two sets of numbers) are equal to each other @@ -401,6 +410,7 @@ def approx(expected, rel=None, abs=None, nan_ok=False): from collections import Mapping, Sequence from _pytest.compat import STRING_TYPES as String + from decimal import Decimal # Delegate the comparison to a class that knows how to deal with the type # of the expected value (e.g. int, float, list, dict, numpy.array, etc). @@ -422,6 +432,8 @@ def approx(expected, rel=None, abs=None, nan_ok=False): cls = ApproxMapping elif isinstance(expected, Sequence) and not isinstance(expected, String): cls = ApproxSequence + elif isinstance(expected, Decimal): + cls = ApproxDecimal else: cls = ApproxScalar diff --git a/_pytest/terminal.py b/_pytest/terminal.py index 18200945b..b84b2414c 100644 --- a/_pytest/terminal.py +++ b/_pytest/terminal.py @@ -329,6 +329,8 @@ class TerminalReporter(object): _PROGRESS_LENGTH = len(' [100%]') def _get_progress_information_message(self): + if self.config.getoption('capture') == 'no': + return '' collected = self._session.testscollected if collected: progress = len(self._progress_nodeids_reported) * 100 // collected diff --git a/changelog/3203.bugfix.rst b/changelog/3203.bugfix.rst new file mode 100644 index 000000000..88f357722 --- /dev/null +++ b/changelog/3203.bugfix.rst @@ -0,0 +1 @@ +Removed progress information when capture option is ``no``. diff --git a/changelog/3247.bugfix.rst b/changelog/3247.bugfix.rst new file mode 100644 index 000000000..9483ceeea --- /dev/null +++ b/changelog/3247.bugfix.rst @@ -0,0 +1 @@ +Fix ``TypeError`` issue when using ``approx`` with a ``Decimal`` value. diff --git a/changelog/3249.bugfix.rst b/changelog/3249.bugfix.rst new file mode 100644 index 000000000..38fa9179f --- /dev/null +++ b/changelog/3249.bugfix.rst @@ -0,0 +1 @@ +Fix reference cycle generated when using the ``request`` fixture. diff --git a/changelog/3259.trivial b/changelog/3259.trivial new file mode 100644 index 000000000..5b5141301 --- /dev/null +++ b/changelog/3259.trivial @@ -0,0 +1 @@ +Fix minor typo in fixture.rst diff --git a/changelog/3260.bugfix.rst b/changelog/3260.bugfix.rst new file mode 100644 index 000000000..eb0ecd345 --- /dev/null +++ b/changelog/3260.bugfix.rst @@ -0,0 +1 @@ +``[tool:pytest]`` sections in ``*.cfg`` files passed by the ``-c`` option are now properly recognized. diff --git a/doc/en/fixture.rst b/doc/en/fixture.rst index 0828bdcf8..d2b2865ef 100644 --- a/doc/en/fixture.rst +++ b/doc/en/fixture.rst @@ -369,7 +369,7 @@ ends, but ``addfinalizer`` has two key differences over ``yield``: Fixtures can introspect the requesting test context ------------------------------------------------------------- -Fixture function can accept the :py:class:`request ` object +Fixture functions can accept the :py:class:`request ` object to introspect the "requesting" test function, class or module context. Further extending the previous ``smtp`` fixture example, let's read an optional server URL from the test module which uses our fixture:: diff --git a/doc/en/getting-started.rst b/doc/en/getting-started.rst index 4a7284480..31af29da8 100644 --- a/doc/en/getting-started.rst +++ b/doc/en/getting-started.rst @@ -160,9 +160,9 @@ 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 handling is available at `Temporary directories and files `_. +More info on tmpdir handling is available at :ref:`Temporary directories and files `. -Find out what kind of builtin ```pytest`` fixtures `_ exist with the command:: +Find out what kind of builtin :ref:`pytest fixtures ` exist with the command:: pytest --fixtures # shows builtin and custom fixtures diff --git a/doc/en/writing_plugins.rst b/doc/en/writing_plugins.rst index f5a8ea41e..d88bb8f15 100644 --- a/doc/en/writing_plugins.rst +++ b/doc/en/writing_plugins.rst @@ -278,7 +278,7 @@ the plugin manager like this: .. sourcecode:: python - plugin = config.pluginmanager.getplugin("name_of_plugin") + plugin = config.pluginmanager.get_plugin("name_of_plugin") If you want to look at the names of existing plugins, use the ``--trace-config`` option. diff --git a/testing/python/approx.py b/testing/python/approx.py index 300e1ce86..341e5fcff 100644 --- a/testing/python/approx.py +++ b/testing/python/approx.py @@ -249,6 +249,7 @@ class TestApprox(object): (Decimal('-1.000001'), Decimal('-1.0')), ] for a, x in within_1e6: + assert a == approx(x) assert a == approx(x, rel=Decimal('5e-6'), abs=0) assert a != approx(x, rel=Decimal('5e-7'), abs=0) assert approx(x, rel=Decimal('5e-6'), abs=0) == a diff --git a/testing/python/fixture.py b/testing/python/fixture.py index 6bcb1ab00..8638e361a 100644 --- a/testing/python/fixture.py +++ b/testing/python/fixture.py @@ -519,6 +519,41 @@ class TestRequestBasic(object): assert len(arg2fixturedefs) == 1 assert arg2fixturedefs['something'][0].argname == "something" + def test_request_garbage(self, testdir): + testdir.makepyfile(""" + import sys + import pytest + import gc + + @pytest.fixture(autouse=True) + def something(request): + # this method of test doesn't work on pypy + if hasattr(sys, "pypy_version_info"): + yield + else: + original = gc.get_debug() + gc.set_debug(gc.DEBUG_SAVEALL) + gc.collect() + + yield + + gc.collect() + leaked_types = sum(1 for _ in gc.garbage + if 'PseudoFixtureDef' in str(_)) + + gc.garbage[:] = [] + + try: + assert leaked_types == 0 + finally: + gc.set_debug(original) + + def test_func(): + pass + """) + reprec = testdir.inline_run() + reprec.assertoutcome(passed=1) + def test_getfixturevalue_recursive(self, testdir): testdir.makeconftest(""" import pytest diff --git a/testing/test_config.py b/testing/test_config.py index f256b512f..39105f5d6 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -110,6 +110,13 @@ class TestConfigCmdlineParsing(object): config = testdir.parseconfig("-c", "custom.cfg") assert config.getini("custom") == "1" + testdir.makefile(".cfg", custom_tool_pytest_section=""" + [tool:pytest] + custom = 1 + """) + config = testdir.parseconfig("-c", "custom_tool_pytest_section.cfg") + assert config.getini("custom") == "1" + def test_absolute_win32_path(self, testdir): temp_cfg_file = testdir.makefile(".cfg", custom=""" [pytest] diff --git a/testing/test_terminal.py b/testing/test_terminal.py index c9e0eb8b3..8ef25062e 100644 --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -1121,6 +1121,9 @@ class TestProgress(object): r'test_foobar.py \.{5}', ]) + output = testdir.runpytest('--capture=no') + assert "%]" not in output.stdout.str() + class TestProgressWithTeardown(object): """Ensure we show the correct percentages for tests that fail during teardown (#3088)"""