Merge pull request #7172 from bluetech/micro-optimizations-1

Some easy micro optimizations to collection
This commit is contained in:
Ran Benita 2020-05-06 18:13:44 +03:00 committed by GitHub
commit 8c2c297e1e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 46 additions and 19 deletions

View File

@ -6,7 +6,6 @@ ignores the external pytest-cache
"""
import json
import os
from collections import OrderedDict
from typing import Dict
from typing import Generator
from typing import List
@ -23,6 +22,7 @@ from .pathlib import rm_rf
from .reports import CollectReport
from _pytest import nodes
from _pytest._io import TerminalWriter
from _pytest.compat import order_preserving_dict
from _pytest.config import Config
from _pytest.main import Session
from _pytest.python import Module
@ -338,8 +338,8 @@ class NFPlugin:
self, session: Session, config: Config, items: List[nodes.Item]
) -> None:
if self.active:
new_items = OrderedDict() # type: OrderedDict[str, nodes.Item]
other_items = OrderedDict() # type: OrderedDict[str, nodes.Item]
new_items = order_preserving_dict() # type: Dict[str, nodes.Item]
other_items = order_preserving_dict() # type: Dict[str, nodes.Item]
for item in items:
if item.nodeid not in self.cached_nodeids:
new_items[item.nodeid] = item

View File

@ -381,3 +381,15 @@ else:
return self
value = instance.__dict__[self.func.__name__] = self.func(instance)
return value
# Sometimes an algorithm needs a dict which yields items in the order in which
# they were inserted when iterated. Since Python 3.7, `dict` preserves
# insertion order. Since `dict` is faster and uses less memory than
# `OrderedDict`, prefer to use it if possible.
if sys.version_info >= (3, 7):
order_preserving_dict = dict
else:
from collections import OrderedDict
order_preserving_dict = OrderedDict

View File

@ -4,7 +4,6 @@ import sys
import warnings
from collections import defaultdict
from collections import deque
from collections import OrderedDict
from typing import Dict
from typing import List
from typing import Tuple
@ -26,6 +25,7 @@ from _pytest.compat import getimfunc
from _pytest.compat import getlocation
from _pytest.compat import is_generator
from _pytest.compat import NOTSET
from _pytest.compat import order_preserving_dict
from _pytest.compat import safe_getattr
from _pytest.compat import TYPE_CHECKING
from _pytest.deprecated import FILLFUNCARGS
@ -220,12 +220,14 @@ def reorder_items(items):
argkeys_cache[scopenum] = d = {}
items_by_argkey[scopenum] = item_d = defaultdict(deque)
for item in items:
keys = OrderedDict.fromkeys(get_parametrized_fixture_keys(item, scopenum))
keys = order_preserving_dict.fromkeys(
get_parametrized_fixture_keys(item, scopenum)
)
if keys:
d[item] = keys
for key in keys:
item_d[key].append(item)
items = OrderedDict.fromkeys(items)
items = order_preserving_dict.fromkeys(items)
return list(reorder_items_atscope(items, argkeys_cache, items_by_argkey, 0))
@ -240,17 +242,17 @@ def reorder_items_atscope(items, argkeys_cache, items_by_argkey, scopenum):
return items
ignore = set()
items_deque = deque(items)
items_done = OrderedDict()
items_done = order_preserving_dict()
scoped_items_by_argkey = items_by_argkey[scopenum]
scoped_argkeys_cache = argkeys_cache[scopenum]
while items_deque:
no_argkey_group = OrderedDict()
no_argkey_group = order_preserving_dict()
slicing_argkey = None
while items_deque:
item = items_deque.popleft()
if item in items_done or item in no_argkey_group:
continue
argkeys = OrderedDict.fromkeys(
argkeys = order_preserving_dict.fromkeys(
k for k in scoped_argkeys_cache.get(item, []) if k not in ignore
)
if not argkeys:

View File

@ -91,6 +91,19 @@ class Node(metaclass=NodeMeta):
""" base class for Collector and Item the test collection tree.
Collector subclasses have children, Items are terminal nodes."""
# Use __slots__ to make attribute access faster.
# Note that __dict__ is still available.
__slots__ = (
"name",
"parent",
"config",
"session",
"fspath",
"_nodeid",
"_store",
"__dict__",
)
def __init__(
self,
name: str,
@ -216,7 +229,7 @@ class Node(metaclass=NodeMeta):
return self._nodeid
def __hash__(self):
return hash(self.nodeid)
return hash(self._nodeid)
def setup(self):
pass

View File

@ -365,15 +365,17 @@ class PyCollector(PyobjMixin, nodes.Collector):
# NB. we avoid random getattrs and peek in the __dict__ instead
# (XXX originally introduced from a PyPy need, still true?)
dicts = [getattr(self.obj, "__dict__", {})]
for basecls in inspect.getmro(self.obj.__class__):
for basecls in self.obj.__class__.__mro__:
dicts.append(basecls.__dict__)
seen = {}
seen = set()
values = []
for dic in dicts:
# Note: seems like the dict can change during iteration -
# be careful not to remove the list() without consideration.
for name, obj in list(dic.items()):
if name in seen:
continue
seen[name] = True
seen.add(name)
res = self._makeitem(name, obj)
if res is None:
continue

View File

@ -3,7 +3,6 @@
This is a good source for looking at the various reporting hooks.
"""
import argparse
import collections
import datetime
import inspect
import platform
@ -28,6 +27,7 @@ from more_itertools import collapse
import pytest
from _pytest import nodes
from _pytest._io import TerminalWriter
from _pytest.compat import order_preserving_dict
from _pytest.config import Config
from _pytest.config import ExitCode
from _pytest.deprecated import TERMINALWRITER_WRITER
@ -839,8 +839,8 @@ class TerminalReporter:
return
reports_grouped_by_message = (
collections.OrderedDict()
) # type: collections.OrderedDict[str, List[WarningReport]]
order_preserving_dict()
) # type: Dict[str, List[WarningReport]]
for wr in warning_reports:
reports_grouped_by_message.setdefault(wr.message, []).append(wr)
@ -854,9 +854,7 @@ class TerminalReporter:
if len(locations) < 10:
return "\n".join(map(str, locations))
counts_by_filename = (
collections.OrderedDict()
) # type: collections.OrderedDict[str, int]
counts_by_filename = order_preserving_dict() # type: Dict[str, int]
for loc in locations:
key = str(loc).split("::", 1)[0]
counts_by_filename[key] = counts_by_filename.get(key, 0) + 1