From 2a1b1107c5395560e7335ab0ef775343b3f65566 Mon Sep 17 00:00:00 2001 From: Brian Maissy Date: Thu, 25 Jan 2018 22:28:27 +0200 Subject: [PATCH 01/22] If we fail to import doctest.UnexpectedException during postmortem, fail quietly and continue --- _pytest/debugging.py | 18 +++++++++++------- changelog/1810.bugfix | 1 + 2 files changed, 12 insertions(+), 7 deletions(-) create mode 100644 changelog/1810.bugfix diff --git a/_pytest/debugging.py b/_pytest/debugging.py index d7dca7809..e458406b5 100644 --- a/_pytest/debugging.py +++ b/_pytest/debugging.py @@ -95,13 +95,17 @@ def _enter_pdb(node, excinfo, rep): def _postmortem_traceback(excinfo): - # A doctest.UnexpectedException is not useful for post_mortem. - # Use the underlying exception instead: - from doctest import UnexpectedException - if isinstance(excinfo.value, UnexpectedException): - return excinfo.value.exc_info[2] - else: - return excinfo._excinfo[2] + try: + from doctest import UnexpectedException + if isinstance(excinfo.value, UnexpectedException): + # A doctest.UnexpectedException is not useful for post_mortem. + # Use the underlying exception instead: + return excinfo.value.exc_info[2] + except ImportError: + # If we fail to import, continue quietly (if we ran out of file descriptors, for example: #1810) + pass + + return excinfo._excinfo[2] def _find_last_non_hidden_frame(stack): diff --git a/changelog/1810.bugfix b/changelog/1810.bugfix new file mode 100644 index 000000000..6e9389671 --- /dev/null +++ b/changelog/1810.bugfix @@ -0,0 +1 @@ +If we fail to import doctest.UnexpectedException during postmortem, fail quietly and continue. \ No newline at end of file From 547070e2d83617988ce07f2de6240f3ba3705d9c Mon Sep 17 00:00:00 2001 From: Alan Velasco Date: Tue, 30 Jan 2018 16:20:43 -0600 Subject: [PATCH 02/22] Switch `for_parameterize` to `for_parametrize` --- _pytest/mark.py | 2 +- _pytest/python.py | 4 ++-- changelog/3166.trivial | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 changelog/3166.trivial diff --git a/_pytest/mark.py b/_pytest/mark.py index 3f1f01b1a..f2d31761a 100644 --- a/_pytest/mark.py +++ b/_pytest/mark.py @@ -70,7 +70,7 @@ class ParameterSet(namedtuple('ParameterSet', 'values, marks, id')): return cls(argval, marks=newmarks, id=None) @classmethod - def _for_parameterize(cls, argnames, argvalues, function): + def _for_parametrize(cls, argnames, argvalues, function, config): if not isinstance(argnames, (tuple, list)): argnames = [x.strip() for x in argnames.split(",") if x.strip()] force_tuple = len(argnames) == 1 diff --git a/_pytest/python.py b/_pytest/python.py index 3940028fa..a4c233076 100644 --- a/_pytest/python.py +++ b/_pytest/python.py @@ -785,8 +785,8 @@ class Metafunc(fixtures.FuncargnamesCompatAttr): from _pytest.fixtures import scope2index from _pytest.mark import ParameterSet from py.io import saferepr - argnames, parameters = ParameterSet._for_parameterize( - argnames, argvalues, self.function) + argnames, parameters = ParameterSet._for_parametrize( + argnames, argvalues, self.function, self.config) del argvalues if scope is None: diff --git a/changelog/3166.trivial b/changelog/3166.trivial new file mode 100644 index 000000000..0f6ca240e --- /dev/null +++ b/changelog/3166.trivial @@ -0,0 +1 @@ +Change the naming of ``_for_parameterize()`` to ``_for_parametrize()`` in order to comply with the naming convention. \ No newline at end of file From 90a8faabbaf22ca376af71cc49d3ab81c510e736 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Tue, 30 Jan 2018 20:01:50 -0200 Subject: [PATCH 03/22] Fix test for py37 In previous Python versions, the list of warnigns appears like: ... list of emitted warnings is: [UserWarning('user',)]. In Python 3.7 apparently the string representation has been improved to: ... list of emitted warnings is: [UserWarning('user')]. Fix #3011 --- testing/test_recwarn.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testing/test_recwarn.py b/testing/test_recwarn.py index 0c80c169e..f1cf542e9 100644 --- a/testing/test_recwarn.py +++ b/testing/test_recwarn.py @@ -206,13 +206,13 @@ class TestWarns(object): with pytest.warns(RuntimeWarning): warnings.warn("user", UserWarning) excinfo.match(r"DID NOT WARN. No warnings of type \(.+RuntimeWarning.+,\) was emitted. " - r"The list of emitted warnings is: \[UserWarning\('user',\)\].") + r"The list of emitted warnings is: \[UserWarning\('user',?\)\].") with pytest.raises(pytest.fail.Exception) as excinfo: with pytest.warns(UserWarning): warnings.warn("runtime", RuntimeWarning) excinfo.match(r"DID NOT WARN. No warnings of type \(.+UserWarning.+,\) was emitted. " - r"The list of emitted warnings is: \[RuntimeWarning\('runtime',\)\].") + r"The list of emitted warnings is: \[RuntimeWarning\('runtime',?\)\].") with pytest.raises(pytest.fail.Exception) as excinfo: with pytest.warns(UserWarning): From ef7df8f167cdcf774bd3688f4df98ee10b80766d Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Tue, 30 Jan 2018 21:40:14 -0200 Subject: [PATCH 04/22] Small update to CHANGELOG --- changelog/3166.trivial | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog/3166.trivial b/changelog/3166.trivial index 0f6ca240e..ce92840cb 100644 --- a/changelog/3166.trivial +++ b/changelog/3166.trivial @@ -1 +1 @@ -Change the naming of ``_for_parameterize()`` to ``_for_parametrize()`` in order to comply with the naming convention. \ No newline at end of file +Rename ``ParameterSet._for_parameterize()`` to ``_for_parametrize()`` in order to comply with the naming convention. From 653abad27bbef8cdc28dd9e3a9393fbf0e0b2606 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 31 Jan 2018 18:18:15 -0200 Subject: [PATCH 05/22] Mention outcome.force_result() and add link to pluggy's docs Related to #3169 --- doc/en/writing_plugins.rst | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/doc/en/writing_plugins.rst b/doc/en/writing_plugins.rst index 7787f8d32..f5a8ea41e 100644 --- a/doc/en/writing_plugins.rst +++ b/doc/en/writing_plugins.rst @@ -462,19 +462,24 @@ Here is an example definition of a hook wrapper:: @pytest.hookimpl(hookwrapper=True) def pytest_pyfunc_call(pyfuncitem): - # do whatever you want before the next hook executes + do_something_before_next_hook_executes() outcome = yield # outcome.excinfo may be None or a (cls, val, tb) tuple res = outcome.get_result() # will raise if outcome was exception - # postprocess result + + post_process_result(res) + + outcome.force_result(new_res) # to override the return value to the plugin system Note that hook wrappers don't return results themselves, they merely perform tracing or other side effects around the actual hook implementations. If the result of the underlying hook is a mutable object, they may modify that result but it's probably better to avoid it. +For more information, consult the `pluggy documentation `_. + Hook function ordering / call example ------------------------------------- From 4458e65fe757ee34eddcf79c50e060018d327803 Mon Sep 17 00:00:00 2001 From: Aaron Date: Thu, 1 Feb 2018 13:07:45 -0800 Subject: [PATCH 06/22] Fix ordering of tests to minimize fixture creating --- _pytest/fixtures.py | 22 ++++++++++++++------- changelog/3161.bugfix | 1 + testing/python/fixture.py | 41 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 7 deletions(-) create mode 100644 changelog/3161.bugfix diff --git a/_pytest/fixtures.py b/_pytest/fixtures.py index 2bc6f108b..a8445767c 100644 --- a/_pytest/fixtures.py +++ b/_pytest/fixtures.py @@ -166,7 +166,7 @@ def reorder_items(items): items_by_argkey = {} for scopenum in range(0, scopenum_function): argkeys_cache[scopenum] = d = {} - items_by_argkey[scopenum] = item_d = defaultdict(list) + items_by_argkey[scopenum] = item_d = defaultdict(deque) for item in items: keys = OrderedDict.fromkeys(get_parametrized_fixture_keys(item, scopenum)) if keys: @@ -174,12 +174,19 @@ def reorder_items(items): for key in keys: item_d[key].append(item) items = OrderedDict.fromkeys(items) - return list(reorder_items_atscope(items, set(), argkeys_cache, items_by_argkey, 0)) + return list(reorder_items_atscope(items, argkeys_cache, items_by_argkey, 0)) -def reorder_items_atscope(items, ignore, argkeys_cache, items_by_argkey, scopenum): +def fix_cache_order(item, argkeys_cache, items_by_argkey): + for scopenum in range(0, scopenum_function): + for key in argkeys_cache[scopenum].get(item, []): + items_by_argkey[scopenum][key].appendleft(item) + + +def reorder_items_atscope(items, argkeys_cache, items_by_argkey, scopenum): if scopenum >= scopenum_function or len(items) < 3: return items + ignore = set() items_deque = deque(items) items_done = OrderedDict() scoped_items_by_argkey = items_by_argkey[scopenum] @@ -197,13 +204,14 @@ def reorder_items_atscope(items, ignore, argkeys_cache, items_by_argkey, scopenu else: slicing_argkey, _ = argkeys.popitem() # we don't have to remove relevant items from later in the deque because they'll just be ignored - for i in reversed(scoped_items_by_argkey[slicing_argkey]): - if i in items: - items_deque.appendleft(i) + matching_items = [i for i in scoped_items_by_argkey[slicing_argkey] if i in items] + for i in reversed(matching_items): + fix_cache_order(i, argkeys_cache, items_by_argkey) + items_deque.appendleft(i) break if no_argkey_group: no_argkey_group = reorder_items_atscope( - no_argkey_group, set(), argkeys_cache, items_by_argkey, scopenum + 1) + no_argkey_group, argkeys_cache, items_by_argkey, scopenum + 1) for item in no_argkey_group: items_done[item] = None ignore.add(slicing_argkey) diff --git a/changelog/3161.bugfix b/changelog/3161.bugfix new file mode 100644 index 000000000..317c64bec --- /dev/null +++ b/changelog/3161.bugfix @@ -0,0 +1 @@ +Fix test ordering bug introduced by PR #3108. \ No newline at end of file diff --git a/testing/python/fixture.py b/testing/python/fixture.py index d22389e71..6bcb1ab00 100644 --- a/testing/python/fixture.py +++ b/testing/python/fixture.py @@ -2168,6 +2168,47 @@ class TestFixtureMarker(object): test_mod1.py::test_func1[m2] PASSED """) + def test_dynamic_parametrized_ordering(self, testdir): + testdir.makeini(""" + [pytest] + console_output_style=classic + """) + testdir.makeconftest(""" + import pytest + + def pytest_configure(config): + class DynamicFixturePlugin(object): + @pytest.fixture(scope='session', params=['flavor1', 'flavor2']) + def flavor(self, request): + return request.param + config.pluginmanager.register(DynamicFixturePlugin(), 'flavor-fixture') + + @pytest.fixture(scope='session', params=['vxlan', 'vlan']) + def encap(request): + return request.param + + @pytest.fixture(scope='session', autouse='True') + def reprovision(request, flavor, encap): + pass + """) + testdir.makepyfile(""" + def test(reprovision): + pass + def test2(reprovision): + pass + """) + result = testdir.runpytest("-v") + result.stdout.fnmatch_lines(""" + test_dynamic_parametrized_ordering.py::test[flavor1-vxlan] PASSED + test_dynamic_parametrized_ordering.py::test2[flavor1-vxlan] PASSED + test_dynamic_parametrized_ordering.py::test[flavor2-vxlan] PASSED + test_dynamic_parametrized_ordering.py::test2[flavor2-vxlan] PASSED + test_dynamic_parametrized_ordering.py::test[flavor2-vlan] PASSED + test_dynamic_parametrized_ordering.py::test2[flavor2-vlan] PASSED + test_dynamic_parametrized_ordering.py::test[flavor1-vlan] PASSED + test_dynamic_parametrized_ordering.py::test2[flavor1-vlan] PASSED + """) + def test_class_ordering(self, testdir): testdir.makeini(""" [pytest] From 3425edd2a52ac314118a593cbe0bff746f81d9b7 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 1 Feb 2018 19:54:51 -0200 Subject: [PATCH 07/22] Reword changelog a bit --- changelog/3161.bugfix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog/3161.bugfix b/changelog/3161.bugfix index 317c64bec..73872be67 100644 --- a/changelog/3161.bugfix +++ b/changelog/3161.bugfix @@ -1 +1 @@ -Fix test ordering bug introduced by PR #3108. \ No newline at end of file +Fix ordering of tests using parametrized fixtures which can lead to fixtures being created more than necessary. From e289c60c3a6c97cc51cce8eced65c86a3d7ae750 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Fri, 2 Feb 2018 17:15:16 -0200 Subject: [PATCH 08/22] Support py37 officially Python 3.7.0b1 has been released: https://www.python.org/downloads/release/python-370b1/ Fix #3168 --- .travis.yml | 3 --- setup.py | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 938391cde..40fe3e8ba 100644 --- a/.travis.yml +++ b/.travis.yml @@ -39,9 +39,6 @@ matrix: python: '3.5' - env: TOXENV=py37 python: 'nightly' - allow_failures: - - env: TOXENV=py37 - python: 'nightly' script: tox --recreate diff --git a/setup.py b/setup.py index e08be845e..30234d2cc 100644 --- a/setup.py +++ b/setup.py @@ -16,7 +16,7 @@ classifiers = [ 'Topic :: Utilities', ] + [ ('Programming Language :: Python :: %s' % x) - for x in '2 2.7 3 3.4 3.5 3.6'.split() + for x in '2 2.7 3 3.4 3.5 3.6 3.7'.split() ] with open('README.rst') as fd: From 74633815aa55f6e38ca2ac4072cc02fb247b43f5 Mon Sep 17 00:00:00 2001 From: Brian Maissy Date: Sat, 3 Feb 2018 23:24:11 +0200 Subject: [PATCH 09/22] skip failing pdb/doctest test on mac --- changelog/985.bugfix | 1 + testing/test_pdb.py | 4 ++++ 2 files changed, 5 insertions(+) create mode 100644 changelog/985.bugfix diff --git a/changelog/985.bugfix b/changelog/985.bugfix new file mode 100644 index 000000000..cf4c2fe2c --- /dev/null +++ b/changelog/985.bugfix @@ -0,0 +1 @@ +Skip failing pdb/doctest test on mac. \ No newline at end of file diff --git a/testing/test_pdb.py b/testing/test_pdb.py index 8618473bb..ff6d994b5 100644 --- a/testing/test_pdb.py +++ b/testing/test_pdb.py @@ -267,6 +267,10 @@ class TestPDB(object): child.read() self.flush(child) + # For some reason the interaction between doctest's and pytest's output + # capturing mechanisms are messing up the stdout on mac. (See #985). + # Should be solvable, but skipping until we have a chance to investigate. + @pytest.mark.skipif("sys.platform == 'darwin'") def test_pdb_interaction_doctest(self, testdir): p1 = testdir.makepyfile(""" import pytest From e64feaba7ab05e06b65dc6b9a722a4f39c01cd58 Mon Sep 17 00:00:00 2001 From: Brian Maissy Date: Sat, 3 Feb 2018 23:28:37 +0200 Subject: [PATCH 10/22] xfail is better than skip --- testing/test_pdb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/test_pdb.py b/testing/test_pdb.py index ff6d994b5..b286d57a8 100644 --- a/testing/test_pdb.py +++ b/testing/test_pdb.py @@ -270,7 +270,7 @@ class TestPDB(object): # For some reason the interaction between doctest's and pytest's output # capturing mechanisms are messing up the stdout on mac. (See #985). # Should be solvable, but skipping until we have a chance to investigate. - @pytest.mark.skipif("sys.platform == 'darwin'") + @pytest.mark.xfail("sys.platform == 'darwin'", reason='See issue #985', run=False) def test_pdb_interaction_doctest(self, testdir): p1 = testdir.makepyfile(""" import pytest From 867344d0d72cfe79bb5ff316b065621f12627f40 Mon Sep 17 00:00:00 2001 From: Brian Maissy Date: Sun, 4 Feb 2018 00:03:17 +0200 Subject: [PATCH 11/22] move import to top --- _pytest/debugging.py | 18 +++++++----------- changelog/1810.bugfix | 2 +- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/_pytest/debugging.py b/_pytest/debugging.py index e458406b5..bd7a33b2d 100644 --- a/_pytest/debugging.py +++ b/_pytest/debugging.py @@ -2,6 +2,7 @@ from __future__ import absolute_import, division, print_function import pdb import sys +from doctest import UnexpectedException def pytest_addoption(parser): @@ -95,17 +96,12 @@ def _enter_pdb(node, excinfo, rep): def _postmortem_traceback(excinfo): - try: - from doctest import UnexpectedException - if isinstance(excinfo.value, UnexpectedException): - # A doctest.UnexpectedException is not useful for post_mortem. - # Use the underlying exception instead: - return excinfo.value.exc_info[2] - except ImportError: - # If we fail to import, continue quietly (if we ran out of file descriptors, for example: #1810) - pass - - return excinfo._excinfo[2] + if isinstance(excinfo.value, UnexpectedException): + # A doctest.UnexpectedException is not useful for post_mortem. + # Use the underlying exception instead: + return excinfo.value.exc_info[2] + else: + return excinfo._excinfo[2] def _find_last_non_hidden_frame(stack): diff --git a/changelog/1810.bugfix b/changelog/1810.bugfix index 6e9389671..5c3000d05 100644 --- a/changelog/1810.bugfix +++ b/changelog/1810.bugfix @@ -1 +1 @@ -If we fail to import doctest.UnexpectedException during postmortem, fail quietly and continue. \ No newline at end of file +Move import of doctest.UnexpectedException to top-level. \ No newline at end of file From ea8997a108eb78a795f461ff1f41e0771e58c2d7 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sat, 3 Feb 2018 20:21:06 -0200 Subject: [PATCH 12/22] Update wording in CHANGELOG --- changelog/1810.bugfix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog/1810.bugfix b/changelog/1810.bugfix index 5c3000d05..c91ed47d0 100644 --- a/changelog/1810.bugfix +++ b/changelog/1810.bugfix @@ -1 +1 @@ -Move import of doctest.UnexpectedException to top-level. \ No newline at end of file +Move import of ``doctest.UnexpectedException`` to top-level to avoid possible errors when using ``--pdb``. From 0d15a4686352ee2e6b8a2842fe47e1cd6d86e4f7 Mon Sep 17 00:00:00 2001 From: Pedro Algarvio Date: Wed, 31 Jan 2018 14:53:58 +0000 Subject: [PATCH 13/22] Don't traceback on unkown sections. --- _pytest/logging.py | 2 +- changelog/3184.bugfix | 1 + testing/logging/test_reporting.py | 54 +++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 changelog/3184.bugfix diff --git a/_pytest/logging.py b/_pytest/logging.py index 095115cd9..f6affc8a2 100644 --- a/_pytest/logging.py +++ b/_pytest/logging.py @@ -477,7 +477,7 @@ class _LiveLoggingStreamHandler(logging.StreamHandler): if not self._first_record_emitted or self._when == 'teardown': self.stream.write('\n') self._first_record_emitted = True - if not self._section_name_shown: + if not self._section_name_shown and self._when: self.stream.section('live log ' + self._when, sep='-', bold=True) self._section_name_shown = True logging.StreamHandler.emit(self, record) diff --git a/changelog/3184.bugfix b/changelog/3184.bugfix new file mode 100644 index 000000000..6269e7dee --- /dev/null +++ b/changelog/3184.bugfix @@ -0,0 +1 @@ +Don't traceback when logging on unknown(unhandled) sections. diff --git a/testing/logging/test_reporting.py b/testing/logging/test_reporting.py index f5272aa09..8dfe04ad9 100644 --- a/testing/logging/test_reporting.py +++ b/testing/logging/test_reporting.py @@ -272,6 +272,60 @@ def test_log_cli_default_level_sections(testdir, request): ]) +def test_live_logs_unknown_sections(testdir, request): + """Check that with live logging enable we are printing the correct headers during + start/setup/call/teardown/finish.""" + filename = request.node.name + '.py' + testdir.makeconftest(''' + import pytest + import logging + + def pytest_runtest_protocol(item, nextitem): + logging.warning('Unknown Section!') + + def pytest_runtest_logstart(): + logging.warning('>>>>> START >>>>>') + + def pytest_runtest_logfinish(): + logging.warning('<<<<< END <<<<<<<') + ''') + + testdir.makepyfile(''' + import pytest + import logging + + @pytest.fixture + def fix(request): + logging.warning("log message from setup of {}".format(request.node.name)) + yield + logging.warning("log message from teardown of {}".format(request.node.name)) + + def test_log_1(fix): + logging.warning("log message from test_log_1") + + ''') + testdir.makeini(''' + [pytest] + log_cli=true + ''') + + result = testdir.runpytest() + result.stdout.fnmatch_lines([ + '*WARNING*Unknown Section*', + '{}::test_log_1 '.format(filename), + '*WARNING* >>>>> START >>>>>*', + '*-- live log setup --*', + '*WARNING*log message from setup of test_log_1*', + '*-- live log call --*', + '*WARNING*log message from test_log_1*', + 'PASSED *100%*', + '*-- live log teardown --*', + '*WARNING*log message from teardown of test_log_1*', + '*WARNING* <<<<< END <<<<<<<*', + '=* 1 passed in *=', + ]) + + def test_log_cli_level(testdir): # Default log file level testdir.makepyfile(''' From 42c1f8525738be6f2b26f2f6ceb057f76446e3c1 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Mon, 5 Feb 2018 20:07:42 -0200 Subject: [PATCH 14/22] Update CHANGELOG --- changelog/3184.bugfix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog/3184.bugfix b/changelog/3184.bugfix index 6269e7dee..875358776 100644 --- a/changelog/3184.bugfix +++ b/changelog/3184.bugfix @@ -1 +1 @@ -Don't traceback when logging on unknown(unhandled) sections. +Fix bug where logging happening at hooks outside of "test run" hooks would cause an internal error. From 71527072801b6d83db168996529be1e76ce96bc4 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Tue, 6 Feb 2018 08:54:44 -0200 Subject: [PATCH 15/22] Update changelog entry to "trivial" as it is a temporary workaround I think this is a more appropriate category given that the underlying problem still exists --- changelog/985.bugfix | 1 - changelog/985.trivial | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 changelog/985.bugfix create mode 100644 changelog/985.trivial diff --git a/changelog/985.bugfix b/changelog/985.bugfix deleted file mode 100644 index cf4c2fe2c..000000000 --- a/changelog/985.bugfix +++ /dev/null @@ -1 +0,0 @@ -Skip failing pdb/doctest test on mac. \ No newline at end of file diff --git a/changelog/985.trivial b/changelog/985.trivial new file mode 100644 index 000000000..8554f2b65 --- /dev/null +++ b/changelog/985.trivial @@ -0,0 +1 @@ +Skip failing pdb/doctest test on mac. From 40d0ade2d9d0ab6d8d8b3df759945511042f86b5 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Tue, 6 Feb 2018 22:13:04 -0200 Subject: [PATCH 16/22] Add changelog/README.rst and streamline our PR template text This streamlines the PR template text and adds a more in-depth explanation about how the changelog entries work because this topic is a common source of confusion: - How to name the files. - Which formatting to use (people in general assume it is Markdown). - Recommend adding `.rst` extension to changelog files to help with the above (`towncrier` doesn't care). This was heavily inspired by the excellent python-trio/trio docs. --- .github/PULL_REQUEST_TEMPLATE.md | 19 ++++++------- changelog/{1810.bugfix => 1810.bugfix.rst} | 0 changelog/{3166.trivial => 3166.trivial.rst} | 0 changelog/{985.trivial => 985.trivial.rst} | 0 changelog/README.rst | 30 ++++++++++++++++++++ 5 files changed, 39 insertions(+), 10 deletions(-) rename changelog/{1810.bugfix => 1810.bugfix.rst} (100%) rename changelog/{3166.trivial => 3166.trivial.rst} (100%) rename changelog/{985.trivial => 985.trivial.rst} (100%) create mode 100644 changelog/README.rst diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 690426d68..64769b8e2 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,15 +1,14 @@ Thanks for submitting a PR, your contribution is really appreciated! -Here's a quick checklist that should be present in PRs: +Here's a quick checklist that should be present in PRs (you can delete this text from the final description, this is +just a guideline): -- [ ] Add a new news fragment into the changelog folder - * name it `$issue_id.$type` for example (588.bugfix) - * if you don't have an issue_id change it to the pr id after creating the pr - * ensure type is one of `removal`, `feature`, `bugfix`, `vendor`, `doc` or `trivial` - * Make sure to use full sentences with correct case and punctuation, for example: "Fix issue with non-ascii contents in doctest text files." -- [ ] Target: for `bugfix`, `vendor`, `doc` or `trivial` fixes, target `master`; for removals or features target `features`; -- [ ] Make sure to include reasonable tests for your change if necessary +- [ ] Create a new changelog file into the `changelog` folder, with a name like `..rst`. See [changelog/README.rst](/changelog/README.rst) for details. +- [ ] Target the `master` branch for bug fixes, documentation updates and trivial changes. +- [ ] Target the `features` branch for new features and removals/deprecations. +- [ ] Include documentation when adding new features. +- [ ] Include new tests or update existing tests when applicable. -Unless your change is a trivial or a documentation fix (e.g., a typo or reword of a small section) please: +Unless your change is trivial or a small documentation fix (e.g., a typo or reword of a small section) please: -- [ ] Add yourself to `AUTHORS`, in alphabetical order; +- [ ] Add yourself to `AUTHORS` in alphabetical order; diff --git a/changelog/1810.bugfix b/changelog/1810.bugfix.rst similarity index 100% rename from changelog/1810.bugfix rename to changelog/1810.bugfix.rst diff --git a/changelog/3166.trivial b/changelog/3166.trivial.rst similarity index 100% rename from changelog/3166.trivial rename to changelog/3166.trivial.rst diff --git a/changelog/985.trivial b/changelog/985.trivial.rst similarity index 100% rename from changelog/985.trivial rename to changelog/985.trivial.rst diff --git a/changelog/README.rst b/changelog/README.rst new file mode 100644 index 000000000..ec1c7f617 --- /dev/null +++ b/changelog/README.rst @@ -0,0 +1,30 @@ +This directory contains "newsfragments" which are short that contain a small **ReST**-formatted +text that will be added to the next ``CHANGELOG``. + +The ``CHANGELOG`` will be read by users, so this description should be aimed to pytest users +instead of describing internal changes which are only relevant to the developers. + +Make sure to use full sentences with correct case and punctuation, for example: *Fix issue with non-ascii contents in doctest text files.* + +Each file should be named like ``..rst``, where +```` is an issue number, and ```` is one of: + +* ``feature``: new user facing features, like new command-line options and new behavior. +* ``bugfix``: fixes a reported bug. +* ``doc``: documentation improvement, like rewording an entire session or adding missing docs. +* ``removal``: feature deprecation or removal; +* ``vendor``: vendoring news; +* ``trivial``: fixing a small typo or internal change that might be noteworthy. + +So for example: ``123.feature.rst``, ``456.bugfix.rst``. + +If your PR fixes an issue, use that number here. If there is no issue, +then after you submit the PR and get the PR number you can add a +changelog using that instead. + +If you are not sure what issue type to use, don't hesitate to ask in your PR. + +Note that the ``towncrier`` tool will automatically +reflow your text, so it will work best if you stick to a single paragraph, but multiple sentences and links are OK +and encouraged. You can install ``towncrier`` and then run ``towncrier --draft`` +if you want to get a preview of how your change will look in the final release notes. From eea169e5151da473909fa8bcc467ac7d2380accd Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 8 Feb 2018 08:03:14 -0200 Subject: [PATCH 17/22] Code review suggestions --- .github/PULL_REQUEST_TEMPLATE.md | 2 +- changelog/README.rst | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 64769b8e2..23a9f8c56 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -3,7 +3,7 @@ Thanks for submitting a PR, your contribution is really appreciated! Here's a quick checklist that should be present in PRs (you can delete this text from the final description, this is just a guideline): -- [ ] Create a new changelog file into the `changelog` folder, with a name like `..rst`. See [changelog/README.rst](/changelog/README.rst) for details. +- [ ] Create a new changelog file in the `changelog` folder, with a name like `..rst`. See [changelog/README.rst](/changelog/README.rst) for details. - [ ] Target the `master` branch for bug fixes, documentation updates and trivial changes. - [ ] Target the `features` branch for new features and removals/deprecations. - [ ] Include documentation when adding new features. diff --git a/changelog/README.rst b/changelog/README.rst index ec1c7f617..f00720de9 100644 --- a/changelog/README.rst +++ b/changelog/README.rst @@ -12,8 +12,8 @@ Each file should be named like ``..rst``, where * ``feature``: new user facing features, like new command-line options and new behavior. * ``bugfix``: fixes a reported bug. * ``doc``: documentation improvement, like rewording an entire session or adding missing docs. -* ``removal``: feature deprecation or removal; -* ``vendor``: vendoring news; +* ``removal``: feature deprecation or removal. +* ``vendor``: changes in packages vendored in pytest. * ``trivial``: fixing a small typo or internal change that might be noteworthy. So for example: ``123.feature.rst``, ``456.bugfix.rst``. From c04e248de54741b39f93d4ad07bd81e6bc6a2b3e Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Fri, 9 Feb 2018 14:06:24 -0200 Subject: [PATCH 18/22] Rename 3161.bugfix to 3161.bugfix.rst --- changelog/{3161.bugfix => 3161.bugfix.rst} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename changelog/{3161.bugfix => 3161.bugfix.rst} (100%) diff --git a/changelog/3161.bugfix b/changelog/3161.bugfix.rst similarity index 100% rename from changelog/3161.bugfix rename to changelog/3161.bugfix.rst From e5b527d0e3d72cf8e2cac4a4f7fb7b9a2d2f6e06 Mon Sep 17 00:00:00 2001 From: Andy Freeland Date: Fri, 9 Feb 2018 16:25:00 -0800 Subject: [PATCH 19/22] Add Sphinx parameter docs for `match` and `message` args to `pytest.raises()` --- _pytest/python_api.py | 4 ++++ changelog/3202.trivial.rst | 1 + 2 files changed, 5 insertions(+) create mode 100644 changelog/3202.trivial.rst diff --git a/_pytest/python_api.py b/_pytest/python_api.py index 81960295b..e6f002849 100644 --- a/_pytest/python_api.py +++ b/_pytest/python_api.py @@ -453,6 +453,10 @@ def raises(expected_exception, *args, **kwargs): Assert that a code block/function call raises ``expected_exception`` and raise a failure exception otherwise. + :arg message: if specified, provides a custom failure message if the + exception is not raised + :arg match: if specified, asserts that the exception matches a text or regex + This helper produces a ``ExceptionInfo()`` object (see below). You may use this function as a context manager:: diff --git a/changelog/3202.trivial.rst b/changelog/3202.trivial.rst new file mode 100644 index 000000000..767a32b42 --- /dev/null +++ b/changelog/3202.trivial.rst @@ -0,0 +1 @@ +Add Sphinx parameter docs for ``match`` and ``message`` args to :func:`pytest.raises`. From ffee213c85a39a7aa60ae84cb6badfb583fecba1 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Fri, 9 Feb 2018 22:51:15 -0200 Subject: [PATCH 20/22] Update and rename 3202.trivial.rst to 3202.doc.rst --- changelog/{3202.trivial.rst => 3202.doc.rst} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename changelog/{3202.trivial.rst => 3202.doc.rst} (73%) diff --git a/changelog/3202.trivial.rst b/changelog/3202.doc.rst similarity index 73% rename from changelog/3202.trivial.rst rename to changelog/3202.doc.rst index 767a32b42..a6f99fbf6 100644 --- a/changelog/3202.trivial.rst +++ b/changelog/3202.doc.rst @@ -1 +1 @@ -Add Sphinx parameter docs for ``match`` and ``message`` args to :func:`pytest.raises`. +Add Sphinx parameter docs for ``match`` and ``message`` args to ``pytest.raises``. From b6166dccb4d2b48173aa7e7739be52db9d2d56a0 Mon Sep 17 00:00:00 2001 From: Marcin Bachry Date: Mon, 12 Feb 2018 18:41:00 +0100 Subject: [PATCH 21/22] Fix mock patchings detection when both mock and unittest.mock are present --- _pytest/compat.py | 7 ++++--- changelog/3206.bugfix.rst | 1 + testing/python/integration.py | 22 ++++++++++++++++++++++ 3 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 changelog/3206.bugfix.rst diff --git a/_pytest/compat.py b/_pytest/compat.py index 7560fbec3..92df65656 100644 --- a/_pytest/compat.py +++ b/_pytest/compat.py @@ -79,10 +79,11 @@ def num_mock_patch_args(function): patchings = getattr(function, "patchings", None) if not patchings: return 0 - mock = sys.modules.get("mock", sys.modules.get("unittest.mock", None)) - if mock is not None: + mock_modules = [sys.modules.get("mock"), sys.modules.get("unittest.mock")] + if any(mock_modules): + sentinels = [m.DEFAULT for m in mock_modules if m is not None] return len([p for p in patchings - if not p.attribute_name and p.new is mock.DEFAULT]) + if not p.attribute_name and p.new in sentinels]) return len(patchings) diff --git a/changelog/3206.bugfix.rst b/changelog/3206.bugfix.rst new file mode 100644 index 000000000..1e2305fa2 --- /dev/null +++ b/changelog/3206.bugfix.rst @@ -0,0 +1 @@ +Detect arguments injected by ``unittest.mock.patch`` decorator correctly when pypi ``mock.patch`` is installed and imported. diff --git a/testing/python/integration.py b/testing/python/integration.py index 6ea29fa98..aade04fa9 100644 --- a/testing/python/integration.py +++ b/testing/python/integration.py @@ -147,6 +147,28 @@ class TestMockDecoration(object): reprec = testdir.inline_run() reprec.assertoutcome(passed=1) + def test_unittest_mock_and_pypi_mock(self, testdir): + pytest.importorskip("unittest.mock") + pytest.importorskip("mock", "1.0.1") + testdir.makepyfile(""" + import mock + import unittest.mock + class TestBoth(object): + @unittest.mock.patch("os.path.abspath") + def test_hello(self, abspath): + import os + os.path.abspath("hello") + abspath.assert_any_call("hello") + + @mock.patch("os.path.abspath") + def test_hello_mock(self, abspath): + import os + os.path.abspath("hello") + abspath.assert_any_call("hello") + """) + reprec = testdir.inline_run() + reprec.assertoutcome(passed=2) + def test_mock(self, testdir): pytest.importorskip("mock", "1.0.1") testdir.makepyfile(""" From 7656fc8320b5920d860e9028f322475a021e7666 Mon Sep 17 00:00:00 2001 From: Brian Maissy Date: Mon, 12 Feb 2018 23:17:51 +0200 Subject: [PATCH 22/22] Added printing of captured stdout and stderr before entering pdb --- AUTHORS | 1 + _pytest/debugging.py | 11 +++++++++++ changelog/3052.bugfix | 1 + testing/test_pdb.py | 39 +++++++++++++++++++++++++++++++++++---- 4 files changed, 48 insertions(+), 4 deletions(-) create mode 100644 changelog/3052.bugfix diff --git a/AUTHORS b/AUTHORS index 583fa6f9e..ab646f406 100644 --- a/AUTHORS +++ b/AUTHORS @@ -29,6 +29,7 @@ Benjamin Peterson Bernard Pratz Bob Ippolito Brian Dorsey +Brian Maissy Brian Okken Brianna Laugher Bruno Oliveira diff --git a/_pytest/debugging.py b/_pytest/debugging.py index 23d94e688..8b5f5cc2c 100644 --- a/_pytest/debugging.py +++ b/_pytest/debugging.py @@ -85,6 +85,17 @@ def _enter_pdb(node, excinfo, rep): # for not completely clear reasons. tw = node.config.pluginmanager.getplugin("terminalreporter")._tw tw.line() + + captured_stdout = rep.capstdout + if len(captured_stdout) > 0: + tw.sep(">", "captured stdout") + tw.line(captured_stdout) + + captured_stderr = rep.capstderr + if len(captured_stderr) > 0: + tw.sep(">", "captured stderr") + tw.line(captured_stderr) + tw.sep(">", "traceback") rep.toterminal(tw) tw.sep(">", "entering PDB") diff --git a/changelog/3052.bugfix b/changelog/3052.bugfix new file mode 100644 index 000000000..ea8c362a4 --- /dev/null +++ b/changelog/3052.bugfix @@ -0,0 +1 @@ +Added printing of captured stdout/stderr before entering pdb, and improved a test which was giving false negatives about output capturing. diff --git a/testing/test_pdb.py b/testing/test_pdb.py index 8618473bb..01604b633 100644 --- a/testing/test_pdb.py +++ b/testing/test_pdb.py @@ -141,19 +141,50 @@ class TestPDB(object): child.sendeof() self.flush(child) - def test_pdb_interaction_capture(self, testdir): + def test_pdb_print_captured_stdout(self, testdir): p1 = testdir.makepyfile(""" def test_1(): - print("getrekt") + print("get\\x20rekt") assert False """) child = testdir.spawn_pytest("--pdb %s" % p1) - child.expect("getrekt") + child.expect("captured stdout") + child.expect("get rekt") child.expect("(Pdb)") child.sendeof() rest = child.read().decode("utf8") assert "1 failed" in rest - assert "getrekt" not in rest + assert "get rekt" not in rest + self.flush(child) + + def test_pdb_print_captured_stderr(self, testdir): + p1 = testdir.makepyfile(""" + def test_1(): + import sys + sys.stderr.write("get\\x20rekt") + assert False + """) + child = testdir.spawn_pytest("--pdb %s" % p1) + child.expect("captured stderr") + child.expect("get rekt") + child.expect("(Pdb)") + child.sendeof() + rest = child.read().decode("utf8") + assert "1 failed" in rest + assert "get rekt" not in rest + self.flush(child) + + def test_pdb_dont_print_empty_captured_stdout_and_stderr(self, testdir): + p1 = testdir.makepyfile(""" + def test_1(): + assert False + """) + child = testdir.spawn_pytest("--pdb %s" % p1) + child.expect("(Pdb)") + output = child.before.decode("utf8") + child.sendeof() + assert "captured stdout" not in output + assert "captured stderr" not in output self.flush(child) def test_pdb_interaction_exception(self, testdir):