diff --git a/_pytest/main.py b/_pytest/main.py index a039cf221..9ef47faf9 100644 --- a/_pytest/main.py +++ b/_pytest/main.py @@ -217,7 +217,7 @@ class Node(object): self.keywords = NodeKeywords(self) #: allow adding of extra keywords to use for matching - self.extra_keyword_matches = [] + self.extra_keyword_matches = set() #self.extrainit() @@ -311,12 +311,11 @@ class Node(object): return chain def listextrakeywords(self): - """ Return a list of all extra keywords in self and any parents.""" - extra_keywords = [] + """ Return a set of all extra keywords in self and any parents.""" + extra_keywords = set() item = self - while item is not None: - extra_keywords.extend(item.extra_keyword_matches) - item = item.parent + for item in self.listchain(): + extra_keywords.update(item.extra_keyword_matches) return extra_keywords def listnames(self): diff --git a/_pytest/mark.py b/_pytest/mark.py index 67ff92817..eda74bff5 100644 --- a/_pytest/mark.py +++ b/_pytest/mark.py @@ -18,7 +18,7 @@ def pytest_addoption(parser): "other' matches all test functions and classes whose name " "contains 'test_method' or 'test_other'. " "Additionally keywords are matched to classes and functions " - "containing extra names in their 'extra_keyword_matches' list, " + "containing extra names in their 'extra_keyword_matches' set, " "as well as functions which have names assigned directly to them." ) @@ -87,10 +87,10 @@ class MarkMapping: :class:`MarkDecorator` items. """ def __init__(self, keywords): - mymarks = [] + mymarks = set() for key, value in keywords.items(): if isinstance(value, MarkInfo) or isinstance(value, MarkDecorator): - mymarks.append(key) + mymarks.add(key) self._mymarks = mymarks def __getitem__(self, markname): @@ -122,24 +122,24 @@ def matchkeyword(colitem, keywordexpr): Will match on the name of colitem, including the names of its parents. Only matches names of items which are either a :class:`Class` or a :class:`Function`. - Additionally, matches on names in the 'extra_keyword_matches' list of + Additionally, matches on names in the 'extra_keyword_matches' set of any item, as well as names directly assigned to test functions. """ keywordexpr = keywordexpr.replace("-", "not ") - mapped_names = [] + mapped_names = set() # Add the names of the current item and any parent items for item in colitem.listchain(): if not isinstance(item, pytest.Instance): - mapped_names.append(item.name) + mapped_names.add(item.name) # Add the names added as extra keywords to current or parent items for name in colitem.listextrakeywords(): - mapped_names.append(name) + mapped_names.add(name) # Add the names attached to the current function through direct assignment for name in colitem.function.func_dict: - mapped_names.append(name) + mapped_names.add(name) return eval(keywordexpr, {}, KeywordMapping(mapped_names)) diff --git a/testing/test_mark.py b/testing/test_mark.py index cc5b3290e..3caf625b2 100644 --- a/testing/test_mark.py +++ b/testing/test_mark.py @@ -418,7 +418,7 @@ class TestKeywordSelection: def pytest_pycollect_makeitem(__multicall__, name): if name == "TestClass": item = __multicall__.execute() - item.extra_keyword_matches.append("xxx") + item.extra_keyword_matches.add("xxx") return item """) reprec = testdir.inline_run(p.dirpath(), '-s', '-k', keyword)