Skip to content
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

[RFC] Introduce attrs #2641

Merged
merged 3 commits into from Nov 6, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
23 changes: 16 additions & 7 deletions _pytest/fixtures.py
Expand Up @@ -7,6 +7,7 @@
import py
from py._code.code import FormattedExcinfo

import attr
import _pytest
from _pytest import nodes
from _pytest._code.code import TerminalRepr
Expand Down Expand Up @@ -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):
Expand Down
50 changes: 29 additions & 21 deletions _pytest/mark.py
Expand Up @@ -3,6 +3,7 @@

import inspect
import warnings
import attr
from collections import namedtuple
from operator import attrgetter
from six.moves import map
Expand Down Expand Up @@ -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.
"""
Expand All @@ -217,7 +222,7 @@ def __getitem__(self, subname):

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):
Expand Down Expand Up @@ -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>`.
Expand Down Expand Up @@ -340,9 +359,7 @@ def test_function():

"""

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')
Expand Down Expand Up @@ -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. """

Expand Down
1 change: 1 addition & 0 deletions changelog/2641.trivial
@@ -0,0 +1 @@
pytest now depends on `attrs <https://pypi.org/project/attrs/>`_ for internal structures to ease code maintainability.
7 changes: 6 additions & 1 deletion setup.py
Expand Up @@ -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:
Expand Down