Skip to content

Features merge master #1234

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 47 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
beaa8e5
Fixes #653 use deprecated_call as context_manager
chiller Sep 21, 2015
9f77a85
removed mutation of global state, changed filter addition in Warnings…
chiller Sep 21, 2015
d8fbb0b
start features branch
hpk42 Sep 22, 2015
97f7815
also change pytest version to target 2.9.0
hpk42 Sep 22, 2015
8a4517f
re-add 2.8.x changelog so that MASTER can be merged into features wrt
hpk42 Sep 22, 2015
79587d4
Merge branch 'master' into features
hpk42 Sep 22, 2015
48df2f6
Merge branch 'master' into features
hpk42 Sep 22, 2015
7c088d1
remove nonsense line
hpk42 Sep 22, 2015
36924b5
Merge branch '653-deprecated-context-manager' of https://github.com/c…
RonnyPfannschmidt Sep 22, 2015
4867554
Merge branch 'master' into features
hpk42 Sep 23, 2015
0c05ca1
Merge branch 'master' into features
hpk42 Sep 26, 2015
cb58eaa
Merge remote-tracking branch 'upstream/master' into features
hpk42 Sep 29, 2015
b71add2
Add MarkEvaluator for skip
MichaelAquilina Sep 21, 2015
4e94135
Remove incorrect use of pytest.mark.skip
MichaelAquilina Sep 21, 2015
f144666
Work towards test coverage of mark.skip
MichaelAquilina Sep 21, 2015
ad0b8e3
Fix case where skip is assigned to as an attribute directly
MichaelAquilina Sep 21, 2015
61b8443
Update docs with new skip marker
MichaelAquilina Sep 21, 2015
5ec08d3
Delete trailing whitespace
MichaelAquilina Sep 21, 2015
dc7153e
Spelling and grammar fixes
MichaelAquilina Sep 21, 2015
771aef9
Add a test_skip_class test
MichaelAquilina Sep 21, 2015
abc27f5
Update skipping.rst with correct version marker
MichaelAquilina Sep 23, 2015
d162894
Update skippings tests for better coverage
MichaelAquilina Sep 27, 2015
04545f8
classes inherit from object
MichaelAquilina Oct 1, 2015
eee2413
Fix failing test
MichaelAquilina Oct 1, 2015
1b5aa28
Check no reason displayed if none specified
MichaelAquilina Oct 1, 2015
9e57954
First argument in pytest.mark.skip is a reason
MichaelAquilina Oct 1, 2015
213dbe7
newlines
MichaelAquilina Oct 1, 2015
25d74a5
Dont explicitly inherit from object
MichaelAquilina Oct 3, 2015
5ff9a0f
Remove redundant comments
MichaelAquilina Oct 3, 2015
fc0bd94
Test that "unconditional skip" is the default reason if none given
MichaelAquilina Oct 3, 2015
122980e
Add myself to AUTHORS
MichaelAquilina Oct 3, 2015
00d0c74
Update reason in test to prevent confusing with test_no_reason
MichaelAquilina Oct 3, 2015
df874db
Update default reason to "unconditional skip"
MichaelAquilina Oct 3, 2015
7504429
Add unconditional skip entry to CHANGELOG
MichaelAquilina Oct 3, 2015
8984177
TestXFail also shouldnt explicitly inherit from object
MichaelAquilina Oct 3, 2015
a24126e
Add credit for pytest.mark.skip to the CHANGELOG
nicoddemus Oct 3, 2015
616d825
unit tests of Config.fromdictargs. currently failing
Sep 24, 2015
49d46a0
an ugly patch to fix all but the most important part =/
Sep 24, 2015
0e55a87
all tests pass
Sep 25, 2015
470e4f9
changelog entry
Oct 8, 2015
67236d6
strengthen the ini assertion
Oct 9, 2015
cee8281
Merge pull request #1124 from bukzor/unit-test-config-fromdictargs
nicoddemus Oct 9, 2015
1f148a9
Mention pytest_enter_pdb in the docs
nicoddemus Nov 23, 2015
b3166a5
Pass pytest's config object to pytest_enter_pdb
nicoddemus Nov 23, 2015
df767cc
Merge pull request #1188 from nicoddemus/pytest_enter_pdb
RonnyPfannschmidt Nov 23, 2015
84eacf3
Small changelog formatting fix
nicoddemus Nov 26, 2015
eabf2f9
Merge branch 'master' into features
Dec 7, 2015
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Dave Hunt
David Mohr
Edison Gustavo Muenz
Eduardo Schettino
Endre Galaczi
Elizaveta Shashkova
Eric Hunsberger
Eric Siegerman
Expand All @@ -53,6 +54,7 @@ Marc Schlaich
Mark Abramowitz
Markus Unterwaditzer
Martijn Faassen
Michael Aquilina
Michael Birtwell
Michael Droettboom
Nicolas Delaby
Expand Down
14 changes: 14 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
2.9.0.dev
---------

* New `pytest.mark.skip` mark, which unconditional skips marked tests.
Thanks Michael Aquilina for the complete PR.

* fix issue #680: the -s and -c options should now work under xdist;
`Config.fromdictargs` now represents its input much more faithfully.
Thanks to Buck Evan for the complete PR.

* `pytest_enter_pdb` now optionally receives the pytest config object.
Thanks Bruno Oliveira for the PR.

2.8.5.dev0
----------

Expand Down Expand Up @@ -119,6 +132,7 @@

- Fix issue #411: Add __eq__ method to assertion comparison example.
Thanks Ben Webb.
- Fix issue #653: deprecated_call can be used as context manager.

- fix issue 877: properly handle assertion explanations with non-ascii repr
Thanks Mathieu Agopian for the report and Ronny Pfannschmidt for the PR.
Expand Down
2 changes: 1 addition & 1 deletion _pytest/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
#
__version__ = '2.8.5.dev0'
__version__ = '2.9.0.dev1'
34 changes: 18 additions & 16 deletions _pytest/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -455,11 +455,11 @@ def addoption(self, *opts, **attrs):
"""
self._anonymous.addoption(*opts, **attrs)

def parse(self, args):
def parse(self, args, namespace=None):
from _pytest._argcomplete import try_argcomplete
self.optparser = self._getparser()
try_argcomplete(self.optparser)
return self.optparser.parse_args([str(x) for x in args])
return self.optparser.parse_args([str(x) for x in args], namespace=namespace)

def _getparser(self):
from _pytest._argcomplete import filescompleter
Expand All @@ -477,25 +477,25 @@ def _getparser(self):
optparser.add_argument(FILE_OR_DIR, nargs='*').completer=filescompleter
return optparser

def parse_setoption(self, args, option):
parsedoption = self.parse(args)
def parse_setoption(self, args, option, namespace=None):
parsedoption = self.parse(args, namespace=namespace)
for name, value in parsedoption.__dict__.items():
setattr(option, name, value)
return getattr(parsedoption, FILE_OR_DIR)

def parse_known_args(self, args):
def parse_known_args(self, args, namespace=None):
"""parses and returns a namespace object with known arguments at this
point.
"""
return self.parse_known_and_unknown_args(args)[0]
return self.parse_known_and_unknown_args(args, namespace=namespace)[0]

def parse_known_and_unknown_args(self, args):
def parse_known_and_unknown_args(self, args, namespace=None):
"""parses and returns a namespace object with known arguments, and
the remaining arguments unknown at this point.
"""
optparser = self._getparser()
args = [str(x) for x in args]
return optparser.parse_known_args(args)
return optparser.parse_known_args(args, namespace=namespace)

def addini(self, name, help, type=None, default=None):
""" register an ini-file option.
Expand Down Expand Up @@ -779,10 +779,12 @@ def _ensure_removed_sysmodule(modname):

class CmdOptions(object):
""" holds cmdline options as attributes."""
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
def __init__(self, values=()):
self.__dict__.update(values)
def __repr__(self):
return "<CmdOptions %r>" %(self.__dict__,)
def copy(self):
return CmdOptions(self.__dict__)

class Notset:
def __repr__(self):
Expand Down Expand Up @@ -879,8 +881,8 @@ def cwd_relative_nodeid(self, nodeid):
def fromdictargs(cls, option_dict, args):
""" constructor useable for subprocesses. """
config = get_config()
config._preparse(args, addopts=False)
config.option.__dict__.update(option_dict)
config.parse(args, addopts=False)
for x in config.option.plugins:
config.pluginmanager.consider_pluginarg(x)
return config
Expand All @@ -898,7 +900,7 @@ def pytest_load_initial_conftests(self, early_config):
self.pluginmanager._set_initial_conftests(early_config.known_args_namespace)

def _initini(self, args):
ns, unknown_args = self._parser.parse_known_and_unknown_args(args)
ns, unknown_args = self._parser.parse_known_and_unknown_args(args, namespace=self.option.copy())
r = determine_setup(ns.inifilename, ns.file_or_dir + unknown_args)
self.rootdir, self.inifile, self.inicfg = r
self._parser.extra_info['rootdir'] = self.rootdir
Expand All @@ -919,7 +921,7 @@ def _preparse(self, args, addopts=True):
except ImportError as e:
self.warn("I2", "could not load setuptools entry import: %s" % (e,))
self.pluginmanager.consider_env()
self.known_args_namespace = ns = self._parser.parse_known_args(args)
self.known_args_namespace = ns = self._parser.parse_known_args(args, namespace=self.option.copy())
if self.known_args_namespace.confcutdir is None and self.inifile:
confcutdir = py.path.local(self.inifile).dirname
self.known_args_namespace.confcutdir = confcutdir
Expand Down Expand Up @@ -947,17 +949,17 @@ def _checkversion(self):
self.inicfg.config.path, self.inicfg.lineof('minversion'),
minver, pytest.__version__))

def parse(self, args):
def parse(self, args, addopts=True):
# parse given cmdline arguments into this config object.
assert not hasattr(self, 'args'), (
"can only parse cmdline args at most once per Config object")
self._origargs = args
self.hook.pytest_addhooks.call_historic(
kwargs=dict(pluginmanager=self.pluginmanager))
self._preparse(args)
self._preparse(args, addopts=addopts)
# XXX deprecated hook:
self.hook.pytest_cmdline_preparse(config=self, args=args)
args = self._parser.parse_setoption(args, self.option)
args = self._parser.parse_setoption(args, self.option, namespace=self.option)
if not args:
cwd = os.getcwd()
if cwd == self.rootdir:
Expand Down
5 changes: 4 additions & 1 deletion _pytest/hookspec.py
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,10 @@ def pytest_exception_interact(node, call, report):
that is not an internal exception like "skip.Exception".
"""

def pytest_enter_pdb():
def pytest_enter_pdb(config):
""" called upon pdb.set_trace(), can be used by plugins to take special
action just before the python debugger enters in interactive mode.

:arg config: pytest config object
:type config: _pytest.config.Config
"""
3 changes: 1 addition & 2 deletions _pytest/pdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,14 @@ def set_trace(self):
""" invoke PDB set_trace debugging, dropping any IO capturing. """
import _pytest.config
frame = sys._getframe().f_back
capman = None
if self._pluginmanager is not None:
capman = self._pluginmanager.getplugin("capturemanager")
if capman:
capman.suspendcapture(in_=True)
tw = _pytest.config.create_terminal_writer(self._config)
tw.line()
tw.sep(">", "PDB set_trace (IO-capturing turned off)")
self._pluginmanager.hook.pytest_enter_pdb()
self._pluginmanager.hook.pytest_enter_pdb(config=self._config)
pdb.Pdb().set_trace(frame)


Expand Down
12 changes: 10 additions & 2 deletions _pytest/recwarn.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,22 @@ def pytest_namespace():
'warns': warns}


def deprecated_call(func, *args, **kwargs):
def deprecated_call(func=None, *args, **kwargs):
""" assert that calling ``func(*args, **kwargs)`` triggers a
``DeprecationWarning`` or ``PendingDeprecationWarning``.

This function can be used as a context manager::

>>> with deprecated_call():
... myobject.deprecated_method()

Note: we cannot use WarningsRecorder here because it is still subject
to the mechanism that prevents warnings of the same type from being
triggered twice for the same module. See #1190.
"""
if not func:
return WarningsChecker(expected_warning=DeprecationWarning)

categories = []

def warn_explicit(message, category, *args, **kwargs):
Expand Down Expand Up @@ -171,8 +179,8 @@ def showwarning(message, category, filename, lineno,
self._module.showwarning = showwarning

# allow the same warning to be raised more than once
self._module.simplefilter('always', append=True)

self._module.simplefilter('always')
return self

def __exit__(self, *exc_info):
Expand Down
30 changes: 26 additions & 4 deletions _pytest/skipping.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@

import py
import pytest
from _pytest.mark import MarkInfo


def pytest_addoption(parser):
group = parser.getgroup("general")
group.addoption('--runxfail',
action="store_true", dest="runxfail", default=False,
help="run tests even if they are marked xfail")


def pytest_configure(config):
if config.option.runxfail:
old = pytest.xfail
Expand All @@ -38,18 +41,22 @@ def nop(*args, **kwargs):
"See http://pytest.org/latest/skipping.html"
)


def pytest_namespace():
return dict(xfail=xfail)


class XFailed(pytest.fail.Exception):
""" raised from an explicit call to pytest.xfail() """


def xfail(reason=""):
""" xfail an executing test or setup functions with the given reason."""
__tracebackhide__ = True
raise XFailed(reason)
xfail.Exception = XFailed


class MarkEvaluator:
def __init__(self, item, name):
self.item = item
Expand Down Expand Up @@ -147,10 +154,25 @@ def getexplanation(self):

@pytest.hookimpl(tryfirst=True)
def pytest_runtest_setup(item):
evalskip = MarkEvaluator(item, 'skipif')
if evalskip.istrue():
item._evalskip = evalskip
pytest.skip(evalskip.getexplanation())
# Check if skip or skipif are specified as pytest marks

skipif_info = item.keywords.get('skipif')
if isinstance(skipif_info, MarkInfo):
eval_skipif = MarkEvaluator(item, 'skipif')
if eval_skipif.istrue():
item._evalskip = eval_skipif
pytest.skip(eval_skipif.getexplanation())

skip_info = item.keywords.get('skip')
if isinstance(skip_info, MarkInfo):
item._evalskip = True
if 'reason' in skip_info.kwargs:
pytest.skip(skip_info.kwargs['reason'])
elif skip_info.args:
pytest.skip(skip_info.args[0])
else:
pytest.skip("unconditional skip")

item._evalxfail = MarkEvaluator(item, 'xfail')
check_xfail_no_run(item)

Expand Down
6 changes: 6 additions & 0 deletions doc/en/recwarn.rst
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,9 @@ command ``warnings.simplefilter('always')``::
warnings.warn("deprecated", DeprecationWarning)
assert len(recwarn) == 1
assert recwarn.pop(DeprecationWarning)

You can also use it as a contextmanager::

def test_global():
with pytest.deprecated_call():
myobject.deprecated_method()
16 changes: 13 additions & 3 deletions doc/en/skipping.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,18 @@ corresponding to the "short" letters shown in the test progress::
Marking a test function to be skipped
-------------------------------------------

.. versionadded:: 2.9

The simplest way to skip a test function is to mark it with the `skip` decorator
which may be passed an optional `reason`:

@pytest.mark.skip(reason="no way of currently testing this")
def test_the_unknown():
...

.. versionadded:: 2.0, 2.4

If you wish to skip something conditionally then you can use `skipif` instead.
Here is an example of marking a test function to be skipped
when run on a Python3.3 interpreter::

Expand Down Expand Up @@ -168,12 +178,12 @@ Running it with the report-on-xfail option gives this output::
platform linux -- Python 3.4.3, pytest-2.8.4, py-1.4.30, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR/example, inifile:
collected 7 items

xfail_demo.py xxxxxxx
======= short test summary info ========
XFAIL xfail_demo.py::test_hello
XFAIL xfail_demo.py::test_hello2
reason: [NOTRUN]
reason: [NOTRUN]
XFAIL xfail_demo.py::test_hello3
condition: hasattr(os, 'sep')
XFAIL xfail_demo.py::test_hello4
Expand All @@ -183,7 +193,7 @@ Running it with the report-on-xfail option gives this output::
XFAIL xfail_demo.py::test_hello6
reason: reason
XFAIL xfail_demo.py::test_hello7

======= 7 xfailed in 0.12 seconds ========

.. _`skip/xfail with parametrize`:
Expand Down
1 change: 0 additions & 1 deletion testing/test_capture.py
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,6 @@ def test_a():
import subprocess
subprocess.call([sys.executable, __file__])

@pytest.mark.skip
def test_foo():
import os;os.write(1, b'\xc3')

Expand Down
Loading