From fcc473ab1c3891c410cc3ea362619a56731787a8 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Thu, 30 Apr 2020 15:49:20 +0300 Subject: [PATCH] Use dict instead of OrderedDict on Python 3.7 OrderedDict is quite a bit heavier than just a dict. --- src/_pytest/cacheprovider.py | 6 +++--- src/_pytest/compat.py | 12 ++++++++++++ src/_pytest/fixtures.py | 14 ++++++++------ src/_pytest/terminal.py | 10 ++++------ 4 files changed, 27 insertions(+), 15 deletions(-) diff --git a/src/_pytest/cacheprovider.py b/src/_pytest/cacheprovider.py index 133352262..a48bd9d6f 100755 --- a/src/_pytest/cacheprovider.py +++ b/src/_pytest/cacheprovider.py @@ -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 diff --git a/src/_pytest/compat.py b/src/_pytest/compat.py index ba225eb8f..6a0614f01 100644 --- a/src/_pytest/compat.py +++ b/src/_pytest/compat.py @@ -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 diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index f673885c7..b7124fa80 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -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: diff --git a/src/_pytest/terminal.py b/src/_pytest/terminal.py index 9c88ca0ca..08bc36354 100644 --- a/src/_pytest/terminal.py +++ b/src/_pytest/terminal.py @@ -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