Merge pull request #9171 from bluetech/optimize-keywords-init
Optimizations/fixes around Function `keywords`
This commit is contained in:
commit
888026f7a6
|
@ -303,6 +303,9 @@ class PyobjMixin(nodes.Node):
|
||||||
# used to avoid Function marker duplication
|
# used to avoid Function marker duplication
|
||||||
if self._ALLOW_MARKERS:
|
if self._ALLOW_MARKERS:
|
||||||
self.own_markers.extend(get_unpacked_marks(self.obj))
|
self.own_markers.extend(get_unpacked_marks(self.obj))
|
||||||
|
# This assumes that `obj` is called before there is a chance
|
||||||
|
# to add custom keys to `self.keywords`, so no fear of overriding.
|
||||||
|
self.keywords.update((mark.name, mark) for mark in self.own_markers)
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
@obj.setter
|
@obj.setter
|
||||||
|
@ -1634,7 +1637,7 @@ class Function(PyobjMixin, nodes.Item):
|
||||||
config: Optional[Config] = None,
|
config: Optional[Config] = None,
|
||||||
callspec: Optional[CallSpec2] = None,
|
callspec: Optional[CallSpec2] = None,
|
||||||
callobj=NOTSET,
|
callobj=NOTSET,
|
||||||
keywords=None,
|
keywords: Optional[Mapping[str, Any]] = None,
|
||||||
session: Optional[Session] = None,
|
session: Optional[Session] = None,
|
||||||
fixtureinfo: Optional[FuncFixtureInfo] = None,
|
fixtureinfo: Optional[FuncFixtureInfo] = None,
|
||||||
originalname: Optional[str] = None,
|
originalname: Optional[str] = None,
|
||||||
|
@ -1655,31 +1658,20 @@ class Function(PyobjMixin, nodes.Item):
|
||||||
# Note: when FunctionDefinition is introduced, we should change ``originalname``
|
# Note: when FunctionDefinition is introduced, we should change ``originalname``
|
||||||
# to a readonly property that returns FunctionDefinition.name.
|
# to a readonly property that returns FunctionDefinition.name.
|
||||||
|
|
||||||
self.keywords.update(self.obj.__dict__)
|
|
||||||
self.own_markers.extend(get_unpacked_marks(self.obj))
|
self.own_markers.extend(get_unpacked_marks(self.obj))
|
||||||
if callspec:
|
if callspec:
|
||||||
self.callspec = callspec
|
self.callspec = callspec
|
||||||
# this is total hostile and a mess
|
self.own_markers.extend(callspec.marks)
|
||||||
# keywords are broken by design by now
|
|
||||||
# this will be redeemed later
|
|
||||||
for mark in callspec.marks:
|
|
||||||
# feel free to cry, this was broken for years before
|
|
||||||
# and keywords can't fix it per design
|
|
||||||
self.keywords[mark.name] = mark
|
|
||||||
self.own_markers.extend(normalize_mark_list(callspec.marks))
|
|
||||||
if keywords:
|
|
||||||
self.keywords.update(keywords)
|
|
||||||
|
|
||||||
# todo: this is a hell of a hack
|
# todo: this is a hell of a hack
|
||||||
# https://github.com/pytest-dev/pytest/issues/4569
|
# https://github.com/pytest-dev/pytest/issues/4569
|
||||||
|
# Note: the order of the updates is important here; indicates what
|
||||||
self.keywords.update(
|
# takes priority (ctor argument over function attributes over markers).
|
||||||
{
|
# Take own_markers only; NodeKeywords handles parent traversal on its own.
|
||||||
mark.name: True
|
self.keywords.update((mark.name, mark) for mark in self.own_markers)
|
||||||
for mark in self.iter_markers()
|
self.keywords.update(self.obj.__dict__)
|
||||||
if mark.name not in self.keywords
|
if keywords:
|
||||||
}
|
self.keywords.update(keywords)
|
||||||
)
|
|
||||||
|
|
||||||
if fixtureinfo is None:
|
if fixtureinfo is None:
|
||||||
fixtureinfo = self.session._fixturemanager.getfixtureinfo(
|
fixtureinfo = self.session._fixturemanager.getfixtureinfo(
|
||||||
|
|
|
@ -7,6 +7,7 @@ from typing import Dict
|
||||||
from typing import Iterable
|
from typing import Iterable
|
||||||
from typing import Iterator
|
from typing import Iterator
|
||||||
from typing import List
|
from typing import List
|
||||||
|
from typing import Mapping
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from typing import Tuple
|
from typing import Tuple
|
||||||
from typing import Type
|
from typing import Type
|
||||||
|
@ -254,7 +255,7 @@ class TestReport(BaseReport):
|
||||||
self,
|
self,
|
||||||
nodeid: str,
|
nodeid: str,
|
||||||
location: Tuple[str, Optional[int], str],
|
location: Tuple[str, Optional[int], str],
|
||||||
keywords,
|
keywords: Mapping[str, Any],
|
||||||
outcome: "Literal['passed', 'failed', 'skipped']",
|
outcome: "Literal['passed', 'failed', 'skipped']",
|
||||||
longrepr: Union[
|
longrepr: Union[
|
||||||
None, ExceptionInfo[BaseException], Tuple[str, int, str], str, TerminalRepr
|
None, ExceptionInfo[BaseException], Tuple[str, int, str], str, TerminalRepr
|
||||||
|
|
|
@ -881,6 +881,36 @@ class TestNodeKeywords:
|
||||||
assert item.keywords["kw"] == "method"
|
assert item.keywords["kw"] == "method"
|
||||||
assert len(item.keywords) == len(set(item.keywords))
|
assert len(item.keywords) == len(set(item.keywords))
|
||||||
|
|
||||||
|
def test_unpacked_marks_added_to_keywords(self, pytester: Pytester) -> None:
|
||||||
|
item = pytester.getitem(
|
||||||
|
"""
|
||||||
|
import pytest
|
||||||
|
pytestmark = pytest.mark.foo
|
||||||
|
class TestClass:
|
||||||
|
pytestmark = pytest.mark.bar
|
||||||
|
def test_method(self): pass
|
||||||
|
test_method.pytestmark = pytest.mark.baz
|
||||||
|
""",
|
||||||
|
"test_method",
|
||||||
|
)
|
||||||
|
assert isinstance(item, pytest.Function)
|
||||||
|
cls = item.getparent(pytest.Class)
|
||||||
|
assert cls is not None
|
||||||
|
mod = item.getparent(pytest.Module)
|
||||||
|
assert mod is not None
|
||||||
|
|
||||||
|
assert item.keywords["foo"] == pytest.mark.foo.mark
|
||||||
|
assert item.keywords["bar"] == pytest.mark.bar.mark
|
||||||
|
assert item.keywords["baz"] == pytest.mark.baz.mark
|
||||||
|
|
||||||
|
assert cls.keywords["foo"] == pytest.mark.foo.mark
|
||||||
|
assert cls.keywords["bar"] == pytest.mark.bar.mark
|
||||||
|
assert "baz" not in cls.keywords
|
||||||
|
|
||||||
|
assert mod.keywords["foo"] == pytest.mark.foo.mark
|
||||||
|
assert "bar" not in mod.keywords
|
||||||
|
assert "baz" not in mod.keywords
|
||||||
|
|
||||||
|
|
||||||
COLLECTION_ERROR_PY_FILES = dict(
|
COLLECTION_ERROR_PY_FILES = dict(
|
||||||
test_01_failure="""
|
test_01_failure="""
|
||||||
|
|
Loading…
Reference in New Issue