Skip to content

Commit

Permalink
Improve annotation. (#802)
Browse files Browse the repository at this point in the history
* Resolve `mypy` errors.

* Improve annotation.

- Add generic generic type arguments to view objects and `BaseDict`.
- Solve conflicting typing in `lib.py`.
- Adjust documentation.

---------

Co-authored-by: knutnergaard <knutnergaard@users.noreply.github.com>
  • Loading branch information
knutnergaard and knutnergaard authored Dec 4, 2024
1 parent 9e78eae commit 06a7c74
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 47 deletions.
98 changes: 53 additions & 45 deletions Lib/fontParts/base/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
TYPE_CHECKING,
Any,
Callable,
Dict,
Generic,
Iterable,
Iterator,
List,
Expand Down Expand Up @@ -36,6 +36,8 @@
from collections.abc import ItemsView

BaseObjectType = TypeVar("BaseObjectType", bound="BaseObject")
KeyType = TypeVar("KeyType")
ValueType = TypeVar("ValueType")

# -------
# Helpers
Expand Down Expand Up @@ -394,7 +396,7 @@ def naked(self) -> Any:
self.raiseNotImplementedError()


class BaseItems:
class BaseItems(Generic[KeyType, ValueType]):
"""Provide the given mapping with an items view object.
This class provides a view of the key-value pairs in a mapping, similar to
Expand All @@ -410,10 +412,10 @@ class BaseItems:
"""

def __init__(self, mapping: BaseDict) -> None:
def __init__(self, mapping: BaseDict[KeyType, ValueType]) -> None:
self._mapping = mapping

def __contains__(self, item: Tuple[str, Any]) -> bool:
def __contains__(self, item: Tuple[KeyType, ValueType]) -> bool:
"""Check if a key-value pair exists in the mapping.
:param item: The key-value pair to check for existence as a :class:`tuple`.
Expand All @@ -427,7 +429,7 @@ def __contains__(self, item: Tuple[str, Any]) -> bool:
)
return normalizedItem in self._mapping._normalizeItems()

def __iter__(self) -> Iterator[Tuple[str, Any]]:
def __iter__(self) -> Iterator[Tuple[KeyType, ValueType]]:
"""Return an iterator over the key-value pairs in the mapping.
This method yields each item one by one, removing it from the list of
Expand Down Expand Up @@ -455,7 +457,7 @@ def __repr__(self) -> str:
return f"{self._mapping.__class__.__name__}_items({list(self)})"


class BaseKeys:
class BaseKeys(Generic[KeyType]):
"""Provide the given mapping with a keys view object.
This class provides a view of the keys in a mapping, similar to the behavior
Expand All @@ -470,10 +472,10 @@ class BaseKeys:
"""

def __init__(self, mapping: BaseDict) -> None:
def __init__(self, mapping: BaseDict[KeyType, Any]) -> None:
self._mapping = mapping

def __contains__(self, key: Any) -> bool:
def __contains__(self, key: KeyType) -> bool:
"""Check if a key exists in the mapping.
:param key: The key to check for existence.
Expand All @@ -482,7 +484,7 @@ def __contains__(self, key: Any) -> bool:
"""
return any(k == key for k, _ in self._mapping._normalizeItems())

def __iter__(self) -> Iterator[Any]:
def __iter__(self) -> Iterator[KeyType]:
"""Return an iterator over the keys in the mapping.
This method yields each key one by one, removing it from the list of
Expand All @@ -509,7 +511,7 @@ def __repr__(self) -> str:
"""
return f"{self._mapping.__class__.__name__}_keys({list(self)})"

def isdisjoint(self, other: Iterable[Any]) -> bool:
def isdisjoint(self, other: Iterable[KeyType]) -> bool:
"""Check if the keys view has no common elements with another iterable.
:param other: The iterable to compare against.
Expand All @@ -519,7 +521,7 @@ def isdisjoint(self, other: Iterable[Any]) -> bool:
return set(self).isdisjoint(other)


class BaseValues:
class BaseValues(Generic[ValueType]):
"""Provide the given mapping with a values view object.
This class provides a view of the values in a mapping, similar to the behavior
Expand All @@ -534,10 +536,10 @@ class BaseValues:
"""

def __init__(self, mapping: BaseDict) -> None:
def __init__(self, mapping: BaseDict[Any, ValueType]) -> None:
self._mapping = mapping

def __contains__(self, value: Any) -> bool:
def __contains__(self, value: ValueType) -> bool:
"""Check if a value exists in the mapping.
:param value: The value to check for existence.
Expand All @@ -546,7 +548,7 @@ def __contains__(self, value: Any) -> bool:
"""
return any(v == value for _, v in self._mapping._normalizeItems())

def __iter__(self) -> Iterator[Any]:
def __iter__(self) -> Iterator[ValueType]:
"""Return an iterator over the values in the mapping.
This method yields each value one by one, removing it from the list of
Expand Down Expand Up @@ -574,26 +576,26 @@ def __repr__(self) -> str:
return f"{self._mapping.__class__.__name__}_values({list(self)})"


class BaseDict(BaseObject):
class BaseDict(BaseObject, Generic[KeyType, ValueType]):
"""Provide objects with basic dictionary-like functionality.
:cvar keyNormalizer: An optional normalizer function for keys.
:cvar valueNormalizer: An optional normalizer function for values.
"""

keyNormalizer: Optional[Callable[[str], str]] = None
valueNormalizer: Optional[Callable[[Any], Any]] = None
keyNormalizer: Optional[Callable[[KeyType], KeyType]] = None
valueNormalizer: Optional[Callable[[ValueType], ValueType]] = None

def _normalizeKey(self, key: str) -> str:
def _normalizeKey(self, key: KeyType) -> KeyType:
keyNormalizer = type(self).keyNormalizer
return keyNormalizer(key) if keyNormalizer is not None else key

def _normalizeValue(self, value: Any) -> Any:
def _normalizeValue(self, value: ValueType) -> ValueType:
valueNormalizer = type(self).valueNormalizer
return valueNormalizer(value) if valueNormalizer is not None else value

def _normalizeItems(self) -> List[Tuple[str, Any]]:
def _normalizeItems(self) -> List[Tuple[KeyType, ValueType]]:
items = self._items()
return [(self._normalizeKey(k), self._normalizeValue(v)) for (k, v) in items]

Expand Down Expand Up @@ -635,20 +637,20 @@ def _len(self) -> int:
"""
return len(self.keys())

def keys(self) -> BaseKeys:
def keys(self) -> BaseKeys[KeyType]:
"""Return a view of the keys in the object.
:return: A :class:`BaseKeys` object instance.
:return: A :class:`BaseKeys` object instance of :class:`str` items.
"""
return self._keys()

def _keys(self) -> BaseKeys:
def _keys(self) -> BaseKeys[KeyType]:
"""Return a view of the keys in the native object.
This is the environment implementation of :meth:`BaseDict.keys`.
:return: A :class:`BaseKeys` object instance. If
:return: A :class:`BaseKeys` object instance of :class:`str` items. If
a :cvar:`BaseDict.keyNormalizer` is set, it will be applied to each
key in the returned view.
Expand All @@ -667,7 +669,7 @@ def items(self) -> BaseItems:
"""
return BaseItems(self)

def _items(self) -> ItemsView:
def _items(self) -> ItemsView[KeyType, ValueType]:
"""Return a view of the key-value pairs in the native object.
This is the environment implementation of :meth:`BaseDict.items`.
Expand All @@ -685,15 +687,15 @@ def _items(self) -> ItemsView:
"""
self.raiseNotImplementedError()

def values(self) -> BaseValues:
def values(self) -> BaseValues[ValueType]:
"""Return a view of the values in the object.
:return: A :class:`BaseValues` object instance.
"""
return self._values()

def _values(self) -> BaseValues:
def _values(self) -> BaseValues[ValueType]:
"""Return a view of the values in the native object.
This is the environment implementation of :meth:`BaseDict.values`.
Expand All @@ -709,7 +711,7 @@ def _values(self) -> BaseValues:
"""
return BaseValues(self)

def __contains__(self, key: Any) -> bool:
def __contains__(self, key: KeyType) -> bool:
"""Check if a key is in the object.
:param key: The key to check for.
Expand All @@ -719,7 +721,7 @@ def __contains__(self, key: Any) -> bool:
key = self._normalizeKey(key)
return self._contains(key)

def _contains(self, key: Any) -> bool:
def _contains(self, key: KeyType) -> bool:
"""Check if a key is in the native object.
This is the environment implementation of :meth:`BaseDict.__contains__`.
Expand All @@ -738,7 +740,7 @@ def _contains(self, key: Any) -> bool:

has_key = __contains__

def __setitem__(self, key: Any, value: Any) -> None:
def __setitem__(self, key: KeyType, value: ValueType) -> None:
"""Set the value for a given key in the object.
:param key: The key to set.
Expand All @@ -749,7 +751,7 @@ def __setitem__(self, key: Any, value: Any) -> None:
value = self._normalizeValue(value)
self._setItem(key, value)

def _setItem(self, key: Any, value: Any) -> None:
def _setItem(self, key: KeyType, value: ValueType) -> None:
"""Set the value for a given key in the native object.
This is the environment implementation of :meth:`BaseDict.__setitem__`.
Expand All @@ -769,7 +771,7 @@ def _setItem(self, key: Any, value: Any) -> None:
"""
self.raiseNotImplementedError()

def __getitem__(self, key: Any) -> Any:
def __getitem__(self, key: KeyType) -> ValueType:
"""Get the value for a given key from the object.
:param key: The key to retrieve the value for.
Expand All @@ -780,7 +782,7 @@ def __getitem__(self, key: Any) -> Any:
value = self._getItem(key)
return self._normalizeValue(value)

def _getItem(self, key: Any) -> Any:
def _getItem(self, key: KeyType) -> ValueType:
"""Get the value for a given key from the native object.
This is the environment implementation of :meth:`BaseDict.__getitem__`.
Expand All @@ -801,7 +803,9 @@ def _getItem(self, key: Any) -> Any:
"""
self.raiseNotImplementedError()

def get(self, key: Any, default: Optional[Any] = None) -> Any:
def get(
self, key: KeyType, default: Optional[ValueType] = None
) -> Optional[ValueType]:
"""Get the value for a given key in the object.
If the given key is not found, The specified `default` will be returned.
Expand All @@ -816,11 +820,11 @@ def get(self, key: Any, default: Optional[Any] = None) -> Any:
key = self._normalizeKey(key)
default = self._normalizeValue(default) if default is not None else default
value = self._get(key, default=default)
if value is not default:
if value is not None and value is not default:
value = self._normalizeValue(value)
return value

def _get(self, key: Any, default: Optional[Any]) -> Any:
def _get(self, key: KeyType, default: Optional[ValueType]) -> Optional[ValueType]:
"""Get the value for a given key in the native object.
This is the environment implementation of :meth:`BaseDict.get`.
Expand All @@ -841,7 +845,7 @@ def _get(self, key: Any, default: Optional[Any]) -> Any:
return self[key]
return default

def __delitem__(self, key: Any) -> None:
def __delitem__(self, key: KeyType) -> None:
"""Delete a key-value pair from the object.
:param key: The key to delete.
Expand All @@ -850,7 +854,7 @@ def __delitem__(self, key: Any) -> None:
key = self._normalizeKey(key)
self._delItem(key)

def _delItem(self, key: Any) -> None:
def _delItem(self, key: KeyType) -> None:
"""Delete a key-value pair from the native object.
This is the environment implementation of :meth:`BaseDict.__delitem__`.
Expand All @@ -867,7 +871,9 @@ def _delItem(self, key: Any) -> None:
"""
self.raiseNotImplementedError()

def pop(self, key: Any, default: Optional[Any] = None) -> Any:
def pop(
self, key: KeyType, default: Optional[ValueType] = None
) -> Optional[ValueType]:
"""Remove a key from the object and return it's value.
If the given key is not found, The specified `default` will be returned.
Expand All @@ -883,9 +889,11 @@ def pop(self, key: Any, default: Optional[Any] = None) -> Any:
if default is not None:
default = self._normalizeValue(default)
value = self._pop(key, default=default)
return self._normalizeValue(value)
if value is not None:
value = self._normalizeValue(value)
return value

def _pop(self, key: Any, default: Optional[Any]) -> Any:
def _pop(self, key: KeyType, default: Optional[ValueType]) -> Optional[ValueType]:
"""Remove a key from the native object and return it's value.
This is the environment implementation of :meth:`BaseDict.pop`.
Expand All @@ -908,7 +916,7 @@ def _pop(self, key: Any, default: Optional[Any]) -> Any:
del self[key]
return value

def __iter__(self) -> Iterator[Any]:
def __iter__(self) -> Iterator[KeyType]:
"""Return an iterator over the keys of the object.
This method yields each key one by one, removing it from the list of
Expand All @@ -919,7 +927,7 @@ def __iter__(self) -> Iterator[Any]:
"""
return self._iter()

def _iter(self) -> Iterator[Any]:
def _iter(self) -> Iterator[KeyType]:
"""Return an iterator over the keys of the native object.
This is the environment implementation of :meth:`BaseDict.__iter__`.
Expand All @@ -934,7 +942,7 @@ def _iter(self) -> Iterator[Any]:
for key in self.keys():
yield key

def update(self, other: MutableMapping) -> None:
def update(self, other: MutableMapping[KeyType, ValueType]) -> None:
"""Update the current object instance with key-value pairs from another.
:param other: A :class:`MutableMapping` of key-value pairs to update
Expand All @@ -950,7 +958,7 @@ def update(self, other: MutableMapping) -> None:
otherCopy = d
self._update(otherCopy)

def _update(self, other: MutableMapping) -> None:
def _update(self, other: MutableMapping[KeyType, ValueType]) -> None:
"""Update the current native object instance with key-value pairs from another.
This is the environment implementation of :meth:`BaseDict.update`.
Expand Down
8 changes: 6 additions & 2 deletions Lib/fontParts/base/lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,9 @@ def clear(self) -> None:
"""
super(BaseLib, self).clear()

def get(self, key: str, default: Optional[LibValueType] = None) -> LibValueType:
def get(
self, key: str, default: Optional[LibValueType] = None
) -> Optional[LibValueType]:
"""Get the value for the given key in the lib.
If the given `key` is not found, The specified `default` will be returned.
Expand Down Expand Up @@ -386,7 +388,9 @@ def values(self) -> BaseValues[LibValueType]:
"""
return super(BaseLib, self).values()

def pop(self, key: str, default: Optional[LibValueType] = None) -> LibValueType:
def pop(
self, key: str, default: Optional[LibValueType] = None
) -> Optional[LibValueType]:
"""Remove the specified key and return its associated value.
If the `key` does not exist, the `default` value is returned.
Expand Down

0 comments on commit 06a7c74

Please sign in to comment.