Skip to content

Commit

Permalink
Merge pull request #2869 from nicoddemus/merge-master-into-features
Browse files Browse the repository at this point in the history
Merge master into features
  • Loading branch information
RonnyPfannschmidt authored Oct 25, 2017
2 parents f743e95 + 4e581b6 commit def471b
Show file tree
Hide file tree
Showing 30 changed files with 191 additions and 55 deletions.
2 changes: 1 addition & 1 deletion .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ Here's a quick checklist that should be present in PRs:

Unless your change is a trivial or a documentation fix (e.g., a typo or reword of a small section) please:

- [ ] Add yourself to `AUTHORS`;
- [ ] Add yourself to `AUTHORS`, in alphabetical order;
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ Tareq Alayan
Ted Xiao
Thomas Grainger
Thomas Hisch
Tom Dalton
Tom Viner
Trevor Bekolay
Tyler Goodlet
Expand Down
3 changes: 2 additions & 1 deletion CONTRIBUTING.rst
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,8 @@ Short version
#. Write a ``changelog`` entry: ``changelog/2574.bugfix``, use issue id number
and one of ``bugfix``, ``removal``, ``feature``, ``vendor``, ``doc`` or
``trivial`` for the issue type.
#. Add yourself to ``AUTHORS`` file if not there yet, in alphabetical order.
#. Unless your change is a trivial or a documentation fix (e.g., a typo or reword of a small section) please
add yourself to the ``AUTHORS`` file, in alphabetical order;


Long version
Expand Down
14 changes: 4 additions & 10 deletions _pytest/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import _pytest
from _pytest.outcomes import TEST_OUTCOME


try:
import enum
except ImportError: # pragma: no cover
Expand Down Expand Up @@ -110,11 +109,10 @@ def getfuncargnames(function, is_method=False, cls=None):
# ordered mapping of parameter names to Parameter instances. This
# creates a tuple of the names of the parameters that don't have
# defaults.
arg_names = tuple(
p.name for p in signature(function).parameters.values()
if (p.kind is Parameter.POSITIONAL_OR_KEYWORD
or p.kind is Parameter.KEYWORD_ONLY) and
p.default is Parameter.empty)
arg_names = tuple(p.name for p in signature(function).parameters.values()
if (p.kind is Parameter.POSITIONAL_OR_KEYWORD or
p.kind is Parameter.KEYWORD_ONLY) and
p.default is Parameter.empty)
# If this function should be treated as a bound method even though
# it's passed as an unbound method or function, remove the first
# parameter name.
Expand All @@ -129,8 +127,6 @@ def getfuncargnames(function, is_method=False, cls=None):


if _PY3:
imap = map
izip = zip
STRING_TYPES = bytes, str
UNICODE_TYPES = str,

Expand Down Expand Up @@ -173,8 +169,6 @@ def ascii_escaped(val):
STRING_TYPES = bytes, str, unicode
UNICODE_TYPES = unicode,

from itertools import imap, izip # NOQA

def ascii_escaped(val):
"""In py2 bytes and str are the same type, so return if it's a bytes
object, return it unchanged if it is a full ascii string,
Expand Down
27 changes: 17 additions & 10 deletions _pytest/fixtures.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
from __future__ import absolute_import, division, print_function
import sys

from py._code.code import FormattedExcinfo
import inspect
import sys
import warnings

import py
import warnings
from py._code.code import FormattedExcinfo

import inspect
import _pytest
from _pytest import nodes
from _pytest._code.code import TerminalRepr
from _pytest.compat import (
NOTSET, exc_clear, _format_args,
getfslineno, get_real_func,
is_generator, isclass, getimfunc,
getlocation, getfuncargnames,
safe_getattr,
FuncargnamesCompatAttr,
)
from _pytest.outcomes import fail, TEST_OUTCOME
from _pytest.compat import FuncargnamesCompatAttr


from collections import OrderedDict

Expand Down Expand Up @@ -977,8 +979,8 @@ def pytest_plugin_registered(self, plugin):
# by their test id)
if p.basename.startswith("conftest.py"):
nodeid = p.dirpath().relto(self.config.rootdir)
if p.sep != "/":
nodeid = nodeid.replace(p.sep, "/")
if p.sep != nodes.SEP:
nodeid = nodeid.replace(p.sep, nodes.SEP)
self.parsefactories(plugin, nodeid)

def _getautousenames(self, nodeid):
Expand Down Expand Up @@ -1033,9 +1035,14 @@ def pytest_generate_tests(self, metafunc):
if faclist:
fixturedef = faclist[-1]
if fixturedef.params is not None:
func_params = getattr(getattr(metafunc.function, 'parametrize', None), 'args', [[None]])
parametrize_func = getattr(metafunc.function, 'parametrize', None)
func_params = getattr(parametrize_func, 'args', [[None]])
func_kwargs = getattr(parametrize_func, 'kwargs', {})
# skip directly parametrized arguments
argnames = func_params[0]
if "argnames" in func_kwargs:
argnames = parametrize_func.kwargs["argnames"]
else:
argnames = func_params[0]
if not isinstance(argnames, (tuple, list)):
argnames = [x.strip() for x in argnames.split(",") if x.strip()]
if argname not in func_params and argname not in argnames:
Expand Down Expand Up @@ -1123,5 +1130,5 @@ def getfixturedefs(self, argname, nodeid):

def _matchfactories(self, fixturedefs, nodeid):
for fixturedef in fixturedefs:
if nodeid.startswith(fixturedef.baseid):
if nodes.ischildnode(fixturedef.baseid, nodeid):
yield fixturedef
3 changes: 2 additions & 1 deletion _pytest/junitxml.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import sys
import time
import pytest
from _pytest import nodes
from _pytest.config import filename_arg

# Python 2.X and 3.X compatibility
Expand Down Expand Up @@ -252,7 +253,7 @@ def mangle_test_address(address):
except ValueError:
pass
# convert file path to dotted path
names[0] = names[0].replace("/", '.')
names[0] = names[0].replace(nodes.SEP, '.')
names[0] = _py_ext_re.sub("", names[0])
# put any params back
names[-1] += possible_open_bracket + params
Expand Down
9 changes: 5 additions & 4 deletions _pytest/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import sys

import _pytest
from _pytest import nodes
import _pytest._code
import py
try:
Expand All @@ -15,8 +16,8 @@
from UserDict import DictMixin as MappingMixin

from _pytest.config import directory_arg, UsageError, hookimpl
from _pytest.runner import collect_one_node
from _pytest.outcomes import exit
from _pytest.runner import collect_one_node

tracebackcutdir = py.path.local(_pytest.__file__).dirpath()

Expand Down Expand Up @@ -494,14 +495,14 @@ def __init__(self, fspath, parent=None, config=None, session=None):
rel = fspath.relto(parent.fspath)
if rel:
name = rel
name = name.replace(os.sep, "/")
name = name.replace(os.sep, nodes.SEP)
super(FSCollector, self).__init__(name, parent, config, session)
self.fspath = fspath

def _makeid(self):
relpath = self.fspath.relto(self.config.rootdir)
if os.sep != "/":
relpath = relpath.replace(os.sep, "/")
if os.sep != nodes.SEP:
relpath = relpath.replace(os.sep, nodes.SEP)
return relpath


Expand Down
6 changes: 3 additions & 3 deletions _pytest/mark.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import warnings
from collections import namedtuple
from operator import attrgetter
from .compat import imap
from six.moves import map
from .deprecated import MARK_PARAMETERSET_UNPACKING


Expand Down Expand Up @@ -379,7 +379,7 @@ def store_mark(obj, mark):
"""
assert isinstance(mark, Mark), mark
# always reassign name to avoid updating pytestmark
# in a referene that was only borrowed
# in a reference that was only borrowed
obj.pytestmark = get_unpacked_marks(obj) + [mark]


Expand Down Expand Up @@ -427,7 +427,7 @@ def add_mark(self, mark):

def __iter__(self):
""" yield MarkInfo objects each relating to a marking-call. """
return imap(MarkInfo, self._marks)
return map(MarkInfo, self._marks)


MARK_GEN = MarkGenerator()
Expand Down
37 changes: 37 additions & 0 deletions _pytest/nodes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
SEP = "/"


def _splitnode(nodeid):
"""Split a nodeid into constituent 'parts'.
Node IDs are strings, and can be things like:
''
'testing/code'
'testing/code/test_excinfo.py'
'testing/code/test_excinfo.py::TestFormattedExcinfo::()'
Return values are lists e.g.
[]
['testing', 'code']
['testing', 'code', 'test_excinfo.py']
['testing', 'code', 'test_excinfo.py', 'TestFormattedExcinfo', '()']
"""
if nodeid == '':
# If there is no root node at all, return an empty list so the caller's logic can remain sane
return []
parts = nodeid.split(SEP)
# Replace single last element 'test_foo.py::Bar::()' with multiple elements 'test_foo.py', 'Bar', '()'
parts[-1:] = parts[-1].split("::")
return parts


def ischildnode(baseid, nodeid):
"""Return True if the nodeid is a child node of the baseid.
E.g. 'foo/bar::Baz::()' is a child of 'foo', 'foo/bar' and 'foo/bar::Baz', but not of 'foo/blorp'
"""
base_parts = _splitnode(baseid)
node_parts = _splitnode(nodeid)
if len(node_parts) < len(base_parts):
return False
return node_parts[:len(base_parts)] == base_parts
4 changes: 1 addition & 3 deletions _pytest/pytester.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,7 @@
from _pytest.assertion.rewrite import AssertionRewritingHook


PYTEST_FULLPATH = os.path.abspath(
pytest.__file__.rstrip("oc")
).replace("$py.class", ".py")
PYTEST_FULLPATH = os.path.abspath(pytest.__file__.rstrip("oc")).replace("$py.class", ".py")


def pytest_addoption(parser):
Expand Down
8 changes: 5 additions & 3 deletions _pytest/python_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
import sys

import py
from six.moves import zip

from _pytest.compat import isclass, izip
from _pytest.compat import isclass
from _pytest.outcomes import fail
import _pytest._code

Expand Down Expand Up @@ -145,7 +146,7 @@ def __eq__(self, actual):
return ApproxBase.__eq__(self, actual)

def _yield_comparisons(self, actual):
return izip(actual, self.expected)
return zip(actual, self.expected)


class ApproxScalar(ApproxBase):
Expand Down Expand Up @@ -217,7 +218,8 @@ def tolerance(self):
absolute tolerance or a relative tolerance, depending on what the user
specified or which would be larger.
"""
def set_default(x, default): return x if x is not None else default
def set_default(x, default):
return x if x is not None else default

# Figure out what the absolute tolerance should be. ``self.abs`` is
# either None or a value specified by the user.
Expand Down
6 changes: 2 additions & 4 deletions _pytest/recwarn.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,5 @@ def __exit__(self, *exc_info):
else:
fail("DID NOT WARN. No warnings of type {0} matching"
" ('{1}') was emitted. The list of emitted warnings"
" is: {2}.".format(
self.expected_warning,
self.match_expr,
[each.message for each in self]))
" is: {2}.".format(self.expected_warning, self.match_expr,
[each.message for each in self]))
5 changes: 3 additions & 2 deletions _pytest/terminal.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@
import time
import warnings

import pluggy
import py
import six

import pluggy
import pytest
from _pytest import nodes
from _pytest.main import EXIT_OK, EXIT_TESTSFAILED, EXIT_INTERRUPTED, \
EXIT_USAGEERROR, EXIT_NOTESTSCOLLECTED

Expand Down Expand Up @@ -466,7 +467,7 @@ def mkrel(nodeid):

if fspath:
res = mkrel(nodeid).replace("::()", "") # parens-normalization
if nodeid.split("::")[0] != fspath.replace("\\", "/"):
if nodeid.split("::")[0] != fspath.replace("\\", nodes.SEP):
res += " <- " + self.startdir.bestrelpath(fspath)
else:
res = "[location]"
Expand Down
1 change: 1 addition & 0 deletions changelog/1997.doc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Explicitly mention ``xpass`` in the documentation of ``xfail``.
1 change: 1 addition & 0 deletions changelog/2819.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix issue with @pytest.parametrize if argnames was specified as kwarg.
1 change: 1 addition & 0 deletions changelog/2836.bug
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Match fixture paths against actual path segments in order to avoid matching folders which share a prefix.
1 change: 1 addition & 0 deletions changelog/538.doc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Clarify the documentation of available fixture scopes.
1 change: 1 addition & 0 deletions changelog/911.doc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add documentation about the ``python -m pytest`` invocation adding the current directory to sys.path.
2 changes: 1 addition & 1 deletion doc/en/example/parametrize.rst
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ Parametrizing test methods through per-class configuration
.. _`unittest parametrizer`: https://github.com/testing-cabal/unittest-ext/blob/master/params.py


Here is an example ``pytest_generate_function`` function implementing a
Here is an example ``pytest_generate_tests`` function implementing a
parametrization scheme similar to Michael Foord's `unittest
parametrizer`_ but in a lot less code::

Expand Down
20 changes: 12 additions & 8 deletions doc/en/fixture.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ functions:
* fixture management scales from simple unit to complex
functional testing, allowing to parametrize fixtures and tests according
to configuration and component options, or to re-use fixtures
across class, module or whole test session scopes.
across function, class, module or whole test session scopes.

In addition, pytest continues to support :ref:`xunitsetup`. You can mix
both styles, moving incrementally from classic to new style, as you
Expand Down Expand Up @@ -129,8 +129,8 @@ functions take the role of the *injector* and test functions are the

.. _smtpshared:

Sharing a fixture across tests in a module (or class/session)
-----------------------------------------------------------------
Scope: Sharing a fixture across tests in a class, module or session
-------------------------------------------------------------------

.. regendoc:wipe
Expand All @@ -139,10 +139,12 @@ usually time-expensive to create. Extending the previous example, we
can add a ``scope='module'`` parameter to the
:py:func:`@pytest.fixture <_pytest.python.fixture>` invocation
to cause the decorated ``smtp`` fixture function to only be invoked once
per test module. Multiple test functions in a test module will thus
each receive the same ``smtp`` fixture instance. The next example puts
the fixture function into a separate ``conftest.py`` file so
that tests from multiple test modules in the directory can
per test *module* (the default is to invoke once per test *function*).
Multiple test functions in a test module will thus
each receive the same ``smtp`` fixture instance, thus saving time.

The next example puts the fixture function into a separate ``conftest.py`` file
so that tests from multiple test modules in the directory can
access the fixture function::

# content of conftest.py
Expand Down Expand Up @@ -223,6 +225,8 @@ instance, you can simply declare it:
# the returned fixture value will be shared for
# all tests needing it
Finally, the ``class`` scope will invoke the fixture once per test *class*.

.. _`finalization`:

Fixture finalization / executing teardown code
Expand Down Expand Up @@ -858,7 +862,7 @@ into a conftest.py file **without** using ``autouse``::

# content of conftest.py
@pytest.fixture
def transact(self, request, db):
def transact(request, db):
db.begin()
yield
db.rollback()
Expand Down
Loading

0 comments on commit def471b

Please sign in to comment.