diff options
author | Ronny Pfannschmidt <opensource@ronnypfannschmidt.de> | 2018-03-17 09:40:32 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-03-17 09:40:32 +0100 |
commit | f61d0525a53de3c51da58313307d2e95fad062e5 (patch) | |
tree | 8d64d5fc6160c2f15b2c5e85fe3b57f8fabf9328 /_pytest | |
parent | 86d6804e60ecfcdb0a2b7a73e2e274ffea00d0fb (diff) | |
parent | 1fff81e21d4145e501d618bced645cbfaa5f5afc (diff) | |
download | pytest-f61d0525a53de3c51da58313307d2e95fad062e5.tar.gz |
Merge pull request #3318 from nicoddemus/merge-master-into-features
Merge master into features
Diffstat (limited to '_pytest')
-rw-r--r-- | _pytest/capture.py | 39 | ||||
-rw-r--r-- | _pytest/config.py | 2 | ||||
-rw-r--r-- | _pytest/doctest.py | 2 | ||||
-rw-r--r-- | _pytest/fixtures.py | 12 | ||||
-rw-r--r-- | _pytest/junitxml.py | 8 | ||||
-rw-r--r-- | _pytest/logging.py | 12 | ||||
-rw-r--r-- | _pytest/mark/__init__.py | 15 | ||||
-rw-r--r-- | _pytest/monkeypatch.py | 2 | ||||
-rw-r--r-- | _pytest/python.py | 2 | ||||
-rw-r--r-- | _pytest/python_api.py | 5 | ||||
-rw-r--r-- | _pytest/recwarn.py | 13 | ||||
-rw-r--r-- | _pytest/tmpdir.py | 2 |
12 files changed, 87 insertions, 27 deletions
diff --git a/_pytest/capture.py b/_pytest/capture.py index 36658acce..9f4f41c41 100644 --- a/_pytest/capture.py +++ b/_pytest/capture.py @@ -197,9 +197,9 @@ def _ensure_only_one_capture_fixture(request, name): @pytest.fixture def capsys(request): - """Enable capturing of writes to sys.stdout/sys.stderr and make + """Enable capturing of writes to ``sys.stdout`` and ``sys.stderr`` and make captured output available via ``capsys.readouterr()`` method calls - which return a ``(out, err)`` tuple. ``out`` and ``err`` will be ``text`` + which return a ``(out, err)`` namedtuple. ``out`` and ``err`` will be ``text`` objects. """ _ensure_only_one_capture_fixture(request, 'capsys') @@ -209,7 +209,7 @@ def capsys(request): @pytest.fixture def capsysbinary(request): - """Enable capturing of writes to sys.stdout/sys.stderr and make + """Enable capturing of writes to ``sys.stdout`` and ``sys.stderr`` and make captured output available via ``capsys.readouterr()`` method calls which return a ``(out, err)`` tuple. ``out`` and ``err`` will be ``bytes`` objects. @@ -225,7 +225,7 @@ def capsysbinary(request): @pytest.fixture def capfd(request): - """Enable capturing of writes to file descriptors 1 and 2 and make + """Enable capturing of writes to file descriptors ``1`` and ``2`` and make captured output available via ``capfd.readouterr()`` method calls which return a ``(out, err)`` tuple. ``out`` and ``err`` will be ``text`` objects. @@ -272,6 +272,10 @@ def _install_capture_fixture_on_item(request, capture_class): class CaptureFixture(object): + """ + Object returned by :py:func:`capsys`, :py:func:`capsysbinary`, :py:func:`capfd` and :py:func:`capfdbinary` + fixtures. + """ def __init__(self, captureclass, request): self.captureclass = captureclass self.request = request @@ -288,6 +292,10 @@ class CaptureFixture(object): cap.stop_capturing() def readouterr(self): + """Read and return the captured output so far, resetting the internal buffer. + + :return: captured content as a namedtuple with ``out`` and ``err`` string attributes + """ try: return self._capture.readouterr() except AttributeError: @@ -295,6 +303,7 @@ class CaptureFixture(object): @contextlib.contextmanager def disabled(self): + """Temporarily disables capture while inside the 'with' block.""" self._capture.suspend_capturing() capmanager = self.request.config.pluginmanager.getplugin('capturemanager') capmanager.suspend_global_capture(item=None, in_=False) @@ -476,7 +485,7 @@ class FDCaptureBinary(object): os.dup2(targetfd_save, self.targetfd) os.close(targetfd_save) self.syscapture.done() - self.tmpfile.close() + _attempt_to_close_capture_file(self.tmpfile) def suspend(self): self.syscapture.suspend() @@ -530,7 +539,7 @@ class SysCapture(object): def done(self): setattr(sys, self.name, self._old) del self._old - self.tmpfile.close() + _attempt_to_close_capture_file(self.tmpfile) def suspend(self): setattr(sys, self.name, self._old) @@ -551,7 +560,7 @@ class SysCaptureBinary(SysCapture): return res -class DontReadFromInput(object): +class DontReadFromInput(six.Iterator): """Temporary stub class. Ideally when stdin is accessed, the capturing should be turned off, with possibly all data captured so far sent to the screen. This should be configurable, though, @@ -565,7 +574,10 @@ class DontReadFromInput(object): raise IOError("reading from stdin while output is captured") readline = read readlines = read - __iter__ = read + __next__ = read + + def __iter__(self): + return self def fileno(self): raise UnsupportedOperation("redirected stdin is pseudofile, " @@ -681,3 +693,14 @@ def _py36_windowsconsoleio_workaround(stream): sys.__stdin__ = sys.stdin = _reopen_stdio(sys.stdin, 'rb') sys.__stdout__ = sys.stdout = _reopen_stdio(sys.stdout, 'wb') sys.__stderr__ = sys.stderr = _reopen_stdio(sys.stderr, 'wb') + + +def _attempt_to_close_capture_file(f): + """Suppress IOError when closing the temporary file used for capturing streams in py27 (#2370)""" + if six.PY2: + try: + f.close() + except IOError: + pass + else: + f.close() diff --git a/_pytest/config.py b/_pytest/config.py index 63c7dc3bb..4087d6e9f 100644 --- a/_pytest/config.py +++ b/_pytest/config.py @@ -171,7 +171,7 @@ class PytestPluginManager(PluginManager): Overwrites :py:class:`pluggy.PluginManager <pluggy.PluginManager>` to add pytest-specific functionality: - * loading plugins from the command line, ``PYTEST_PLUGIN`` env variable and + * loading plugins from the command line, ``PYTEST_PLUGINS`` env variable and ``pytest_plugins`` global variables found in plugins being loaded; * ``conftest.py`` loading during start-up; """ diff --git a/_pytest/doctest.py b/_pytest/doctest.py index 03775a09a..131109cba 100644 --- a/_pytest/doctest.py +++ b/_pytest/doctest.py @@ -462,6 +462,6 @@ def _fix_spoof_python2(runner, encoding): @pytest.fixture(scope='session') def doctest_namespace(): """ - Inject names into the doctest namespace. + Fixture that returns a :py:class:`dict` that will be injected into the namespace of doctests. """ return dict() diff --git a/_pytest/fixtures.py b/_pytest/fixtures.py index df0d1a41a..e2ea84e30 100644 --- a/_pytest/fixtures.py +++ b/_pytest/fixtures.py @@ -858,7 +858,7 @@ class FixtureFunctionMarker(object): def fixture(scope="function", params=None, autouse=False, ids=None, name=None): - """ (return a) decorator to mark a fixture factory function. + """Decorator to mark a fixture factory function. This decorator can be used (with or without parameters) to define a fixture function. The name of the fixture function can later be @@ -923,7 +923,15 @@ defaultfuncargprefixmarker = fixture() @fixture(scope="session") def pytestconfig(request): - """ the pytest config object with access to command line opts.""" + """Session-scoped fixture that returns the :class:`_pytest.config.Config` object. + + Example:: + + def test_foo(pytestconfig): + if pytestconfig.getoption("verbose"): + ... + + """ return request.config diff --git a/_pytest/junitxml.py b/_pytest/junitxml.py index 98b2d13cf..5207c2514 100644 --- a/_pytest/junitxml.py +++ b/_pytest/junitxml.py @@ -237,7 +237,13 @@ def record_property(request): """Add an extra properties the calling test. User properties become part of the test report and are available to the configured reporters, like JUnit XML. - The fixture is callable with ``(name, value)``. + The fixture is callable with ``(name, value)``, with value being automatically + xml-encoded. + + Example:: + + def test_function(record_property): + record_property("example_key", 1) """ request.node.warn( code='C3', diff --git a/_pytest/logging.py b/_pytest/logging.py index 7234bdeb0..902872e45 100644 --- a/_pytest/logging.py +++ b/_pytest/logging.py @@ -176,6 +176,10 @@ class LogCaptureHandler(logging.StreamHandler): self.records.append(record) logging.StreamHandler.emit(self, record) + def reset(self): + self.records = [] + self.stream = py.io.TextIO() + class LogCaptureFixture(object): """Provides access and control of log capturing.""" @@ -197,6 +201,9 @@ class LogCaptureFixture(object): @property def handler(self): + """ + :rtype: LogCaptureHandler + """ return self._item.catch_log_handler def get_records(self, when): @@ -239,8 +246,8 @@ class LogCaptureFixture(object): return [(r.name, r.levelno, r.getMessage()) for r in self.records] def clear(self): - """Reset the list of log records.""" - self.handler.records = [] + """Reset the list of log records and the captured log text.""" + self.handler.reset() def set_level(self, level, logger=None): """Sets the level for capturing of logs. The level will be restored to its previous value at the end of @@ -285,6 +292,7 @@ def caplog(request): * caplog.text() -> string containing formatted log output * caplog.records() -> list of logging.LogRecord instances * caplog.record_tuples() -> list of (logger_name, level, message) tuples + * caplog.clear() -> clear captured records and formatted log output string """ result = LogCaptureFixture(request.node) yield result diff --git a/_pytest/mark/__init__.py b/_pytest/mark/__init__.py index f22db5820..51540dbd7 100644 --- a/_pytest/mark/__init__.py +++ b/_pytest/mark/__init__.py @@ -20,6 +20,21 @@ class MarkerError(Exception): def param(*values, **kw): + """Specify a parameter in a `pytest.mark.parametrize`_ call. + + .. code-block:: python + + @pytest.mark.parametrize("test_input,expected", [ + ("3+5", 8), + pytest.param("6*9", 42, marks=pytest.mark.xfail), + ]) + def test_eval(test_input, expected): + assert eval(test_input) == expected + + :param values: variable args of the values of the parameter set, in order. + :keyword marks: a single mark or a list of marks to be applied to this parameter set. + :keyword str id: the id to attribute to this parameter set. + """ return ParameterSet.param(*values, **kw) diff --git a/_pytest/monkeypatch.py b/_pytest/monkeypatch.py index c402213e8..c82ffd053 100644 --- a/_pytest/monkeypatch.py +++ b/_pytest/monkeypatch.py @@ -113,7 +113,7 @@ class MonkeyPatch(object): For convenience you can specify a string as ``target`` which will be interpreted as a dotted import path, with the last part being the attribute name. Example: - ``monkeypatch.setattr("os.getcwd", lambda x: "/")`` + ``monkeypatch.setattr("os.getcwd", lambda: "/")`` would set the ``getcwd`` function of the ``os`` module. The ``raising`` value determines if the setattr should fail diff --git a/_pytest/python.py b/_pytest/python.py index cdcfed49b..f9f17afd7 100644 --- a/_pytest/python.py +++ b/_pytest/python.py @@ -717,7 +717,7 @@ class CallSpec2(object): class Metafunc(fixtures.FuncargnamesCompatAttr): """ - Metafunc objects are passed to the ``pytest_generate_tests`` hook. + Metafunc objects are passed to the :func:`pytest_generate_tests <_pytest.hookspec.pytest_generate_tests>` hook. They help to inspect a test function and to generate tests according to test configuration or values specified in the class or module where a test function is defined. diff --git a/_pytest/python_api.py b/_pytest/python_api.py index 9de4dd2a8..69ae6ed0e 100644 --- a/_pytest/python_api.py +++ b/_pytest/python_api.py @@ -564,8 +564,9 @@ def raises(expected_exception, *args, **kwargs): The string will be evaluated using the same ``locals()`` and ``globals()`` at the moment of the ``raises`` call. - .. autoclass:: _pytest._code.ExceptionInfo - :members: + .. currentmodule:: _pytest._code + + Consult the API of ``excinfo`` objects: :class:`ExceptionInfo`. .. note:: Similar to caught exception objects in Python, explicitly clearing diff --git a/_pytest/recwarn.py b/_pytest/recwarn.py index 4fceb10a7..ab0f79c75 100644 --- a/_pytest/recwarn.py +++ b/_pytest/recwarn.py @@ -16,10 +16,7 @@ from _pytest.outcomes import fail @yield_fixture def recwarn(): - """Return a WarningsRecorder instance that provides these methods: - - * ``pop(category=None)``: return last warning matching the category. - * ``clear()``: clear list of warnings + """Return a :class:`WarningsRecorder` instance that records all warnings emitted by test functions. See http://docs.python.org/library/warnings.html for information on warning categories. @@ -88,11 +85,11 @@ class _DeprecatedCallContext(object): def warns(expected_warning, *args, **kwargs): """Assert that code raises a particular class of warning. - Specifically, the input @expected_warning can be a warning class or - tuple of warning classes, and the code must return that warning - (if a single class) or one of those warnings (if a tuple). + Specifically, the parameter ``expected_warning`` can be a warning class or + sequence of warning classes, and the inside the ``with`` block must issue a warning of that class or + classes. - This helper produces a list of ``warnings.WarningMessage`` objects, + This helper produces a list of :class:`warnings.WarningMessage` objects, one for each warning raised. This function can be used as a context manager, or any of the other ways diff --git a/_pytest/tmpdir.py b/_pytest/tmpdir.py index 66b4a2d2f..315ead302 100644 --- a/_pytest/tmpdir.py +++ b/_pytest/tmpdir.py @@ -116,6 +116,8 @@ def tmpdir(request, tmpdir_factory): created as a sub directory of the base temporary directory. The returned object is a `py.path.local`_ path object. + + .. _`py.path.local`: https://py.readthedocs.io/en/latest/path.html """ name = request.node.name name = re.sub(r"[\W]", "_", name) |