Skip to content

Commit def471b

Browse files
Merge pull request #2869 from nicoddemus/merge-master-into-features
Merge master into features
2 parents f743e95 + 4e581b6 commit def471b

30 files changed

+191
-55
lines changed

.github/PULL_REQUEST_TEMPLATE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@ Here's a quick checklist that should be present in PRs:
1212

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

15-
- [ ] Add yourself to `AUTHORS`;
15+
- [ ] Add yourself to `AUTHORS`, in alphabetical order;

AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ Tareq Alayan
171171
Ted Xiao
172172
Thomas Grainger
173173
Thomas Hisch
174+
Tom Dalton
174175
Tom Viner
175176
Trevor Bekolay
176177
Tyler Goodlet

CONTRIBUTING.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,8 @@ Short version
177177
#. Write a ``changelog`` entry: ``changelog/2574.bugfix``, use issue id number
178178
and one of ``bugfix``, ``removal``, ``feature``, ``vendor``, ``doc`` or
179179
``trivial`` for the issue type.
180-
#. Add yourself to ``AUTHORS`` file if not there yet, in alphabetical order.
180+
#. Unless your change is a trivial or a documentation fix (e.g., a typo or reword of a small section) please
181+
add yourself to the ``AUTHORS`` file, in alphabetical order;
181182

182183

183184
Long version

_pytest/compat.py

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
import _pytest
1515
from _pytest.outcomes import TEST_OUTCOME
1616

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

130128

131129
if _PY3:
132-
imap = map
133-
izip = zip
134130
STRING_TYPES = bytes, str
135131
UNICODE_TYPES = str,
136132

@@ -173,8 +169,6 @@ def ascii_escaped(val):
173169
STRING_TYPES = bytes, str, unicode
174170
UNICODE_TYPES = unicode,
175171

176-
from itertools import imap, izip # NOQA
177-
178172
def ascii_escaped(val):
179173
"""In py2 bytes and str are the same type, so return if it's a bytes
180174
object, return it unchanged if it is a full ascii string,

_pytest/fixtures.py

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,25 @@
11
from __future__ import absolute_import, division, print_function
2-
import sys
32

4-
from py._code.code import FormattedExcinfo
3+
import inspect
4+
import sys
5+
import warnings
56

67
import py
7-
import warnings
8+
from py._code.code import FormattedExcinfo
89

9-
import inspect
1010
import _pytest
11+
from _pytest import nodes
1112
from _pytest._code.code import TerminalRepr
1213
from _pytest.compat import (
1314
NOTSET, exc_clear, _format_args,
1415
getfslineno, get_real_func,
1516
is_generator, isclass, getimfunc,
1617
getlocation, getfuncargnames,
1718
safe_getattr,
19+
FuncargnamesCompatAttr,
1820
)
1921
from _pytest.outcomes import fail, TEST_OUTCOME
20-
from _pytest.compat import FuncargnamesCompatAttr
22+
2123

2224
from collections import OrderedDict
2325

@@ -977,8 +979,8 @@ def pytest_plugin_registered(self, plugin):
977979
# by their test id)
978980
if p.basename.startswith("conftest.py"):
979981
nodeid = p.dirpath().relto(self.config.rootdir)
980-
if p.sep != "/":
981-
nodeid = nodeid.replace(p.sep, "/")
982+
if p.sep != nodes.SEP:
983+
nodeid = nodeid.replace(p.sep, nodes.SEP)
982984
self.parsefactories(plugin, nodeid)
983985

984986
def _getautousenames(self, nodeid):
@@ -1033,9 +1035,14 @@ def pytest_generate_tests(self, metafunc):
10331035
if faclist:
10341036
fixturedef = faclist[-1]
10351037
if fixturedef.params is not None:
1036-
func_params = getattr(getattr(metafunc.function, 'parametrize', None), 'args', [[None]])
1038+
parametrize_func = getattr(metafunc.function, 'parametrize', None)
1039+
func_params = getattr(parametrize_func, 'args', [[None]])
1040+
func_kwargs = getattr(parametrize_func, 'kwargs', {})
10371041
# skip directly parametrized arguments
1038-
argnames = func_params[0]
1042+
if "argnames" in func_kwargs:
1043+
argnames = parametrize_func.kwargs["argnames"]
1044+
else:
1045+
argnames = func_params[0]
10391046
if not isinstance(argnames, (tuple, list)):
10401047
argnames = [x.strip() for x in argnames.split(",") if x.strip()]
10411048
if argname not in func_params and argname not in argnames:
@@ -1123,5 +1130,5 @@ def getfixturedefs(self, argname, nodeid):
11231130

11241131
def _matchfactories(self, fixturedefs, nodeid):
11251132
for fixturedef in fixturedefs:
1126-
if nodeid.startswith(fixturedef.baseid):
1133+
if nodes.ischildnode(fixturedef.baseid, nodeid):
11271134
yield fixturedef

_pytest/junitxml.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import sys
1818
import time
1919
import pytest
20+
from _pytest import nodes
2021
from _pytest.config import filename_arg
2122

2223
# Python 2.X and 3.X compatibility
@@ -252,7 +253,7 @@ def mangle_test_address(address):
252253
except ValueError:
253254
pass
254255
# convert file path to dotted path
255-
names[0] = names[0].replace("/", '.')
256+
names[0] = names[0].replace(nodes.SEP, '.')
256257
names[0] = _py_ext_re.sub("", names[0])
257258
# put any params back
258259
names[-1] += possible_open_bracket + params

_pytest/main.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import sys
88

99
import _pytest
10+
from _pytest import nodes
1011
import _pytest._code
1112
import py
1213
try:
@@ -15,8 +16,8 @@
1516
from UserDict import DictMixin as MappingMixin
1617

1718
from _pytest.config import directory_arg, UsageError, hookimpl
18-
from _pytest.runner import collect_one_node
1919
from _pytest.outcomes import exit
20+
from _pytest.runner import collect_one_node
2021

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

@@ -494,14 +495,14 @@ def __init__(self, fspath, parent=None, config=None, session=None):
494495
rel = fspath.relto(parent.fspath)
495496
if rel:
496497
name = rel
497-
name = name.replace(os.sep, "/")
498+
name = name.replace(os.sep, nodes.SEP)
498499
super(FSCollector, self).__init__(name, parent, config, session)
499500
self.fspath = fspath
500501

501502
def _makeid(self):
502503
relpath = self.fspath.relto(self.config.rootdir)
503-
if os.sep != "/":
504-
relpath = relpath.replace(os.sep, "/")
504+
if os.sep != nodes.SEP:
505+
relpath = relpath.replace(os.sep, nodes.SEP)
505506
return relpath
506507

507508

_pytest/mark.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import warnings
66
from collections import namedtuple
77
from operator import attrgetter
8-
from .compat import imap
8+
from six.moves import map
99
from .deprecated import MARK_PARAMETERSET_UNPACKING
1010

1111

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

385385

@@ -427,7 +427,7 @@ def add_mark(self, mark):
427427

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

432432

433433
MARK_GEN = MarkGenerator()

_pytest/nodes.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
SEP = "/"
2+
3+
4+
def _splitnode(nodeid):
5+
"""Split a nodeid into constituent 'parts'.
6+
7+
Node IDs are strings, and can be things like:
8+
''
9+
'testing/code'
10+
'testing/code/test_excinfo.py'
11+
'testing/code/test_excinfo.py::TestFormattedExcinfo::()'
12+
13+
Return values are lists e.g.
14+
[]
15+
['testing', 'code']
16+
['testing', 'code', 'test_excinfo.py']
17+
['testing', 'code', 'test_excinfo.py', 'TestFormattedExcinfo', '()']
18+
"""
19+
if nodeid == '':
20+
# If there is no root node at all, return an empty list so the caller's logic can remain sane
21+
return []
22+
parts = nodeid.split(SEP)
23+
# Replace single last element 'test_foo.py::Bar::()' with multiple elements 'test_foo.py', 'Bar', '()'
24+
parts[-1:] = parts[-1].split("::")
25+
return parts
26+
27+
28+
def ischildnode(baseid, nodeid):
29+
"""Return True if the nodeid is a child node of the baseid.
30+
31+
E.g. 'foo/bar::Baz::()' is a child of 'foo', 'foo/bar' and 'foo/bar::Baz', but not of 'foo/blorp'
32+
"""
33+
base_parts = _splitnode(baseid)
34+
node_parts = _splitnode(nodeid)
35+
if len(node_parts) < len(base_parts):
36+
return False
37+
return node_parts[:len(base_parts)] == base_parts

_pytest/pytester.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,7 @@
2323
from _pytest.assertion.rewrite import AssertionRewritingHook
2424

2525

26-
PYTEST_FULLPATH = os.path.abspath(
27-
pytest.__file__.rstrip("oc")
28-
).replace("$py.class", ".py")
26+
PYTEST_FULLPATH = os.path.abspath(pytest.__file__.rstrip("oc")).replace("$py.class", ".py")
2927

3028

3129
def pytest_addoption(parser):

0 commit comments

Comments
 (0)