From ecd072ea94546024683a71f5f00208e9b4f5d3e0 Mon Sep 17 00:00:00 2001 From: Evan Kepner Date: Sun, 12 May 2019 09:52:22 -0400 Subject: [PATCH 01/15] add env example to monkeypatch docs --- doc/en/monkeypatch.rst | 68 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 3 deletions(-) diff --git a/doc/en/monkeypatch.rst b/doc/en/monkeypatch.rst index 1a69c3a42..b6f80f8ef 100644 --- a/doc/en/monkeypatch.rst +++ b/doc/en/monkeypatch.rst @@ -16,7 +16,7 @@ and a discussion of its motivation. Simple example: monkeypatching functions ---------------------------------------------------- +---------------------------------------- If you want to pretend that ``os.expanduser`` returns a certain directory, you can use the :py:meth:`monkeypatch.setattr` method to @@ -38,8 +38,8 @@ Here our test function monkeypatches ``os.path.expanduser`` and then calls into a function that calls it. After the test function finishes the ``os.path.expanduser`` modification will be undone. -example: preventing "requests" from remote operations ------------------------------------------------------- +Global patch example: preventing "requests" from remote operations +------------------------------------------------------------------ If you want to prevent the "requests" library from performing http requests in all your tests, you can do:: @@ -81,6 +81,68 @@ so that any attempts within tests to create http requests will fail. See issue `#3290 `_ for details. +Monkeypatching environment variables +------------------------------------ + +If you are working with environment variables you often need to safely change the values +or delete them from the system for testing purposes. ``Monkeypatch`` provides a mechanism +to do this using the ``setenv`` and ``delenv`` method. Our example code to test:: + + # contents of our original code file e.g. code.py + import os + + def get_os_user_lower(): + """Simple retrieval function. + Returns lowercase USER or raises EnvironmentError.""" + username = os.getenv("USER") + + if username is None: + raise EnvironmentError("USER environment is not set.") + + return username.lower() + +There are two potential paths. First, the ``USER`` environment variable is set to a +value. Second, the ``USER`` environment variable does not exist. Using ``monkeypatch`` +both paths can be safely tested without impacting the running environment:: + + # contents of our test file e.g. test_code.py + import pytest + + def test_upper_to_lower(monkeypatch): + """Set the USER env var to assert the behavior.""" + monkeypatch.setenv("USER", "TestingUser") + assert get_os_user_lower() == "testinguser" + + def test_raise_exception(monkeypatch): + """Remove the USER env var and assert EnvironmentError is raised.""" + monkeypatch.delenv("USER", raising=False) + + with pytest.raises(EnvironmentError): + _ = get_os_user_lower() + +This behavior can be be moved into ``fixture`` structures and shared across tests:: + + import pytest + + @pytest.fixture + def mock_env_user(monkeypatch): + monkeypatch.setenv("USER", "TestingUser") + + @pytest.fixture + def mock_env_missing(monkeypatch): + monkeypatch.delenv("USER", raising=False) + + # Notice the tests reference the fixtures for mocks + def test_upper_to_lower(mock_env_user): + assert get_os_user_lower() == "testinguser" + + def test_raise_exception(mock_env_missing): + with pytest.raises(EnvironmentError): + _ = get_os_user_lower() + + + + .. currentmodule:: _pytest.monkeypatch API Reference From 6ca3e1e425dc247e7f8c3f422e31d26e3ca727aa Mon Sep 17 00:00:00 2001 From: Evan Kepner Date: Sun, 12 May 2019 10:04:24 -0400 Subject: [PATCH 02/15] add @EvanKepner to authors --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index a5bb34039..95f763a96 100644 --- a/AUTHORS +++ b/AUTHORS @@ -86,6 +86,7 @@ Endre Galaczi Eric Hunsberger Eric Siegerman Erik M. Bray +Evan Kepner Fabien Zarifian Fabio Zadrozny Feng Ma From 2ad36b14029cbb96351d22248038f9057b496469 Mon Sep 17 00:00:00 2001 From: Evan Kepner Date: Sun, 12 May 2019 10:08:46 -0400 Subject: [PATCH 03/15] add #5250 changelog entry --- changelog/5250.doc.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/5250.doc.rst diff --git a/changelog/5250.doc.rst b/changelog/5250.doc.rst new file mode 100644 index 000000000..1b4c564a0 --- /dev/null +++ b/changelog/5250.doc.rst @@ -0,0 +1 @@ +Expand docs on use of ``setenv`` and ``delenv`` with ``monkeypatch``. From e668aaf885f15eca3d466af5688db179cffda0e3 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Tue, 14 May 2019 18:59:27 -0300 Subject: [PATCH 04/15] Fix indentation and use code-block directives The code-block directives are required by our blacken-docs hook --- doc/en/monkeypatch.rst | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/doc/en/monkeypatch.rst b/doc/en/monkeypatch.rst index b6f80f8ef..b45ad07c1 100644 --- a/doc/en/monkeypatch.rst +++ b/doc/en/monkeypatch.rst @@ -86,33 +86,40 @@ Monkeypatching environment variables If you are working with environment variables you often need to safely change the values or delete them from the system for testing purposes. ``Monkeypatch`` provides a mechanism -to do this using the ``setenv`` and ``delenv`` method. Our example code to test:: +to do this using the ``setenv`` and ``delenv`` method. Our example code to test: + +.. code-block:: python # contents of our original code file e.g. code.py import os + def get_os_user_lower(): - """Simple retrieval function. - Returns lowercase USER or raises EnvironmentError.""" - username = os.getenv("USER") + """Simple retrieval function. + Returns lowercase USER or raises EnvironmentError.""" + username = os.getenv("USER") - if username is None: - raise EnvironmentError("USER environment is not set.") + if username is None: + raise EnvironmentError("USER environment is not set.") - return username.lower() + return username.lower() There are two potential paths. First, the ``USER`` environment variable is set to a value. Second, the ``USER`` environment variable does not exist. Using ``monkeypatch`` -both paths can be safely tested without impacting the running environment:: +both paths can be safely tested without impacting the running environment: + +.. code-block:: python # contents of our test file e.g. test_code.py import pytest + def test_upper_to_lower(monkeypatch): """Set the USER env var to assert the behavior.""" monkeypatch.setenv("USER", "TestingUser") assert get_os_user_lower() == "testinguser" + def test_raise_exception(monkeypatch): """Remove the USER env var and assert EnvironmentError is raised.""" monkeypatch.delenv("USER", raising=False) @@ -120,29 +127,34 @@ both paths can be safely tested without impacting the running environment:: with pytest.raises(EnvironmentError): _ = get_os_user_lower() -This behavior can be be moved into ``fixture`` structures and shared across tests:: +This behavior can be be moved into ``fixture`` structures and shared across tests: + +.. code-block:: python import pytest + @pytest.fixture def mock_env_user(monkeypatch): monkeypatch.setenv("USER", "TestingUser") + @pytest.fixture def mock_env_missing(monkeypatch): monkeypatch.delenv("USER", raising=False) + # Notice the tests reference the fixtures for mocks def test_upper_to_lower(mock_env_user): assert get_os_user_lower() == "testinguser" + def test_raise_exception(mock_env_missing): with pytest.raises(EnvironmentError): _ = get_os_user_lower() - .. currentmodule:: _pytest.monkeypatch API Reference From 4318698baeab38a413d8c3684248f95a2fed3979 Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Tue, 14 May 2019 14:54:03 -0700 Subject: [PATCH 05/15] Remove 'b' from sys.stdout.mode --- changelog/5257.bugfix.rst | 1 + src/_pytest/capture.py | 4 ++++ testing/test_capture.py | 3 +++ 3 files changed, 8 insertions(+) create mode 100644 changelog/5257.bugfix.rst diff --git a/changelog/5257.bugfix.rst b/changelog/5257.bugfix.rst new file mode 100644 index 000000000..520519088 --- /dev/null +++ b/changelog/5257.bugfix.rst @@ -0,0 +1 @@ +Ensure that ``sys.stdout.mode`` does not include ``'b'`` as it is a text stream. diff --git a/src/_pytest/capture.py b/src/_pytest/capture.py index 25eab7fdf..560171134 100644 --- a/src/_pytest/capture.py +++ b/src/_pytest/capture.py @@ -447,6 +447,10 @@ class EncodedFile(object): """Ensure that file.name is a string.""" return repr(self.buffer) + @property + def mode(self): + return self.buffer.mode.replace("b", "") + def __getattr__(self, name): return getattr(object.__getattribute__(self, "buffer"), name) diff --git a/testing/test_capture.py b/testing/test_capture.py index 5d80eb63d..f1802cc16 100644 --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -1051,6 +1051,9 @@ class TestFDCapture(object): cap.done() pytest.raises(AttributeError, cap.suspend) + def test_capfd_sys_stdout_mode(self, capfd): + assert "b" not in sys.stdout.mode + @contextlib.contextmanager def saved_fd(fd): From 7e8044f9b819e1b137518f5a5051f983cd1fd824 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Tue, 14 May 2019 20:49:47 -0300 Subject: [PATCH 06/15] Revamp the mark section in the docs Add an introductory section about how to register marks, including doing so programatically (#5255). --- doc/en/mark.rst | 51 +++++++++++++++++++++++++++++--------------- doc/en/reference.rst | 2 ++ 2 files changed, 36 insertions(+), 17 deletions(-) diff --git a/doc/en/mark.rst b/doc/en/mark.rst index 05ffdb36c..2f96ee3ae 100644 --- a/doc/en/mark.rst +++ b/doc/en/mark.rst @@ -24,40 +24,57 @@ which also serve as documentation. :ref:`fixtures `. -.. _unknown-marks: +Registering marks +----------------- -Raising errors on unknown marks -------------------------------- - -Unknown marks applied with the ``@pytest.mark.name_of_the_mark`` decorator -will always emit a warning, in order to avoid silently doing something -surprising due to mis-typed names. You can disable the warning for custom -marks by registering them in ``pytest.ini`` like this: +You can register custom marks in your ``pytest.ini`` file like this: .. code-block:: ini [pytest] markers = - slow + slow: marks tests as slow (deselect with '-m "not slow"') serial +Note that everything after the ``:`` is an optional description. + +Alternatively, you can register new markers programatically in a +:ref:`pytest_configure ` hook: + +.. code-block:: python + + def pytest_configure(config): + config.addinivalue_line( + "markers", "env(name): mark test to run only on named environment" + ) + + +Registered marks appear in pytest's help text and do not emit warnings (see the next section). It +is recommended that third-party plugins always :ref:`register their markers `. + +.. _unknown-marks: + +Raising errors on unknown marks +------------------------------- + +Unregistered marks applied with the ``@pytest.mark.name_of_the_mark`` decorator +will always emit a warning in order to avoid silently doing something +surprising due to mis-typed names. As described in the previous section, you can disable +the warning for custom marks by registering them in your ``pytest.ini`` file or +using a custom ``pytest_configure`` hook. + When the ``--strict-markers`` command-line flag is passed, any unknown marks applied -with the ``@pytest.mark.name_of_the_mark`` decorator will trigger an error. -Marks added by pytest or by a plugin instead of the decorator will not trigger -this error. To enforce validation of markers, add ``--strict-markers`` to ``addopts``: +with the ``@pytest.mark.name_of_the_mark`` decorator will trigger an error. You can +enforce this validation in your project by adding ``--strict-markers`` to ``addopts``: .. code-block:: ini [pytest] addopts = --strict-markers markers = - slow + slow: marks tests as slow (deselect with '-m "not slow"') serial -Third-party plugins should always :ref:`register their markers ` -so that they appear in pytest's help text and do not emit warnings. - - .. _marker-revamp: Marker revamp and iteration diff --git a/doc/en/reference.rst b/doc/en/reference.rst index 61f8034ed..f76fc7765 100644 --- a/doc/en/reference.rst +++ b/doc/en/reference.rst @@ -581,6 +581,8 @@ Bootstrapping hooks called for plugins registered early enough (internal and set .. autofunction:: pytest_cmdline_parse .. autofunction:: pytest_cmdline_main +.. _`initialization-hooks`: + Initialization hooks ~~~~~~~~~~~~~~~~~~~~ From a31098a74e69d72db6d9bf5b0ba1680fcdc5c265 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Tue, 14 May 2019 20:52:59 -0300 Subject: [PATCH 07/15] Move section about mark revamp and iteration to historical notes This has been in place for a long time now, since 3.6. --- doc/en/historical-notes.rst | 108 ++++++++++++++++++++++++++++++++++++ doc/en/mark.rst | 106 ----------------------------------- 2 files changed, 108 insertions(+), 106 deletions(-) diff --git a/doc/en/historical-notes.rst b/doc/en/historical-notes.rst index 145dc4529..c06796801 100644 --- a/doc/en/historical-notes.rst +++ b/doc/en/historical-notes.rst @@ -4,6 +4,114 @@ Historical Notes This page lists features or behavior from previous versions of pytest which have changed over the years. They are kept here as a historical note so users looking at old code can find documentation related to them. + +.. _marker-revamp: + +Marker revamp and iteration +--------------------------- + +.. versionchanged:: 3.6 + +pytest's marker implementation traditionally worked by simply updating the ``__dict__`` attribute of functions to cumulatively add markers. As a result, markers would unintentionally be passed along class hierarchies in surprising ways. Further, the API for retrieving them was inconsistent, as markers from parameterization would be stored differently than markers applied using the ``@pytest.mark`` decorator and markers added via ``node.add_marker``. + +This state of things made it technically next to impossible to use data from markers correctly without having a deep understanding of the internals, leading to subtle and hard to understand bugs in more advanced usages. + +Depending on how a marker got declared/changed one would get either a ``MarkerInfo`` which might contain markers from sibling classes, +``MarkDecorators`` when marks came from parameterization or from a ``node.add_marker`` call, discarding prior marks. Also ``MarkerInfo`` acts like a single mark, when it in fact represents a merged view on multiple marks with the same name. + +On top of that markers were not accessible the same way for modules, classes, and functions/methods. +In fact, markers were only accessible in functions, even if they were declared on classes/modules. + +A new API to access markers has been introduced in pytest 3.6 in order to solve the problems with the initial design, providing :func:`_pytest.nodes.Node.iter_markers` method to iterate over markers in a consistent manner and reworking the internals, which solved great deal of problems with the initial design. + + +.. _update marker code: + +Updating code +~~~~~~~~~~~~~ + +The old ``Node.get_marker(name)`` function is considered deprecated because it returns an internal ``MarkerInfo`` object +which contains the merged name, ``*args`` and ``**kwargs`` of all the markers which apply to that node. + +In general there are two scenarios on how markers should be handled: + +1. Marks overwrite each other. Order matters but you only want to think of your mark as a single item. E.g. +``log_level('info')`` at a module level can be overwritten by ``log_level('debug')`` for a specific test. + + In this case, use ``Node.get_closest_marker(name)``: + + .. code-block:: python + + # replace this: + marker = item.get_marker("log_level") + if marker: + level = marker.args[0] + + # by this: + marker = item.get_closest_marker("log_level") + if marker: + level = marker.args[0] + +2. Marks compose in an additive manner. E.g. ``skipif(condition)`` marks mean you just want to evaluate all of them, +order doesn't even matter. You probably want to think of your marks as a set here. + + In this case iterate over each mark and handle their ``*args`` and ``**kwargs`` individually. + + .. code-block:: python + + # replace this + skipif = item.get_marker("skipif") + if skipif: + for condition in skipif.args: + # eval condition + ... + + # by this: + for skipif in item.iter_markers("skipif"): + condition = skipif.args[0] + # eval condition + + +If you are unsure or have any questions, please consider opening +`an issue `_. + +Related issues +~~~~~~~~~~~~~~ + +Here is a non-exhaustive list of issues fixed by the new implementation: + +* Marks don't pick up nested classes (`#199 `_). + +* Markers stain on all related classes (`#568 `_). + +* Combining marks - args and kwargs calculation (`#2897 `_). + +* ``request.node.get_marker('name')`` returns ``None`` for markers applied in classes (`#902 `_). + +* Marks applied in parametrize are stored as markdecorator (`#2400 `_). + +* Fix marker interaction in a backward incompatible way (`#1670 `_). + +* Refactor marks to get rid of the current "marks transfer" mechanism (`#2363 `_). + +* Introduce FunctionDefinition node, use it in generate_tests (`#2522 `_). + +* Remove named marker attributes and collect markers in items (`#891 `_). + +* skipif mark from parametrize hides module level skipif mark (`#1540 `_). + +* skipif + parametrize not skipping tests (`#1296 `_). + +* Marker transfer incompatible with inheritance (`#535 `_). + +More details can be found in the `original PR `_. + +.. note:: + + in a future major relase of pytest we will introduce class based markers, + at which point markers will no longer be limited to instances of :py:class:`Mark`. + + cache plugin integrated into the core ------------------------------------- diff --git a/doc/en/mark.rst b/doc/en/mark.rst index 2f96ee3ae..27a3c789c 100644 --- a/doc/en/mark.rst +++ b/doc/en/mark.rst @@ -74,109 +74,3 @@ enforce this validation in your project by adding ``--strict-markers`` to ``addo markers = slow: marks tests as slow (deselect with '-m "not slow"') serial - -.. _marker-revamp: - -Marker revamp and iteration ---------------------------- - - - -pytest's marker implementation traditionally worked by simply updating the ``__dict__`` attribute of functions to cumulatively add markers. As a result, markers would unintentionally be passed along class hierarchies in surprising ways. Further, the API for retrieving them was inconsistent, as markers from parameterization would be stored differently than markers applied using the ``@pytest.mark`` decorator and markers added via ``node.add_marker``. - -This state of things made it technically next to impossible to use data from markers correctly without having a deep understanding of the internals, leading to subtle and hard to understand bugs in more advanced usages. - -Depending on how a marker got declared/changed one would get either a ``MarkerInfo`` which might contain markers from sibling classes, -``MarkDecorators`` when marks came from parameterization or from a ``node.add_marker`` call, discarding prior marks. Also ``MarkerInfo`` acts like a single mark, when it in fact represents a merged view on multiple marks with the same name. - -On top of that markers were not accessible the same way for modules, classes, and functions/methods. -In fact, markers were only accessible in functions, even if they were declared on classes/modules. - -A new API to access markers has been introduced in pytest 3.6 in order to solve the problems with the initial design, providing :func:`_pytest.nodes.Node.iter_markers` method to iterate over markers in a consistent manner and reworking the internals, which solved great deal of problems with the initial design. - - -.. _update marker code: - -Updating code -~~~~~~~~~~~~~ - -The old ``Node.get_marker(name)`` function is considered deprecated because it returns an internal ``MarkerInfo`` object -which contains the merged name, ``*args`` and ``**kwargs`` of all the markers which apply to that node. - -In general there are two scenarios on how markers should be handled: - -1. Marks overwrite each other. Order matters but you only want to think of your mark as a single item. E.g. -``log_level('info')`` at a module level can be overwritten by ``log_level('debug')`` for a specific test. - - In this case, use ``Node.get_closest_marker(name)``: - - .. code-block:: python - - # replace this: - marker = item.get_marker("log_level") - if marker: - level = marker.args[0] - - # by this: - marker = item.get_closest_marker("log_level") - if marker: - level = marker.args[0] - -2. Marks compose in an additive manner. E.g. ``skipif(condition)`` marks mean you just want to evaluate all of them, -order doesn't even matter. You probably want to think of your marks as a set here. - - In this case iterate over each mark and handle their ``*args`` and ``**kwargs`` individually. - - .. code-block:: python - - # replace this - skipif = item.get_marker("skipif") - if skipif: - for condition in skipif.args: - # eval condition - ... - - # by this: - for skipif in item.iter_markers("skipif"): - condition = skipif.args[0] - # eval condition - - -If you are unsure or have any questions, please consider opening -`an issue `_. - -Related issues -~~~~~~~~~~~~~~ - -Here is a non-exhaustive list of issues fixed by the new implementation: - -* Marks don't pick up nested classes (`#199 `_). - -* Markers stain on all related classes (`#568 `_). - -* Combining marks - args and kwargs calculation (`#2897 `_). - -* ``request.node.get_marker('name')`` returns ``None`` for markers applied in classes (`#902 `_). - -* Marks applied in parametrize are stored as markdecorator (`#2400 `_). - -* Fix marker interaction in a backward incompatible way (`#1670 `_). - -* Refactor marks to get rid of the current "marks transfer" mechanism (`#2363 `_). - -* Introduce FunctionDefinition node, use it in generate_tests (`#2522 `_). - -* Remove named marker attributes and collect markers in items (`#891 `_). - -* skipif mark from parametrize hides module level skipif mark (`#1540 `_). - -* skipif + parametrize not skipping tests (`#1296 `_). - -* Marker transfer incompatible with inheritance (`#535 `_). - -More details can be found in the `original PR `_. - -.. note:: - - in a future major relase of pytest we will introduce class based markers, - at which point markers will no longer be limited to instances of :py:class:`Mark`. From c6e3ff3ce5ce7d7759f9750a572fb504e1680724 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Tue, 14 May 2019 20:58:41 -0300 Subject: [PATCH 08/15] Mention "-m" in the main mark docs --- doc/en/example/markers.rst | 2 ++ doc/en/mark.rst | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/doc/en/example/markers.rst b/doc/en/example/markers.rst index f143e448c..0c3df0497 100644 --- a/doc/en/example/markers.rst +++ b/doc/en/example/markers.rst @@ -6,6 +6,8 @@ Working with custom markers Here are some example using the :ref:`mark` mechanism. +.. _`mark run`: + Marking test functions and selecting them for a run ---------------------------------------------------- diff --git a/doc/en/mark.rst b/doc/en/mark.rst index 27a3c789c..de6ab7822 100644 --- a/doc/en/mark.rst +++ b/doc/en/mark.rst @@ -15,8 +15,10 @@ some builtin markers, for example: to the same test function. It's easy to create custom markers or to apply markers -to whole test classes or modules. See :ref:`mark examples` for examples -which also serve as documentation. +to whole test classes or modules. Those markers can be used by plugins, and also +are commonly used to :ref:`select tests ` on the command-line with the ``-m`` option. + +See :ref:`mark examples` for examples which also serve as documentation. .. note:: From e44a2ef6530ab762d64b97a583accdbb3739f1a3 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 15 May 2019 20:43:07 -0300 Subject: [PATCH 09/15] Apply suggestions from code review Co-Authored-By: Daniel Hahler --- doc/en/example/markers.rst | 2 +- doc/en/historical-notes.rst | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/doc/en/example/markers.rst b/doc/en/example/markers.rst index 0c3df0497..b8d8bef0c 100644 --- a/doc/en/example/markers.rst +++ b/doc/en/example/markers.rst @@ -4,7 +4,7 @@ Working with custom markers ================================================= -Here are some example using the :ref:`mark` mechanism. +Here are some examples using the :ref:`mark` mechanism. .. _`mark run`: diff --git a/doc/en/historical-notes.rst b/doc/en/historical-notes.rst index c06796801..c8403728f 100644 --- a/doc/en/historical-notes.rst +++ b/doc/en/historical-notes.rst @@ -19,10 +19,13 @@ This state of things made it technically next to impossible to use data from mar Depending on how a marker got declared/changed one would get either a ``MarkerInfo`` which might contain markers from sibling classes, ``MarkDecorators`` when marks came from parameterization or from a ``node.add_marker`` call, discarding prior marks. Also ``MarkerInfo`` acts like a single mark, when it in fact represents a merged view on multiple marks with the same name. -On top of that markers were not accessible the same way for modules, classes, and functions/methods. +On top of that markers were not accessible in the same way for modules, classes, and functions/methods. In fact, markers were only accessible in functions, even if they were declared on classes/modules. -A new API to access markers has been introduced in pytest 3.6 in order to solve the problems with the initial design, providing :func:`_pytest.nodes.Node.iter_markers` method to iterate over markers in a consistent manner and reworking the internals, which solved great deal of problems with the initial design. +A new API to access markers has been introduced in pytest 3.6 in order to solve the problems with +the initial design, providing the :func:`_pytest.nodes.Node.iter_markers` method to iterate over +markers in a consistent manner and reworking the internals, which solved a great deal of problems +with the initial design. .. _update marker code: From 858010e2143397909853721826e798d4e9d7bbff Mon Sep 17 00:00:00 2001 From: Nikita Krokosh Date: Thu, 16 May 2019 18:22:41 +1000 Subject: [PATCH 10/15] Fixed double `be` word on monkeypatch docs page. --- doc/en/monkeypatch.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/en/monkeypatch.rst b/doc/en/monkeypatch.rst index b45ad07c1..c9304e0fb 100644 --- a/doc/en/monkeypatch.rst +++ b/doc/en/monkeypatch.rst @@ -127,7 +127,7 @@ both paths can be safely tested without impacting the running environment: with pytest.raises(EnvironmentError): _ = get_os_user_lower() -This behavior can be be moved into ``fixture`` structures and shared across tests: +This behavior can be moved into ``fixture`` structures and shared across tests: .. code-block:: python From 8abd4aec6eef988590691c766c40e0d74c5820fc Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 16 May 2019 08:07:59 -0300 Subject: [PATCH 11/15] Remove customization of (python.exe) from Azure [skip travis] This is no longer needed it seems --- azure-pipelines.yml | 8 ++------ scripts/upload-coverage.bat | 10 +++++----- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 42be786b5..7b580ffb9 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -5,7 +5,6 @@ trigger: variables: PYTEST_ADDOPTS: "--junitxml=build/test-results/$(tox.env).xml -vv" python.needs_vc: False - python.exe: "python" COVERAGE_FILE: "$(Build.Repository.LocalPath)/.coverage" COVERAGE_PROCESS_START: "$(Build.Repository.LocalPath)/.coveragerc" PYTEST_COVERAGE: '0' @@ -45,12 +44,10 @@ jobs: pypy: python.version: 'pypy2' tox.env: 'pypy' - python.exe: 'pypy' # NOTE: pypy3 fails to install pip currently due to an internal error. # pypy3: # python.version: 'pypy3' # tox.env: 'pypy3' - # python.exe: 'pypy3' py34-xdist: python.version: '3.4' tox.env: 'py34-xdist' @@ -94,12 +91,12 @@ jobs: condition: eq(variables['python.needs_vc'], True) displayName: 'Install VC for py27' - - script: $(python.exe) -m pip install --upgrade pip && $(python.exe) -m pip install tox + - script: python -m pip install --upgrade pip && python -m pip install tox displayName: 'Install tox' - script: | call scripts/setup-coverage-vars.bat || goto :eof - $(python.exe) -m tox -e $(tox.env) + python -m tox -e $(tox.env) displayName: 'Run tests' - task: PublishTestResults@2 @@ -112,6 +109,5 @@ jobs: displayName: 'Report and upload coverage' condition: eq(variables['PYTEST_COVERAGE'], '1') env: - PYTHON: $(python.exe) CODECOV_TOKEN: $(CODECOV_TOKEN) PYTEST_CODECOV_NAME: $(tox.env) diff --git a/scripts/upload-coverage.bat b/scripts/upload-coverage.bat index b21e0126e..08ed57791 100644 --- a/scripts/upload-coverage.bat +++ b/scripts/upload-coverage.bat @@ -6,11 +6,11 @@ if "%PYTEST_COVERAGE%" == "1" ( ) else ( echo CODECOV_TOKEN NOT defined ) - %PYTHON% -m pip install codecov - %PYTHON% -m coverage combine - %PYTHON% -m coverage xml - %PYTHON% -m coverage report -m - scripts\retry %PYTHON% -m codecov --required -X gcov pycov search -f coverage.xml --name %PYTEST_CODECOV_NAME% + python -m pip install codecov + python -m coverage combine + python -m coverage xml + python -m coverage report -m + scripts\retry python -m codecov --required -X gcov pycov search -f coverage.xml --name %PYTEST_CODECOV_NAME% ) else ( echo Skipping coverage upload, PYTEST_COVERAGE=%PYTEST_COVERAGE% ) From 06fa2bc0b87e78a2947ce6faa6fd3b759239d4aa Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 16 May 2019 08:10:46 -0300 Subject: [PATCH 12/15] Re-enable pypy3 to see how it fares on Azure --- azure-pipelines.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 7b580ffb9..de5d27c01 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -44,10 +44,9 @@ jobs: pypy: python.version: 'pypy2' tox.env: 'pypy' - # NOTE: pypy3 fails to install pip currently due to an internal error. - # pypy3: - # python.version: 'pypy3' - # tox.env: 'pypy3' + pypy3: + python.version: 'pypy3' + tox.env: 'pypy3' py34-xdist: python.version: '3.4' tox.env: 'py34-xdist' From 0571e1ee8ec3031a46871f45db2fc98752d3fe0c Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 16 May 2019 10:54:50 -0300 Subject: [PATCH 13/15] Make it clear that small typos in docs don't require a CHANGELOG entry From: https://github.com/pytest-dev/pytest/pull/5273#issuecomment-493076587 --- .github/PULL_REQUEST_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 366f63df2..aaf13b1a4 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -5,7 +5,6 @@ Here's a quick checklist that should be present in PRs. (please delete this text from the final description, this is just a guideline) --> -- [ ] Create a new changelog file in the `changelog` folder, with a name like `..rst`. See [changelog/README.rst](https://github.com/pytest-dev/pytest/blob/master/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. @@ -13,4 +12,5 @@ Here's a quick checklist that should be present in PRs. Unless your change is trivial or a small documentation fix (e.g., a typo or reword of a small section) please: +- [ ] Create a new changelog file in the `changelog` folder, with a name like `..rst`. See [changelog/README.rst](https://github.com/pytest-dev/pytest/blob/master/changelog/README.rst) for details. - [ ] Add yourself to `AUTHORS` in alphabetical order; From 56bf7446f6820a2ff392148a53625a5f2fa78921 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 16 May 2019 12:18:30 -0300 Subject: [PATCH 14/15] Disable pypy2 and pypy3 on Azure [travis skip] Related to #5279 --- azure-pipelines.yml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index de5d27c01..8e50486de 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -41,12 +41,13 @@ jobs: # Also seen with py27-nobyte (using xdist), and py27-xdist. # But no exception with py27-pexpect,py27-twisted,py27-numpy. PYTEST_COVERAGE: '1' - pypy: - python.version: 'pypy2' - tox.env: 'pypy' - pypy3: - python.version: 'pypy3' - tox.env: 'pypy3' + # -- pypy2 and pypy3 are disabled for now: #5279 -- + # pypy: + # python.version: 'pypy2' + # tox.env: 'pypy' + # pypy3: + # python.version: 'pypy3' + # tox.env: 'pypy3' py34-xdist: python.version: '3.4' tox.env: 'py34-xdist' From e253029ad0bd9a3a7cb4f477c5c8d2d23672188c Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 15 May 2019 20:08:28 -0300 Subject: [PATCH 15/15] Handle lone surrogate unicode character not being representable in Jython No tests for this because we don't test with Jython currently. Fix #5256 --- changelog/5256.bugfix.rst | 1 + src/_pytest/terminal.py | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 changelog/5256.bugfix.rst diff --git a/changelog/5256.bugfix.rst b/changelog/5256.bugfix.rst new file mode 100644 index 000000000..4df83454e --- /dev/null +++ b/changelog/5256.bugfix.rst @@ -0,0 +1 @@ +Handle internal error due to a lone surrogate unicode character not being representable in Jython. diff --git a/src/_pytest/terminal.py b/src/_pytest/terminal.py index 771e6a835..1780792b4 100644 --- a/src/_pytest/terminal.py +++ b/src/_pytest/terminal.py @@ -998,7 +998,15 @@ def _get_line_with_reprcrash_message(config, rep, termwidth): # u'😄' will result in a High Surrogate (U+D83D) character, which is # rendered as u'�'; in this case we just strip that character out as it # serves no purpose being rendered - msg = msg.rstrip(u"\uD83D") + try: + surrogate = six.unichr(0xD83D) + msg = msg.rstrip(surrogate) + except ValueError: # pragma: no cover + # Jython cannot represent this lone surrogate at all (#5256): + # ValueError: unichr() arg is a lone surrogate in range + # (0xD800, 0xDFFF) (Jython UTF-16 encoding) + # ignore this case as it shouldn't appear in the string anyway + pass msg += ellipsis line += sep + msg return line