Merge pull request #7671 from bluetech/ignored-names

RFC: python: skip work pytest_pycollect_makeitem work on certain names
This commit is contained in:
Ran Benita 2020-08-27 10:03:09 +03:00 committed by GitHub
commit 2fcf763d7e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 57 additions and 0 deletions

View File

@ -0,0 +1,6 @@
When collecting tests, pytest finds test classes and functions by examining the
attributes of python objects (modules, classes and instances). To speed up this
process, pytest now ignores builtin attributes (like ``__class__``,
``__delattr__`` and ``__new__``) without consulting the ``python_classes`` and
``python_functions`` configuration options and without passing them to plugins
using the ``pytest_pycollect_makeitem`` hook.

View File

@ -5,6 +5,7 @@ import inspect
import itertools import itertools
import os import os
import sys import sys
import types
import typing import typing
import warnings import warnings
from collections import Counter from collections import Counter
@ -343,6 +344,26 @@ class PyobjMixin:
return fspath, lineno, modpath return fspath, lineno, modpath
# As an optimization, these builtin attribute names are pre-ignored when
# iterating over an object during collection -- the pytest_pycollect_makeitem
# hook is not called for them.
# fmt: off
class _EmptyClass: pass # noqa: E701
IGNORED_ATTRIBUTES = frozenset.union( # noqa: E305
frozenset(),
# Module.
dir(types.ModuleType("empty_module")),
# Some extra module attributes the above doesn't catch.
{"__builtins__", "__file__", "__cached__"},
# Class.
dir(_EmptyClass),
# Instance.
dir(_EmptyClass()),
)
del _EmptyClass
# fmt: on
class PyCollector(PyobjMixin, nodes.Collector): class PyCollector(PyobjMixin, nodes.Collector):
def funcnamefilter(self, name: str) -> bool: def funcnamefilter(self, name: str) -> bool:
return self._matches_prefix_or_glob_option("python_functions", name) return self._matches_prefix_or_glob_option("python_functions", name)
@ -404,6 +425,8 @@ class PyCollector(PyobjMixin, nodes.Collector):
# Note: seems like the dict can change during iteration - # Note: seems like the dict can change during iteration -
# be careful not to remove the list() without consideration. # be careful not to remove the list() without consideration.
for name, obj in list(dic.items()): for name, obj in list(dic.items()):
if name in IGNORED_ATTRIBUTES:
continue
if name in seen: if name in seen:
continue continue
seen.add(name) seen.add(name)

View File

@ -885,6 +885,34 @@ class TestConftestCustomization:
result = testdir.runpytest_subprocess() result = testdir.runpytest_subprocess()
result.stdout.fnmatch_lines(["*1 passed*"]) result.stdout.fnmatch_lines(["*1 passed*"])
def test_early_ignored_attributes(self, testdir: Testdir) -> None:
"""Builtin attributes should be ignored early on, even if
configuration would otherwise allow them.
This tests a performance optimization, not correctness, really,
although it tests PytestCollectionWarning is not raised, while
it would have been raised otherwise.
"""
testdir.makeini(
"""
[pytest]
python_classes=*
python_functions=*
"""
)
testdir.makepyfile(
"""
class TestEmpty:
pass
test_empty = TestEmpty()
def test_real():
pass
"""
)
items, rec = testdir.inline_genitems()
assert rec.ret == 0
assert len(items) == 1
def test_setup_only_available_in_subdir(testdir): def test_setup_only_available_in_subdir(testdir):
sub1 = testdir.mkpydir("sub1") sub1 = testdir.mkpydir("sub1")