introduce attrs as dependency and use it

for FixtureFunctionMarker and marks
This commit is contained in:
Ronny Pfannschmidt 2017-08-01 11:52:09 +02:00
parent cb30848e5a
commit 07b2b18a01
3 changed files with 51 additions and 29 deletions

View File

@ -7,6 +7,7 @@ import warnings
import py import py
from py._code.code import FormattedExcinfo from py._code.code import FormattedExcinfo
import attr
import _pytest import _pytest
from _pytest import nodes from _pytest import nodes
from _pytest._code.code import TerminalRepr from _pytest._code.code import TerminalRepr
@ -822,13 +823,21 @@ def pytest_fixture_setup(fixturedef, request):
return result return result
class FixtureFunctionMarker: def _ensure_immutable_ids(ids):
def __init__(self, scope, params, autouse=False, ids=None, name=None): if ids is None:
self.scope = scope return
self.params = params if callable(ids):
self.autouse = autouse return ids
self.ids = ids return tuple(ids)
self.name = name
@attr.s(frozen=True)
class FixtureFunctionMarker(object):
scope = attr.ib()
params = attr.ib(convert=attr.converters.optional(tuple))
autouse = attr.ib(default=False)
ids = attr.ib(default=None, convert=_ensure_immutable_ids)
name = attr.ib(default=None)
def __call__(self, function): def __call__(self, function):
if isclass(function): if isclass(function):

View File

@ -3,6 +3,7 @@ from __future__ import absolute_import, division, print_function
import inspect import inspect
import warnings import warnings
import attr
from collections import namedtuple from collections import namedtuple
from operator import attrgetter from operator import attrgetter
from six.moves import map from six.moves import map
@ -160,22 +161,26 @@ def pytest_collection_modifyitems(items, config):
items[:] = remaining items[:] = remaining
class MarkMapping: @attr.s
class MarkMapping(object):
"""Provides a local mapping for markers where item access """Provides a local mapping for markers where item access
resolves to True if the marker is present. """ resolves to True if the marker is present. """
def __init__(self, keywords): own_mark_names = attr.ib()
mymarks = set()
@classmethod
def from_keywords(cls, keywords):
mark_names = set()
for key, value in keywords.items(): for key, value in keywords.items():
if isinstance(value, MarkInfo) or isinstance(value, MarkDecorator): if isinstance(value, MarkInfo) or isinstance(value, MarkDecorator):
mymarks.add(key) mark_names.add(key)
self._mymarks = mymarks return cls(mark_names)
def __getitem__(self, name): def __getitem__(self, name):
return name in self._mymarks return name in self.own_mark_names
class KeywordMapping: class KeywordMapping(object):
"""Provides a local mapping for keywords. """Provides a local mapping for keywords.
Given a list of names, map any substring of one of these names to True. Given a list of names, map any substring of one of these names to True.
""" """
@ -192,7 +197,7 @@ class KeywordMapping:
def matchmark(colitem, markexpr): def matchmark(colitem, markexpr):
"""Tries to match on any marker names, attached to the given colitem.""" """Tries to match on any marker names, attached to the given colitem."""
return eval(markexpr, {}, MarkMapping(colitem.keywords)) return eval(markexpr, {}, MarkMapping.from_keywords(colitem.keywords))
def matchkeyword(colitem, keywordexpr): def matchkeyword(colitem, keywordexpr):
@ -280,7 +285,21 @@ def istestfunc(func):
getattr(func, "__name__", "<lambda>") != "<lambda>" getattr(func, "__name__", "<lambda>") != "<lambda>"
class MarkDecorator: @attr.s(frozen=True)
class Mark(object):
name = attr.ib()
args = attr.ib()
kwargs = attr.ib()
def combined_with(self, other):
assert self.name == other.name
return Mark(
self.name, self.args + other.args,
dict(self.kwargs, **other.kwargs))
@attr.s
class MarkDecorator(object):
""" A decorator for test functions and test classes. When applied """ A decorator for test functions and test classes. When applied
it will create :class:`MarkInfo` objects which may be it will create :class:`MarkInfo` objects which may be
:ref:`retrieved by hooks as item keywords <excontrolskip>`. :ref:`retrieved by hooks as item keywords <excontrolskip>`.
@ -314,9 +333,7 @@ class MarkDecorator:
""" """
def __init__(self, mark): mark = attr.ib(validator=attr.validators.instance_of(Mark))
assert isinstance(mark, Mark), repr(mark)
self.mark = mark
name = alias('mark.name') name = alias('mark.name')
args = alias('mark.args') args = alias('mark.args')
@ -396,15 +413,6 @@ def store_legacy_markinfo(func, mark):
holder.add_mark(mark) holder.add_mark(mark)
class Mark(namedtuple('Mark', 'name, args, kwargs')):
def combined_with(self, other):
assert self.name == other.name
return Mark(
self.name, self.args + other.args,
dict(self.kwargs, **other.kwargs))
class MarkInfo(object): class MarkInfo(object):
""" Marking object created by :class:`MarkDecorator` instances. """ """ Marking object created by :class:`MarkDecorator` instances. """

View File

@ -43,8 +43,13 @@ def has_environment_marker_support():
def main(): def main():
install_requires = ['py>=1.4.34', 'six>=1.10.0', 'setuptools']
extras_require = {} extras_require = {}
install_requires = [
'py>=1.4.33',
'six>=1.10.0',
'setuptools',
'attrs>=17.2.0',
]
# if _PYTEST_SETUP_SKIP_PLUGGY_DEP is set, skip installing pluggy; # if _PYTEST_SETUP_SKIP_PLUGGY_DEP is set, skip installing pluggy;
# used by tox.ini to test with pluggy master # used by tox.ini to test with pluggy master
if '_PYTEST_SETUP_SKIP_PLUGGY_DEP' not in os.environ: if '_PYTEST_SETUP_SKIP_PLUGGY_DEP' not in os.environ: