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

Performance fixes #4237

Merged
merged 4 commits into from
Nov 3, 2018
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 18 additions & 12 deletions src/_pytest/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from _pytest.config import hookimpl
from _pytest.config import UsageError
from _pytest.outcomes import exit
from _pytest.pathlib import parts
from _pytest.runner import collect_one_node


Expand Down Expand Up @@ -469,8 +470,8 @@ def _perform_collect(self, args, genitems):
return items

def collect(self):
for parts in self._initialparts:
arg = "::".join(map(str, parts))
for initialpart in self._initialparts:
arg = "::".join(map(str, initialpart))
self.trace("processing argument", arg)
self.trace.root.indent += 1
try:
Expand All @@ -488,7 +489,7 @@ def _collect(self, arg):

names = self._parsearg(arg)
argpath = names.pop(0).realpath()
paths = []
paths = set()

root = self
# Start with a Session root, and delve to argpath item (dir or file)
Expand Down Expand Up @@ -528,21 +529,26 @@ def filter_(f):
def filter_(f):
return f.check(file=1)

seen_dirs = set()
for path in argpath.visit(
fil=filter_, rec=self._recurse, bf=True, sort=True
):
pkginit = path.dirpath().join("__init__.py")
if pkginit.exists() and not any(x in pkginit.parts() for x in paths):
for x in root._collectfile(pkginit):
yield x
paths.append(x.fspath.dirpath())
dirpath = path.dirpath()
if dirpath not in seen_dirs:
seen_dirs.add(dirpath)
pkginit = dirpath.join("__init__.py")
if pkginit.exists() and parts(pkginit.strpath).isdisjoint(paths):
for x in root._collectfile(pkginit):
yield x
paths.add(x.fspath.dirpath())

if not any(x in path.parts() for x in paths):
if parts(path.strpath).isdisjoint(paths):
for x in root._collectfile(path):
if (type(x), x.fspath) in self._node_cache:
yield self._node_cache[(type(x), x.fspath)]
key = (type(x), x.fspath)
if key in self._node_cache:
yield self._node_cache[key]
else:
self._node_cache[(type(x), x.fspath)] = x
self._node_cache[key] = x
yield x
else:
assert argpath.check(file=1)
Expand Down
5 changes: 5 additions & 0 deletions src/_pytest/pathlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,3 +303,8 @@ def fnmatch_ex(pattern, path):
else:
name = six.text_type(path)
return fnmatch.fnmatch(name, pattern)


def parts(s):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please take a look if a Path.parents or Path.parts works out here as well as this looks like a nice potential match

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using p = Path(s); list(p.parents) + [p] makes it slower.
This would only make sense if it would be a Path already I think.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No surprise there really. Path.parents is a pretty complex chain of stuff that is hard to follow. Would be amazing if it wasn't slower than the simple string operations in this function.

parts = s.split(sep)
return {sep.join(parts[: i + 1]) or sep for i in range(len(parts))}
5 changes: 3 additions & 2 deletions src/_pytest/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
from _pytest.mark.structures import normalize_mark_list
from _pytest.mark.structures import transfer_markers
from _pytest.outcomes import fail
from _pytest.pathlib import parts
from _pytest.warning_types import PytestWarning
from _pytest.warning_types import RemovedInPytest4Warning

Expand Down Expand Up @@ -567,9 +568,9 @@ def collect(self):
if path.basename == "__init__.py" and path.dirpath() == this_path:
continue

parts = path.parts()
parts_ = parts(path.strpath)
if any(
pkg_prefix in parts and pkg_prefix.join("__init__.py") != path
pkg_prefix in parts_ and pkg_prefix.join("__init__.py") != path
for pkg_prefix in pkg_prefixes
):
continue
Expand Down