From a5a76be925835688bfdca5e40e291e905a266df4 Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Tue, 2 Nov 2021 10:05:31 +0000 Subject: [PATCH] Use typecheck workaround for custom list/dicts As reported in python/mypy#11427, a "white lie" in typeshed messes completely the type inference for classes inheriting from ``(MutableMapping, dict)`` and ``(MutableSequence, list)``. This is a workaround that results in useful type inference. --- atoml/container.py | 7 +++---- atoml/items.py | 32 +++++++++++++++++++++++++++----- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/atoml/container.py b/atoml/container.py index 00c97a5..91e7c47 100644 --- a/atoml/container.py +++ b/atoml/container.py @@ -1,19 +1,18 @@ import copy -from collections.abc import MutableMapping from typing import Any, Dict, Iterator, List, Optional, Tuple, Union from ._compat import decode from ._utils import merge_dicts from .exceptions import ATOMLError, KeyAlreadyPresent, NonExistentKey -from .items import AoT, Comment, Item, Key, Null, Table, Whitespace +from .items import AoT, Comment, Item, Key, Null, Table, Whitespace, _CustomDict from .items import item as _item _NOT_SET = object() -class Container(MutableMapping, dict): +class Container(_CustomDict): """ A container for items within a TOMLDocument. """ @@ -675,7 +674,7 @@ def _previous_item( return prev[-1] if prev else None -class OutOfOrderTableProxy(MutableMapping, dict): +class OutOfOrderTableProxy(_CustomDict): def __init__(self, container: Container, indices: Tuple[int]) -> None: self._container = container self._internal_container = Container(True) diff --git a/atoml/items.py b/atoml/items.py index 69e1089..f382708 100644 --- a/atoml/items.py +++ b/atoml/items.py @@ -1,7 +1,6 @@ import re import string -from collections.abc import MutableMapping, MutableSequence from datetime import date, datetime, time, tzinfo from enum import Enum from functools import lru_cache @@ -22,8 +21,31 @@ from .toml_char import TOMLChar -if TYPE_CHECKING: +if TYPE_CHECKING: # pragma: no cover + # Define _CustomList and _CustomDict as a workaround for: + # https://github.com/python/mypy/issues/11427 + # + # According to this issue, the typeshed contains a "white lie" + # (it adds MutableSequence to the ancestry of list and MutableMapping to + # the ancestry of dict) which completely messes with the type inference for + # Table, InlineTable, Array and Container. + # + # Importing from builtins is preferred over simple assignment, see issues: + # https://github.com/python/mypy/issues/8715 + # https://github.com/python/mypy/issues/10068 + from builtins import dict as _CustomDict + from builtins import list as _CustomList + + # Allow type annotations but break circular imports from . import container +else: + from collections.abc import MutableMapping, MutableSequence + + class _CustomList(MutableSequence, list): + """Adds MutableSequence mixin while pretending to be a builtin list""" + + class _CustomDict(MutableMapping, dict): + """Adds MutableMapping mixin while pretending to be a builtin dict""" def item(value, _parent=None, _sort_keys=False): @@ -792,7 +814,7 @@ def _getstate(self, protocol: int = 3) -> tuple: ) -class Array(Item, MutableSequence, list): +class Array(Item, _CustomList): """ An array literal """ @@ -1006,7 +1028,7 @@ def _getstate(self, protocol=3): return self._value, self._trivia -class Table(Item, MutableMapping, dict): +class Table(Item, _CustomDict): """ A table literal. """ @@ -1197,7 +1219,7 @@ def _getstate(self, protocol: int = 3) -> tuple: ) -class InlineTable(Item, MutableMapping, dict): +class InlineTable(Item, _CustomDict): """ An inline table literal. """