Merge pull request #2641 from RonnyPfannschmidt/introduce-attrs

[RFC] Introduce attrs
This commit is contained in:
Bruno Oliveira 2017-11-06 12:56:57 -02:00 committed by GitHub
commit c33074c8b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 52 additions and 29 deletions

View File

@ -7,6 +7,7 @@ import warnings
import py
from py._code.code import FormattedExcinfo
import attr
import _pytest
from _pytest import nodes
from _pytest._code.code import TerminalRepr
@ -822,13 +823,21 @@ def pytest_fixture_setup(fixturedef, request):
return result
class FixtureFunctionMarker:
def __init__(self, scope, params, autouse=False, ids=None, name=None):
self.scope = scope
self.params = params
self.autouse = autouse
self.ids = ids
self.name = name
def _ensure_immutable_ids(ids):
if ids is None:
return
if callable(ids):
return ids
return tuple(ids)
@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):
if isclass(function):

View File

@ -3,6 +3,7 @@ from __future__ import absolute_import, division, print_function
import inspect
import warnings
import attr
from collections import namedtuple
from operator import attrgetter
from six.moves import map
@ -185,22 +186,26 @@ def pytest_collection_modifyitems(items, config):
items[:] = remaining
class MarkMapping:
@attr.s
class MarkMapping(object):
"""Provides a local mapping for markers where item access
resolves to True if the marker is present. """
def __init__(self, keywords):
mymarks = set()
own_mark_names = attr.ib()
@classmethod
def from_keywords(cls, keywords):
mark_names = set()
for key, value in keywords.items():
if isinstance(value, MarkInfo) or isinstance(value, MarkDecorator):
mymarks.add(key)
self._mymarks = mymarks
mark_names.add(key)
return cls(mark_names)
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.
Given a list of names, map any substring of one of these names to True.
"""
@ -217,7 +222,7 @@ class KeywordMapping:
def matchmark(colitem, markexpr):
"""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):
@ -306,7 +311,21 @@ def istestfunc(func):
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
it will create :class:`MarkInfo` objects which may be
:ref:`retrieved by hooks as item keywords <excontrolskip>`.
@ -340,9 +359,7 @@ class MarkDecorator:
"""
def __init__(self, mark):
assert isinstance(mark, Mark), repr(mark)
self.mark = mark
mark = attr.ib(validator=attr.validators.instance_of(Mark))
name = alias('mark.name')
args = alias('mark.args')
@ -422,15 +439,6 @@ def store_legacy_markinfo(func, 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):
""" Marking object created by :class:`MarkDecorator` instances. """

1
changelog/2641.trivial Normal file
View File

@ -0,0 +1 @@
pytest now depends on `attrs <https://pypi.org/project/attrs/>`_ for internal structures to ease code maintainability.

View File

@ -43,8 +43,13 @@ def has_environment_marker_support():
def main():
install_requires = ['py>=1.4.34', 'six>=1.10.0', 'setuptools']
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;
# used by tox.ini to test with pluggy master
if '_PYTEST_SETUP_SKIP_PLUGGY_DEP' not in os.environ: