Skip to content

Commit c67bf9d

Browse files
committed
Merge remote-tracking branch 'upstream/master' into features
2 parents 9adf513 + 57e2ced commit c67bf9d

17 files changed

+266
-139
lines changed

AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ Ross Lawley
144144
Russel Winder
145145
Ryan Wooden
146146
Samuele Pedroni
147+
Segev Finer
147148
Simon Gomizelj
148149
Skylar Downes
149150
Stefan Farmbauer

_pytest/assertion/rewrite.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
import struct
1212
import sys
1313
import types
14-
from fnmatch import fnmatch
1514

1615
import py
1716
from _pytest.assertion import util
@@ -167,7 +166,7 @@ def _should_rewrite(self, name, fn_pypath, state):
167166
# latter might trigger an import to fnmatch.fnmatch
168167
# internally, which would cause this method to be
169168
# called recursively
170-
if fnmatch(fn_pypath.basename, pat):
169+
if fn_pypath.fnmatch(pat):
171170
state.trace("matched test file %r" % (fn,))
172171
return True
173172

_pytest/capture.py

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import contextlib
88
import sys
99
import os
10+
import io
1011
from io import UnsupportedOperation
1112
from tempfile import TemporaryFile
1213

@@ -33,8 +34,10 @@ def pytest_addoption(parser):
3334

3435
@pytest.hookimpl(hookwrapper=True)
3536
def pytest_load_initial_conftests(early_config, parser, args):
36-
_readline_workaround()
3737
ns = early_config.known_args_namespace
38+
if ns.capture == "fd":
39+
_py36_windowsconsoleio_workaround()
40+
_readline_workaround()
3841
pluginmanager = early_config.pluginmanager
3942
capman = CaptureManager(ns.capture)
4043
pluginmanager.register(capman, "capturemanager")
@@ -491,3 +494,49 @@ def _readline_workaround():
491494
import readline # noqa
492495
except ImportError:
493496
pass
497+
498+
499+
def _py36_windowsconsoleio_workaround():
500+
"""
501+
Python 3.6 implemented unicode console handling for Windows. This works
502+
by reading/writing to the raw console handle using
503+
``{Read,Write}ConsoleW``.
504+
505+
The problem is that we are going to ``dup2`` over the stdio file
506+
descriptors when doing ``FDCapture`` and this will ``CloseHandle`` the
507+
handles used by Python to write to the console. Though there is still some
508+
weirdness and the console handle seems to only be closed randomly and not
509+
on the first call to ``CloseHandle``, or maybe it gets reopened with the
510+
same handle value when we suspend capturing.
511+
512+
The workaround in this case will reopen stdio with a different fd which
513+
also means a different handle by replicating the logic in
514+
"Py_lifecycle.c:initstdio/create_stdio".
515+
516+
See https://github.com/pytest-dev/py/issues/103
517+
"""
518+
if not sys.platform.startswith('win32') or sys.version_info[:2] < (3, 6):
519+
return
520+
521+
buffered = hasattr(sys.stdout.buffer, 'raw')
522+
raw_stdout = sys.stdout.buffer.raw if buffered else sys.stdout.buffer
523+
524+
if not isinstance(raw_stdout, io._WindowsConsoleIO):
525+
return
526+
527+
def _reopen_stdio(f, mode):
528+
if not buffered and mode[0] == 'w':
529+
buffering = 0
530+
else:
531+
buffering = -1
532+
533+
return io.TextIOWrapper(
534+
open(os.dup(f.fileno()), mode, buffering),
535+
f.encoding,
536+
f.errors,
537+
f.newlines,
538+
f.line_buffering)
539+
540+
sys.__stdin__ = sys.stdin = _reopen_stdio(sys.stdin, 'rb')
541+
sys.__stdout__ = sys.stdout = _reopen_stdio(sys.stdout, 'wb')
542+
sys.__stderr__ = sys.stderr = _reopen_stdio(sys.stderr, 'wb')

_pytest/config.py

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,12 @@ class UsageError(Exception):
7171
""" error in pytest usage or invocation"""
7272

7373

74+
class PrintHelp(Exception):
75+
"""Raised when pytest should print it's help to skip the rest of the
76+
argument parsing and validation."""
77+
pass
78+
79+
7480
def filename_arg(path, optname):
7581
""" Argparse type validator for filename arguments.
7682
@@ -163,7 +169,7 @@ def _prepareconfig(args=None, plugins=None):
163169

164170
class PytestPluginManager(PluginManager):
165171
"""
166-
Overwrites :py:class:`pluggy.PluginManager` to add pytest-specific
172+
Overwrites :py:class:`pluggy.PluginManager <_pytest.vendored_packages.pluggy.PluginManager>` to add pytest-specific
167173
functionality:
168174
169175
* loading plugins from the command line, ``PYTEST_PLUGIN`` env variable and
@@ -200,7 +206,7 @@ def addhooks(self, module_or_class):
200206
"""
201207
.. deprecated:: 2.8
202208
203-
Use :py:meth:`pluggy.PluginManager.add_hookspecs` instead.
209+
Use :py:meth:`pluggy.PluginManager.add_hookspecs <_pytest.vendored_packages.pluggy.PluginManager.add_hookspecs>` instead.
204210
"""
205211
warning = dict(code="I2",
206212
fslocation=_pytest._code.getfslineno(sys._getframe(1)),
@@ -1100,14 +1106,18 @@ def parse(self, args, addopts=True):
11001106
self._preparse(args, addopts=addopts)
11011107
# XXX deprecated hook:
11021108
self.hook.pytest_cmdline_preparse(config=self, args=args)
1103-
args = self._parser.parse_setoption(args, self.option, namespace=self.option)
1104-
if not args:
1105-
cwd = os.getcwd()
1106-
if cwd == self.rootdir:
1107-
args = self.getini('testpaths')
1109+
self._parser.after_preparse = True
1110+
try:
1111+
args = self._parser.parse_setoption(args, self.option, namespace=self.option)
11081112
if not args:
1109-
args = [cwd]
1110-
self.args = args
1113+
cwd = os.getcwd()
1114+
if cwd == self.rootdir:
1115+
args = self.getini('testpaths')
1116+
if not args:
1117+
args = [cwd]
1118+
self.args = args
1119+
except PrintHelp:
1120+
pass
11111121

11121122
def addinivalue_line(self, name, line):
11131123
""" add a line to an ini-file option. The option must have been
@@ -1120,7 +1130,7 @@ def addinivalue_line(self, name, line):
11201130
def getini(self, name):
11211131
""" return configuration value from an :ref:`ini file <inifiles>`. If the
11221132
specified name hasn't been registered through a prior
1123-
:py:func:`parser.addini <pytest.config.Parser.addini>`
1133+
:py:func:`parser.addini <_pytest.config.Parser.addini>`
11241134
call (usually from a plugin), a ValueError is raised. """
11251135
try:
11261136
return self._inicache[name]

_pytest/helpconfig.py

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,46 @@
33

44
import py
55
import pytest
6+
from _pytest.config import PrintHelp
67
import os, sys
8+
from argparse import Action
9+
10+
11+
class HelpAction(Action):
12+
"""This is an argparse Action that will raise an exception in
13+
order to skip the rest of the argument parsing when --help is passed.
14+
This prevents argparse from quitting due to missing required arguments
15+
when any are defined, for example by ``pytest_addoption``.
16+
This is similar to the way that the builtin argparse --help option is
17+
implemented by raising SystemExit.
18+
"""
19+
20+
def __init__(self,
21+
option_strings,
22+
dest=None,
23+
default=False,
24+
help=None):
25+
super(HelpAction, self).__init__(
26+
option_strings=option_strings,
27+
dest=dest,
28+
const=True,
29+
default=default,
30+
nargs=0,
31+
help=help)
32+
33+
def __call__(self, parser, namespace, values, option_string=None):
34+
setattr(namespace, self.dest, self.const)
35+
36+
# We should only skip the rest of the parsing after preparse is done
37+
if getattr(parser._parser, 'after_preparse', False):
38+
raise PrintHelp
39+
740

841
def pytest_addoption(parser):
942
group = parser.getgroup('debugconfig')
1043
group.addoption('--version', action="store_true",
1144
help="display pytest lib version and import information.")
12-
group._addoption("-h", "--help", action="store_true", dest="help",
45+
group._addoption("-h", "--help", action=HelpAction, dest="help",
1346
help="show help message and configuration info")
1447
group._addoption('-p', action="append", dest="plugins", default = [],
1548
metavar="name",

_pytest/hookspec.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ def pytest_runtest_teardown(item, nextitem):
213213
@hookspec(firstresult=True)
214214
def pytest_runtest_makereport(item, call):
215215
""" return a :py:class:`_pytest.runner.TestReport` object
216-
for the given :py:class:`pytest.Item` and
216+
for the given :py:class:`pytest.Item <_pytest.main.Item>` and
217217
:py:class:`_pytest.runner.CallInfo`.
218218
"""
219219

_pytest/vendored_packages/pluggy.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -540,7 +540,7 @@ def add_hookcall_monitoring(self, before, after):
540540
of HookImpl instances and the keyword arguments for the hook call.
541541
542542
``after(outcome, hook_name, hook_impls, kwargs)`` receives the
543-
same arguments as ``before`` but also a :py:class:`_CallOutcome`` object
543+
same arguments as ``before`` but also a :py:class:`_CallOutcome <_pytest.vendored_packages.pluggy._CallOutcome>` object
544544
which represents the result of the overall hook call.
545545
"""
546546
return _TracedHookExecution(self, before, after).undo

changelog/1999.bugfix

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Required options added via ``pytest_addoption`` will no longer prevent
2+
using --help without passing them.

changelog/2121.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Respect ``python_files`` in assertion rewriting.

changelog/2331.doc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix internal API links to ``pluggy`` objects.

changelog/2467.bugfix

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Added a workaround for Python 3.6 WindowsConsoleIO breaking due to Pytests's
2+
FDCapture. Other code using console handles might still be affected by the
3+
very same issue and might require further workarounds/fixes, i.e. colorama.

changelog/810.doc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Make it clear that ``pytest.xfail`` stops test execution at the calling point and improve overall flow of the ``skipping`` docs.

0 commit comments

Comments
 (0)