From bfb5bcfe32fe99911cebe2a657e19573bb422511 Mon Sep 17 00:00:00 2001 From: Fuexfollets Date: Mon, 24 Mar 2025 09:11:49 -0400 Subject: [PATCH 01/23] Implemented typehints for `value` parameter of `IntervalDict` --- .gitignore | 1 + portion/dict.py | 197 +++++++++++++++++++++++++++++++++++++----------- 2 files changed, 155 insertions(+), 43 deletions(-) diff --git a/.gitignore b/.gitignore index b45046e..198935e 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ .* *.pyc *.egg-info* +build/ diff --git a/portion/dict.py b/portion/dict.py index 0462219..4362f86 100644 --- a/portion/dict.py +++ b/portion/dict.py @@ -1,18 +1,40 @@ +# pyright: reportIncompatibleMethodOverride=false +# pyright: reportMissingTypeStubs=false + import contextlib -from collections.abc import Mapping, MutableMapping +from collections.abc import ( + Callable, + Collection, + Iterable, + Iterator, + Mapping, + MutableMapping, + Sequence, +) +from types import UnionType +from typing import Protocol, cast, overload, override from sortedcontainers import SortedDict +from sortedcontainers.sorteddict import ItemsView, KeysView, ValuesView from .const import Bound from .interval import Interval -def _sortkey(i): +def _sortkey(i: tuple[Interval, object]) -> tuple[object, bool]: # Sort by lower bound, closed first return (i[0].lower, i[0].left is Bound.OPEN) -class IntervalDict(MutableMapping): +class HowToCombineSingle[V](Protocol): + def __call__(self, x: V, y: V) -> V: ... + + +class HowToCombineWithInterval[V](Protocol): + def __call__(self, x: V, y: V, i: Interval) -> V: ... + + +class IntervalDict[V](MutableMapping[object, V]): """ An IntervalDict is a dict-like data structure that maps from intervals to data,where keys can be single values or Interval instances. @@ -30,12 +52,17 @@ class IntervalDict(MutableMapping): values (not keys) that are stored. """ - __slots__ = ("_storage",) + __slots__: tuple[str, ...] = ("_storage",) # Class to use when creating Interval instances - _klass = Interval + _klass: type = Interval - def __init__(self, mapping_or_iterable=None): + def __init__( + self, + mapping_or_iterable: Mapping[object, V] + | Iterable[tuple[object, V]] + | None = None, + ): """ Return a new IntervalDict. @@ -46,13 +73,15 @@ def __init__(self, mapping_or_iterable=None): :param mapping_or_iterable: optional mapping or iterable. """ - self._storage = SortedDict(_sortkey) # Mapping from intervals to values + self._storage: SortedDict = SortedDict( + _sortkey + ) # Mapping from intervals to values if mapping_or_iterable is not None: self.update(mapping_or_iterable) @classmethod - def _from_items(cls, items): + def _from_items(cls, items: Collection[tuple[object, V]]): """ Fast creation of an IntervalDict with the provided items. @@ -68,6 +97,7 @@ def _from_items(cls, items): return d + @override def clear(self): """ Remove all items from the IntervalDict. @@ -82,7 +112,18 @@ def copy(self): """ return self.__class__._from_items(self.items()) - def get(self, key, default=None): + @overload + def get(self, key: object, default: V = None) -> V | None: ... + + @overload + def get( + self, key: Interval, default: V | None = None + ) -> "IntervalDict[V] | None": ... + + @override + def get( + self, key: object | Interval, default: V | None = None + ) -> "IntervalDict[V]" | V | None: """ Return the values associated to given key. @@ -97,7 +138,8 @@ def get(self, key, default=None): """ if isinstance(key, Interval): d = self[key] - d[key - d.domain()] = default + if default is not None: + d[key - d.domain()] = default return d else: try: @@ -105,7 +147,7 @@ def get(self, key, default=None): except KeyError: return default - def find(self, value): + def find(self, value: V) -> Interval: """ Return a (possibly empty) Interval i such that self[i] = value, and self[~i] != value. @@ -113,9 +155,19 @@ def find(self, value): :param value: value to look for. :return: an Interval instance. """ - return self._klass(*(i for i, v in self._storage.items() if v == value)) + return cast( + Interval, + self._klass( + *( + i + for i, v in cast(ItemsView[Interval, V], self._storage.items()) + if v == value + ) + ), + ) - def items(self): + @override + def items(self) -> ItemsView[Interval, V]: """ Return a view object on the contained items sorted by their key (see https://docs.python.org/3/library/stdtypes.html#dict-views). @@ -124,7 +176,8 @@ def items(self): """ return self._storage.items() - def keys(self): + @override + def keys(self) -> KeysView[Interval]: """ Return a view object on the contained keys (sorted) (see https://docs.python.org/3/library/stdtypes.html#dict-views). @@ -133,7 +186,8 @@ def keys(self): """ return self._storage.keys() - def values(self): + @override + def values(self) -> ValuesView[V]: """ Return a view object on the contained values sorted by their key (see https://docs.python.org/3/library/stdtypes.html#dict-views). @@ -142,15 +196,24 @@ def values(self): """ return self._storage.values() - def domain(self): + def domain(self) -> Interval: """ Return an Interval corresponding to the domain of this IntervalDict. :return: an Interval. """ - return self._klass(*self._storage.keys()) + return cast(Interval, self._klass(*self._storage.keys())) + + @overload + def pop(self, key: Interval, default: V | None = None) -> "IntervalDict[V]": ... - def pop(self, key, default=None): + @overload + def pop(self, key: object, default: V | None = None) -> V | None: ... + + @override + def pop( + self, key: object, default: V | None = None + ) -> "IntervalDict[V]" | V | None: """ Remove key and return the corresponding value if key is not an Interval. If key is an interval, it returns an IntervalDict instance. @@ -173,16 +236,28 @@ def pop(self, key, default=None): del self[key] return value - def popitem(self): + @override + def popitem(self) -> tuple[Interval, V]: """ Remove and return some (key, value) pair as a 2-tuple. Raise KeyError if D is empty. :return: a (key, value) pair. """ - return self._storage.popitem() + return cast(tuple[Interval, V], self._storage.popitem()) + + @overload + def setdefault( + self, key: Interval, default: V | None = None + ) -> "IntervalDict[V]": ... + + @overload + def setdefault(self, key: object, default: V | None = None) -> V: ... - def setdefault(self, key, default=None): + @override + def setdefault( + self, key: object, default: V | None = None + ) -> V | "IntervalDict[V]" | None: """ Return given key. If it does not exist, set its value to given default and return it. @@ -193,16 +268,24 @@ def setdefault(self, key, default=None): """ if isinstance(key, Interval): value = self.get(key, default) - self.update(value) + if value is not None: + self.update(value) return value else: try: return self[key] except KeyError: - self[key] = default + if default is not None: + self[key] = default return default - def update(self, mapping_or_iterable): + @override + def update( + self, + mapping_or_iterable: Mapping[object, V] + | Iterable[tuple[object, V]] + | "IntervalDict[V]", + ): """ Update current IntervalDict with provided values. @@ -213,14 +296,24 @@ def update(self, mapping_or_iterable): :param mapping_or_iterable: mapping or iterable. """ if isinstance(mapping_or_iterable, Mapping): - data = mapping_or_iterable.items() + data = cast(ItemsView[Interval, V], mapping_or_iterable.items()) else: data = mapping_or_iterable for i, v in data: self[i] = v - def combine(self, other, how, *, missing=..., pass_interval=False): + def combine( + self, + other: "IntervalDict[V]", + how: HowToCombineSingle[V] + | HowToCombineWithInterval[ + V + ], # Callable[[V, V], V] | Callable[[V, V, Interval], V], + *, + missing: V = ..., + pass_interval: bool = False, + ) -> "IntervalDict[V]": """ Return a new IntervalDict that combines the values from current and provided IntervalDict. @@ -246,10 +339,12 @@ def combine(self, other, how, *, missing=..., pass_interval=False): new_items = [] if not pass_interval: + how = cast(HowToCombineSingle[V], how) - def _how(x, y, i): + def _how(x: V, y: V, i: Interval) -> V: return how(x, y) else: + how = cast(HowToCombineWithInterval[V], how) _how = how dom1, dom2 = self.domain(), other.domain() @@ -274,7 +369,7 @@ def _how(x, y, i): return self.__class__(new_items) - def as_dict(self, atomic=False): + def as_dict(self, atomic: bool = False) -> dict[Interval, V]: """ Return the content as a classical Python dict. @@ -290,10 +385,17 @@ def as_dict(self, atomic=False): else: return dict(self._storage) - def __getitem__(self, key): + @overload + def __getitem__(self, key: Interval) -> "IntervalDict[V]": ... + + @overload + def __getitem__(self, key: object) -> V: ... + + @override + def __getitem__(self, key: object | Interval) -> V | "IntervalDict[V]": if isinstance(key, Interval): items = [] - for i, v in self._storage.items(): + for i, v in cast(ItemsView[Interval, V], self._storage.items()): # Early out if key.upper < i.lower: break @@ -303,7 +405,7 @@ def __getitem__(self, key): items.append((intersection, v)) return self.__class__._from_items(items) else: - for i, v in self._storage.items(): + for i, v in cast(ItemsView[Interval, V], self._storage.items()): # Early out if key < i.lower: break @@ -311,11 +413,14 @@ def __getitem__(self, key): return v raise KeyError(key) - def __setitem__(self, key, value): + @override + def __setitem__(self, key: object | Interval, value: V): if isinstance(key, Interval): interval = key else: - interval = self._klass.from_atomic(Bound.CLOSED, key, key, Bound.CLOSED) + interval = cast( + Interval, self._klass.from_atomic(Bound.CLOSED, key, key, Bound.CLOSED) + ) if interval.empty: return @@ -324,7 +429,7 @@ def __setitem__(self, key, value): added_items = [] found = False - for i, v in self._storage.items(): + for i, v in cast(ItemsView[Interval, V], self._storage.items()): if value == v: found = True # Extend existing key @@ -347,7 +452,8 @@ def __setitem__(self, key, value): for key, value in added_items: self._storage[key] = value - def __delitem__(self, key): + @override + def __delitem__(self, key: object | Interval): if isinstance(key, Interval): interval = key else: @@ -382,31 +488,36 @@ def __delitem__(self, key): for key, value in added_items: self._storage[key] = value - def __or__(self, other): + def __or__(self, other: "IntervalDict[V]") -> "IntervalDict[V]": d = self.copy() d.update(other) return d - def __ior__(self, other): + def __ior__(self, other: "IntervalDict[V]") -> "IntervalDict[V]": self.update(other) return self - def __iter__(self): + @override + def __iter__(self) -> Iterator[object]: return iter(self._storage) - def __len__(self): + @override + def __len__(self) -> int: return len(self._storage) - def __contains__(self, key): + @override + def __contains__(self, key: object) -> bool: return key in self.domain() + @override def __repr__(self): return "{{{}}}".format( ", ".join(f"{i!r}: {v!r}" for i, v in self.items()), ) - def __eq__(self, other): + @override + def __eq__(self, other: object) -> bool: if isinstance(other, IntervalDict): return self.as_dict() == other.as_dict() - else: - return NotImplemented + + return NotImplemented From 8fec03036e286b4b2b6a3b1d03b6addcd5c71943 Mon Sep 17 00:00:00 2001 From: Fuexfollets Date: Mon, 24 Mar 2025 09:33:37 -0400 Subject: [PATCH 02/23] Added None-setting typehint to interval dict values --- portion/dict.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/portion/dict.py b/portion/dict.py index 4362f86..a2344d2 100644 --- a/portion/dict.py +++ b/portion/dict.py @@ -3,15 +3,12 @@ import contextlib from collections.abc import ( - Callable, Collection, Iterable, Iterator, Mapping, MutableMapping, - Sequence, ) -from types import UnionType from typing import Protocol, cast, overload, override from sortedcontainers import SortedDict @@ -138,8 +135,7 @@ def get( """ if isinstance(key, Interval): d = self[key] - if default is not None: - d[key - d.domain()] = default + d[key - d.domain()] = default return d else: try: @@ -284,7 +280,7 @@ def update( self, mapping_or_iterable: Mapping[object, V] | Iterable[tuple[object, V]] - | "IntervalDict[V]", + | type["IntervalDict[V]"], ): """ Update current IntervalDict with provided values. @@ -300,7 +296,7 @@ def update( else: data = mapping_or_iterable - for i, v in data: + for i, v in cast(Collection[tuple[object, V]], data): self[i] = v def combine( @@ -414,7 +410,7 @@ def __getitem__(self, key: object | Interval) -> V | "IntervalDict[V]": raise KeyError(key) @override - def __setitem__(self, key: object | Interval, value: V): + def __setitem__(self, key: object | Interval, value: V | None): if isinstance(key, Interval): interval = key else: From a672addbb6687686d90cb6c2bc81e8d1e1834d2b Mon Sep 17 00:00:00 2001 From: Fuexfollets Date: Mon, 24 Mar 2025 10:48:58 -0400 Subject: [PATCH 03/23] Fixed type compatibility for python version 3.9 --- portion/dict.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/portion/dict.py b/portion/dict.py index a2344d2..d924ea2 100644 --- a/portion/dict.py +++ b/portion/dict.py @@ -9,7 +9,7 @@ Mapping, MutableMapping, ) -from typing import Protocol, cast, overload, override +from typing import Protocol, TypeVar, cast, overload, override from sortedcontainers import SortedDict from sortedcontainers.sorteddict import ItemsView, KeysView, ValuesView @@ -23,11 +23,14 @@ def _sortkey(i: tuple[Interval, object]) -> tuple[object, bool]: return (i[0].lower, i[0].left is Bound.OPEN) -class HowToCombineSingle[V](Protocol): +V = TypeVar("V") + + +class HowToCombineSingle(Protocol): def __call__(self, x: V, y: V) -> V: ... -class HowToCombineWithInterval[V](Protocol): +class HowToCombineWithInterval(Protocol): def __call__(self, x: V, y: V, i: Interval) -> V: ... @@ -302,10 +305,8 @@ def update( def combine( self, other: "IntervalDict[V]", - how: HowToCombineSingle[V] - | HowToCombineWithInterval[ - V - ], # Callable[[V, V], V] | Callable[[V, V, Interval], V], + how: HowToCombineSingle + | HowToCombineWithInterval, # Callable[[V, V], V] | Callable[[V, V, Interval], V], *, missing: V = ..., pass_interval: bool = False, @@ -335,12 +336,12 @@ def combine( new_items = [] if not pass_interval: - how = cast(HowToCombineSingle[V], how) + how = cast(HowToCombineSingle, how) def _how(x: V, y: V, i: Interval) -> V: return how(x, y) else: - how = cast(HowToCombineWithInterval[V], how) + how = cast(HowToCombineWithInterval, how) _how = how dom1, dom2 = self.domain(), other.domain() From 1975ee55bdc696dd9196c8c505c79a3e61d67213 Mon Sep 17 00:00:00 2001 From: Fuexfollets Date: Mon, 24 Mar 2025 10:50:40 -0400 Subject: [PATCH 04/23] Fixed format error --- portion/dict.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/portion/dict.py b/portion/dict.py index d924ea2..3691138 100644 --- a/portion/dict.py +++ b/portion/dict.py @@ -305,8 +305,7 @@ def update( def combine( self, other: "IntervalDict[V]", - how: HowToCombineSingle - | HowToCombineWithInterval, # Callable[[V, V], V] | Callable[[V, V, Interval], V], + how: HowToCombineSingle | HowToCombineWithInterval, *, missing: V = ..., pass_interval: bool = False, From d74c8e7d8e10162111feaf89fef052431d52077b Mon Sep 17 00:00:00 2001 From: Fuexfollets Date: Mon, 24 Mar 2025 10:53:02 -0400 Subject: [PATCH 05/23] Fixed type compatibility for python version 3.9 #2 --- portion/dict.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/portion/dict.py b/portion/dict.py index 3691138..f55b91a 100644 --- a/portion/dict.py +++ b/portion/dict.py @@ -9,7 +9,7 @@ Mapping, MutableMapping, ) -from typing import Protocol, TypeVar, cast, overload, override +from typing import Generic, Protocol, TypeVar, cast, overload, override from sortedcontainers import SortedDict from sortedcontainers.sorteddict import ItemsView, KeysView, ValuesView @@ -34,7 +34,7 @@ class HowToCombineWithInterval(Protocol): def __call__(self, x: V, y: V, i: Interval) -> V: ... -class IntervalDict[V](MutableMapping[object, V]): +class IntervalDict(Generic[V], MutableMapping[object, V]): """ An IntervalDict is a dict-like data structure that maps from intervals to data,where keys can be single values or Interval instances. From bf11110e28a23733584e1f3abe372b2cf981d033 Mon Sep 17 00:00:00 2001 From: Fuexfollets Date: Mon, 24 Mar 2025 10:57:10 -0400 Subject: [PATCH 06/23] Fixed type compatibility for python version 3.9 #3 --- portion/dict.py | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/portion/dict.py b/portion/dict.py index f55b91a..5191806 100644 --- a/portion/dict.py +++ b/portion/dict.py @@ -9,7 +9,7 @@ Mapping, MutableMapping, ) -from typing import Generic, Protocol, TypeVar, cast, overload, override +from typing import Generic, Protocol, TypeVar, cast, overload from sortedcontainers import SortedDict from sortedcontainers.sorteddict import ItemsView, KeysView, ValuesView @@ -97,7 +97,6 @@ def _from_items(cls, items: Collection[tuple[object, V]]): return d - @override def clear(self): """ Remove all items from the IntervalDict. @@ -120,7 +119,6 @@ def get( self, key: Interval, default: V | None = None ) -> "IntervalDict[V] | None": ... - @override def get( self, key: object | Interval, default: V | None = None ) -> "IntervalDict[V]" | V | None: @@ -165,7 +163,6 @@ def find(self, value: V) -> Interval: ), ) - @override def items(self) -> ItemsView[Interval, V]: """ Return a view object on the contained items sorted by their key @@ -175,7 +172,6 @@ def items(self) -> ItemsView[Interval, V]: """ return self._storage.items() - @override def keys(self) -> KeysView[Interval]: """ Return a view object on the contained keys (sorted) @@ -185,7 +181,6 @@ def keys(self) -> KeysView[Interval]: """ return self._storage.keys() - @override def values(self) -> ValuesView[V]: """ Return a view object on the contained values sorted by their key @@ -209,7 +204,6 @@ def pop(self, key: Interval, default: V | None = None) -> "IntervalDict[V]": ... @overload def pop(self, key: object, default: V | None = None) -> V | None: ... - @override def pop( self, key: object, default: V | None = None ) -> "IntervalDict[V]" | V | None: @@ -235,7 +229,6 @@ def pop( del self[key] return value - @override def popitem(self) -> tuple[Interval, V]: """ Remove and return some (key, value) pair as a 2-tuple. @@ -253,7 +246,6 @@ def setdefault( @overload def setdefault(self, key: object, default: V | None = None) -> V: ... - @override def setdefault( self, key: object, default: V | None = None ) -> V | "IntervalDict[V]" | None: @@ -278,7 +270,6 @@ def setdefault( self[key] = default return default - @override def update( self, mapping_or_iterable: Mapping[object, V] @@ -387,7 +378,6 @@ def __getitem__(self, key: Interval) -> "IntervalDict[V]": ... @overload def __getitem__(self, key: object) -> V: ... - @override def __getitem__(self, key: object | Interval) -> V | "IntervalDict[V]": if isinstance(key, Interval): items = [] @@ -409,7 +399,6 @@ def __getitem__(self, key: object | Interval) -> V | "IntervalDict[V]": return v raise KeyError(key) - @override def __setitem__(self, key: object | Interval, value: V | None): if isinstance(key, Interval): interval = key @@ -448,7 +437,6 @@ def __setitem__(self, key: object | Interval, value: V | None): for key, value in added_items: self._storage[key] = value - @override def __delitem__(self, key: object | Interval): if isinstance(key, Interval): interval = key @@ -493,25 +481,20 @@ def __ior__(self, other: "IntervalDict[V]") -> "IntervalDict[V]": self.update(other) return self - @override def __iter__(self) -> Iterator[object]: return iter(self._storage) - @override def __len__(self) -> int: return len(self._storage) - @override def __contains__(self, key: object) -> bool: return key in self.domain() - @override def __repr__(self): return "{{{}}}".format( ", ".join(f"{i!r}: {v!r}" for i, v in self.items()), ) - @override def __eq__(self, other: object) -> bool: if isinstance(other, IntervalDict): return self.as_dict() == other.as_dict() From 037201a39664dea3732cd2349a417e7b6872a5a8 Mon Sep 17 00:00:00 2001 From: Fuexfollets Date: Mon, 24 Mar 2025 13:27:37 -0400 Subject: [PATCH 07/23] Fixed type compatibility for python version 3.9 #3, fixed amniguous overload method resolution --- portion/dict.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/portion/dict.py b/portion/dict.py index 5191806..af5c47e 100644 --- a/portion/dict.py +++ b/portion/dict.py @@ -9,7 +9,7 @@ Mapping, MutableMapping, ) -from typing import Generic, Protocol, TypeVar, cast, overload +from typing import Generic, Protocol, TypeVar, Union, cast, overload from sortedcontainers import SortedDict from sortedcontainers.sorteddict import ItemsView, KeysView, ValuesView @@ -59,9 +59,9 @@ class IntervalDict(Generic[V], MutableMapping[object, V]): def __init__( self, - mapping_or_iterable: Mapping[object, V] - | Iterable[tuple[object, V]] - | None = None, + mapping_or_iterable: Union[ + Mapping[object, V], Iterable[tuple[object, V]], None + ] = None, ): """ Return a new IntervalDict. @@ -111,14 +111,14 @@ def copy(self): """ return self.__class__._from_items(self.items()) - @overload - def get(self, key: object, default: V = None) -> V | None: ... - @overload def get( self, key: Interval, default: V | None = None ) -> "IntervalDict[V] | None": ... + @overload + def get(self, key: object, default: V = None) -> V | None: ... + def get( self, key: object | Interval, default: V | None = None ) -> "IntervalDict[V]" | V | None: From 8d27f9d2ff5156ea290bb87edd1c67f65c71d623 Mon Sep 17 00:00:00 2001 From: Fuexfollets Date: Mon, 24 Mar 2025 13:52:32 -0400 Subject: [PATCH 08/23] Fixed type compatibility for python version 3.9 #4 (type unions and optional) --- portion/dict.py | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/portion/dict.py b/portion/dict.py index af5c47e..4fc0977 100644 --- a/portion/dict.py +++ b/portion/dict.py @@ -9,7 +9,7 @@ Mapping, MutableMapping, ) -from typing import Generic, Protocol, TypeVar, Union, cast, overload +from typing import Generic, Optional, Protocol, TypeVar, Union, cast, overload from sortedcontainers import SortedDict from sortedcontainers.sorteddict import ItemsView, KeysView, ValuesView @@ -113,15 +113,15 @@ def copy(self): @overload def get( - self, key: Interval, default: V | None = None + self, key: Interval, default: Optional[V] = None ) -> "IntervalDict[V] | None": ... @overload - def get(self, key: object, default: V = None) -> V | None: ... + def get(self, key: object, default: V = None) -> Optional[V]: ... def get( - self, key: object | Interval, default: V | None = None - ) -> "IntervalDict[V]" | V | None: + self, key: Union[object, Interval], default: Optional[V] = None + ) -> Union["IntervalDict[V]", V, None]: """ Return the values associated to given key. @@ -199,14 +199,14 @@ def domain(self) -> Interval: return cast(Interval, self._klass(*self._storage.keys())) @overload - def pop(self, key: Interval, default: V | None = None) -> "IntervalDict[V]": ... + def pop(self, key: Interval, default: Optional[V] = None) -> "IntervalDict[V]": ... @overload - def pop(self, key: object, default: V | None = None) -> V | None: ... + def pop(self, key: object, default: Optional[V] = None) -> Optional[V]: ... def pop( - self, key: object, default: V | None = None - ) -> "IntervalDict[V]" | V | None: + self, key: object, default: Optional[V] = None + ) -> Union["IntervalDict[V]", V, None]: """ Remove key and return the corresponding value if key is not an Interval. If key is an interval, it returns an IntervalDict instance. @@ -240,15 +240,15 @@ def popitem(self) -> tuple[Interval, V]: @overload def setdefault( - self, key: Interval, default: V | None = None + self, key: Interval, default: Optional[V] = None ) -> "IntervalDict[V]": ... @overload - def setdefault(self, key: object, default: V | None = None) -> V: ... + def setdefault(self, key: object, default: Optional[V] = None) -> V: ... def setdefault( - self, key: object, default: V | None = None - ) -> V | "IntervalDict[V]" | None: + self, key: object, default: Optional[V] = None + ) -> Union[V, "IntervalDict[V]", None]: """ Return given key. If it does not exist, set its value to given default and return it. @@ -272,9 +272,9 @@ def setdefault( def update( self, - mapping_or_iterable: Mapping[object, V] - | Iterable[tuple[object, V]] - | type["IntervalDict[V]"], + mapping_or_iterable: Union[ + Mapping[object, V], Iterable[tuple[object, V]], type["IntervalDict[V]"] + ], ): """ Update current IntervalDict with provided values. @@ -296,7 +296,7 @@ def update( def combine( self, other: "IntervalDict[V]", - how: HowToCombineSingle | HowToCombineWithInterval, + how: Union[HowToCombineSingle, HowToCombineWithInterval], *, missing: V = ..., pass_interval: bool = False, @@ -378,7 +378,7 @@ def __getitem__(self, key: Interval) -> "IntervalDict[V]": ... @overload def __getitem__(self, key: object) -> V: ... - def __getitem__(self, key: object | Interval) -> V | "IntervalDict[V]": + def __getitem__(self, key: Union[object, Interval]) -> Union[V, "IntervalDict[V]"]: if isinstance(key, Interval): items = [] for i, v in cast(ItemsView[Interval, V], self._storage.items()): @@ -399,7 +399,7 @@ def __getitem__(self, key: object | Interval) -> V | "IntervalDict[V]": return v raise KeyError(key) - def __setitem__(self, key: object | Interval, value: V | None): + def __setitem__(self, key: Union[object, Interval], value: Optional[V]): if isinstance(key, Interval): interval = key else: @@ -437,7 +437,7 @@ def __setitem__(self, key: object | Interval, value: V | None): for key, value in added_items: self._storage[key] = value - def __delitem__(self, key: object | Interval): + def __delitem__(self, key: Union[object, Interval]): if isinstance(key, Interval): interval = key else: From 4b0ad908e5d844819de2371358478f42d99ac2d5 Mon Sep 17 00:00:00 2001 From: Fuexfollets Date: Mon, 24 Mar 2025 13:56:01 -0400 Subject: [PATCH 09/23] Added stubfile --- typings/portion/__init__.pyi | 16 ++ typings/portion/api.pyi | 28 ++++ typings/portion/const.pyi | 94 ++++++++++++ typings/portion/dict.pyi | 273 ++++++++++++++++++++++++++++++++++ typings/portion/func.pyi | 97 ++++++++++++ typings/portion/interval.pyi | 280 +++++++++++++++++++++++++++++++++++ typings/portion/io.pyi | 71 +++++++++ 7 files changed, 859 insertions(+) create mode 100644 typings/portion/__init__.pyi create mode 100644 typings/portion/api.pyi create mode 100644 typings/portion/const.pyi create mode 100644 typings/portion/dict.pyi create mode 100644 typings/portion/func.pyi create mode 100644 typings/portion/interval.pyi create mode 100644 typings/portion/io.pyi diff --git a/typings/portion/__init__.pyi b/typings/portion/__init__.pyi new file mode 100644 index 0000000..97231ce --- /dev/null +++ b/typings/portion/__init__.pyi @@ -0,0 +1,16 @@ +""" +This type stub file was generated by pyright. +""" + +import importlib.metadata +from .api import create_api +from .const import Bound, inf +from .dict import IntervalDict +from .func import closed, closedopen, empty, iterate, open, openclosed, singleton +from .interval import AbstractDiscreteInterval, Interval +from .io import from_data, from_string, to_data, to_string + +__all__ = ["create_api", "inf", "CLOSED", "OPEN", "Interval", "AbstractDiscreteInterval", "open", "closed", "openclosed", "closedopen", "singleton", "empty", "iterate", "from_string", "to_string", "from_data", "to_data", "IntervalDict"] +CLOSED = ... +OPEN = ... +__version__ = ... diff --git a/typings/portion/api.pyi b/typings/portion/api.pyi new file mode 100644 index 0000000..6dbf44f --- /dev/null +++ b/typings/portion/api.pyi @@ -0,0 +1,28 @@ +""" +This type stub file was generated by pyright. +""" + +__all__ = ["create_api"] +def partial(wrapped, *args, **kwargs): # -> _Wrapped[Callable[..., Any], Any, Callable[..., Any], Any]: + """ + Convenient helper that combines functools.update_wrapper and + functools.partial. It has exactly the same signature than functools.partial. + """ + ... + +def create_api(interval, *, interval_dict=..., name=...): # -> ModuleType: + """Create a spe + + Dynamically create a module whose API is similar to the one of portion, but + configured to use given Interval class. Unless specified, a new IntervalDict + subclass is automatically generated to use given Interval subclass. + + This feature is experimental, and may be changed even in minor or patch + updates of portion. + + :param interval: a subclass of Interval. + :param interval_dict: a subclass of IntervalDict. + :param name: the name of the new module. + """ + ... + diff --git a/typings/portion/const.pyi b/typings/portion/const.pyi new file mode 100644 index 0000000..ceffc1f --- /dev/null +++ b/typings/portion/const.pyi @@ -0,0 +1,94 @@ +""" +This type stub file was generated by pyright. +""" + +import enum + +class Bound(enum.Enum): + """ + Bound types, either CLOSED for inclusive, or OPEN for exclusive. + """ + CLOSED = ... + OPEN = ... + def __bool__(self): + ... + + def __invert__(self): # -> Literal[Bound.CLOSED, Bound.OPEN]: + ... + + def __str__(self) -> str: + ... + + def __repr__(self): # -> Literal['CLOSED', 'OPEN']: + ... + + + +class _Singleton: + __instance = ... + def __new__(cls, *args, **kwargs): # -> Self: + ... + + + +class _PInf(_Singleton): + """ + Represent positive infinity. + """ + def __neg__(self): # -> _NInf: + ... + + def __lt__(self, o) -> bool: + ... + + def __le__(self, o) -> bool: + ... + + def __gt__(self, o) -> bool: + ... + + def __ge__(self, o) -> bool: + ... + + def __eq__(self, o) -> bool: + ... + + def __repr__(self): # -> Literal['+inf']: + ... + + def __hash__(self) -> int: + ... + + + +class _NInf(_Singleton): + """ + Represent negative infinity. + """ + def __neg__(self): # -> _PInf: + ... + + def __lt__(self, o) -> bool: + ... + + def __le__(self, o) -> bool: + ... + + def __gt__(self, o) -> bool: + ... + + def __ge__(self, o) -> bool: + ... + + def __eq__(self, o) -> bool: + ... + + def __repr__(self): # -> Literal['-inf']: + ... + + def __hash__(self) -> int: + ... + + + +inf = ... diff --git a/typings/portion/dict.pyi b/typings/portion/dict.pyi new file mode 100644 index 0000000..4534f8a --- /dev/null +++ b/typings/portion/dict.pyi @@ -0,0 +1,273 @@ +""" +This type stub file was generated by pyright. +""" + +from collections.abc import Iterable, Iterator, Mapping, MutableMapping +from typing import Generic, Optional, Protocol, TypeVar, Union, overload +from sortedcontainers.sorteddict import ItemsView, KeysView, ValuesView +from .interval import Interval + +V = TypeVar("V") +class HowToCombineSingle(Protocol): + def __call__(self, x: V, y: V) -> V: + ... + + + +class HowToCombineWithInterval(Protocol): + def __call__(self, x: V, y: V, i: Interval) -> V: + ... + + + +class IntervalDict(Generic[V], MutableMapping[object, V]): + """ + An IntervalDict is a dict-like data structure that maps from intervals to data,where + keys can be single values or Interval instances. + + When keys are Interval instances, its behaviour merely corresponds to range queries + and it returns IntervalDict instances corresponding to the subset of values covered + by the given interval. If no matching value is found, an empty IntervalDict is + returned. + + When keys are "single values", its behaviour corresponds to the one of Python + built-in dict. When no matching value is found, a KeyError is raised. + + Note that this class does not aim to have the best performance, but is provided + mainly for convenience. Its performance mainly depends on the number of distinct + values (not keys) that are stored. + """ + __slots__: tuple[str, ...] = ... + _klass: type = ... + def __init__(self, mapping_or_iterable: Union[Mapping[object, V], Iterable[tuple[object, V]], None] = ...) -> None: + """ + Return a new IntervalDict. + + If no argument is given, an empty IntervalDict is created. If an argument + is given, and is a mapping object (e.g., another IntervalDict), an + new IntervalDict with the same key-value pairs is created. If an + iterable is provided, it has to be a list of (key, value) pairs. + + :param mapping_or_iterable: optional mapping or iterable. + """ + ... + + def clear(self): # -> None: + """ + Remove all items from the IntervalDict. + """ + ... + + def copy(self): # -> Self: + """ + Return a shallow copy. + + :return: a shallow copy. + """ + ... + + @overload + def get(self, key: Interval, default: Optional[V] = ...) -> IntervalDict[V] | None: + ... + + @overload + def get(self, key: object, default: V = ...) -> Optional[V]: + ... + + def get(self, key: Union[object, Interval], default: Optional[V] = ...) -> Union[IntervalDict[V], V, None]: + """ + Return the values associated to given key. + + If the key is a single value, it returns a single value (if it exists) or + the default value. If the key is an Interval, it returns a new IntervalDict + restricted to given interval. In that case, the default value is used to + "fill the gaps" (if any) w.r.t. given key. + + :param key: a single value or an Interval instance. + :param default: default value (default to None). + :return: an IntervalDict, or a single value if key is not an Interval. + """ + ... + + def find(self, value: V) -> Interval: + """ + Return a (possibly empty) Interval i such that self[i] = value, and + self[~i] != value. + + :param value: value to look for. + :return: an Interval instance. + """ + ... + + def items(self) -> ItemsView[Interval, V]: + """ + Return a view object on the contained items sorted by their key + (see https://docs.python.org/3/library/stdtypes.html#dict-views). + + :return: a view object. + """ + ... + + def keys(self) -> KeysView[Interval]: + """ + Return a view object on the contained keys (sorted) + (see https://docs.python.org/3/library/stdtypes.html#dict-views). + + :return: a view object. + """ + ... + + def values(self) -> ValuesView[V]: + """ + Return a view object on the contained values sorted by their key + (see https://docs.python.org/3/library/stdtypes.html#dict-views). + + :return: a view object. + """ + ... + + def domain(self) -> Interval: + """ + Return an Interval corresponding to the domain of this IntervalDict. + + :return: an Interval. + """ + ... + + @overload + def pop(self, key: Interval, default: Optional[V] = ...) -> IntervalDict[V]: + ... + + @overload + def pop(self, key: object, default: Optional[V] = ...) -> Optional[V]: + ... + + def pop(self, key: object, default: Optional[V] = ...) -> Union[IntervalDict[V], V, None]: + """ + Remove key and return the corresponding value if key is not an Interval. + If key is an interval, it returns an IntervalDict instance. + + This method combines self[key] and del self[key]. If a default value + is provided and is not None, it uses self.get(key, default) instead of + self[key]. + + :param key: a single value or an Interval instance. + :param default: optional default value. + :return: an IntervalDict, or a single value if key is not an Interval. + """ + ... + + def popitem(self) -> tuple[Interval, V]: + """ + Remove and return some (key, value) pair as a 2-tuple. + Raise KeyError if D is empty. + + :return: a (key, value) pair. + """ + ... + + @overload + def setdefault(self, key: Interval, default: Optional[V] = ...) -> IntervalDict[V]: + ... + + @overload + def setdefault(self, key: object, default: Optional[V] = ...) -> V: + ... + + def setdefault(self, key: object, default: Optional[V] = ...) -> Union[V, IntervalDict[V], None]: + """ + Return given key. If it does not exist, set its value to given default + and return it. + + :param key: a single value or an Interval instance. + :param default: default value (default to None). + :return: an IntervalDict, or a single value if key is not an Interval. + """ + ... + + def update(self, mapping_or_iterable: Union[Mapping[object, V], Iterable[tuple[object, V]], type[IntervalDict[V]]]): # -> None: + """ + Update current IntervalDict with provided values. + + If a mapping is provided, it must map Interval instances to values (e.g., + another IntervalDict). If an iterable is provided, it must consist of a + list of (key, value) pairs. + + :param mapping_or_iterable: mapping or iterable. + """ + ... + + def combine(self, other: IntervalDict[V], how: Union[HowToCombineSingle, HowToCombineWithInterval], *, missing: V = ..., pass_interval: bool = ...) -> IntervalDict[V]: + """ + Return a new IntervalDict that combines the values from current and + provided IntervalDict. + + If d = d1.combine(d2, f), then d contains (1) all values from d1 whose + keys do not intersect the ones of d2, (2) all values from d2 whose keys + do not intersect the ones of d1, and (3) f(x, y) for x in d1, y in d2 for + intersecting keys. + + When missing is set, the how function is called even for non-intersecting + keys using the value of missing to replace the missing values. This is, + case (1) corresponds to f(x, missing) and case (2) to f(missing, y). + + If pass_interval is set to True, the current interval will be passed to + the "how" function as third parameter. + + :param other: another IntervalDict instance. + :param how: a function combining two values. + :param missing: if set, use this value for missing values when calling "how". + :param pass_interval: if set, provide the current interval to the how function. + :return: a new IntervalDict instance. + """ + ... + + def as_dict(self, atomic: bool = ...) -> dict[Interval, V]: + """ + Return the content as a classical Python dict. + + :param atomic: whether keys are atomic intervals. + :return: a Python dict. + """ + ... + + @overload + def __getitem__(self, key: Interval) -> IntervalDict[V]: + ... + + @overload + def __getitem__(self, key: object) -> V: + ... + + def __getitem__(self, key: Union[object, Interval]) -> Union[V, IntervalDict[V]]: + ... + + def __setitem__(self, key: Union[object, Interval], value: Optional[V]): # -> None: + ... + + def __delitem__(self, key: Union[object, Interval]): # -> None: + ... + + def __or__(self, other: IntervalDict[V]) -> IntervalDict[V]: + ... + + def __ior__(self, other: IntervalDict[V]) -> IntervalDict[V]: + ... + + def __iter__(self) -> Iterator[object]: + ... + + def __len__(self) -> int: + ... + + def __contains__(self, key: object) -> bool: + ... + + def __repr__(self): # -> str: + ... + + def __eq__(self, other: object) -> bool: + ... + + + diff --git a/typings/portion/func.pyi b/typings/portion/func.pyi new file mode 100644 index 0000000..00dc2d6 --- /dev/null +++ b/typings/portion/func.pyi @@ -0,0 +1,97 @@ +""" +This type stub file was generated by pyright. +""" + +def open(lower, upper, *, klass=...): # -> Interval: + """ + Create an open interval with given bounds. + + :param lower: value of the lower bound. + :param upper: value of the upper bound. + :param klass: class to use for creating intervals (default to Interval). + :return: an interval. + """ + ... + +def closed(lower, upper, *, klass=...): # -> Interval: + """ + Create a closed interval with given bounds. + + :param lower: value of the lower bound. + :param upper: value of the upper bound. + :param klass: class to use for creating intervals (default to Interval). + :return: an interval. + """ + ... + +def openclosed(lower, upper, *, klass=...): # -> Interval: + """ + Create a left-open interval with given bounds. + + :param lower: value of the lower bound. + :param upper: value of the upper bound. + :param klass: class to use for creating intervals (default to Interval). + :return: an interval. + """ + ... + +def closedopen(lower, upper, *, klass=...): # -> Interval: + """ + Create a right-open interval with given bounds. + + :param lower: value of the lower bound. + :param upper: value of the upper bound. + :param klass: class to use for creating intervals (default to Interval). + :return: an interval. + """ + ... + +def singleton(value, *, klass=...): # -> Interval: + """ + Create a singleton interval. + + :param value: value of the lower and upper bounds. + :param klass: class to use for creating intervals (default to Interval). + :return: an interval. + """ + ... + +def empty(*, klass=...): # -> Interval: + """ + Create an empty interval. + + :param klass: class to use for creating intervals (default to Interval). + :return: an interval. + """ + ... + +def iterate(interval, step, *, base=..., reverse=...): # -> Generator[Any | object, Any, None]: + """ + Iterate on the (discrete) values of given interval. + + This function returns a (lazy) iterator over the values of given interval, + starting from its lower bound and ending on its upper bound (if interval is + not open). Each returned value merely corresponds to lower + i * step, where + "step" defines the step between consecutive values. If reverse=True, a + negative step must be passed (as in Python's range function). + It also accepts a callable that is used to compute the next possible + value based on the current one. + + When a non-atomic interval is provided, this function chains the iterators obtained + by calling itself on the underlying atomic intervals. + + The values returned by the iterator can be aligned with a base value with the + "base" parameter. This parameter must be a callable that accepts the lower bound + of the (atomic) interval as input, and returns the first value that needs to be + considered for the iteration. + By default, the identity function is used. If reverse=True, then the upper bound + will be passed instead of the lower one. + + :param interval: an interval. + :param step: step between values, or a callable that returns the next value. + :param base: a callable that accepts a bound and returns an initial value. + :param reverse: set to True for descending order. + :return: a lazy iterator. + """ + ... + diff --git a/typings/portion/interval.pyi b/typings/portion/interval.pyi new file mode 100644 index 0000000..ad668c0 --- /dev/null +++ b/typings/portion/interval.pyi @@ -0,0 +1,280 @@ +""" +This type stub file was generated by pyright. +""" + +Atomic = ... +def mergeable(a, b): + """ + Tester whether two atomic intervals can be merged (i.e. they overlap or + are adjacent). + + :param a: an atomic interval. + :param b: an atomic interval. + :return: True if mergeable, False otherwise. + """ + ... + +class Interval: + """ + This class represents an interval. + + An interval is an (automatically simplified) union of atomic intervals. + It can be created with Interval.from_atomic(...) or by passing Interval + instances to __init__. + """ + __slots__ = ... + __match_args__ = ... + def __init__(self, *intervals) -> None: + """ + Create a disjunction of zero, one or more intervals. + + :param intervals: zero, one or more intervals. + """ + ... + + @classmethod + def from_atomic(cls, left, lower, upper, right): # -> Self: + """ + Create an Interval instance containing a single atomic interval. + + :param left: either CLOSED or OPEN. + :param lower: value of the lower bound. + :param upper: value of the upper bound. + :param right: either CLOSED or OPEN. + """ + ... + + @property + def left(self): # -> Literal[Bound.OPEN]: + """ + Lowest left boundary is either CLOSED or OPEN. + """ + ... + + @property + def lower(self): # -> _PInf: + """ + Lowest lower bound value. + """ + ... + + @property + def upper(self): # -> _NInf: + """ + Highest upper bound value. + """ + ... + + @property + def right(self): # -> Literal[Bound.OPEN]: + """ + Highest right boundary is either CLOSED or OPEN. + """ + ... + + @property + def empty(self): # -> bool: + """ + True if interval is empty, False otherwise. + """ + ... + + @property + def atomic(self): # -> bool: + """ + True if this interval is atomic, False otherwise. + An interval is atomic if it is empty or composed of a single interval. + """ + ... + + @property + def enclosure(self): # -> Self: + """ + Return the smallest interval composed of a single atomic interval that encloses + the current interval. + + :return: an Interval instance. + """ + ... + + def replace(self, left=..., lower=..., upper=..., right=..., *, ignore_inf=...): # -> Self | Any: + """ + Create a new interval based on the current one and the provided values. + + If current interval is not atomic, it is extended or restricted such that + its enclosure satisfies the new bounds. In other words, its new enclosure + will be equal to self.enclosure.replace(left, lower, upper, right). + + Callable can be passed instead of values. In that case, it is called with the + current corresponding value except if ignore_inf if set (default) and the + corresponding bound is an infinity. + + :param left: (a function of) left boundary. + :param lower: (a function of) value of the lower bound. + :param upper: (a function of) value of the upper bound. + :param right: (a function of) right boundary. + :param ignore_inf: ignore infinities if functions are provided (default + is True). + :return: an Interval instance + """ + ... + + def apply(self, func): # -> Self: + """ + Apply a function on each of the underlying atomic intervals and return their + union as a new interval instance + + Given function is expected to return an interval (possibly empty or not + atomic) or a 4-uple (left, lower, upper, right) whose values correspond to + the parameters of Interval.from_atomic(left, lower, upper, right). + + This method is merely a shortcut for Interval(*list(map(func, self))). + + :param func: function to apply on each underlying atomic interval. + :return: an Interval instance. + """ + ... + + def adjacent(self, other): + """ + Test if two intervals are adjacent. + + Two intervals are adjacent if they do not overlap and their union form a + single atomic interval. + + While this definition corresponds to the usual notion of adjacency for atomic + intervals, it has stronger requirements for non-atomic ones since it requires + all underlying atomic intervals to be adjacent (i.e. that one + interval fills the gaps between the atomic intervals of the other one). + + :param other: an interval. + :return: True if intervals are adjacent, False otherwise. + """ + ... + + def overlaps(self, other): # -> bool: + """ + Test if two intervals overlap (i.e. if their intersection is non-empty). + + :param other: an interval. + :return: True if intervals overlap, False otherwise. + """ + ... + + def intersection(self, other): + """ + Return the intersection of two intervals. + + :param other: an interval. + :return: the intersection of the intervals. + """ + ... + + def union(self, other): + """ + Return the union of two intervals. + + :param other: an interval. + :return: the union of the intervals. + """ + ... + + def contains(self, item): # -> bool: + """ + Test if given item is contained in this interval. + This method accepts intervals and arbitrary comparable values. + + :param item: an interval or any arbitrary comparable value. + :return: True if given item is contained, False otherwise. + """ + ... + + def complement(self): # -> Interval: + """ + Return the complement of this interval. + + :return: the complement of this interval. + """ + ... + + def difference(self, other): + """ + Return the difference of two intervals. + + :param other: an interval. + :return: the difference of the intervals. + """ + ... + + def __getattr__(self, name): # -> None: + ... + + def __len__(self): # -> int: + ... + + def __iter__(self): # -> Generator[Self, Any, None]: + ... + + def __getitem__(self, item): # -> Self: + ... + + def __and__(self, other): # -> _NotImplementedType | Self: + ... + + def __or__(self, other): # -> Self | _NotImplementedType: + ... + + def __contains__(self, item): # -> bool: + ... + + def __invert__(self): # -> Self: + ... + + def __sub__(self, other): # -> _NotImplementedType | Self: + ... + + def __eq__(self, other) -> bool: + ... + + def __lt__(self, other) -> bool: + ... + + def __gt__(self, other) -> bool: + ... + + def __le__(self, other) -> bool: + ... + + def __ge__(self, other) -> bool: + ... + + def __hash__(self) -> int: + ... + + def __repr__(self): # -> LiteralString | Literal['()']: + ... + + + +class AbstractDiscreteInterval(Interval): + """ + An abstract class for discrete interval. + + This class is not expected to be used as-is, and should be subclassed + first. The only attribute/method that should be overriden is the `_step` + class variable. This variable defines the step between two consecutive + values of the discrete domain (e.g., 1 for integers). + If a meaningfull step cannot be provided (e.g., for characters), the + _incr and _decr class methods can be overriden. They respectively return + the next and previous value given the current one. + + This class is still experimental and backward incompatible changes may + occur even in minor or patch updates of portion. + """ + _step = ... + @classmethod + def from_atomic(cls, left, lower, upper, right): # -> Self: + ... + + + diff --git a/typings/portion/io.pyi b/typings/portion/io.pyi new file mode 100644 index 0000000..34426d4 --- /dev/null +++ b/typings/portion/io.pyi @@ -0,0 +1,71 @@ +""" +This type stub file was generated by pyright. +""" + +def from_string(string, conv, *, bound=..., disj=..., sep=..., left_open=..., left_closed=..., right_open=..., right_closed=..., pinf=..., ninf=..., klass=...): # -> Interval: + """ + Parse given string and create an Interval instance. + A converter function has to be provided to convert a bound (as string) to a value. + This function raises a ValueError if given string cannot be parsed to an interval. + + :param string: string to parse. + :param conv: function to convert a bound (as string) to an object. + :param bound: regex pattern for a value. + :param disj: regex pattern for disjunctive operator (default matches '|' and ' | '). + :param sep: regex pattern for bound separator (default matches ','). + :param left_open: regex pattern for left open boundary (default matches '('). + :param left_closed: regex pattern for left closed boundary (default + matches '['). + :param right_open: regex pattern for right open boundary (default matches ')'). + :param right_closed: regex pattern for right closed boundary (default + matches ']'). + :param pinf: regex pattern for positive infinity (default matches '+inf'). + :param ninf: regex pattern for negative infinity (default matches '-inf'). + :param klass: class to use for creating intervals (default to Interval). + :return: an interval. + """ + ... + +def to_string(interval, conv=..., *, disj=..., sep=..., left_open=..., left_closed=..., right_open=..., right_closed=..., pinf=..., ninf=...): # -> str: + """ + Export given interval to string. + + :param interval: an interval. + :param conv: function that is used to represent a bound (default is `repr`). + :param disj: string representing disjunctive operator (default is ' | '). + :param sep: string representing bound separator (default is ','). + :param left_open: string representing left open boundary (default is '('). + :param left_closed: string representing left closed boundary (default is '['). + :param right_open: string representing right open boundary (default is ')'). + :param right_closed: string representing right closed boundary (default is ']'). + :param pinf: string representing a positive infinity (default is '+inf'). + :param ninf: string representing a negative infinity (default is '-inf'). + :return: a string representation for given interval. + """ + ... + +def from_data(data, conv=..., *, pinf=..., ninf=..., klass=...): # -> Interval: + """ + Import an interval from a list of 4-uples (left, lower, upper, right). + + :param data: a list of 4-uples (left, lower, upper, right). + :param conv: function to convert bound values, default to identity. + :param pinf: value used to represent positive infinity. + :param ninf: value used to represent negative infinity. + :param klass: class to use for creating intervals (default to Interval). + :return: an interval. + """ + ... + +def to_data(interval, conv=..., *, pinf=..., ninf=...): # -> list[Any]: + """ + Export given interval to a list of 4-uples (left, lower, upper, right). + + :param interval: an interval. + :param conv: function to convert bound values, default to identity. + :param pinf: value used to encode positive infinity. + :param ninf: value used to encode negative infinity. + :return: a list of 4-uples (left, lower, upper, right) + """ + ... + From 669f289bf02e048be5d114fc92e8990c4f555989 Mon Sep 17 00:00:00 2001 From: Fuexfollets Date: Mon, 24 Mar 2025 13:59:04 -0400 Subject: [PATCH 10/23] Formatted typestubs. Reverted dict.py to commit 0241a66 --- portion/dict.py | 176 +++++++++-------------------------- typings/portion/__init__.pyi | 21 ++++- typings/portion/api.pyi | 8 +- typings/portion/const.pyi | 82 +++++----------- typings/portion/dict.pyi | 164 +++++++++++++++----------------- typings/portion/func.pyi | 17 ++-- typings/portion/interval.pyi | 127 +++++++++++-------------- typings/portion/io.pyi | 35 ++++++- 8 files changed, 259 insertions(+), 371 deletions(-) diff --git a/portion/dict.py b/portion/dict.py index 4fc0977..0462219 100644 --- a/portion/dict.py +++ b/portion/dict.py @@ -1,40 +1,18 @@ -# pyright: reportIncompatibleMethodOverride=false -# pyright: reportMissingTypeStubs=false - import contextlib -from collections.abc import ( - Collection, - Iterable, - Iterator, - Mapping, - MutableMapping, -) -from typing import Generic, Optional, Protocol, TypeVar, Union, cast, overload +from collections.abc import Mapping, MutableMapping from sortedcontainers import SortedDict -from sortedcontainers.sorteddict import ItemsView, KeysView, ValuesView from .const import Bound from .interval import Interval -def _sortkey(i: tuple[Interval, object]) -> tuple[object, bool]: +def _sortkey(i): # Sort by lower bound, closed first return (i[0].lower, i[0].left is Bound.OPEN) -V = TypeVar("V") - - -class HowToCombineSingle(Protocol): - def __call__(self, x: V, y: V) -> V: ... - - -class HowToCombineWithInterval(Protocol): - def __call__(self, x: V, y: V, i: Interval) -> V: ... - - -class IntervalDict(Generic[V], MutableMapping[object, V]): +class IntervalDict(MutableMapping): """ An IntervalDict is a dict-like data structure that maps from intervals to data,where keys can be single values or Interval instances. @@ -52,17 +30,12 @@ class IntervalDict(Generic[V], MutableMapping[object, V]): values (not keys) that are stored. """ - __slots__: tuple[str, ...] = ("_storage",) + __slots__ = ("_storage",) # Class to use when creating Interval instances - _klass: type = Interval + _klass = Interval - def __init__( - self, - mapping_or_iterable: Union[ - Mapping[object, V], Iterable[tuple[object, V]], None - ] = None, - ): + def __init__(self, mapping_or_iterable=None): """ Return a new IntervalDict. @@ -73,15 +46,13 @@ def __init__( :param mapping_or_iterable: optional mapping or iterable. """ - self._storage: SortedDict = SortedDict( - _sortkey - ) # Mapping from intervals to values + self._storage = SortedDict(_sortkey) # Mapping from intervals to values if mapping_or_iterable is not None: self.update(mapping_or_iterable) @classmethod - def _from_items(cls, items: Collection[tuple[object, V]]): + def _from_items(cls, items): """ Fast creation of an IntervalDict with the provided items. @@ -111,17 +82,7 @@ def copy(self): """ return self.__class__._from_items(self.items()) - @overload - def get( - self, key: Interval, default: Optional[V] = None - ) -> "IntervalDict[V] | None": ... - - @overload - def get(self, key: object, default: V = None) -> Optional[V]: ... - - def get( - self, key: Union[object, Interval], default: Optional[V] = None - ) -> Union["IntervalDict[V]", V, None]: + def get(self, key, default=None): """ Return the values associated to given key. @@ -144,7 +105,7 @@ def get( except KeyError: return default - def find(self, value: V) -> Interval: + def find(self, value): """ Return a (possibly empty) Interval i such that self[i] = value, and self[~i] != value. @@ -152,18 +113,9 @@ def find(self, value: V) -> Interval: :param value: value to look for. :return: an Interval instance. """ - return cast( - Interval, - self._klass( - *( - i - for i, v in cast(ItemsView[Interval, V], self._storage.items()) - if v == value - ) - ), - ) + return self._klass(*(i for i, v in self._storage.items() if v == value)) - def items(self) -> ItemsView[Interval, V]: + def items(self): """ Return a view object on the contained items sorted by their key (see https://docs.python.org/3/library/stdtypes.html#dict-views). @@ -172,7 +124,7 @@ def items(self) -> ItemsView[Interval, V]: """ return self._storage.items() - def keys(self) -> KeysView[Interval]: + def keys(self): """ Return a view object on the contained keys (sorted) (see https://docs.python.org/3/library/stdtypes.html#dict-views). @@ -181,7 +133,7 @@ def keys(self) -> KeysView[Interval]: """ return self._storage.keys() - def values(self) -> ValuesView[V]: + def values(self): """ Return a view object on the contained values sorted by their key (see https://docs.python.org/3/library/stdtypes.html#dict-views). @@ -190,23 +142,15 @@ def values(self) -> ValuesView[V]: """ return self._storage.values() - def domain(self) -> Interval: + def domain(self): """ Return an Interval corresponding to the domain of this IntervalDict. :return: an Interval. """ - return cast(Interval, self._klass(*self._storage.keys())) + return self._klass(*self._storage.keys()) - @overload - def pop(self, key: Interval, default: Optional[V] = None) -> "IntervalDict[V]": ... - - @overload - def pop(self, key: object, default: Optional[V] = None) -> Optional[V]: ... - - def pop( - self, key: object, default: Optional[V] = None - ) -> Union["IntervalDict[V]", V, None]: + def pop(self, key, default=None): """ Remove key and return the corresponding value if key is not an Interval. If key is an interval, it returns an IntervalDict instance. @@ -229,26 +173,16 @@ def pop( del self[key] return value - def popitem(self) -> tuple[Interval, V]: + def popitem(self): """ Remove and return some (key, value) pair as a 2-tuple. Raise KeyError if D is empty. :return: a (key, value) pair. """ - return cast(tuple[Interval, V], self._storage.popitem()) - - @overload - def setdefault( - self, key: Interval, default: Optional[V] = None - ) -> "IntervalDict[V]": ... + return self._storage.popitem() - @overload - def setdefault(self, key: object, default: Optional[V] = None) -> V: ... - - def setdefault( - self, key: object, default: Optional[V] = None - ) -> Union[V, "IntervalDict[V]", None]: + def setdefault(self, key, default=None): """ Return given key. If it does not exist, set its value to given default and return it. @@ -259,23 +193,16 @@ def setdefault( """ if isinstance(key, Interval): value = self.get(key, default) - if value is not None: - self.update(value) + self.update(value) return value else: try: return self[key] except KeyError: - if default is not None: - self[key] = default + self[key] = default return default - def update( - self, - mapping_or_iterable: Union[ - Mapping[object, V], Iterable[tuple[object, V]], type["IntervalDict[V]"] - ], - ): + def update(self, mapping_or_iterable): """ Update current IntervalDict with provided values. @@ -286,21 +213,14 @@ def update( :param mapping_or_iterable: mapping or iterable. """ if isinstance(mapping_or_iterable, Mapping): - data = cast(ItemsView[Interval, V], mapping_or_iterable.items()) + data = mapping_or_iterable.items() else: data = mapping_or_iterable - for i, v in cast(Collection[tuple[object, V]], data): + for i, v in data: self[i] = v - def combine( - self, - other: "IntervalDict[V]", - how: Union[HowToCombineSingle, HowToCombineWithInterval], - *, - missing: V = ..., - pass_interval: bool = False, - ) -> "IntervalDict[V]": + def combine(self, other, how, *, missing=..., pass_interval=False): """ Return a new IntervalDict that combines the values from current and provided IntervalDict. @@ -326,12 +246,10 @@ def combine( new_items = [] if not pass_interval: - how = cast(HowToCombineSingle, how) - def _how(x: V, y: V, i: Interval) -> V: + def _how(x, y, i): return how(x, y) else: - how = cast(HowToCombineWithInterval, how) _how = how dom1, dom2 = self.domain(), other.domain() @@ -356,7 +274,7 @@ def _how(x: V, y: V, i: Interval) -> V: return self.__class__(new_items) - def as_dict(self, atomic: bool = False) -> dict[Interval, V]: + def as_dict(self, atomic=False): """ Return the content as a classical Python dict. @@ -372,16 +290,10 @@ def as_dict(self, atomic: bool = False) -> dict[Interval, V]: else: return dict(self._storage) - @overload - def __getitem__(self, key: Interval) -> "IntervalDict[V]": ... - - @overload - def __getitem__(self, key: object) -> V: ... - - def __getitem__(self, key: Union[object, Interval]) -> Union[V, "IntervalDict[V]"]: + def __getitem__(self, key): if isinstance(key, Interval): items = [] - for i, v in cast(ItemsView[Interval, V], self._storage.items()): + for i, v in self._storage.items(): # Early out if key.upper < i.lower: break @@ -391,7 +303,7 @@ def __getitem__(self, key: Union[object, Interval]) -> Union[V, "IntervalDict[V] items.append((intersection, v)) return self.__class__._from_items(items) else: - for i, v in cast(ItemsView[Interval, V], self._storage.items()): + for i, v in self._storage.items(): # Early out if key < i.lower: break @@ -399,13 +311,11 @@ def __getitem__(self, key: Union[object, Interval]) -> Union[V, "IntervalDict[V] return v raise KeyError(key) - def __setitem__(self, key: Union[object, Interval], value: Optional[V]): + def __setitem__(self, key, value): if isinstance(key, Interval): interval = key else: - interval = cast( - Interval, self._klass.from_atomic(Bound.CLOSED, key, key, Bound.CLOSED) - ) + interval = self._klass.from_atomic(Bound.CLOSED, key, key, Bound.CLOSED) if interval.empty: return @@ -414,7 +324,7 @@ def __setitem__(self, key: Union[object, Interval], value: Optional[V]): added_items = [] found = False - for i, v in cast(ItemsView[Interval, V], self._storage.items()): + for i, v in self._storage.items(): if value == v: found = True # Extend existing key @@ -437,7 +347,7 @@ def __setitem__(self, key: Union[object, Interval], value: Optional[V]): for key, value in added_items: self._storage[key] = value - def __delitem__(self, key: Union[object, Interval]): + def __delitem__(self, key): if isinstance(key, Interval): interval = key else: @@ -472,22 +382,22 @@ def __delitem__(self, key: Union[object, Interval]): for key, value in added_items: self._storage[key] = value - def __or__(self, other: "IntervalDict[V]") -> "IntervalDict[V]": + def __or__(self, other): d = self.copy() d.update(other) return d - def __ior__(self, other: "IntervalDict[V]") -> "IntervalDict[V]": + def __ior__(self, other): self.update(other) return self - def __iter__(self) -> Iterator[object]: + def __iter__(self): return iter(self._storage) - def __len__(self) -> int: + def __len__(self): return len(self._storage) - def __contains__(self, key: object) -> bool: + def __contains__(self, key): return key in self.domain() def __repr__(self): @@ -495,8 +405,8 @@ def __repr__(self): ", ".join(f"{i!r}: {v!r}" for i, v in self.items()), ) - def __eq__(self, other: object) -> bool: + def __eq__(self, other): if isinstance(other, IntervalDict): return self.as_dict() == other.as_dict() - - return NotImplemented + else: + return NotImplemented diff --git a/typings/portion/__init__.pyi b/typings/portion/__init__.pyi index 97231ce..8c10479 100644 --- a/typings/portion/__init__.pyi +++ b/typings/portion/__init__.pyi @@ -10,7 +10,26 @@ from .func import closed, closedopen, empty, iterate, open, openclosed, singleto from .interval import AbstractDiscreteInterval, Interval from .io import from_data, from_string, to_data, to_string -__all__ = ["create_api", "inf", "CLOSED", "OPEN", "Interval", "AbstractDiscreteInterval", "open", "closed", "openclosed", "closedopen", "singleton", "empty", "iterate", "from_string", "to_string", "from_data", "to_data", "IntervalDict"] +__all__ = [ + "create_api", + "inf", + "CLOSED", + "OPEN", + "Interval", + "AbstractDiscreteInterval", + "open", + "closed", + "openclosed", + "closedopen", + "singleton", + "empty", + "iterate", + "from_string", + "to_string", + "from_data", + "to_data", + "IntervalDict", +] CLOSED = ... OPEN = ... __version__ = ... diff --git a/typings/portion/api.pyi b/typings/portion/api.pyi index 6dbf44f..a3cccbf 100644 --- a/typings/portion/api.pyi +++ b/typings/portion/api.pyi @@ -3,14 +3,17 @@ This type stub file was generated by pyright. """ __all__ = ["create_api"] -def partial(wrapped, *args, **kwargs): # -> _Wrapped[Callable[..., Any], Any, Callable[..., Any], Any]: + +def partial( + wrapped, *args, **kwargs +): # -> _Wrapped[Callable[..., Any], Any, Callable[..., Any], Any]: """ Convenient helper that combines functools.update_wrapper and functools.partial. It has exactly the same signature than functools.partial. """ ... -def create_api(interval, *, interval_dict=..., name=...): # -> ModuleType: +def create_api(interval, *, interval_dict=..., name=...): # -> ModuleType: """Create a spe Dynamically create a module whose API is similar to the one of portion, but @@ -25,4 +28,3 @@ def create_api(interval, *, interval_dict=..., name=...): # -> ModuleType: :param name: the name of the new module. """ ... - diff --git a/typings/portion/const.pyi b/typings/portion/const.pyi index ceffc1f..b99c602 100644 --- a/typings/portion/const.pyi +++ b/typings/portion/const.pyi @@ -8,87 +8,49 @@ class Bound(enum.Enum): """ Bound types, either CLOSED for inclusive, or OPEN for exclusive. """ + CLOSED = ... OPEN = ... - def __bool__(self): - ... - - def __invert__(self): # -> Literal[Bound.CLOSED, Bound.OPEN]: - ... - - def __str__(self) -> str: + def __bool__(self): ... + def __invert__(self): # -> Literal[Bound.CLOSED, Bound.OPEN]: ... - - def __repr__(self): # -> Literal['CLOSED', 'OPEN']: + def __str__(self) -> str: ... + def __repr__(self): # -> Literal['CLOSED', 'OPEN']: ... - - class _Singleton: __instance = ... - def __new__(cls, *args, **kwargs): # -> Self: + def __new__(cls, *args, **kwargs): # -> Self: ... - - class _PInf(_Singleton): """ Represent positive infinity. """ - def __neg__(self): # -> _NInf: - ... - - def __lt__(self, o) -> bool: - ... - - def __le__(self, o) -> bool: - ... - - def __gt__(self, o) -> bool: - ... - - def __ge__(self, o) -> bool: - ... - - def __eq__(self, o) -> bool: + def __neg__(self): # -> _NInf: ... - - def __repr__(self): # -> Literal['+inf']: + def __lt__(self, o) -> bool: ... + def __le__(self, o) -> bool: ... + def __gt__(self, o) -> bool: ... + def __ge__(self, o) -> bool: ... + def __eq__(self, o) -> bool: ... + def __repr__(self): # -> Literal['+inf']: ... - - def __hash__(self) -> int: - ... - - + def __hash__(self) -> int: ... class _NInf(_Singleton): """ Represent negative infinity. """ - def __neg__(self): # -> _PInf: - ... - - def __lt__(self, o) -> bool: - ... - - def __le__(self, o) -> bool: - ... - - def __gt__(self, o) -> bool: + def __neg__(self): # -> _PInf: ... - - def __ge__(self, o) -> bool: + def __lt__(self, o) -> bool: ... + def __le__(self, o) -> bool: ... + def __gt__(self, o) -> bool: ... + def __ge__(self, o) -> bool: ... + def __eq__(self, o) -> bool: ... + def __repr__(self): # -> Literal['-inf']: ... - - def __eq__(self, o) -> bool: - ... - - def __repr__(self): # -> Literal['-inf']: - ... - - def __hash__(self) -> int: - ... - - + def __hash__(self) -> int: ... inf = ... diff --git a/typings/portion/dict.pyi b/typings/portion/dict.pyi index 4534f8a..73e19d0 100644 --- a/typings/portion/dict.pyi +++ b/typings/portion/dict.pyi @@ -8,17 +8,12 @@ from sortedcontainers.sorteddict import ItemsView, KeysView, ValuesView from .interval import Interval V = TypeVar("V") -class HowToCombineSingle(Protocol): - def __call__(self, x: V, y: V) -> V: - ... - +class HowToCombineSingle(Protocol): + def __call__(self, x: V, y: V) -> V: ... class HowToCombineWithInterval(Protocol): - def __call__(self, x: V, y: V, i: Interval) -> V: - ... - - + def __call__(self, x: V, y: V, i: Interval) -> V: ... class IntervalDict(Generic[V], MutableMapping[object, V]): """ @@ -37,9 +32,15 @@ class IntervalDict(Generic[V], MutableMapping[object, V]): mainly for convenience. Its performance mainly depends on the number of distinct values (not keys) that are stored. """ + __slots__: tuple[str, ...] = ... _klass: type = ... - def __init__(self, mapping_or_iterable: Union[Mapping[object, V], Iterable[tuple[object, V]], None] = ...) -> None: + def __init__( + self, + mapping_or_iterable: Union[ + Mapping[object, V], Iterable[tuple[object, V]], None + ] = ..., + ) -> None: """ Return a new IntervalDict. @@ -51,30 +52,30 @@ class IntervalDict(Generic[V], MutableMapping[object, V]): :param mapping_or_iterable: optional mapping or iterable. """ ... - - def clear(self): # -> None: + + def clear(self): # -> None: """ Remove all items from the IntervalDict. """ ... - - def copy(self): # -> Self: + + def copy(self): # -> Self: """ Return a shallow copy. :return: a shallow copy. """ ... - + @overload - def get(self, key: Interval, default: Optional[V] = ...) -> IntervalDict[V] | None: - ... - + def get( + self, key: Interval, default: Optional[V] = ... + ) -> IntervalDict[V] | None: ... @overload - def get(self, key: object, default: V = ...) -> Optional[V]: - ... - - def get(self, key: Union[object, Interval], default: Optional[V] = ...) -> Union[IntervalDict[V], V, None]: + def get(self, key: object, default: V = ...) -> Optional[V]: ... + def get( + self, key: Union[object, Interval], default: Optional[V] = ... + ) -> Union[IntervalDict[V], V, None]: """ Return the values associated to given key. @@ -88,7 +89,7 @@ class IntervalDict(Generic[V], MutableMapping[object, V]): :return: an IntervalDict, or a single value if key is not an Interval. """ ... - + def find(self, value: V) -> Interval: """ Return a (possibly empty) Interval i such that self[i] = value, and @@ -98,7 +99,7 @@ class IntervalDict(Generic[V], MutableMapping[object, V]): :return: an Interval instance. """ ... - + def items(self) -> ItemsView[Interval, V]: """ Return a view object on the contained items sorted by their key @@ -107,7 +108,7 @@ class IntervalDict(Generic[V], MutableMapping[object, V]): :return: a view object. """ ... - + def keys(self) -> KeysView[Interval]: """ Return a view object on the contained keys (sorted) @@ -116,7 +117,7 @@ class IntervalDict(Generic[V], MutableMapping[object, V]): :return: a view object. """ ... - + def values(self) -> ValuesView[V]: """ Return a view object on the contained values sorted by their key @@ -125,7 +126,7 @@ class IntervalDict(Generic[V], MutableMapping[object, V]): :return: a view object. """ ... - + def domain(self) -> Interval: """ Return an Interval corresponding to the domain of this IntervalDict. @@ -133,16 +134,14 @@ class IntervalDict(Generic[V], MutableMapping[object, V]): :return: an Interval. """ ... - + @overload - def pop(self, key: Interval, default: Optional[V] = ...) -> IntervalDict[V]: - ... - + def pop(self, key: Interval, default: Optional[V] = ...) -> IntervalDict[V]: ... @overload - def pop(self, key: object, default: Optional[V] = ...) -> Optional[V]: - ... - - def pop(self, key: object, default: Optional[V] = ...) -> Union[IntervalDict[V], V, None]: + def pop(self, key: object, default: Optional[V] = ...) -> Optional[V]: ... + def pop( + self, key: object, default: Optional[V] = ... + ) -> Union[IntervalDict[V], V, None]: """ Remove key and return the corresponding value if key is not an Interval. If key is an interval, it returns an IntervalDict instance. @@ -156,7 +155,7 @@ class IntervalDict(Generic[V], MutableMapping[object, V]): :return: an IntervalDict, or a single value if key is not an Interval. """ ... - + def popitem(self) -> tuple[Interval, V]: """ Remove and return some (key, value) pair as a 2-tuple. @@ -165,16 +164,16 @@ class IntervalDict(Generic[V], MutableMapping[object, V]): :return: a (key, value) pair. """ ... - + @overload - def setdefault(self, key: Interval, default: Optional[V] = ...) -> IntervalDict[V]: - ... - + def setdefault( + self, key: Interval, default: Optional[V] = ... + ) -> IntervalDict[V]: ... @overload - def setdefault(self, key: object, default: Optional[V] = ...) -> V: - ... - - def setdefault(self, key: object, default: Optional[V] = ...) -> Union[V, IntervalDict[V], None]: + def setdefault(self, key: object, default: Optional[V] = ...) -> V: ... + def setdefault( + self, key: object, default: Optional[V] = ... + ) -> Union[V, IntervalDict[V], None]: """ Return given key. If it does not exist, set its value to given default and return it. @@ -184,8 +183,13 @@ class IntervalDict(Generic[V], MutableMapping[object, V]): :return: an IntervalDict, or a single value if key is not an Interval. """ ... - - def update(self, mapping_or_iterable: Union[Mapping[object, V], Iterable[tuple[object, V]], type[IntervalDict[V]]]): # -> None: + + def update( + self, + mapping_or_iterable: Union[ + Mapping[object, V], Iterable[tuple[object, V]], type[IntervalDict[V]] + ], + ): # -> None: """ Update current IntervalDict with provided values. @@ -196,8 +200,15 @@ class IntervalDict(Generic[V], MutableMapping[object, V]): :param mapping_or_iterable: mapping or iterable. """ ... - - def combine(self, other: IntervalDict[V], how: Union[HowToCombineSingle, HowToCombineWithInterval], *, missing: V = ..., pass_interval: bool = ...) -> IntervalDict[V]: + + def combine( + self, + other: IntervalDict[V], + how: Union[HowToCombineSingle, HowToCombineWithInterval], + *, + missing: V = ..., + pass_interval: bool = ..., + ) -> IntervalDict[V]: """ Return a new IntervalDict that combines the values from current and provided IntervalDict. @@ -221,7 +232,7 @@ class IntervalDict(Generic[V], MutableMapping[object, V]): :return: a new IntervalDict instance. """ ... - + def as_dict(self, atomic: bool = ...) -> dict[Interval, V]: """ Return the content as a classical Python dict. @@ -230,44 +241,23 @@ class IntervalDict(Generic[V], MutableMapping[object, V]): :return: a Python dict. """ ... - + @overload - def __getitem__(self, key: Interval) -> IntervalDict[V]: - ... - + def __getitem__(self, key: Interval) -> IntervalDict[V]: ... @overload - def __getitem__(self, key: object) -> V: - ... - - def __getitem__(self, key: Union[object, Interval]) -> Union[V, IntervalDict[V]]: - ... - - def __setitem__(self, key: Union[object, Interval], value: Optional[V]): # -> None: - ... - - def __delitem__(self, key: Union[object, Interval]): # -> None: - ... - - def __or__(self, other: IntervalDict[V]) -> IntervalDict[V]: - ... - - def __ior__(self, other: IntervalDict[V]) -> IntervalDict[V]: - ... - - def __iter__(self) -> Iterator[object]: - ... - - def __len__(self) -> int: - ... - - def __contains__(self, key: object) -> bool: - ... - - def __repr__(self): # -> str: - ... - - def __eq__(self, other: object) -> bool: - ... - - - + def __getitem__(self, key: object) -> V: ... + def __getitem__( + self, key: Union[object, Interval] + ) -> Union[V, IntervalDict[V]]: ... + def __setitem__(self, key: Union[object, Interval], value: Optional[V]): # -> None: + ... + def __delitem__(self, key: Union[object, Interval]): # -> None: + ... + def __or__(self, other: IntervalDict[V]) -> IntervalDict[V]: ... + def __ior__(self, other: IntervalDict[V]) -> IntervalDict[V]: ... + def __iter__(self) -> Iterator[object]: ... + def __len__(self) -> int: ... + def __contains__(self, key: object) -> bool: ... + def __repr__(self): # -> str: + ... + def __eq__(self, other: object) -> bool: ... diff --git a/typings/portion/func.pyi b/typings/portion/func.pyi index 00dc2d6..c77cc16 100644 --- a/typings/portion/func.pyi +++ b/typings/portion/func.pyi @@ -2,7 +2,7 @@ This type stub file was generated by pyright. """ -def open(lower, upper, *, klass=...): # -> Interval: +def open(lower, upper, *, klass=...): # -> Interval: """ Create an open interval with given bounds. @@ -13,7 +13,7 @@ def open(lower, upper, *, klass=...): # -> Interval: """ ... -def closed(lower, upper, *, klass=...): # -> Interval: +def closed(lower, upper, *, klass=...): # -> Interval: """ Create a closed interval with given bounds. @@ -24,7 +24,7 @@ def closed(lower, upper, *, klass=...): # -> Interval: """ ... -def openclosed(lower, upper, *, klass=...): # -> Interval: +def openclosed(lower, upper, *, klass=...): # -> Interval: """ Create a left-open interval with given bounds. @@ -35,7 +35,7 @@ def openclosed(lower, upper, *, klass=...): # -> Interval: """ ... -def closedopen(lower, upper, *, klass=...): # -> Interval: +def closedopen(lower, upper, *, klass=...): # -> Interval: """ Create a right-open interval with given bounds. @@ -46,7 +46,7 @@ def closedopen(lower, upper, *, klass=...): # -> Interval: """ ... -def singleton(value, *, klass=...): # -> Interval: +def singleton(value, *, klass=...): # -> Interval: """ Create a singleton interval. @@ -56,7 +56,7 @@ def singleton(value, *, klass=...): # -> Interval: """ ... -def empty(*, klass=...): # -> Interval: +def empty(*, klass=...): # -> Interval: """ Create an empty interval. @@ -65,7 +65,9 @@ def empty(*, klass=...): # -> Interval: """ ... -def iterate(interval, step, *, base=..., reverse=...): # -> Generator[Any | object, Any, None]: +def iterate( + interval, step, *, base=..., reverse=... +): # -> Generator[Any | object, Any, None]: """ Iterate on the (discrete) values of given interval. @@ -94,4 +96,3 @@ def iterate(interval, step, *, base=..., reverse=...): # -> Generator[Any | obje :return: a lazy iterator. """ ... - diff --git a/typings/portion/interval.pyi b/typings/portion/interval.pyi index ad668c0..c6608e4 100644 --- a/typings/portion/interval.pyi +++ b/typings/portion/interval.pyi @@ -3,6 +3,7 @@ This type stub file was generated by pyright. """ Atomic = ... + def mergeable(a, b): """ Tester whether two atomic intervals can be merged (i.e. they overlap or @@ -22,6 +23,7 @@ class Interval: It can be created with Interval.from_atomic(...) or by passing Interval instances to __init__. """ + __slots__ = ... __match_args__ = ... def __init__(self, *intervals) -> None: @@ -31,9 +33,9 @@ class Interval: :param intervals: zero, one or more intervals. """ ... - + @classmethod - def from_atomic(cls, left, lower, upper, right): # -> Self: + def from_atomic(cls, left, lower, upper, right): # -> Self: """ Create an Interval instance containing a single atomic interval. @@ -43,52 +45,52 @@ class Interval: :param right: either CLOSED or OPEN. """ ... - + @property - def left(self): # -> Literal[Bound.OPEN]: + def left(self): # -> Literal[Bound.OPEN]: """ Lowest left boundary is either CLOSED or OPEN. """ ... - + @property - def lower(self): # -> _PInf: + def lower(self): # -> _PInf: """ Lowest lower bound value. """ ... - + @property - def upper(self): # -> _NInf: + def upper(self): # -> _NInf: """ Highest upper bound value. """ ... - + @property - def right(self): # -> Literal[Bound.OPEN]: + def right(self): # -> Literal[Bound.OPEN]: """ Highest right boundary is either CLOSED or OPEN. """ ... - + @property - def empty(self): # -> bool: + def empty(self): # -> bool: """ True if interval is empty, False otherwise. """ ... - + @property - def atomic(self): # -> bool: + def atomic(self): # -> bool: """ True if this interval is atomic, False otherwise. An interval is atomic if it is empty or composed of a single interval. """ ... - + @property - def enclosure(self): # -> Self: + def enclosure(self): # -> Self: """ Return the smallest interval composed of a single atomic interval that encloses the current interval. @@ -96,8 +98,10 @@ class Interval: :return: an Interval instance. """ ... - - def replace(self, left=..., lower=..., upper=..., right=..., *, ignore_inf=...): # -> Self | Any: + + def replace( + self, left=..., lower=..., upper=..., right=..., *, ignore_inf=... + ): # -> Self | Any: """ Create a new interval based on the current one and the provided values. @@ -118,8 +122,8 @@ class Interval: :return: an Interval instance """ ... - - def apply(self, func): # -> Self: + + def apply(self, func): # -> Self: """ Apply a function on each of the underlying atomic intervals and return their union as a new interval instance @@ -134,7 +138,7 @@ class Interval: :return: an Interval instance. """ ... - + def adjacent(self, other): """ Test if two intervals are adjacent. @@ -151,8 +155,8 @@ class Interval: :return: True if intervals are adjacent, False otherwise. """ ... - - def overlaps(self, other): # -> bool: + + def overlaps(self, other): # -> bool: """ Test if two intervals overlap (i.e. if their intersection is non-empty). @@ -160,7 +164,7 @@ class Interval: :return: True if intervals overlap, False otherwise. """ ... - + def intersection(self, other): """ Return the intersection of two intervals. @@ -169,7 +173,7 @@ class Interval: :return: the intersection of the intervals. """ ... - + def union(self, other): """ Return the union of two intervals. @@ -178,8 +182,8 @@ class Interval: :return: the union of the intervals. """ ... - - def contains(self, item): # -> bool: + + def contains(self, item): # -> bool: """ Test if given item is contained in this interval. This method accepts intervals and arbitrary comparable values. @@ -188,15 +192,15 @@ class Interval: :return: True if given item is contained, False otherwise. """ ... - - def complement(self): # -> Interval: + + def complement(self): # -> Interval: """ Return the complement of this interval. :return: the complement of this interval. """ ... - + def difference(self, other): """ Return the difference of two intervals. @@ -205,56 +209,33 @@ class Interval: :return: the difference of the intervals. """ ... - - def __getattr__(self, name): # -> None: - ... - - def __len__(self): # -> int: - ... - - def __iter__(self): # -> Generator[Self, Any, None]: - ... - - def __getitem__(self, item): # -> Self: - ... - - def __and__(self, other): # -> _NotImplementedType | Self: - ... - - def __or__(self, other): # -> Self | _NotImplementedType: - ... - - def __contains__(self, item): # -> bool: + + def __getattr__(self, name): # -> None: ... - - def __invert__(self): # -> Self: + def __len__(self): # -> int: ... - - def __sub__(self, other): # -> _NotImplementedType | Self: + def __iter__(self): # -> Generator[Self, Any, None]: ... - - def __eq__(self, other) -> bool: + def __getitem__(self, item): # -> Self: ... - - def __lt__(self, other) -> bool: + def __and__(self, other): # -> _NotImplementedType | Self: ... - - def __gt__(self, other) -> bool: + def __or__(self, other): # -> Self | _NotImplementedType: ... - - def __le__(self, other) -> bool: + def __contains__(self, item): # -> bool: ... - - def __ge__(self, other) -> bool: + def __invert__(self): # -> Self: ... - - def __hash__(self) -> int: + def __sub__(self, other): # -> _NotImplementedType | Self: ... - - def __repr__(self): # -> LiteralString | Literal['()']: + def __eq__(self, other) -> bool: ... + def __lt__(self, other) -> bool: ... + def __gt__(self, other) -> bool: ... + def __le__(self, other) -> bool: ... + def __ge__(self, other) -> bool: ... + def __hash__(self) -> int: ... + def __repr__(self): # -> LiteralString | Literal['()']: ... - - class AbstractDiscreteInterval(Interval): """ @@ -271,10 +252,8 @@ class AbstractDiscreteInterval(Interval): This class is still experimental and backward incompatible changes may occur even in minor or patch updates of portion. """ + _step = ... @classmethod - def from_atomic(cls, left, lower, upper, right): # -> Self: + def from_atomic(cls, left, lower, upper, right): # -> Self: ... - - - diff --git a/typings/portion/io.pyi b/typings/portion/io.pyi index 34426d4..7db005a 100644 --- a/typings/portion/io.pyi +++ b/typings/portion/io.pyi @@ -2,7 +2,21 @@ This type stub file was generated by pyright. """ -def from_string(string, conv, *, bound=..., disj=..., sep=..., left_open=..., left_closed=..., right_open=..., right_closed=..., pinf=..., ninf=..., klass=...): # -> Interval: +def from_string( + string, + conv, + *, + bound=..., + disj=..., + sep=..., + left_open=..., + left_closed=..., + right_open=..., + right_closed=..., + pinf=..., + ninf=..., + klass=..., +): # -> Interval: """ Parse given string and create an Interval instance. A converter function has to be provided to convert a bound (as string) to a value. @@ -26,7 +40,19 @@ def from_string(string, conv, *, bound=..., disj=..., sep=..., left_open=..., le """ ... -def to_string(interval, conv=..., *, disj=..., sep=..., left_open=..., left_closed=..., right_open=..., right_closed=..., pinf=..., ninf=...): # -> str: +def to_string( + interval, + conv=..., + *, + disj=..., + sep=..., + left_open=..., + left_closed=..., + right_open=..., + right_closed=..., + pinf=..., + ninf=..., +): # -> str: """ Export given interval to string. @@ -44,7 +70,7 @@ def to_string(interval, conv=..., *, disj=..., sep=..., left_open=..., left_clos """ ... -def from_data(data, conv=..., *, pinf=..., ninf=..., klass=...): # -> Interval: +def from_data(data, conv=..., *, pinf=..., ninf=..., klass=...): # -> Interval: """ Import an interval from a list of 4-uples (left, lower, upper, right). @@ -57,7 +83,7 @@ def from_data(data, conv=..., *, pinf=..., ninf=..., klass=...): # -> Interval: """ ... -def to_data(interval, conv=..., *, pinf=..., ninf=...): # -> list[Any]: +def to_data(interval, conv=..., *, pinf=..., ninf=...): # -> list[Any]: """ Export given interval to a list of 4-uples (left, lower, upper, right). @@ -68,4 +94,3 @@ def to_data(interval, conv=..., *, pinf=..., ninf=...): # -> list[Any]: :return: a list of 4-uples (left, lower, upper, right) """ ... - From d6fef310c07518f777fff108b08daa755e4b7324 Mon Sep 17 00:00:00 2001 From: Fuexfollets Date: Mon, 24 Mar 2025 14:03:33 -0400 Subject: [PATCH 11/23] ruff --fix on stubfiles --- typings/portion/__init__.pyi | 3 +-- typings/portion/api.pyi | 2 -- typings/portion/dict.pyi | 17 ++--------------- typings/portion/func.pyi | 7 ------- typings/portion/interval.pyi | 19 ------------------- typings/portion/io.pyi | 4 ---- 6 files changed, 3 insertions(+), 49 deletions(-) diff --git a/typings/portion/__init__.pyi b/typings/portion/__init__.pyi index 8c10479..49acafc 100644 --- a/typings/portion/__init__.pyi +++ b/typings/portion/__init__.pyi @@ -2,9 +2,8 @@ This type stub file was generated by pyright. """ -import importlib.metadata from .api import create_api -from .const import Bound, inf +from .const import inf from .dict import IntervalDict from .func import closed, closedopen, empty, iterate, open, openclosed, singleton from .interval import AbstractDiscreteInterval, Interval diff --git a/typings/portion/api.pyi b/typings/portion/api.pyi index a3cccbf..269930f 100644 --- a/typings/portion/api.pyi +++ b/typings/portion/api.pyi @@ -11,7 +11,6 @@ def partial( Convenient helper that combines functools.update_wrapper and functools.partial. It has exactly the same signature than functools.partial. """ - ... def create_api(interval, *, interval_dict=..., name=...): # -> ModuleType: """Create a spe @@ -27,4 +26,3 @@ def create_api(interval, *, interval_dict=..., name=...): # -> ModuleType: :param interval_dict: a subclass of IntervalDict. :param name: the name of the new module. """ - ... diff --git a/typings/portion/dict.pyi b/typings/portion/dict.pyi index 73e19d0..d9c7300 100644 --- a/typings/portion/dict.pyi +++ b/typings/portion/dict.pyi @@ -4,7 +4,9 @@ This type stub file was generated by pyright. from collections.abc import Iterable, Iterator, Mapping, MutableMapping from typing import Generic, Optional, Protocol, TypeVar, Union, overload + from sortedcontainers.sorteddict import ItemsView, KeysView, ValuesView + from .interval import Interval V = TypeVar("V") @@ -51,13 +53,11 @@ class IntervalDict(Generic[V], MutableMapping[object, V]): :param mapping_or_iterable: optional mapping or iterable. """ - ... def clear(self): # -> None: """ Remove all items from the IntervalDict. """ - ... def copy(self): # -> Self: """ @@ -65,7 +65,6 @@ class IntervalDict(Generic[V], MutableMapping[object, V]): :return: a shallow copy. """ - ... @overload def get( @@ -88,7 +87,6 @@ class IntervalDict(Generic[V], MutableMapping[object, V]): :param default: default value (default to None). :return: an IntervalDict, or a single value if key is not an Interval. """ - ... def find(self, value: V) -> Interval: """ @@ -98,7 +96,6 @@ class IntervalDict(Generic[V], MutableMapping[object, V]): :param value: value to look for. :return: an Interval instance. """ - ... def items(self) -> ItemsView[Interval, V]: """ @@ -107,7 +104,6 @@ class IntervalDict(Generic[V], MutableMapping[object, V]): :return: a view object. """ - ... def keys(self) -> KeysView[Interval]: """ @@ -116,7 +112,6 @@ class IntervalDict(Generic[V], MutableMapping[object, V]): :return: a view object. """ - ... def values(self) -> ValuesView[V]: """ @@ -125,7 +120,6 @@ class IntervalDict(Generic[V], MutableMapping[object, V]): :return: a view object. """ - ... def domain(self) -> Interval: """ @@ -133,7 +127,6 @@ class IntervalDict(Generic[V], MutableMapping[object, V]): :return: an Interval. """ - ... @overload def pop(self, key: Interval, default: Optional[V] = ...) -> IntervalDict[V]: ... @@ -154,7 +147,6 @@ class IntervalDict(Generic[V], MutableMapping[object, V]): :param default: optional default value. :return: an IntervalDict, or a single value if key is not an Interval. """ - ... def popitem(self) -> tuple[Interval, V]: """ @@ -163,7 +155,6 @@ class IntervalDict(Generic[V], MutableMapping[object, V]): :return: a (key, value) pair. """ - ... @overload def setdefault( @@ -182,7 +173,6 @@ class IntervalDict(Generic[V], MutableMapping[object, V]): :param default: default value (default to None). :return: an IntervalDict, or a single value if key is not an Interval. """ - ... def update( self, @@ -199,7 +189,6 @@ class IntervalDict(Generic[V], MutableMapping[object, V]): :param mapping_or_iterable: mapping or iterable. """ - ... def combine( self, @@ -231,7 +220,6 @@ class IntervalDict(Generic[V], MutableMapping[object, V]): :param pass_interval: if set, provide the current interval to the how function. :return: a new IntervalDict instance. """ - ... def as_dict(self, atomic: bool = ...) -> dict[Interval, V]: """ @@ -240,7 +228,6 @@ class IntervalDict(Generic[V], MutableMapping[object, V]): :param atomic: whether keys are atomic intervals. :return: a Python dict. """ - ... @overload def __getitem__(self, key: Interval) -> IntervalDict[V]: ... diff --git a/typings/portion/func.pyi b/typings/portion/func.pyi index c77cc16..ed8c575 100644 --- a/typings/portion/func.pyi +++ b/typings/portion/func.pyi @@ -11,7 +11,6 @@ def open(lower, upper, *, klass=...): # -> Interval: :param klass: class to use for creating intervals (default to Interval). :return: an interval. """ - ... def closed(lower, upper, *, klass=...): # -> Interval: """ @@ -22,7 +21,6 @@ def closed(lower, upper, *, klass=...): # -> Interval: :param klass: class to use for creating intervals (default to Interval). :return: an interval. """ - ... def openclosed(lower, upper, *, klass=...): # -> Interval: """ @@ -33,7 +31,6 @@ def openclosed(lower, upper, *, klass=...): # -> Interval: :param klass: class to use for creating intervals (default to Interval). :return: an interval. """ - ... def closedopen(lower, upper, *, klass=...): # -> Interval: """ @@ -44,7 +41,6 @@ def closedopen(lower, upper, *, klass=...): # -> Interval: :param klass: class to use for creating intervals (default to Interval). :return: an interval. """ - ... def singleton(value, *, klass=...): # -> Interval: """ @@ -54,7 +50,6 @@ def singleton(value, *, klass=...): # -> Interval: :param klass: class to use for creating intervals (default to Interval). :return: an interval. """ - ... def empty(*, klass=...): # -> Interval: """ @@ -63,7 +58,6 @@ def empty(*, klass=...): # -> Interval: :param klass: class to use for creating intervals (default to Interval). :return: an interval. """ - ... def iterate( interval, step, *, base=..., reverse=... @@ -95,4 +89,3 @@ def iterate( :param reverse: set to True for descending order. :return: a lazy iterator. """ - ... diff --git a/typings/portion/interval.pyi b/typings/portion/interval.pyi index c6608e4..4d2e006 100644 --- a/typings/portion/interval.pyi +++ b/typings/portion/interval.pyi @@ -13,7 +13,6 @@ def mergeable(a, b): :param b: an atomic interval. :return: True if mergeable, False otherwise. """ - ... class Interval: """ @@ -32,7 +31,6 @@ class Interval: :param intervals: zero, one or more intervals. """ - ... @classmethod def from_atomic(cls, left, lower, upper, right): # -> Self: @@ -44,42 +42,36 @@ class Interval: :param upper: value of the upper bound. :param right: either CLOSED or OPEN. """ - ... @property def left(self): # -> Literal[Bound.OPEN]: """ Lowest left boundary is either CLOSED or OPEN. """ - ... @property def lower(self): # -> _PInf: """ Lowest lower bound value. """ - ... @property def upper(self): # -> _NInf: """ Highest upper bound value. """ - ... @property def right(self): # -> Literal[Bound.OPEN]: """ Highest right boundary is either CLOSED or OPEN. """ - ... @property def empty(self): # -> bool: """ True if interval is empty, False otherwise. """ - ... @property def atomic(self): # -> bool: @@ -87,7 +79,6 @@ class Interval: True if this interval is atomic, False otherwise. An interval is atomic if it is empty or composed of a single interval. """ - ... @property def enclosure(self): # -> Self: @@ -97,7 +88,6 @@ class Interval: :return: an Interval instance. """ - ... def replace( self, left=..., lower=..., upper=..., right=..., *, ignore_inf=... @@ -121,7 +111,6 @@ class Interval: is True). :return: an Interval instance """ - ... def apply(self, func): # -> Self: """ @@ -137,7 +126,6 @@ class Interval: :param func: function to apply on each underlying atomic interval. :return: an Interval instance. """ - ... def adjacent(self, other): """ @@ -154,7 +142,6 @@ class Interval: :param other: an interval. :return: True if intervals are adjacent, False otherwise. """ - ... def overlaps(self, other): # -> bool: """ @@ -163,7 +150,6 @@ class Interval: :param other: an interval. :return: True if intervals overlap, False otherwise. """ - ... def intersection(self, other): """ @@ -172,7 +158,6 @@ class Interval: :param other: an interval. :return: the intersection of the intervals. """ - ... def union(self, other): """ @@ -181,7 +166,6 @@ class Interval: :param other: an interval. :return: the union of the intervals. """ - ... def contains(self, item): # -> bool: """ @@ -191,7 +175,6 @@ class Interval: :param item: an interval or any arbitrary comparable value. :return: True if given item is contained, False otherwise. """ - ... def complement(self): # -> Interval: """ @@ -199,7 +182,6 @@ class Interval: :return: the complement of this interval. """ - ... def difference(self, other): """ @@ -208,7 +190,6 @@ class Interval: :param other: an interval. :return: the difference of the intervals. """ - ... def __getattr__(self, name): # -> None: ... diff --git a/typings/portion/io.pyi b/typings/portion/io.pyi index 7db005a..93589d6 100644 --- a/typings/portion/io.pyi +++ b/typings/portion/io.pyi @@ -38,7 +38,6 @@ def from_string( :param klass: class to use for creating intervals (default to Interval). :return: an interval. """ - ... def to_string( interval, @@ -68,7 +67,6 @@ def to_string( :param ninf: string representing a negative infinity (default is '-inf'). :return: a string representation for given interval. """ - ... def from_data(data, conv=..., *, pinf=..., ninf=..., klass=...): # -> Interval: """ @@ -81,7 +79,6 @@ def from_data(data, conv=..., *, pinf=..., ninf=..., klass=...): # -> Interval: :param klass: class to use for creating intervals (default to Interval). :return: an interval. """ - ... def to_data(interval, conv=..., *, pinf=..., ninf=...): # -> list[Any]: """ @@ -93,4 +90,3 @@ def to_data(interval, conv=..., *, pinf=..., ninf=...): # -> list[Any]: :param ninf: value used to encode negative infinity. :return: a list of 4-uples (left, lower, upper, right) """ - ... From 917d0e7d0faa3e7cbce3a546d502083114d958ad Mon Sep 17 00:00:00 2001 From: Fuexfollets Date: Mon, 24 Mar 2025 14:09:39 -0400 Subject: [PATCH 12/23] Reverted typestubs. Added `typings` to tool.ruff extend-exclude --- pyproject.toml | 2 +- typings/portion/__init__.pyi | 24 +---- typings/portion/api.pyi | 10 +- typings/portion/const.pyi | 82 +++++++++++----- typings/portion/dict.pyi | 180 +++++++++++++++-------------------- typings/portion/func.pyi | 24 +++-- typings/portion/interval.pyi | 146 +++++++++++++++++----------- typings/portion/io.pyi | 39 ++------ 8 files changed, 264 insertions(+), 243 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 462efb6..5ba2d77 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,7 +34,7 @@ classifiers = [ test = ["pytest ~= 7.0", "coverage ~= 6.0", "ruff >= 0.6.9"] [tool.ruff] -extend-exclude = ["tests/"] +extend-exclude = ["tests/", "typings/"] [tool.ruff.lint] select = [ diff --git a/typings/portion/__init__.pyi b/typings/portion/__init__.pyi index 49acafc..97231ce 100644 --- a/typings/portion/__init__.pyi +++ b/typings/portion/__init__.pyi @@ -2,33 +2,15 @@ This type stub file was generated by pyright. """ +import importlib.metadata from .api import create_api -from .const import inf +from .const import Bound, inf from .dict import IntervalDict from .func import closed, closedopen, empty, iterate, open, openclosed, singleton from .interval import AbstractDiscreteInterval, Interval from .io import from_data, from_string, to_data, to_string -__all__ = [ - "create_api", - "inf", - "CLOSED", - "OPEN", - "Interval", - "AbstractDiscreteInterval", - "open", - "closed", - "openclosed", - "closedopen", - "singleton", - "empty", - "iterate", - "from_string", - "to_string", - "from_data", - "to_data", - "IntervalDict", -] +__all__ = ["create_api", "inf", "CLOSED", "OPEN", "Interval", "AbstractDiscreteInterval", "open", "closed", "openclosed", "closedopen", "singleton", "empty", "iterate", "from_string", "to_string", "from_data", "to_data", "IntervalDict"] CLOSED = ... OPEN = ... __version__ = ... diff --git a/typings/portion/api.pyi b/typings/portion/api.pyi index 269930f..6dbf44f 100644 --- a/typings/portion/api.pyi +++ b/typings/portion/api.pyi @@ -3,16 +3,14 @@ This type stub file was generated by pyright. """ __all__ = ["create_api"] - -def partial( - wrapped, *args, **kwargs -): # -> _Wrapped[Callable[..., Any], Any, Callable[..., Any], Any]: +def partial(wrapped, *args, **kwargs): # -> _Wrapped[Callable[..., Any], Any, Callable[..., Any], Any]: """ Convenient helper that combines functools.update_wrapper and functools.partial. It has exactly the same signature than functools.partial. """ + ... -def create_api(interval, *, interval_dict=..., name=...): # -> ModuleType: +def create_api(interval, *, interval_dict=..., name=...): # -> ModuleType: """Create a spe Dynamically create a module whose API is similar to the one of portion, but @@ -26,3 +24,5 @@ def create_api(interval, *, interval_dict=..., name=...): # -> ModuleType: :param interval_dict: a subclass of IntervalDict. :param name: the name of the new module. """ + ... + diff --git a/typings/portion/const.pyi b/typings/portion/const.pyi index b99c602..ceffc1f 100644 --- a/typings/portion/const.pyi +++ b/typings/portion/const.pyi @@ -8,49 +8,87 @@ class Bound(enum.Enum): """ Bound types, either CLOSED for inclusive, or OPEN for exclusive. """ - CLOSED = ... OPEN = ... - def __bool__(self): ... - def __invert__(self): # -> Literal[Bound.CLOSED, Bound.OPEN]: + def __bool__(self): + ... + + def __invert__(self): # -> Literal[Bound.CLOSED, Bound.OPEN]: + ... + + def __str__(self) -> str: ... - def __str__(self) -> str: ... - def __repr__(self): # -> Literal['CLOSED', 'OPEN']: + + def __repr__(self): # -> Literal['CLOSED', 'OPEN']: ... + + class _Singleton: __instance = ... - def __new__(cls, *args, **kwargs): # -> Self: + def __new__(cls, *args, **kwargs): # -> Self: ... + + class _PInf(_Singleton): """ Represent positive infinity. """ - def __neg__(self): # -> _NInf: + def __neg__(self): # -> _NInf: + ... + + def __lt__(self, o) -> bool: + ... + + def __le__(self, o) -> bool: + ... + + def __gt__(self, o) -> bool: + ... + + def __ge__(self, o) -> bool: + ... + + def __eq__(self, o) -> bool: ... - def __lt__(self, o) -> bool: ... - def __le__(self, o) -> bool: ... - def __gt__(self, o) -> bool: ... - def __ge__(self, o) -> bool: ... - def __eq__(self, o) -> bool: ... - def __repr__(self): # -> Literal['+inf']: + + def __repr__(self): # -> Literal['+inf']: ... - def __hash__(self) -> int: ... + + def __hash__(self) -> int: + ... + + class _NInf(_Singleton): """ Represent negative infinity. """ - def __neg__(self): # -> _PInf: + def __neg__(self): # -> _PInf: + ... + + def __lt__(self, o) -> bool: + ... + + def __le__(self, o) -> bool: + ... + + def __gt__(self, o) -> bool: ... - def __lt__(self, o) -> bool: ... - def __le__(self, o) -> bool: ... - def __gt__(self, o) -> bool: ... - def __ge__(self, o) -> bool: ... - def __eq__(self, o) -> bool: ... - def __repr__(self): # -> Literal['-inf']: + + def __ge__(self, o) -> bool: ... - def __hash__(self) -> int: ... + + def __eq__(self, o) -> bool: + ... + + def __repr__(self): # -> Literal['-inf']: + ... + + def __hash__(self) -> int: + ... + + inf = ... diff --git a/typings/portion/dict.pyi b/typings/portion/dict.pyi index d9c7300..44c5191 100644 --- a/typings/portion/dict.pyi +++ b/typings/portion/dict.pyi @@ -2,22 +2,10 @@ This type stub file was generated by pyright. """ -from collections.abc import Iterable, Iterator, Mapping, MutableMapping -from typing import Generic, Optional, Protocol, TypeVar, Union, overload - -from sortedcontainers.sorteddict import ItemsView, KeysView, ValuesView - +from collections.abc import MutableMapping from .interval import Interval -V = TypeVar("V") - -class HowToCombineSingle(Protocol): - def __call__(self, x: V, y: V) -> V: ... - -class HowToCombineWithInterval(Protocol): - def __call__(self, x: V, y: V, i: Interval) -> V: ... - -class IntervalDict(Generic[V], MutableMapping[object, V]): +class IntervalDict(MutableMapping): """ An IntervalDict is a dict-like data structure that maps from intervals to data,where keys can be single values or Interval instances. @@ -34,15 +22,9 @@ class IntervalDict(Generic[V], MutableMapping[object, V]): mainly for convenience. Its performance mainly depends on the number of distinct values (not keys) that are stored. """ - - __slots__: tuple[str, ...] = ... - _klass: type = ... - def __init__( - self, - mapping_or_iterable: Union[ - Mapping[object, V], Iterable[tuple[object, V]], None - ] = ..., - ) -> None: + __slots__ = ... + _klass = Interval + def __init__(self, mapping_or_iterable=...) -> None: """ Return a new IntervalDict. @@ -53,28 +35,23 @@ class IntervalDict(Generic[V], MutableMapping[object, V]): :param mapping_or_iterable: optional mapping or iterable. """ - - def clear(self): # -> None: + ... + + def clear(self): # -> None: """ Remove all items from the IntervalDict. """ - - def copy(self): # -> Self: + ... + + def copy(self): # -> Self: """ Return a shallow copy. :return: a shallow copy. """ - - @overload - def get( - self, key: Interval, default: Optional[V] = ... - ) -> IntervalDict[V] | None: ... - @overload - def get(self, key: object, default: V = ...) -> Optional[V]: ... - def get( - self, key: Union[object, Interval], default: Optional[V] = ... - ) -> Union[IntervalDict[V], V, None]: + ... + + def get(self, key, default=...): # -> Self | None: """ Return the values associated to given key. @@ -87,8 +64,9 @@ class IntervalDict(Generic[V], MutableMapping[object, V]): :param default: default value (default to None). :return: an IntervalDict, or a single value if key is not an Interval. """ - - def find(self, value: V) -> Interval: + ... + + def find(self, value): # -> _klass: """ Return a (possibly empty) Interval i such that self[i] = value, and self[~i] != value. @@ -96,45 +74,44 @@ class IntervalDict(Generic[V], MutableMapping[object, V]): :param value: value to look for. :return: an Interval instance. """ - - def items(self) -> ItemsView[Interval, V]: + ... + + def items(self): # -> SortedItemsView: """ Return a view object on the contained items sorted by their key (see https://docs.python.org/3/library/stdtypes.html#dict-views). :return: a view object. """ - - def keys(self) -> KeysView[Interval]: + ... + + def keys(self): # -> SortedKeysView: """ Return a view object on the contained keys (sorted) (see https://docs.python.org/3/library/stdtypes.html#dict-views). :return: a view object. """ - - def values(self) -> ValuesView[V]: + ... + + def values(self): # -> SortedValuesView: """ Return a view object on the contained values sorted by their key (see https://docs.python.org/3/library/stdtypes.html#dict-views). :return: a view object. """ - - def domain(self) -> Interval: + ... + + def domain(self): # -> _klass: """ Return an Interval corresponding to the domain of this IntervalDict. :return: an Interval. """ - - @overload - def pop(self, key: Interval, default: Optional[V] = ...) -> IntervalDict[V]: ... - @overload - def pop(self, key: object, default: Optional[V] = ...) -> Optional[V]: ... - def pop( - self, key: object, default: Optional[V] = ... - ) -> Union[IntervalDict[V], V, None]: + ... + + def pop(self, key, default=...): # -> Self | None: """ Remove key and return the corresponding value if key is not an Interval. If key is an interval, it returns an IntervalDict instance. @@ -147,24 +124,18 @@ class IntervalDict(Generic[V], MutableMapping[object, V]): :param default: optional default value. :return: an IntervalDict, or a single value if key is not an Interval. """ - - def popitem(self) -> tuple[Interval, V]: + ... + + def popitem(self): # -> tuple[Any, Any]: """ Remove and return some (key, value) pair as a 2-tuple. Raise KeyError if D is empty. :return: a (key, value) pair. """ - - @overload - def setdefault( - self, key: Interval, default: Optional[V] = ... - ) -> IntervalDict[V]: ... - @overload - def setdefault(self, key: object, default: Optional[V] = ...) -> V: ... - def setdefault( - self, key: object, default: Optional[V] = ... - ) -> Union[V, IntervalDict[V], None]: + ... + + def setdefault(self, key, default=...): # -> Self | None: """ Return given key. If it does not exist, set its value to given default and return it. @@ -173,13 +144,9 @@ class IntervalDict(Generic[V], MutableMapping[object, V]): :param default: default value (default to None). :return: an IntervalDict, or a single value if key is not an Interval. """ - - def update( - self, - mapping_or_iterable: Union[ - Mapping[object, V], Iterable[tuple[object, V]], type[IntervalDict[V]] - ], - ): # -> None: + ... + + def update(self, mapping_or_iterable): # -> None: """ Update current IntervalDict with provided values. @@ -189,15 +156,9 @@ class IntervalDict(Generic[V], MutableMapping[object, V]): :param mapping_or_iterable: mapping or iterable. """ - - def combine( - self, - other: IntervalDict[V], - how: Union[HowToCombineSingle, HowToCombineWithInterval], - *, - missing: V = ..., - pass_interval: bool = ..., - ) -> IntervalDict[V]: + ... + + def combine(self, other, how, *, missing=..., pass_interval=...): # -> Self: """ Return a new IntervalDict that combines the values from current and provided IntervalDict. @@ -220,31 +181,46 @@ class IntervalDict(Generic[V], MutableMapping[object, V]): :param pass_interval: if set, provide the current interval to the how function. :return: a new IntervalDict instance. """ - - def as_dict(self, atomic: bool = ...) -> dict[Interval, V]: + ... + + def as_dict(self, atomic=...): # -> dict[Any, Any]: """ Return the content as a classical Python dict. :param atomic: whether keys are atomic intervals. :return: a Python dict. """ - - @overload - def __getitem__(self, key: Interval) -> IntervalDict[V]: ... - @overload - def __getitem__(self, key: object) -> V: ... - def __getitem__( - self, key: Union[object, Interval] - ) -> Union[V, IntervalDict[V]]: ... - def __setitem__(self, key: Union[object, Interval], value: Optional[V]): # -> None: ... - def __delitem__(self, key: Union[object, Interval]): # -> None: + + def __getitem__(self, key): # -> Self: + ... + + def __setitem__(self, key, value): # -> None: + ... + + def __delitem__(self, key): # -> None: + ... + + def __or__(self, other): # -> Self: + ... + + def __ior__(self, other): # -> Self: + ... + + def __iter__(self): # -> chain[Any]: ... - def __or__(self, other: IntervalDict[V]) -> IntervalDict[V]: ... - def __ior__(self, other: IntervalDict[V]) -> IntervalDict[V]: ... - def __iter__(self) -> Iterator[object]: ... - def __len__(self) -> int: ... - def __contains__(self, key: object) -> bool: ... - def __repr__(self): # -> str: + + def __len__(self): # -> int: ... - def __eq__(self, other: object) -> bool: ... + + def __contains__(self, key): # -> bool: + ... + + def __repr__(self): # -> str: + ... + + def __eq__(self, other) -> bool: + ... + + + diff --git a/typings/portion/func.pyi b/typings/portion/func.pyi index ed8c575..00dc2d6 100644 --- a/typings/portion/func.pyi +++ b/typings/portion/func.pyi @@ -2,7 +2,7 @@ This type stub file was generated by pyright. """ -def open(lower, upper, *, klass=...): # -> Interval: +def open(lower, upper, *, klass=...): # -> Interval: """ Create an open interval with given bounds. @@ -11,8 +11,9 @@ def open(lower, upper, *, klass=...): # -> Interval: :param klass: class to use for creating intervals (default to Interval). :return: an interval. """ + ... -def closed(lower, upper, *, klass=...): # -> Interval: +def closed(lower, upper, *, klass=...): # -> Interval: """ Create a closed interval with given bounds. @@ -21,8 +22,9 @@ def closed(lower, upper, *, klass=...): # -> Interval: :param klass: class to use for creating intervals (default to Interval). :return: an interval. """ + ... -def openclosed(lower, upper, *, klass=...): # -> Interval: +def openclosed(lower, upper, *, klass=...): # -> Interval: """ Create a left-open interval with given bounds. @@ -31,8 +33,9 @@ def openclosed(lower, upper, *, klass=...): # -> Interval: :param klass: class to use for creating intervals (default to Interval). :return: an interval. """ + ... -def closedopen(lower, upper, *, klass=...): # -> Interval: +def closedopen(lower, upper, *, klass=...): # -> Interval: """ Create a right-open interval with given bounds. @@ -41,8 +44,9 @@ def closedopen(lower, upper, *, klass=...): # -> Interval: :param klass: class to use for creating intervals (default to Interval). :return: an interval. """ + ... -def singleton(value, *, klass=...): # -> Interval: +def singleton(value, *, klass=...): # -> Interval: """ Create a singleton interval. @@ -50,18 +54,18 @@ def singleton(value, *, klass=...): # -> Interval: :param klass: class to use for creating intervals (default to Interval). :return: an interval. """ + ... -def empty(*, klass=...): # -> Interval: +def empty(*, klass=...): # -> Interval: """ Create an empty interval. :param klass: class to use for creating intervals (default to Interval). :return: an interval. """ + ... -def iterate( - interval, step, *, base=..., reverse=... -): # -> Generator[Any | object, Any, None]: +def iterate(interval, step, *, base=..., reverse=...): # -> Generator[Any | object, Any, None]: """ Iterate on the (discrete) values of given interval. @@ -89,3 +93,5 @@ def iterate( :param reverse: set to True for descending order. :return: a lazy iterator. """ + ... + diff --git a/typings/portion/interval.pyi b/typings/portion/interval.pyi index 4d2e006..ad668c0 100644 --- a/typings/portion/interval.pyi +++ b/typings/portion/interval.pyi @@ -3,7 +3,6 @@ This type stub file was generated by pyright. """ Atomic = ... - def mergeable(a, b): """ Tester whether two atomic intervals can be merged (i.e. they overlap or @@ -13,6 +12,7 @@ def mergeable(a, b): :param b: an atomic interval. :return: True if mergeable, False otherwise. """ + ... class Interval: """ @@ -22,7 +22,6 @@ class Interval: It can be created with Interval.from_atomic(...) or by passing Interval instances to __init__. """ - __slots__ = ... __match_args__ = ... def __init__(self, *intervals) -> None: @@ -31,9 +30,10 @@ class Interval: :param intervals: zero, one or more intervals. """ - + ... + @classmethod - def from_atomic(cls, left, lower, upper, right): # -> Self: + def from_atomic(cls, left, lower, upper, right): # -> Self: """ Create an Interval instance containing a single atomic interval. @@ -42,56 +42,62 @@ class Interval: :param upper: value of the upper bound. :param right: either CLOSED or OPEN. """ - + ... + @property - def left(self): # -> Literal[Bound.OPEN]: + def left(self): # -> Literal[Bound.OPEN]: """ Lowest left boundary is either CLOSED or OPEN. """ - + ... + @property - def lower(self): # -> _PInf: + def lower(self): # -> _PInf: """ Lowest lower bound value. """ - + ... + @property - def upper(self): # -> _NInf: + def upper(self): # -> _NInf: """ Highest upper bound value. """ - + ... + @property - def right(self): # -> Literal[Bound.OPEN]: + def right(self): # -> Literal[Bound.OPEN]: """ Highest right boundary is either CLOSED or OPEN. """ - + ... + @property - def empty(self): # -> bool: + def empty(self): # -> bool: """ True if interval is empty, False otherwise. """ - + ... + @property - def atomic(self): # -> bool: + def atomic(self): # -> bool: """ True if this interval is atomic, False otherwise. An interval is atomic if it is empty or composed of a single interval. """ - + ... + @property - def enclosure(self): # -> Self: + def enclosure(self): # -> Self: """ Return the smallest interval composed of a single atomic interval that encloses the current interval. :return: an Interval instance. """ - - def replace( - self, left=..., lower=..., upper=..., right=..., *, ignore_inf=... - ): # -> Self | Any: + ... + + def replace(self, left=..., lower=..., upper=..., right=..., *, ignore_inf=...): # -> Self | Any: """ Create a new interval based on the current one and the provided values. @@ -111,8 +117,9 @@ class Interval: is True). :return: an Interval instance """ - - def apply(self, func): # -> Self: + ... + + def apply(self, func): # -> Self: """ Apply a function on each of the underlying atomic intervals and return their union as a new interval instance @@ -126,7 +133,8 @@ class Interval: :param func: function to apply on each underlying atomic interval. :return: an Interval instance. """ - + ... + def adjacent(self, other): """ Test if two intervals are adjacent. @@ -142,15 +150,17 @@ class Interval: :param other: an interval. :return: True if intervals are adjacent, False otherwise. """ - - def overlaps(self, other): # -> bool: + ... + + def overlaps(self, other): # -> bool: """ Test if two intervals overlap (i.e. if their intersection is non-empty). :param other: an interval. :return: True if intervals overlap, False otherwise. """ - + ... + def intersection(self, other): """ Return the intersection of two intervals. @@ -158,7 +168,8 @@ class Interval: :param other: an interval. :return: the intersection of the intervals. """ - + ... + def union(self, other): """ Return the union of two intervals. @@ -166,8 +177,9 @@ class Interval: :param other: an interval. :return: the union of the intervals. """ - - def contains(self, item): # -> bool: + ... + + def contains(self, item): # -> bool: """ Test if given item is contained in this interval. This method accepts intervals and arbitrary comparable values. @@ -175,14 +187,16 @@ class Interval: :param item: an interval or any arbitrary comparable value. :return: True if given item is contained, False otherwise. """ - - def complement(self): # -> Interval: + ... + + def complement(self): # -> Interval: """ Return the complement of this interval. :return: the complement of this interval. """ - + ... + def difference(self, other): """ Return the difference of two intervals. @@ -190,33 +204,57 @@ class Interval: :param other: an interval. :return: the difference of the intervals. """ - - def __getattr__(self, name): # -> None: ... - def __len__(self): # -> int: + + def __getattr__(self, name): # -> None: + ... + + def __len__(self): # -> int: + ... + + def __iter__(self): # -> Generator[Self, Any, None]: + ... + + def __getitem__(self, item): # -> Self: + ... + + def __and__(self, other): # -> _NotImplementedType | Self: + ... + + def __or__(self, other): # -> Self | _NotImplementedType: ... - def __iter__(self): # -> Generator[Self, Any, None]: + + def __contains__(self, item): # -> bool: ... - def __getitem__(self, item): # -> Self: + + def __invert__(self): # -> Self: ... - def __and__(self, other): # -> _NotImplementedType | Self: + + def __sub__(self, other): # -> _NotImplementedType | Self: ... - def __or__(self, other): # -> Self | _NotImplementedType: + + def __eq__(self, other) -> bool: ... - def __contains__(self, item): # -> bool: + + def __lt__(self, other) -> bool: ... - def __invert__(self): # -> Self: + + def __gt__(self, other) -> bool: ... - def __sub__(self, other): # -> _NotImplementedType | Self: + + def __le__(self, other) -> bool: ... - def __eq__(self, other) -> bool: ... - def __lt__(self, other) -> bool: ... - def __gt__(self, other) -> bool: ... - def __le__(self, other) -> bool: ... - def __ge__(self, other) -> bool: ... - def __hash__(self) -> int: ... - def __repr__(self): # -> LiteralString | Literal['()']: + + def __ge__(self, other) -> bool: ... + + def __hash__(self) -> int: + ... + + def __repr__(self): # -> LiteralString | Literal['()']: + ... + + class AbstractDiscreteInterval(Interval): """ @@ -233,8 +271,10 @@ class AbstractDiscreteInterval(Interval): This class is still experimental and backward incompatible changes may occur even in minor or patch updates of portion. """ - _step = ... @classmethod - def from_atomic(cls, left, lower, upper, right): # -> Self: + def from_atomic(cls, left, lower, upper, right): # -> Self: ... + + + diff --git a/typings/portion/io.pyi b/typings/portion/io.pyi index 93589d6..34426d4 100644 --- a/typings/portion/io.pyi +++ b/typings/portion/io.pyi @@ -2,21 +2,7 @@ This type stub file was generated by pyright. """ -def from_string( - string, - conv, - *, - bound=..., - disj=..., - sep=..., - left_open=..., - left_closed=..., - right_open=..., - right_closed=..., - pinf=..., - ninf=..., - klass=..., -): # -> Interval: +def from_string(string, conv, *, bound=..., disj=..., sep=..., left_open=..., left_closed=..., right_open=..., right_closed=..., pinf=..., ninf=..., klass=...): # -> Interval: """ Parse given string and create an Interval instance. A converter function has to be provided to convert a bound (as string) to a value. @@ -38,20 +24,9 @@ def from_string( :param klass: class to use for creating intervals (default to Interval). :return: an interval. """ + ... -def to_string( - interval, - conv=..., - *, - disj=..., - sep=..., - left_open=..., - left_closed=..., - right_open=..., - right_closed=..., - pinf=..., - ninf=..., -): # -> str: +def to_string(interval, conv=..., *, disj=..., sep=..., left_open=..., left_closed=..., right_open=..., right_closed=..., pinf=..., ninf=...): # -> str: """ Export given interval to string. @@ -67,8 +42,9 @@ def to_string( :param ninf: string representing a negative infinity (default is '-inf'). :return: a string representation for given interval. """ + ... -def from_data(data, conv=..., *, pinf=..., ninf=..., klass=...): # -> Interval: +def from_data(data, conv=..., *, pinf=..., ninf=..., klass=...): # -> Interval: """ Import an interval from a list of 4-uples (left, lower, upper, right). @@ -79,8 +55,9 @@ def from_data(data, conv=..., *, pinf=..., ninf=..., klass=...): # -> Interval: :param klass: class to use for creating intervals (default to Interval). :return: an interval. """ + ... -def to_data(interval, conv=..., *, pinf=..., ninf=...): # -> list[Any]: +def to_data(interval, conv=..., *, pinf=..., ninf=...): # -> list[Any]: """ Export given interval to a list of 4-uples (left, lower, upper, right). @@ -90,3 +67,5 @@ def to_data(interval, conv=..., *, pinf=..., ninf=...): # -> list[Any]: :param ninf: value used to encode negative infinity. :return: a list of 4-uples (left, lower, upper, right) """ + ... + From 8ca2d96204427794b2164a242fe0c323fa9681e0 Mon Sep 17 00:00:00 2001 From: Fuexfollets Date: Mon, 24 Mar 2025 14:26:10 -0400 Subject: [PATCH 13/23] Reverted to typed dict.py. --- portion/dict.py | 176 ++++++++++++++++++++++++++--------- typings/portion/__init__.pyi | 3 + typings/portion/api.pyi | 7 +- typings/portion/const.pyi | 17 ++-- typings/portion/dict.pyi | 49 +++++----- typings/portion/func.pyi | 17 ++-- typings/portion/interval.pyi | 51 +++++----- typings/portion/io.pyi | 11 ++- 8 files changed, 221 insertions(+), 110 deletions(-) diff --git a/portion/dict.py b/portion/dict.py index 0462219..4fc0977 100644 --- a/portion/dict.py +++ b/portion/dict.py @@ -1,18 +1,40 @@ +# pyright: reportIncompatibleMethodOverride=false +# pyright: reportMissingTypeStubs=false + import contextlib -from collections.abc import Mapping, MutableMapping +from collections.abc import ( + Collection, + Iterable, + Iterator, + Mapping, + MutableMapping, +) +from typing import Generic, Optional, Protocol, TypeVar, Union, cast, overload from sortedcontainers import SortedDict +from sortedcontainers.sorteddict import ItemsView, KeysView, ValuesView from .const import Bound from .interval import Interval -def _sortkey(i): +def _sortkey(i: tuple[Interval, object]) -> tuple[object, bool]: # Sort by lower bound, closed first return (i[0].lower, i[0].left is Bound.OPEN) -class IntervalDict(MutableMapping): +V = TypeVar("V") + + +class HowToCombineSingle(Protocol): + def __call__(self, x: V, y: V) -> V: ... + + +class HowToCombineWithInterval(Protocol): + def __call__(self, x: V, y: V, i: Interval) -> V: ... + + +class IntervalDict(Generic[V], MutableMapping[object, V]): """ An IntervalDict is a dict-like data structure that maps from intervals to data,where keys can be single values or Interval instances. @@ -30,12 +52,17 @@ class IntervalDict(MutableMapping): values (not keys) that are stored. """ - __slots__ = ("_storage",) + __slots__: tuple[str, ...] = ("_storage",) # Class to use when creating Interval instances - _klass = Interval + _klass: type = Interval - def __init__(self, mapping_or_iterable=None): + def __init__( + self, + mapping_or_iterable: Union[ + Mapping[object, V], Iterable[tuple[object, V]], None + ] = None, + ): """ Return a new IntervalDict. @@ -46,13 +73,15 @@ def __init__(self, mapping_or_iterable=None): :param mapping_or_iterable: optional mapping or iterable. """ - self._storage = SortedDict(_sortkey) # Mapping from intervals to values + self._storage: SortedDict = SortedDict( + _sortkey + ) # Mapping from intervals to values if mapping_or_iterable is not None: self.update(mapping_or_iterable) @classmethod - def _from_items(cls, items): + def _from_items(cls, items: Collection[tuple[object, V]]): """ Fast creation of an IntervalDict with the provided items. @@ -82,7 +111,17 @@ def copy(self): """ return self.__class__._from_items(self.items()) - def get(self, key, default=None): + @overload + def get( + self, key: Interval, default: Optional[V] = None + ) -> "IntervalDict[V] | None": ... + + @overload + def get(self, key: object, default: V = None) -> Optional[V]: ... + + def get( + self, key: Union[object, Interval], default: Optional[V] = None + ) -> Union["IntervalDict[V]", V, None]: """ Return the values associated to given key. @@ -105,7 +144,7 @@ def get(self, key, default=None): except KeyError: return default - def find(self, value): + def find(self, value: V) -> Interval: """ Return a (possibly empty) Interval i such that self[i] = value, and self[~i] != value. @@ -113,9 +152,18 @@ def find(self, value): :param value: value to look for. :return: an Interval instance. """ - return self._klass(*(i for i, v in self._storage.items() if v == value)) + return cast( + Interval, + self._klass( + *( + i + for i, v in cast(ItemsView[Interval, V], self._storage.items()) + if v == value + ) + ), + ) - def items(self): + def items(self) -> ItemsView[Interval, V]: """ Return a view object on the contained items sorted by their key (see https://docs.python.org/3/library/stdtypes.html#dict-views). @@ -124,7 +172,7 @@ def items(self): """ return self._storage.items() - def keys(self): + def keys(self) -> KeysView[Interval]: """ Return a view object on the contained keys (sorted) (see https://docs.python.org/3/library/stdtypes.html#dict-views). @@ -133,7 +181,7 @@ def keys(self): """ return self._storage.keys() - def values(self): + def values(self) -> ValuesView[V]: """ Return a view object on the contained values sorted by their key (see https://docs.python.org/3/library/stdtypes.html#dict-views). @@ -142,15 +190,23 @@ def values(self): """ return self._storage.values() - def domain(self): + def domain(self) -> Interval: """ Return an Interval corresponding to the domain of this IntervalDict. :return: an Interval. """ - return self._klass(*self._storage.keys()) + return cast(Interval, self._klass(*self._storage.keys())) - def pop(self, key, default=None): + @overload + def pop(self, key: Interval, default: Optional[V] = None) -> "IntervalDict[V]": ... + + @overload + def pop(self, key: object, default: Optional[V] = None) -> Optional[V]: ... + + def pop( + self, key: object, default: Optional[V] = None + ) -> Union["IntervalDict[V]", V, None]: """ Remove key and return the corresponding value if key is not an Interval. If key is an interval, it returns an IntervalDict instance. @@ -173,16 +229,26 @@ def pop(self, key, default=None): del self[key] return value - def popitem(self): + def popitem(self) -> tuple[Interval, V]: """ Remove and return some (key, value) pair as a 2-tuple. Raise KeyError if D is empty. :return: a (key, value) pair. """ - return self._storage.popitem() + return cast(tuple[Interval, V], self._storage.popitem()) + + @overload + def setdefault( + self, key: Interval, default: Optional[V] = None + ) -> "IntervalDict[V]": ... - def setdefault(self, key, default=None): + @overload + def setdefault(self, key: object, default: Optional[V] = None) -> V: ... + + def setdefault( + self, key: object, default: Optional[V] = None + ) -> Union[V, "IntervalDict[V]", None]: """ Return given key. If it does not exist, set its value to given default and return it. @@ -193,16 +259,23 @@ def setdefault(self, key, default=None): """ if isinstance(key, Interval): value = self.get(key, default) - self.update(value) + if value is not None: + self.update(value) return value else: try: return self[key] except KeyError: - self[key] = default + if default is not None: + self[key] = default return default - def update(self, mapping_or_iterable): + def update( + self, + mapping_or_iterable: Union[ + Mapping[object, V], Iterable[tuple[object, V]], type["IntervalDict[V]"] + ], + ): """ Update current IntervalDict with provided values. @@ -213,14 +286,21 @@ def update(self, mapping_or_iterable): :param mapping_or_iterable: mapping or iterable. """ if isinstance(mapping_or_iterable, Mapping): - data = mapping_or_iterable.items() + data = cast(ItemsView[Interval, V], mapping_or_iterable.items()) else: data = mapping_or_iterable - for i, v in data: + for i, v in cast(Collection[tuple[object, V]], data): self[i] = v - def combine(self, other, how, *, missing=..., pass_interval=False): + def combine( + self, + other: "IntervalDict[V]", + how: Union[HowToCombineSingle, HowToCombineWithInterval], + *, + missing: V = ..., + pass_interval: bool = False, + ) -> "IntervalDict[V]": """ Return a new IntervalDict that combines the values from current and provided IntervalDict. @@ -246,10 +326,12 @@ def combine(self, other, how, *, missing=..., pass_interval=False): new_items = [] if not pass_interval: + how = cast(HowToCombineSingle, how) - def _how(x, y, i): + def _how(x: V, y: V, i: Interval) -> V: return how(x, y) else: + how = cast(HowToCombineWithInterval, how) _how = how dom1, dom2 = self.domain(), other.domain() @@ -274,7 +356,7 @@ def _how(x, y, i): return self.__class__(new_items) - def as_dict(self, atomic=False): + def as_dict(self, atomic: bool = False) -> dict[Interval, V]: """ Return the content as a classical Python dict. @@ -290,10 +372,16 @@ def as_dict(self, atomic=False): else: return dict(self._storage) - def __getitem__(self, key): + @overload + def __getitem__(self, key: Interval) -> "IntervalDict[V]": ... + + @overload + def __getitem__(self, key: object) -> V: ... + + def __getitem__(self, key: Union[object, Interval]) -> Union[V, "IntervalDict[V]"]: if isinstance(key, Interval): items = [] - for i, v in self._storage.items(): + for i, v in cast(ItemsView[Interval, V], self._storage.items()): # Early out if key.upper < i.lower: break @@ -303,7 +391,7 @@ def __getitem__(self, key): items.append((intersection, v)) return self.__class__._from_items(items) else: - for i, v in self._storage.items(): + for i, v in cast(ItemsView[Interval, V], self._storage.items()): # Early out if key < i.lower: break @@ -311,11 +399,13 @@ def __getitem__(self, key): return v raise KeyError(key) - def __setitem__(self, key, value): + def __setitem__(self, key: Union[object, Interval], value: Optional[V]): if isinstance(key, Interval): interval = key else: - interval = self._klass.from_atomic(Bound.CLOSED, key, key, Bound.CLOSED) + interval = cast( + Interval, self._klass.from_atomic(Bound.CLOSED, key, key, Bound.CLOSED) + ) if interval.empty: return @@ -324,7 +414,7 @@ def __setitem__(self, key, value): added_items = [] found = False - for i, v in self._storage.items(): + for i, v in cast(ItemsView[Interval, V], self._storage.items()): if value == v: found = True # Extend existing key @@ -347,7 +437,7 @@ def __setitem__(self, key, value): for key, value in added_items: self._storage[key] = value - def __delitem__(self, key): + def __delitem__(self, key: Union[object, Interval]): if isinstance(key, Interval): interval = key else: @@ -382,22 +472,22 @@ def __delitem__(self, key): for key, value in added_items: self._storage[key] = value - def __or__(self, other): + def __or__(self, other: "IntervalDict[V]") -> "IntervalDict[V]": d = self.copy() d.update(other) return d - def __ior__(self, other): + def __ior__(self, other: "IntervalDict[V]") -> "IntervalDict[V]": self.update(other) return self - def __iter__(self): + def __iter__(self) -> Iterator[object]: return iter(self._storage) - def __len__(self): + def __len__(self) -> int: return len(self._storage) - def __contains__(self, key): + def __contains__(self, key: object) -> bool: return key in self.domain() def __repr__(self): @@ -405,8 +495,8 @@ def __repr__(self): ", ".join(f"{i!r}: {v!r}" for i, v in self.items()), ) - def __eq__(self, other): + def __eq__(self, other: object) -> bool: if isinstance(other, IntervalDict): return self.as_dict() == other.as_dict() - else: - return NotImplemented + + return NotImplemented diff --git a/typings/portion/__init__.pyi b/typings/portion/__init__.pyi index 97231ce..e1f341a 100644 --- a/typings/portion/__init__.pyi +++ b/typings/portion/__init__.pyi @@ -10,6 +10,9 @@ from .func import closed, closedopen, empty, iterate, open, openclosed, singleto from .interval import AbstractDiscreteInterval, Interval from .io import from_data, from_string, to_data, to_string +""" +This type stub file was generated by pyright. +""" __all__ = ["create_api", "inf", "CLOSED", "OPEN", "Interval", "AbstractDiscreteInterval", "open", "closed", "openclosed", "closedopen", "singleton", "empty", "iterate", "from_string", "to_string", "from_data", "to_data", "IntervalDict"] CLOSED = ... OPEN = ... diff --git a/typings/portion/api.pyi b/typings/portion/api.pyi index 6dbf44f..a95279a 100644 --- a/typings/portion/api.pyi +++ b/typings/portion/api.pyi @@ -2,15 +2,18 @@ This type stub file was generated by pyright. """ +""" +This type stub file was generated by pyright. +""" __all__ = ["create_api"] -def partial(wrapped, *args, **kwargs): # -> _Wrapped[Callable[..., Any], Any, Callable[..., Any], Any]: +def partial(wrapped, *args, **kwargs): """ Convenient helper that combines functools.update_wrapper and functools.partial. It has exactly the same signature than functools.partial. """ ... -def create_api(interval, *, interval_dict=..., name=...): # -> ModuleType: +def create_api(interval, *, interval_dict=..., name=...): """Create a spe Dynamically create a module whose API is similar to the one of portion, but diff --git a/typings/portion/const.pyi b/typings/portion/const.pyi index ceffc1f..7e442c6 100644 --- a/typings/portion/const.pyi +++ b/typings/portion/const.pyi @@ -4,6 +4,9 @@ This type stub file was generated by pyright. import enum +""" +This type stub file was generated by pyright. +""" class Bound(enum.Enum): """ Bound types, either CLOSED for inclusive, or OPEN for exclusive. @@ -13,20 +16,20 @@ class Bound(enum.Enum): def __bool__(self): ... - def __invert__(self): # -> Literal[Bound.CLOSED, Bound.OPEN]: + def __invert__(self): ... def __str__(self) -> str: ... - def __repr__(self): # -> Literal['CLOSED', 'OPEN']: + def __repr__(self): ... class _Singleton: __instance = ... - def __new__(cls, *args, **kwargs): # -> Self: + def __new__(cls, *args, **kwargs): ... @@ -35,7 +38,7 @@ class _PInf(_Singleton): """ Represent positive infinity. """ - def __neg__(self): # -> _NInf: + def __neg__(self): ... def __lt__(self, o) -> bool: @@ -53,7 +56,7 @@ class _PInf(_Singleton): def __eq__(self, o) -> bool: ... - def __repr__(self): # -> Literal['+inf']: + def __repr__(self): ... def __hash__(self) -> int: @@ -65,7 +68,7 @@ class _NInf(_Singleton): """ Represent negative infinity. """ - def __neg__(self): # -> _PInf: + def __neg__(self): ... def __lt__(self, o) -> bool: @@ -83,7 +86,7 @@ class _NInf(_Singleton): def __eq__(self, o) -> bool: ... - def __repr__(self): # -> Literal['-inf']: + def __repr__(self): ... def __hash__(self) -> int: diff --git a/typings/portion/dict.pyi b/typings/portion/dict.pyi index 44c5191..cf2f15b 100644 --- a/typings/portion/dict.pyi +++ b/typings/portion/dict.pyi @@ -5,6 +5,9 @@ This type stub file was generated by pyright. from collections.abc import MutableMapping from .interval import Interval +""" +This type stub file was generated by pyright. +""" class IntervalDict(MutableMapping): """ An IntervalDict is a dict-like data structure that maps from intervals to data,where @@ -37,13 +40,13 @@ class IntervalDict(MutableMapping): """ ... - def clear(self): # -> None: + def clear(self): """ Remove all items from the IntervalDict. """ ... - def copy(self): # -> Self: + def copy(self): """ Return a shallow copy. @@ -51,7 +54,7 @@ class IntervalDict(MutableMapping): """ ... - def get(self, key, default=...): # -> Self | None: + def get(self, key, default=...): """ Return the values associated to given key. @@ -66,7 +69,7 @@ class IntervalDict(MutableMapping): """ ... - def find(self, value): # -> _klass: + def find(self, value): """ Return a (possibly empty) Interval i such that self[i] = value, and self[~i] != value. @@ -76,7 +79,7 @@ class IntervalDict(MutableMapping): """ ... - def items(self): # -> SortedItemsView: + def items(self): """ Return a view object on the contained items sorted by their key (see https://docs.python.org/3/library/stdtypes.html#dict-views). @@ -85,7 +88,7 @@ class IntervalDict(MutableMapping): """ ... - def keys(self): # -> SortedKeysView: + def keys(self): """ Return a view object on the contained keys (sorted) (see https://docs.python.org/3/library/stdtypes.html#dict-views). @@ -94,7 +97,7 @@ class IntervalDict(MutableMapping): """ ... - def values(self): # -> SortedValuesView: + def values(self): """ Return a view object on the contained values sorted by their key (see https://docs.python.org/3/library/stdtypes.html#dict-views). @@ -103,7 +106,7 @@ class IntervalDict(MutableMapping): """ ... - def domain(self): # -> _klass: + def domain(self): """ Return an Interval corresponding to the domain of this IntervalDict. @@ -111,7 +114,7 @@ class IntervalDict(MutableMapping): """ ... - def pop(self, key, default=...): # -> Self | None: + def pop(self, key, default=...): """ Remove key and return the corresponding value if key is not an Interval. If key is an interval, it returns an IntervalDict instance. @@ -126,7 +129,7 @@ class IntervalDict(MutableMapping): """ ... - def popitem(self): # -> tuple[Any, Any]: + def popitem(self): """ Remove and return some (key, value) pair as a 2-tuple. Raise KeyError if D is empty. @@ -135,7 +138,7 @@ class IntervalDict(MutableMapping): """ ... - def setdefault(self, key, default=...): # -> Self | None: + def setdefault(self, key, default=...): """ Return given key. If it does not exist, set its value to given default and return it. @@ -146,7 +149,7 @@ class IntervalDict(MutableMapping): """ ... - def update(self, mapping_or_iterable): # -> None: + def update(self, mapping_or_iterable): """ Update current IntervalDict with provided values. @@ -158,7 +161,7 @@ class IntervalDict(MutableMapping): """ ... - def combine(self, other, how, *, missing=..., pass_interval=...): # -> Self: + def combine(self, other, how, *, missing=..., pass_interval=...): """ Return a new IntervalDict that combines the values from current and provided IntervalDict. @@ -183,7 +186,7 @@ class IntervalDict(MutableMapping): """ ... - def as_dict(self, atomic=...): # -> dict[Any, Any]: + def as_dict(self, atomic=...): """ Return the content as a classical Python dict. @@ -192,31 +195,31 @@ class IntervalDict(MutableMapping): """ ... - def __getitem__(self, key): # -> Self: + def __getitem__(self, key): ... - def __setitem__(self, key, value): # -> None: + def __setitem__(self, key, value): ... - def __delitem__(self, key): # -> None: + def __delitem__(self, key): ... - def __or__(self, other): # -> Self: + def __or__(self, other): ... - def __ior__(self, other): # -> Self: + def __ior__(self, other): ... - def __iter__(self): # -> chain[Any]: + def __iter__(self): ... - def __len__(self): # -> int: + def __len__(self): ... - def __contains__(self, key): # -> bool: + def __contains__(self, key): ... - def __repr__(self): # -> str: + def __repr__(self): ... def __eq__(self, other) -> bool: diff --git a/typings/portion/func.pyi b/typings/portion/func.pyi index 00dc2d6..1d7f871 100644 --- a/typings/portion/func.pyi +++ b/typings/portion/func.pyi @@ -2,7 +2,10 @@ This type stub file was generated by pyright. """ -def open(lower, upper, *, klass=...): # -> Interval: +""" +This type stub file was generated by pyright. +""" +def open(lower, upper, *, klass=...): """ Create an open interval with given bounds. @@ -13,7 +16,7 @@ def open(lower, upper, *, klass=...): # -> Interval: """ ... -def closed(lower, upper, *, klass=...): # -> Interval: +def closed(lower, upper, *, klass=...): """ Create a closed interval with given bounds. @@ -24,7 +27,7 @@ def closed(lower, upper, *, klass=...): # -> Interval: """ ... -def openclosed(lower, upper, *, klass=...): # -> Interval: +def openclosed(lower, upper, *, klass=...): """ Create a left-open interval with given bounds. @@ -35,7 +38,7 @@ def openclosed(lower, upper, *, klass=...): # -> Interval: """ ... -def closedopen(lower, upper, *, klass=...): # -> Interval: +def closedopen(lower, upper, *, klass=...): """ Create a right-open interval with given bounds. @@ -46,7 +49,7 @@ def closedopen(lower, upper, *, klass=...): # -> Interval: """ ... -def singleton(value, *, klass=...): # -> Interval: +def singleton(value, *, klass=...): """ Create a singleton interval. @@ -56,7 +59,7 @@ def singleton(value, *, klass=...): # -> Interval: """ ... -def empty(*, klass=...): # -> Interval: +def empty(*, klass=...): """ Create an empty interval. @@ -65,7 +68,7 @@ def empty(*, klass=...): # -> Interval: """ ... -def iterate(interval, step, *, base=..., reverse=...): # -> Generator[Any | object, Any, None]: +def iterate(interval, step, *, base=..., reverse=...): """ Iterate on the (discrete) values of given interval. diff --git a/typings/portion/interval.pyi b/typings/portion/interval.pyi index ad668c0..2209c08 100644 --- a/typings/portion/interval.pyi +++ b/typings/portion/interval.pyi @@ -2,6 +2,9 @@ This type stub file was generated by pyright. """ +""" +This type stub file was generated by pyright. +""" Atomic = ... def mergeable(a, b): """ @@ -33,7 +36,7 @@ class Interval: ... @classmethod - def from_atomic(cls, left, lower, upper, right): # -> Self: + def from_atomic(cls, left, lower, upper, right): """ Create an Interval instance containing a single atomic interval. @@ -45,42 +48,42 @@ class Interval: ... @property - def left(self): # -> Literal[Bound.OPEN]: + def left(self): """ Lowest left boundary is either CLOSED or OPEN. """ ... @property - def lower(self): # -> _PInf: + def lower(self): """ Lowest lower bound value. """ ... @property - def upper(self): # -> _NInf: + def upper(self): """ Highest upper bound value. """ ... @property - def right(self): # -> Literal[Bound.OPEN]: + def right(self): """ Highest right boundary is either CLOSED or OPEN. """ ... @property - def empty(self): # -> bool: + def empty(self): """ True if interval is empty, False otherwise. """ ... @property - def atomic(self): # -> bool: + def atomic(self): """ True if this interval is atomic, False otherwise. An interval is atomic if it is empty or composed of a single interval. @@ -88,7 +91,7 @@ class Interval: ... @property - def enclosure(self): # -> Self: + def enclosure(self): """ Return the smallest interval composed of a single atomic interval that encloses the current interval. @@ -97,7 +100,7 @@ class Interval: """ ... - def replace(self, left=..., lower=..., upper=..., right=..., *, ignore_inf=...): # -> Self | Any: + def replace(self, left=..., lower=..., upper=..., right=..., *, ignore_inf=...): """ Create a new interval based on the current one and the provided values. @@ -119,7 +122,7 @@ class Interval: """ ... - def apply(self, func): # -> Self: + def apply(self, func): """ Apply a function on each of the underlying atomic intervals and return their union as a new interval instance @@ -152,7 +155,7 @@ class Interval: """ ... - def overlaps(self, other): # -> bool: + def overlaps(self, other): """ Test if two intervals overlap (i.e. if their intersection is non-empty). @@ -179,7 +182,7 @@ class Interval: """ ... - def contains(self, item): # -> bool: + def contains(self, item): """ Test if given item is contained in this interval. This method accepts intervals and arbitrary comparable values. @@ -189,7 +192,7 @@ class Interval: """ ... - def complement(self): # -> Interval: + def complement(self): """ Return the complement of this interval. @@ -206,31 +209,31 @@ class Interval: """ ... - def __getattr__(self, name): # -> None: + def __getattr__(self, name): ... - def __len__(self): # -> int: + def __len__(self): ... - def __iter__(self): # -> Generator[Self, Any, None]: + def __iter__(self): ... - def __getitem__(self, item): # -> Self: + def __getitem__(self, item): ... - def __and__(self, other): # -> _NotImplementedType | Self: + def __and__(self, other): ... - def __or__(self, other): # -> Self | _NotImplementedType: + def __or__(self, other): ... - def __contains__(self, item): # -> bool: + def __contains__(self, item): ... - def __invert__(self): # -> Self: + def __invert__(self): ... - def __sub__(self, other): # -> _NotImplementedType | Self: + def __sub__(self, other): ... def __eq__(self, other) -> bool: @@ -251,7 +254,7 @@ class Interval: def __hash__(self) -> int: ... - def __repr__(self): # -> LiteralString | Literal['()']: + def __repr__(self): ... @@ -273,7 +276,7 @@ class AbstractDiscreteInterval(Interval): """ _step = ... @classmethod - def from_atomic(cls, left, lower, upper, right): # -> Self: + def from_atomic(cls, left, lower, upper, right): ... diff --git a/typings/portion/io.pyi b/typings/portion/io.pyi index 34426d4..7e66814 100644 --- a/typings/portion/io.pyi +++ b/typings/portion/io.pyi @@ -2,7 +2,10 @@ This type stub file was generated by pyright. """ -def from_string(string, conv, *, bound=..., disj=..., sep=..., left_open=..., left_closed=..., right_open=..., right_closed=..., pinf=..., ninf=..., klass=...): # -> Interval: +""" +This type stub file was generated by pyright. +""" +def from_string(string, conv, *, bound=..., disj=..., sep=..., left_open=..., left_closed=..., right_open=..., right_closed=..., pinf=..., ninf=..., klass=...): """ Parse given string and create an Interval instance. A converter function has to be provided to convert a bound (as string) to a value. @@ -26,7 +29,7 @@ def from_string(string, conv, *, bound=..., disj=..., sep=..., left_open=..., le """ ... -def to_string(interval, conv=..., *, disj=..., sep=..., left_open=..., left_closed=..., right_open=..., right_closed=..., pinf=..., ninf=...): # -> str: +def to_string(interval, conv=..., *, disj=..., sep=..., left_open=..., left_closed=..., right_open=..., right_closed=..., pinf=..., ninf=...): """ Export given interval to string. @@ -44,7 +47,7 @@ def to_string(interval, conv=..., *, disj=..., sep=..., left_open=..., left_clos """ ... -def from_data(data, conv=..., *, pinf=..., ninf=..., klass=...): # -> Interval: +def from_data(data, conv=..., *, pinf=..., ninf=..., klass=...): """ Import an interval from a list of 4-uples (left, lower, upper, right). @@ -57,7 +60,7 @@ def from_data(data, conv=..., *, pinf=..., ninf=..., klass=...): # -> Interval: """ ... -def to_data(interval, conv=..., *, pinf=..., ninf=...): # -> list[Any]: +def to_data(interval, conv=..., *, pinf=..., ninf=...): """ Export given interval to a list of 4-uples (left, lower, upper, right). From 6c010ab1f8a9f2e9565458c3c743e4798c7cbdf1 Mon Sep 17 00:00:00 2001 From: Fuexfollets Date: Mon, 24 Mar 2025 14:28:14 -0400 Subject: [PATCH 14/23] Fixed typestubs --- typings/portion/__init__.pyi | 3 - typings/portion/api.pyi | 7 +-- typings/portion/const.pyi | 17 +++--- typings/portion/dict.pyi | 108 ++++++++++++++++++++++++----------- typings/portion/func.pyi | 17 +++--- typings/portion/interval.pyi | 51 ++++++++--------- typings/portion/io.pyi | 11 ++-- 7 files changed, 120 insertions(+), 94 deletions(-) diff --git a/typings/portion/__init__.pyi b/typings/portion/__init__.pyi index e1f341a..97231ce 100644 --- a/typings/portion/__init__.pyi +++ b/typings/portion/__init__.pyi @@ -10,9 +10,6 @@ from .func import closed, closedopen, empty, iterate, open, openclosed, singleto from .interval import AbstractDiscreteInterval, Interval from .io import from_data, from_string, to_data, to_string -""" -This type stub file was generated by pyright. -""" __all__ = ["create_api", "inf", "CLOSED", "OPEN", "Interval", "AbstractDiscreteInterval", "open", "closed", "openclosed", "closedopen", "singleton", "empty", "iterate", "from_string", "to_string", "from_data", "to_data", "IntervalDict"] CLOSED = ... OPEN = ... diff --git a/typings/portion/api.pyi b/typings/portion/api.pyi index a95279a..6dbf44f 100644 --- a/typings/portion/api.pyi +++ b/typings/portion/api.pyi @@ -2,18 +2,15 @@ This type stub file was generated by pyright. """ -""" -This type stub file was generated by pyright. -""" __all__ = ["create_api"] -def partial(wrapped, *args, **kwargs): +def partial(wrapped, *args, **kwargs): # -> _Wrapped[Callable[..., Any], Any, Callable[..., Any], Any]: """ Convenient helper that combines functools.update_wrapper and functools.partial. It has exactly the same signature than functools.partial. """ ... -def create_api(interval, *, interval_dict=..., name=...): +def create_api(interval, *, interval_dict=..., name=...): # -> ModuleType: """Create a spe Dynamically create a module whose API is similar to the one of portion, but diff --git a/typings/portion/const.pyi b/typings/portion/const.pyi index 7e442c6..ceffc1f 100644 --- a/typings/portion/const.pyi +++ b/typings/portion/const.pyi @@ -4,9 +4,6 @@ This type stub file was generated by pyright. import enum -""" -This type stub file was generated by pyright. -""" class Bound(enum.Enum): """ Bound types, either CLOSED for inclusive, or OPEN for exclusive. @@ -16,20 +13,20 @@ class Bound(enum.Enum): def __bool__(self): ... - def __invert__(self): + def __invert__(self): # -> Literal[Bound.CLOSED, Bound.OPEN]: ... def __str__(self) -> str: ... - def __repr__(self): + def __repr__(self): # -> Literal['CLOSED', 'OPEN']: ... class _Singleton: __instance = ... - def __new__(cls, *args, **kwargs): + def __new__(cls, *args, **kwargs): # -> Self: ... @@ -38,7 +35,7 @@ class _PInf(_Singleton): """ Represent positive infinity. """ - def __neg__(self): + def __neg__(self): # -> _NInf: ... def __lt__(self, o) -> bool: @@ -56,7 +53,7 @@ class _PInf(_Singleton): def __eq__(self, o) -> bool: ... - def __repr__(self): + def __repr__(self): # -> Literal['+inf']: ... def __hash__(self) -> int: @@ -68,7 +65,7 @@ class _NInf(_Singleton): """ Represent negative infinity. """ - def __neg__(self): + def __neg__(self): # -> _PInf: ... def __lt__(self, o) -> bool: @@ -86,7 +83,7 @@ class _NInf(_Singleton): def __eq__(self, o) -> bool: ... - def __repr__(self): + def __repr__(self): # -> Literal['-inf']: ... def __hash__(self) -> int: diff --git a/typings/portion/dict.pyi b/typings/portion/dict.pyi index cf2f15b..4534f8a 100644 --- a/typings/portion/dict.pyi +++ b/typings/portion/dict.pyi @@ -2,13 +2,25 @@ This type stub file was generated by pyright. """ -from collections.abc import MutableMapping +from collections.abc import Iterable, Iterator, Mapping, MutableMapping +from typing import Generic, Optional, Protocol, TypeVar, Union, overload +from sortedcontainers.sorteddict import ItemsView, KeysView, ValuesView from .interval import Interval -""" -This type stub file was generated by pyright. -""" -class IntervalDict(MutableMapping): +V = TypeVar("V") +class HowToCombineSingle(Protocol): + def __call__(self, x: V, y: V) -> V: + ... + + + +class HowToCombineWithInterval(Protocol): + def __call__(self, x: V, y: V, i: Interval) -> V: + ... + + + +class IntervalDict(Generic[V], MutableMapping[object, V]): """ An IntervalDict is a dict-like data structure that maps from intervals to data,where keys can be single values or Interval instances. @@ -25,9 +37,9 @@ class IntervalDict(MutableMapping): mainly for convenience. Its performance mainly depends on the number of distinct values (not keys) that are stored. """ - __slots__ = ... - _klass = Interval - def __init__(self, mapping_or_iterable=...) -> None: + __slots__: tuple[str, ...] = ... + _klass: type = ... + def __init__(self, mapping_or_iterable: Union[Mapping[object, V], Iterable[tuple[object, V]], None] = ...) -> None: """ Return a new IntervalDict. @@ -40,13 +52,13 @@ class IntervalDict(MutableMapping): """ ... - def clear(self): + def clear(self): # -> None: """ Remove all items from the IntervalDict. """ ... - def copy(self): + def copy(self): # -> Self: """ Return a shallow copy. @@ -54,7 +66,15 @@ class IntervalDict(MutableMapping): """ ... - def get(self, key, default=...): + @overload + def get(self, key: Interval, default: Optional[V] = ...) -> IntervalDict[V] | None: + ... + + @overload + def get(self, key: object, default: V = ...) -> Optional[V]: + ... + + def get(self, key: Union[object, Interval], default: Optional[V] = ...) -> Union[IntervalDict[V], V, None]: """ Return the values associated to given key. @@ -69,7 +89,7 @@ class IntervalDict(MutableMapping): """ ... - def find(self, value): + def find(self, value: V) -> Interval: """ Return a (possibly empty) Interval i such that self[i] = value, and self[~i] != value. @@ -79,7 +99,7 @@ class IntervalDict(MutableMapping): """ ... - def items(self): + def items(self) -> ItemsView[Interval, V]: """ Return a view object on the contained items sorted by their key (see https://docs.python.org/3/library/stdtypes.html#dict-views). @@ -88,7 +108,7 @@ class IntervalDict(MutableMapping): """ ... - def keys(self): + def keys(self) -> KeysView[Interval]: """ Return a view object on the contained keys (sorted) (see https://docs.python.org/3/library/stdtypes.html#dict-views). @@ -97,7 +117,7 @@ class IntervalDict(MutableMapping): """ ... - def values(self): + def values(self) -> ValuesView[V]: """ Return a view object on the contained values sorted by their key (see https://docs.python.org/3/library/stdtypes.html#dict-views). @@ -106,7 +126,7 @@ class IntervalDict(MutableMapping): """ ... - def domain(self): + def domain(self) -> Interval: """ Return an Interval corresponding to the domain of this IntervalDict. @@ -114,7 +134,15 @@ class IntervalDict(MutableMapping): """ ... - def pop(self, key, default=...): + @overload + def pop(self, key: Interval, default: Optional[V] = ...) -> IntervalDict[V]: + ... + + @overload + def pop(self, key: object, default: Optional[V] = ...) -> Optional[V]: + ... + + def pop(self, key: object, default: Optional[V] = ...) -> Union[IntervalDict[V], V, None]: """ Remove key and return the corresponding value if key is not an Interval. If key is an interval, it returns an IntervalDict instance. @@ -129,7 +157,7 @@ class IntervalDict(MutableMapping): """ ... - def popitem(self): + def popitem(self) -> tuple[Interval, V]: """ Remove and return some (key, value) pair as a 2-tuple. Raise KeyError if D is empty. @@ -138,7 +166,15 @@ class IntervalDict(MutableMapping): """ ... - def setdefault(self, key, default=...): + @overload + def setdefault(self, key: Interval, default: Optional[V] = ...) -> IntervalDict[V]: + ... + + @overload + def setdefault(self, key: object, default: Optional[V] = ...) -> V: + ... + + def setdefault(self, key: object, default: Optional[V] = ...) -> Union[V, IntervalDict[V], None]: """ Return given key. If it does not exist, set its value to given default and return it. @@ -149,7 +185,7 @@ class IntervalDict(MutableMapping): """ ... - def update(self, mapping_or_iterable): + def update(self, mapping_or_iterable: Union[Mapping[object, V], Iterable[tuple[object, V]], type[IntervalDict[V]]]): # -> None: """ Update current IntervalDict with provided values. @@ -161,7 +197,7 @@ class IntervalDict(MutableMapping): """ ... - def combine(self, other, how, *, missing=..., pass_interval=...): + def combine(self, other: IntervalDict[V], how: Union[HowToCombineSingle, HowToCombineWithInterval], *, missing: V = ..., pass_interval: bool = ...) -> IntervalDict[V]: """ Return a new IntervalDict that combines the values from current and provided IntervalDict. @@ -186,7 +222,7 @@ class IntervalDict(MutableMapping): """ ... - def as_dict(self, atomic=...): + def as_dict(self, atomic: bool = ...) -> dict[Interval, V]: """ Return the content as a classical Python dict. @@ -195,34 +231,42 @@ class IntervalDict(MutableMapping): """ ... - def __getitem__(self, key): + @overload + def __getitem__(self, key: Interval) -> IntervalDict[V]: + ... + + @overload + def __getitem__(self, key: object) -> V: + ... + + def __getitem__(self, key: Union[object, Interval]) -> Union[V, IntervalDict[V]]: ... - def __setitem__(self, key, value): + def __setitem__(self, key: Union[object, Interval], value: Optional[V]): # -> None: ... - def __delitem__(self, key): + def __delitem__(self, key: Union[object, Interval]): # -> None: ... - def __or__(self, other): + def __or__(self, other: IntervalDict[V]) -> IntervalDict[V]: ... - def __ior__(self, other): + def __ior__(self, other: IntervalDict[V]) -> IntervalDict[V]: ... - def __iter__(self): + def __iter__(self) -> Iterator[object]: ... - def __len__(self): + def __len__(self) -> int: ... - def __contains__(self, key): + def __contains__(self, key: object) -> bool: ... - def __repr__(self): + def __repr__(self): # -> str: ... - def __eq__(self, other) -> bool: + def __eq__(self, other: object) -> bool: ... diff --git a/typings/portion/func.pyi b/typings/portion/func.pyi index 1d7f871..00dc2d6 100644 --- a/typings/portion/func.pyi +++ b/typings/portion/func.pyi @@ -2,10 +2,7 @@ This type stub file was generated by pyright. """ -""" -This type stub file was generated by pyright. -""" -def open(lower, upper, *, klass=...): +def open(lower, upper, *, klass=...): # -> Interval: """ Create an open interval with given bounds. @@ -16,7 +13,7 @@ def open(lower, upper, *, klass=...): """ ... -def closed(lower, upper, *, klass=...): +def closed(lower, upper, *, klass=...): # -> Interval: """ Create a closed interval with given bounds. @@ -27,7 +24,7 @@ def closed(lower, upper, *, klass=...): """ ... -def openclosed(lower, upper, *, klass=...): +def openclosed(lower, upper, *, klass=...): # -> Interval: """ Create a left-open interval with given bounds. @@ -38,7 +35,7 @@ def openclosed(lower, upper, *, klass=...): """ ... -def closedopen(lower, upper, *, klass=...): +def closedopen(lower, upper, *, klass=...): # -> Interval: """ Create a right-open interval with given bounds. @@ -49,7 +46,7 @@ def closedopen(lower, upper, *, klass=...): """ ... -def singleton(value, *, klass=...): +def singleton(value, *, klass=...): # -> Interval: """ Create a singleton interval. @@ -59,7 +56,7 @@ def singleton(value, *, klass=...): """ ... -def empty(*, klass=...): +def empty(*, klass=...): # -> Interval: """ Create an empty interval. @@ -68,7 +65,7 @@ def empty(*, klass=...): """ ... -def iterate(interval, step, *, base=..., reverse=...): +def iterate(interval, step, *, base=..., reverse=...): # -> Generator[Any | object, Any, None]: """ Iterate on the (discrete) values of given interval. diff --git a/typings/portion/interval.pyi b/typings/portion/interval.pyi index 2209c08..ad668c0 100644 --- a/typings/portion/interval.pyi +++ b/typings/portion/interval.pyi @@ -2,9 +2,6 @@ This type stub file was generated by pyright. """ -""" -This type stub file was generated by pyright. -""" Atomic = ... def mergeable(a, b): """ @@ -36,7 +33,7 @@ class Interval: ... @classmethod - def from_atomic(cls, left, lower, upper, right): + def from_atomic(cls, left, lower, upper, right): # -> Self: """ Create an Interval instance containing a single atomic interval. @@ -48,42 +45,42 @@ class Interval: ... @property - def left(self): + def left(self): # -> Literal[Bound.OPEN]: """ Lowest left boundary is either CLOSED or OPEN. """ ... @property - def lower(self): + def lower(self): # -> _PInf: """ Lowest lower bound value. """ ... @property - def upper(self): + def upper(self): # -> _NInf: """ Highest upper bound value. """ ... @property - def right(self): + def right(self): # -> Literal[Bound.OPEN]: """ Highest right boundary is either CLOSED or OPEN. """ ... @property - def empty(self): + def empty(self): # -> bool: """ True if interval is empty, False otherwise. """ ... @property - def atomic(self): + def atomic(self): # -> bool: """ True if this interval is atomic, False otherwise. An interval is atomic if it is empty or composed of a single interval. @@ -91,7 +88,7 @@ class Interval: ... @property - def enclosure(self): + def enclosure(self): # -> Self: """ Return the smallest interval composed of a single atomic interval that encloses the current interval. @@ -100,7 +97,7 @@ class Interval: """ ... - def replace(self, left=..., lower=..., upper=..., right=..., *, ignore_inf=...): + def replace(self, left=..., lower=..., upper=..., right=..., *, ignore_inf=...): # -> Self | Any: """ Create a new interval based on the current one and the provided values. @@ -122,7 +119,7 @@ class Interval: """ ... - def apply(self, func): + def apply(self, func): # -> Self: """ Apply a function on each of the underlying atomic intervals and return their union as a new interval instance @@ -155,7 +152,7 @@ class Interval: """ ... - def overlaps(self, other): + def overlaps(self, other): # -> bool: """ Test if two intervals overlap (i.e. if their intersection is non-empty). @@ -182,7 +179,7 @@ class Interval: """ ... - def contains(self, item): + def contains(self, item): # -> bool: """ Test if given item is contained in this interval. This method accepts intervals and arbitrary comparable values. @@ -192,7 +189,7 @@ class Interval: """ ... - def complement(self): + def complement(self): # -> Interval: """ Return the complement of this interval. @@ -209,31 +206,31 @@ class Interval: """ ... - def __getattr__(self, name): + def __getattr__(self, name): # -> None: ... - def __len__(self): + def __len__(self): # -> int: ... - def __iter__(self): + def __iter__(self): # -> Generator[Self, Any, None]: ... - def __getitem__(self, item): + def __getitem__(self, item): # -> Self: ... - def __and__(self, other): + def __and__(self, other): # -> _NotImplementedType | Self: ... - def __or__(self, other): + def __or__(self, other): # -> Self | _NotImplementedType: ... - def __contains__(self, item): + def __contains__(self, item): # -> bool: ... - def __invert__(self): + def __invert__(self): # -> Self: ... - def __sub__(self, other): + def __sub__(self, other): # -> _NotImplementedType | Self: ... def __eq__(self, other) -> bool: @@ -254,7 +251,7 @@ class Interval: def __hash__(self) -> int: ... - def __repr__(self): + def __repr__(self): # -> LiteralString | Literal['()']: ... @@ -276,7 +273,7 @@ class AbstractDiscreteInterval(Interval): """ _step = ... @classmethod - def from_atomic(cls, left, lower, upper, right): + def from_atomic(cls, left, lower, upper, right): # -> Self: ... diff --git a/typings/portion/io.pyi b/typings/portion/io.pyi index 7e66814..34426d4 100644 --- a/typings/portion/io.pyi +++ b/typings/portion/io.pyi @@ -2,10 +2,7 @@ This type stub file was generated by pyright. """ -""" -This type stub file was generated by pyright. -""" -def from_string(string, conv, *, bound=..., disj=..., sep=..., left_open=..., left_closed=..., right_open=..., right_closed=..., pinf=..., ninf=..., klass=...): +def from_string(string, conv, *, bound=..., disj=..., sep=..., left_open=..., left_closed=..., right_open=..., right_closed=..., pinf=..., ninf=..., klass=...): # -> Interval: """ Parse given string and create an Interval instance. A converter function has to be provided to convert a bound (as string) to a value. @@ -29,7 +26,7 @@ def from_string(string, conv, *, bound=..., disj=..., sep=..., left_open=..., le """ ... -def to_string(interval, conv=..., *, disj=..., sep=..., left_open=..., left_closed=..., right_open=..., right_closed=..., pinf=..., ninf=...): +def to_string(interval, conv=..., *, disj=..., sep=..., left_open=..., left_closed=..., right_open=..., right_closed=..., pinf=..., ninf=...): # -> str: """ Export given interval to string. @@ -47,7 +44,7 @@ def to_string(interval, conv=..., *, disj=..., sep=..., left_open=..., left_clos """ ... -def from_data(data, conv=..., *, pinf=..., ninf=..., klass=...): +def from_data(data, conv=..., *, pinf=..., ninf=..., klass=...): # -> Interval: """ Import an interval from a list of 4-uples (left, lower, upper, right). @@ -60,7 +57,7 @@ def from_data(data, conv=..., *, pinf=..., ninf=..., klass=...): """ ... -def to_data(interval, conv=..., *, pinf=..., ninf=...): +def to_data(interval, conv=..., *, pinf=..., ninf=...): # -> list[Any]: """ Export given interval to a list of 4-uples (left, lower, upper, right). From a27ffa576b1a4f78f62c0cecd99715eea969163b Mon Sep 17 00:00:00 2001 From: Fuexfollets Date: Mon, 24 Mar 2025 14:48:33 -0400 Subject: [PATCH 15/23] Added typings directory to portion package data --- pyproject.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 5ba2d77..15c52f1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -54,3 +54,6 @@ ignore = ["B028"] [tool.setuptools] packages = ["portion"] + +[tool.setuptools.package-data] +portion = ["../typings/**/*.pyi"] From 0063b52ca7e43bebeea042fa396bd2ce6a90c612 Mon Sep 17 00:00:00 2001 From: Fuexfollets Date: Mon, 24 Mar 2025 15:34:49 -0400 Subject: [PATCH 16/23] Added typings directory to portion package data (revision) --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 15c52f1..19a1971 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -56,4 +56,4 @@ ignore = ["B028"] packages = ["portion"] [tool.setuptools.package-data] -portion = ["../typings/**/*.pyi"] +portion = ["typings/**/*.pyi"] From 1d07370bf5bfbf404dbf81ff885daed902ea8eab Mon Sep 17 00:00:00 2001 From: Fuexfollets Date: Mon, 24 Mar 2025 16:03:28 -0400 Subject: [PATCH 17/23] Fixed typing stub file resolution --- portion/py.typed | 1 + pyproject.toml | 2 +- typings/portion/api.pyi | 28 ---- typings/portion/const.pyi | 94 ------------ typings/portion/func.pyi | 97 ------------ typings/portion/interval.pyi | 280 ----------------------------------- typings/portion/io.pyi | 71 --------- 7 files changed, 2 insertions(+), 571 deletions(-) create mode 100644 portion/py.typed delete mode 100644 typings/portion/api.pyi delete mode 100644 typings/portion/const.pyi delete mode 100644 typings/portion/func.pyi delete mode 100644 typings/portion/interval.pyi delete mode 100644 typings/portion/io.pyi diff --git a/portion/py.typed b/portion/py.typed new file mode 100644 index 0000000..b648ac9 --- /dev/null +++ b/portion/py.typed @@ -0,0 +1 @@ +partial diff --git a/pyproject.toml b/pyproject.toml index 19a1971..8e3cfc7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -56,4 +56,4 @@ ignore = ["B028"] packages = ["portion"] [tool.setuptools.package-data] -portion = ["typings/**/*.pyi"] +portion = ["py.typed", "**/*.pyi"] diff --git a/typings/portion/api.pyi b/typings/portion/api.pyi deleted file mode 100644 index 6dbf44f..0000000 --- a/typings/portion/api.pyi +++ /dev/null @@ -1,28 +0,0 @@ -""" -This type stub file was generated by pyright. -""" - -__all__ = ["create_api"] -def partial(wrapped, *args, **kwargs): # -> _Wrapped[Callable[..., Any], Any, Callable[..., Any], Any]: - """ - Convenient helper that combines functools.update_wrapper and - functools.partial. It has exactly the same signature than functools.partial. - """ - ... - -def create_api(interval, *, interval_dict=..., name=...): # -> ModuleType: - """Create a spe - - Dynamically create a module whose API is similar to the one of portion, but - configured to use given Interval class. Unless specified, a new IntervalDict - subclass is automatically generated to use given Interval subclass. - - This feature is experimental, and may be changed even in minor or patch - updates of portion. - - :param interval: a subclass of Interval. - :param interval_dict: a subclass of IntervalDict. - :param name: the name of the new module. - """ - ... - diff --git a/typings/portion/const.pyi b/typings/portion/const.pyi deleted file mode 100644 index ceffc1f..0000000 --- a/typings/portion/const.pyi +++ /dev/null @@ -1,94 +0,0 @@ -""" -This type stub file was generated by pyright. -""" - -import enum - -class Bound(enum.Enum): - """ - Bound types, either CLOSED for inclusive, or OPEN for exclusive. - """ - CLOSED = ... - OPEN = ... - def __bool__(self): - ... - - def __invert__(self): # -> Literal[Bound.CLOSED, Bound.OPEN]: - ... - - def __str__(self) -> str: - ... - - def __repr__(self): # -> Literal['CLOSED', 'OPEN']: - ... - - - -class _Singleton: - __instance = ... - def __new__(cls, *args, **kwargs): # -> Self: - ... - - - -class _PInf(_Singleton): - """ - Represent positive infinity. - """ - def __neg__(self): # -> _NInf: - ... - - def __lt__(self, o) -> bool: - ... - - def __le__(self, o) -> bool: - ... - - def __gt__(self, o) -> bool: - ... - - def __ge__(self, o) -> bool: - ... - - def __eq__(self, o) -> bool: - ... - - def __repr__(self): # -> Literal['+inf']: - ... - - def __hash__(self) -> int: - ... - - - -class _NInf(_Singleton): - """ - Represent negative infinity. - """ - def __neg__(self): # -> _PInf: - ... - - def __lt__(self, o) -> bool: - ... - - def __le__(self, o) -> bool: - ... - - def __gt__(self, o) -> bool: - ... - - def __ge__(self, o) -> bool: - ... - - def __eq__(self, o) -> bool: - ... - - def __repr__(self): # -> Literal['-inf']: - ... - - def __hash__(self) -> int: - ... - - - -inf = ... diff --git a/typings/portion/func.pyi b/typings/portion/func.pyi deleted file mode 100644 index 00dc2d6..0000000 --- a/typings/portion/func.pyi +++ /dev/null @@ -1,97 +0,0 @@ -""" -This type stub file was generated by pyright. -""" - -def open(lower, upper, *, klass=...): # -> Interval: - """ - Create an open interval with given bounds. - - :param lower: value of the lower bound. - :param upper: value of the upper bound. - :param klass: class to use for creating intervals (default to Interval). - :return: an interval. - """ - ... - -def closed(lower, upper, *, klass=...): # -> Interval: - """ - Create a closed interval with given bounds. - - :param lower: value of the lower bound. - :param upper: value of the upper bound. - :param klass: class to use for creating intervals (default to Interval). - :return: an interval. - """ - ... - -def openclosed(lower, upper, *, klass=...): # -> Interval: - """ - Create a left-open interval with given bounds. - - :param lower: value of the lower bound. - :param upper: value of the upper bound. - :param klass: class to use for creating intervals (default to Interval). - :return: an interval. - """ - ... - -def closedopen(lower, upper, *, klass=...): # -> Interval: - """ - Create a right-open interval with given bounds. - - :param lower: value of the lower bound. - :param upper: value of the upper bound. - :param klass: class to use for creating intervals (default to Interval). - :return: an interval. - """ - ... - -def singleton(value, *, klass=...): # -> Interval: - """ - Create a singleton interval. - - :param value: value of the lower and upper bounds. - :param klass: class to use for creating intervals (default to Interval). - :return: an interval. - """ - ... - -def empty(*, klass=...): # -> Interval: - """ - Create an empty interval. - - :param klass: class to use for creating intervals (default to Interval). - :return: an interval. - """ - ... - -def iterate(interval, step, *, base=..., reverse=...): # -> Generator[Any | object, Any, None]: - """ - Iterate on the (discrete) values of given interval. - - This function returns a (lazy) iterator over the values of given interval, - starting from its lower bound and ending on its upper bound (if interval is - not open). Each returned value merely corresponds to lower + i * step, where - "step" defines the step between consecutive values. If reverse=True, a - negative step must be passed (as in Python's range function). - It also accepts a callable that is used to compute the next possible - value based on the current one. - - When a non-atomic interval is provided, this function chains the iterators obtained - by calling itself on the underlying atomic intervals. - - The values returned by the iterator can be aligned with a base value with the - "base" parameter. This parameter must be a callable that accepts the lower bound - of the (atomic) interval as input, and returns the first value that needs to be - considered for the iteration. - By default, the identity function is used. If reverse=True, then the upper bound - will be passed instead of the lower one. - - :param interval: an interval. - :param step: step between values, or a callable that returns the next value. - :param base: a callable that accepts a bound and returns an initial value. - :param reverse: set to True for descending order. - :return: a lazy iterator. - """ - ... - diff --git a/typings/portion/interval.pyi b/typings/portion/interval.pyi deleted file mode 100644 index ad668c0..0000000 --- a/typings/portion/interval.pyi +++ /dev/null @@ -1,280 +0,0 @@ -""" -This type stub file was generated by pyright. -""" - -Atomic = ... -def mergeable(a, b): - """ - Tester whether two atomic intervals can be merged (i.e. they overlap or - are adjacent). - - :param a: an atomic interval. - :param b: an atomic interval. - :return: True if mergeable, False otherwise. - """ - ... - -class Interval: - """ - This class represents an interval. - - An interval is an (automatically simplified) union of atomic intervals. - It can be created with Interval.from_atomic(...) or by passing Interval - instances to __init__. - """ - __slots__ = ... - __match_args__ = ... - def __init__(self, *intervals) -> None: - """ - Create a disjunction of zero, one or more intervals. - - :param intervals: zero, one or more intervals. - """ - ... - - @classmethod - def from_atomic(cls, left, lower, upper, right): # -> Self: - """ - Create an Interval instance containing a single atomic interval. - - :param left: either CLOSED or OPEN. - :param lower: value of the lower bound. - :param upper: value of the upper bound. - :param right: either CLOSED or OPEN. - """ - ... - - @property - def left(self): # -> Literal[Bound.OPEN]: - """ - Lowest left boundary is either CLOSED or OPEN. - """ - ... - - @property - def lower(self): # -> _PInf: - """ - Lowest lower bound value. - """ - ... - - @property - def upper(self): # -> _NInf: - """ - Highest upper bound value. - """ - ... - - @property - def right(self): # -> Literal[Bound.OPEN]: - """ - Highest right boundary is either CLOSED or OPEN. - """ - ... - - @property - def empty(self): # -> bool: - """ - True if interval is empty, False otherwise. - """ - ... - - @property - def atomic(self): # -> bool: - """ - True if this interval is atomic, False otherwise. - An interval is atomic if it is empty or composed of a single interval. - """ - ... - - @property - def enclosure(self): # -> Self: - """ - Return the smallest interval composed of a single atomic interval that encloses - the current interval. - - :return: an Interval instance. - """ - ... - - def replace(self, left=..., lower=..., upper=..., right=..., *, ignore_inf=...): # -> Self | Any: - """ - Create a new interval based on the current one and the provided values. - - If current interval is not atomic, it is extended or restricted such that - its enclosure satisfies the new bounds. In other words, its new enclosure - will be equal to self.enclosure.replace(left, lower, upper, right). - - Callable can be passed instead of values. In that case, it is called with the - current corresponding value except if ignore_inf if set (default) and the - corresponding bound is an infinity. - - :param left: (a function of) left boundary. - :param lower: (a function of) value of the lower bound. - :param upper: (a function of) value of the upper bound. - :param right: (a function of) right boundary. - :param ignore_inf: ignore infinities if functions are provided (default - is True). - :return: an Interval instance - """ - ... - - def apply(self, func): # -> Self: - """ - Apply a function on each of the underlying atomic intervals and return their - union as a new interval instance - - Given function is expected to return an interval (possibly empty or not - atomic) or a 4-uple (left, lower, upper, right) whose values correspond to - the parameters of Interval.from_atomic(left, lower, upper, right). - - This method is merely a shortcut for Interval(*list(map(func, self))). - - :param func: function to apply on each underlying atomic interval. - :return: an Interval instance. - """ - ... - - def adjacent(self, other): - """ - Test if two intervals are adjacent. - - Two intervals are adjacent if they do not overlap and their union form a - single atomic interval. - - While this definition corresponds to the usual notion of adjacency for atomic - intervals, it has stronger requirements for non-atomic ones since it requires - all underlying atomic intervals to be adjacent (i.e. that one - interval fills the gaps between the atomic intervals of the other one). - - :param other: an interval. - :return: True if intervals are adjacent, False otherwise. - """ - ... - - def overlaps(self, other): # -> bool: - """ - Test if two intervals overlap (i.e. if their intersection is non-empty). - - :param other: an interval. - :return: True if intervals overlap, False otherwise. - """ - ... - - def intersection(self, other): - """ - Return the intersection of two intervals. - - :param other: an interval. - :return: the intersection of the intervals. - """ - ... - - def union(self, other): - """ - Return the union of two intervals. - - :param other: an interval. - :return: the union of the intervals. - """ - ... - - def contains(self, item): # -> bool: - """ - Test if given item is contained in this interval. - This method accepts intervals and arbitrary comparable values. - - :param item: an interval or any arbitrary comparable value. - :return: True if given item is contained, False otherwise. - """ - ... - - def complement(self): # -> Interval: - """ - Return the complement of this interval. - - :return: the complement of this interval. - """ - ... - - def difference(self, other): - """ - Return the difference of two intervals. - - :param other: an interval. - :return: the difference of the intervals. - """ - ... - - def __getattr__(self, name): # -> None: - ... - - def __len__(self): # -> int: - ... - - def __iter__(self): # -> Generator[Self, Any, None]: - ... - - def __getitem__(self, item): # -> Self: - ... - - def __and__(self, other): # -> _NotImplementedType | Self: - ... - - def __or__(self, other): # -> Self | _NotImplementedType: - ... - - def __contains__(self, item): # -> bool: - ... - - def __invert__(self): # -> Self: - ... - - def __sub__(self, other): # -> _NotImplementedType | Self: - ... - - def __eq__(self, other) -> bool: - ... - - def __lt__(self, other) -> bool: - ... - - def __gt__(self, other) -> bool: - ... - - def __le__(self, other) -> bool: - ... - - def __ge__(self, other) -> bool: - ... - - def __hash__(self) -> int: - ... - - def __repr__(self): # -> LiteralString | Literal['()']: - ... - - - -class AbstractDiscreteInterval(Interval): - """ - An abstract class for discrete interval. - - This class is not expected to be used as-is, and should be subclassed - first. The only attribute/method that should be overriden is the `_step` - class variable. This variable defines the step between two consecutive - values of the discrete domain (e.g., 1 for integers). - If a meaningfull step cannot be provided (e.g., for characters), the - _incr and _decr class methods can be overriden. They respectively return - the next and previous value given the current one. - - This class is still experimental and backward incompatible changes may - occur even in minor or patch updates of portion. - """ - _step = ... - @classmethod - def from_atomic(cls, left, lower, upper, right): # -> Self: - ... - - - diff --git a/typings/portion/io.pyi b/typings/portion/io.pyi deleted file mode 100644 index 34426d4..0000000 --- a/typings/portion/io.pyi +++ /dev/null @@ -1,71 +0,0 @@ -""" -This type stub file was generated by pyright. -""" - -def from_string(string, conv, *, bound=..., disj=..., sep=..., left_open=..., left_closed=..., right_open=..., right_closed=..., pinf=..., ninf=..., klass=...): # -> Interval: - """ - Parse given string and create an Interval instance. - A converter function has to be provided to convert a bound (as string) to a value. - This function raises a ValueError if given string cannot be parsed to an interval. - - :param string: string to parse. - :param conv: function to convert a bound (as string) to an object. - :param bound: regex pattern for a value. - :param disj: regex pattern for disjunctive operator (default matches '|' and ' | '). - :param sep: regex pattern for bound separator (default matches ','). - :param left_open: regex pattern for left open boundary (default matches '('). - :param left_closed: regex pattern for left closed boundary (default - matches '['). - :param right_open: regex pattern for right open boundary (default matches ')'). - :param right_closed: regex pattern for right closed boundary (default - matches ']'). - :param pinf: regex pattern for positive infinity (default matches '+inf'). - :param ninf: regex pattern for negative infinity (default matches '-inf'). - :param klass: class to use for creating intervals (default to Interval). - :return: an interval. - """ - ... - -def to_string(interval, conv=..., *, disj=..., sep=..., left_open=..., left_closed=..., right_open=..., right_closed=..., pinf=..., ninf=...): # -> str: - """ - Export given interval to string. - - :param interval: an interval. - :param conv: function that is used to represent a bound (default is `repr`). - :param disj: string representing disjunctive operator (default is ' | '). - :param sep: string representing bound separator (default is ','). - :param left_open: string representing left open boundary (default is '('). - :param left_closed: string representing left closed boundary (default is '['). - :param right_open: string representing right open boundary (default is ')'). - :param right_closed: string representing right closed boundary (default is ']'). - :param pinf: string representing a positive infinity (default is '+inf'). - :param ninf: string representing a negative infinity (default is '-inf'). - :return: a string representation for given interval. - """ - ... - -def from_data(data, conv=..., *, pinf=..., ninf=..., klass=...): # -> Interval: - """ - Import an interval from a list of 4-uples (left, lower, upper, right). - - :param data: a list of 4-uples (left, lower, upper, right). - :param conv: function to convert bound values, default to identity. - :param pinf: value used to represent positive infinity. - :param ninf: value used to represent negative infinity. - :param klass: class to use for creating intervals (default to Interval). - :return: an interval. - """ - ... - -def to_data(interval, conv=..., *, pinf=..., ninf=...): # -> list[Any]: - """ - Export given interval to a list of 4-uples (left, lower, upper, right). - - :param interval: an interval. - :param conv: function to convert bound values, default to identity. - :param pinf: value used to encode positive infinity. - :param ninf: value used to encode negative infinity. - :return: a list of 4-uples (left, lower, upper, right) - """ - ... - From 00691115d964152c768bd6e9823b3e4e9af00d25 Mon Sep 17 00:00:00 2001 From: Fuexfollets Date: Mon, 24 Mar 2025 16:15:05 -0400 Subject: [PATCH 18/23] Added optional value typehint --- portion/dict.py | 1 + 1 file changed, 1 insertion(+) diff --git a/portion/dict.py b/portion/dict.py index 4fc0977..6711044 100644 --- a/portion/dict.py +++ b/portion/dict.py @@ -23,6 +23,7 @@ def _sortkey(i: tuple[Interval, object]) -> tuple[object, bool]: return (i[0].lower, i[0].left is Bound.OPEN) +# V = TypeVar("V", bound=object) V = TypeVar("V") From e908074625c117593488e97d67997e96007f82af Mon Sep 17 00:00:00 2001 From: Fuexfollets Date: Mon, 24 Mar 2025 16:20:48 -0400 Subject: [PATCH 19/23] Added optional value typehint --- portion/dict.py | 3 +-- typings/portion/dict.pyi | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/portion/dict.py b/portion/dict.py index 6711044..d43d338 100644 --- a/portion/dict.py +++ b/portion/dict.py @@ -23,8 +23,7 @@ def _sortkey(i: tuple[Interval, object]) -> tuple[object, bool]: return (i[0].lower, i[0].left is Bound.OPEN) -# V = TypeVar("V", bound=object) -V = TypeVar("V") +V = TypeVar("V", bound=object) class HowToCombineSingle(Protocol): diff --git a/typings/portion/dict.pyi b/typings/portion/dict.pyi index 4534f8a..a11bfbd 100644 --- a/typings/portion/dict.pyi +++ b/typings/portion/dict.pyi @@ -7,7 +7,7 @@ from typing import Generic, Optional, Protocol, TypeVar, Union, overload from sortedcontainers.sorteddict import ItemsView, KeysView, ValuesView from .interval import Interval -V = TypeVar("V") +V = TypeVar("V", bound=object) class HowToCombineSingle(Protocol): def __call__(self, x: V, y: V) -> V: ... From 91f3dba40474f4abfb416832664a959b1392210c Mon Sep 17 00:00:00 2001 From: Fuexfollets Date: Mon, 24 Mar 2025 16:15:05 -0400 Subject: [PATCH 20/23] Added typevar bounds for fallback --- portion/dict.py | 2 +- typings/portion/dict.pyi | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/portion/dict.py b/portion/dict.py index 4fc0977..d43d338 100644 --- a/portion/dict.py +++ b/portion/dict.py @@ -23,7 +23,7 @@ def _sortkey(i: tuple[Interval, object]) -> tuple[object, bool]: return (i[0].lower, i[0].left is Bound.OPEN) -V = TypeVar("V") +V = TypeVar("V", bound=object) class HowToCombineSingle(Protocol): diff --git a/typings/portion/dict.pyi b/typings/portion/dict.pyi index 4534f8a..a11bfbd 100644 --- a/typings/portion/dict.pyi +++ b/typings/portion/dict.pyi @@ -7,7 +7,7 @@ from typing import Generic, Optional, Protocol, TypeVar, Union, overload from sortedcontainers.sorteddict import ItemsView, KeysView, ValuesView from .interval import Interval -V = TypeVar("V") +V = TypeVar("V", bound=object) class HowToCombineSingle(Protocol): def __call__(self, x: V, y: V) -> V: ... From c856f3d440fa723111460fad577e7a0023fc2dd4 Mon Sep 17 00:00:00 2001 From: Fuexfollets Date: Tue, 25 Mar 2025 07:57:16 -0400 Subject: [PATCH 21/23] Reverted dict.py --- portion/dict.py | 176 ++++++++++++------------------------------------ 1 file changed, 43 insertions(+), 133 deletions(-) diff --git a/portion/dict.py b/portion/dict.py index d43d338..0462219 100644 --- a/portion/dict.py +++ b/portion/dict.py @@ -1,40 +1,18 @@ -# pyright: reportIncompatibleMethodOverride=false -# pyright: reportMissingTypeStubs=false - import contextlib -from collections.abc import ( - Collection, - Iterable, - Iterator, - Mapping, - MutableMapping, -) -from typing import Generic, Optional, Protocol, TypeVar, Union, cast, overload +from collections.abc import Mapping, MutableMapping from sortedcontainers import SortedDict -from sortedcontainers.sorteddict import ItemsView, KeysView, ValuesView from .const import Bound from .interval import Interval -def _sortkey(i: tuple[Interval, object]) -> tuple[object, bool]: +def _sortkey(i): # Sort by lower bound, closed first return (i[0].lower, i[0].left is Bound.OPEN) -V = TypeVar("V", bound=object) - - -class HowToCombineSingle(Protocol): - def __call__(self, x: V, y: V) -> V: ... - - -class HowToCombineWithInterval(Protocol): - def __call__(self, x: V, y: V, i: Interval) -> V: ... - - -class IntervalDict(Generic[V], MutableMapping[object, V]): +class IntervalDict(MutableMapping): """ An IntervalDict is a dict-like data structure that maps from intervals to data,where keys can be single values or Interval instances. @@ -52,17 +30,12 @@ class IntervalDict(Generic[V], MutableMapping[object, V]): values (not keys) that are stored. """ - __slots__: tuple[str, ...] = ("_storage",) + __slots__ = ("_storage",) # Class to use when creating Interval instances - _klass: type = Interval + _klass = Interval - def __init__( - self, - mapping_or_iterable: Union[ - Mapping[object, V], Iterable[tuple[object, V]], None - ] = None, - ): + def __init__(self, mapping_or_iterable=None): """ Return a new IntervalDict. @@ -73,15 +46,13 @@ def __init__( :param mapping_or_iterable: optional mapping or iterable. """ - self._storage: SortedDict = SortedDict( - _sortkey - ) # Mapping from intervals to values + self._storage = SortedDict(_sortkey) # Mapping from intervals to values if mapping_or_iterable is not None: self.update(mapping_or_iterable) @classmethod - def _from_items(cls, items: Collection[tuple[object, V]]): + def _from_items(cls, items): """ Fast creation of an IntervalDict with the provided items. @@ -111,17 +82,7 @@ def copy(self): """ return self.__class__._from_items(self.items()) - @overload - def get( - self, key: Interval, default: Optional[V] = None - ) -> "IntervalDict[V] | None": ... - - @overload - def get(self, key: object, default: V = None) -> Optional[V]: ... - - def get( - self, key: Union[object, Interval], default: Optional[V] = None - ) -> Union["IntervalDict[V]", V, None]: + def get(self, key, default=None): """ Return the values associated to given key. @@ -144,7 +105,7 @@ def get( except KeyError: return default - def find(self, value: V) -> Interval: + def find(self, value): """ Return a (possibly empty) Interval i such that self[i] = value, and self[~i] != value. @@ -152,18 +113,9 @@ def find(self, value: V) -> Interval: :param value: value to look for. :return: an Interval instance. """ - return cast( - Interval, - self._klass( - *( - i - for i, v in cast(ItemsView[Interval, V], self._storage.items()) - if v == value - ) - ), - ) + return self._klass(*(i for i, v in self._storage.items() if v == value)) - def items(self) -> ItemsView[Interval, V]: + def items(self): """ Return a view object on the contained items sorted by their key (see https://docs.python.org/3/library/stdtypes.html#dict-views). @@ -172,7 +124,7 @@ def items(self) -> ItemsView[Interval, V]: """ return self._storage.items() - def keys(self) -> KeysView[Interval]: + def keys(self): """ Return a view object on the contained keys (sorted) (see https://docs.python.org/3/library/stdtypes.html#dict-views). @@ -181,7 +133,7 @@ def keys(self) -> KeysView[Interval]: """ return self._storage.keys() - def values(self) -> ValuesView[V]: + def values(self): """ Return a view object on the contained values sorted by their key (see https://docs.python.org/3/library/stdtypes.html#dict-views). @@ -190,23 +142,15 @@ def values(self) -> ValuesView[V]: """ return self._storage.values() - def domain(self) -> Interval: + def domain(self): """ Return an Interval corresponding to the domain of this IntervalDict. :return: an Interval. """ - return cast(Interval, self._klass(*self._storage.keys())) + return self._klass(*self._storage.keys()) - @overload - def pop(self, key: Interval, default: Optional[V] = None) -> "IntervalDict[V]": ... - - @overload - def pop(self, key: object, default: Optional[V] = None) -> Optional[V]: ... - - def pop( - self, key: object, default: Optional[V] = None - ) -> Union["IntervalDict[V]", V, None]: + def pop(self, key, default=None): """ Remove key and return the corresponding value if key is not an Interval. If key is an interval, it returns an IntervalDict instance. @@ -229,26 +173,16 @@ def pop( del self[key] return value - def popitem(self) -> tuple[Interval, V]: + def popitem(self): """ Remove and return some (key, value) pair as a 2-tuple. Raise KeyError if D is empty. :return: a (key, value) pair. """ - return cast(tuple[Interval, V], self._storage.popitem()) - - @overload - def setdefault( - self, key: Interval, default: Optional[V] = None - ) -> "IntervalDict[V]": ... + return self._storage.popitem() - @overload - def setdefault(self, key: object, default: Optional[V] = None) -> V: ... - - def setdefault( - self, key: object, default: Optional[V] = None - ) -> Union[V, "IntervalDict[V]", None]: + def setdefault(self, key, default=None): """ Return given key. If it does not exist, set its value to given default and return it. @@ -259,23 +193,16 @@ def setdefault( """ if isinstance(key, Interval): value = self.get(key, default) - if value is not None: - self.update(value) + self.update(value) return value else: try: return self[key] except KeyError: - if default is not None: - self[key] = default + self[key] = default return default - def update( - self, - mapping_or_iterable: Union[ - Mapping[object, V], Iterable[tuple[object, V]], type["IntervalDict[V]"] - ], - ): + def update(self, mapping_or_iterable): """ Update current IntervalDict with provided values. @@ -286,21 +213,14 @@ def update( :param mapping_or_iterable: mapping or iterable. """ if isinstance(mapping_or_iterable, Mapping): - data = cast(ItemsView[Interval, V], mapping_or_iterable.items()) + data = mapping_or_iterable.items() else: data = mapping_or_iterable - for i, v in cast(Collection[tuple[object, V]], data): + for i, v in data: self[i] = v - def combine( - self, - other: "IntervalDict[V]", - how: Union[HowToCombineSingle, HowToCombineWithInterval], - *, - missing: V = ..., - pass_interval: bool = False, - ) -> "IntervalDict[V]": + def combine(self, other, how, *, missing=..., pass_interval=False): """ Return a new IntervalDict that combines the values from current and provided IntervalDict. @@ -326,12 +246,10 @@ def combine( new_items = [] if not pass_interval: - how = cast(HowToCombineSingle, how) - def _how(x: V, y: V, i: Interval) -> V: + def _how(x, y, i): return how(x, y) else: - how = cast(HowToCombineWithInterval, how) _how = how dom1, dom2 = self.domain(), other.domain() @@ -356,7 +274,7 @@ def _how(x: V, y: V, i: Interval) -> V: return self.__class__(new_items) - def as_dict(self, atomic: bool = False) -> dict[Interval, V]: + def as_dict(self, atomic=False): """ Return the content as a classical Python dict. @@ -372,16 +290,10 @@ def as_dict(self, atomic: bool = False) -> dict[Interval, V]: else: return dict(self._storage) - @overload - def __getitem__(self, key: Interval) -> "IntervalDict[V]": ... - - @overload - def __getitem__(self, key: object) -> V: ... - - def __getitem__(self, key: Union[object, Interval]) -> Union[V, "IntervalDict[V]"]: + def __getitem__(self, key): if isinstance(key, Interval): items = [] - for i, v in cast(ItemsView[Interval, V], self._storage.items()): + for i, v in self._storage.items(): # Early out if key.upper < i.lower: break @@ -391,7 +303,7 @@ def __getitem__(self, key: Union[object, Interval]) -> Union[V, "IntervalDict[V] items.append((intersection, v)) return self.__class__._from_items(items) else: - for i, v in cast(ItemsView[Interval, V], self._storage.items()): + for i, v in self._storage.items(): # Early out if key < i.lower: break @@ -399,13 +311,11 @@ def __getitem__(self, key: Union[object, Interval]) -> Union[V, "IntervalDict[V] return v raise KeyError(key) - def __setitem__(self, key: Union[object, Interval], value: Optional[V]): + def __setitem__(self, key, value): if isinstance(key, Interval): interval = key else: - interval = cast( - Interval, self._klass.from_atomic(Bound.CLOSED, key, key, Bound.CLOSED) - ) + interval = self._klass.from_atomic(Bound.CLOSED, key, key, Bound.CLOSED) if interval.empty: return @@ -414,7 +324,7 @@ def __setitem__(self, key: Union[object, Interval], value: Optional[V]): added_items = [] found = False - for i, v in cast(ItemsView[Interval, V], self._storage.items()): + for i, v in self._storage.items(): if value == v: found = True # Extend existing key @@ -437,7 +347,7 @@ def __setitem__(self, key: Union[object, Interval], value: Optional[V]): for key, value in added_items: self._storage[key] = value - def __delitem__(self, key: Union[object, Interval]): + def __delitem__(self, key): if isinstance(key, Interval): interval = key else: @@ -472,22 +382,22 @@ def __delitem__(self, key: Union[object, Interval]): for key, value in added_items: self._storage[key] = value - def __or__(self, other: "IntervalDict[V]") -> "IntervalDict[V]": + def __or__(self, other): d = self.copy() d.update(other) return d - def __ior__(self, other: "IntervalDict[V]") -> "IntervalDict[V]": + def __ior__(self, other): self.update(other) return self - def __iter__(self) -> Iterator[object]: + def __iter__(self): return iter(self._storage) - def __len__(self) -> int: + def __len__(self): return len(self._storage) - def __contains__(self, key: object) -> bool: + def __contains__(self, key): return key in self.domain() def __repr__(self): @@ -495,8 +405,8 @@ def __repr__(self): ", ".join(f"{i!r}: {v!r}" for i, v in self.items()), ) - def __eq__(self, other: object) -> bool: + def __eq__(self, other): if isinstance(other, IntervalDict): return self.as_dict() == other.as_dict() - - return NotImplemented + else: + return NotImplemented From 2f2c2e6a289bdb9efe104420b8a13b48ed23d07e Mon Sep 17 00:00:00 2001 From: Fuexfollets Date: Tue, 25 Mar 2025 09:11:10 -0400 Subject: [PATCH 22/23] Relocated typestub files --- {typings/portion => portion}/__init__.pyi | 0 {typings/portion => portion}/dict.pyi | 0 pyproject.toml | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) rename {typings/portion => portion}/__init__.pyi (100%) rename {typings/portion => portion}/dict.pyi (100%) diff --git a/typings/portion/__init__.pyi b/portion/__init__.pyi similarity index 100% rename from typings/portion/__init__.pyi rename to portion/__init__.pyi diff --git a/typings/portion/dict.pyi b/portion/dict.pyi similarity index 100% rename from typings/portion/dict.pyi rename to portion/dict.pyi diff --git a/pyproject.toml b/pyproject.toml index 8e3cfc7..140238e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,7 +34,7 @@ classifiers = [ test = ["pytest ~= 7.0", "coverage ~= 6.0", "ruff >= 0.6.9"] [tool.ruff] -extend-exclude = ["tests/", "typings/"] +extend-exclude = ["tests/", "**/*.pyi"] [tool.ruff.lint] select = [ From afbe614a3b026d5157d35d7d602b32aaa6d3a093 Mon Sep 17 00:00:00 2001 From: Fuexfollets Date: Tue, 25 Mar 2025 10:45:38 -0400 Subject: [PATCH 23/23] Removed __init__.pyi --- portion/__init__.pyi | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 portion/__init__.pyi diff --git a/portion/__init__.pyi b/portion/__init__.pyi deleted file mode 100644 index 97231ce..0000000 --- a/portion/__init__.pyi +++ /dev/null @@ -1,16 +0,0 @@ -""" -This type stub file was generated by pyright. -""" - -import importlib.metadata -from .api import create_api -from .const import Bound, inf -from .dict import IntervalDict -from .func import closed, closedopen, empty, iterate, open, openclosed, singleton -from .interval import AbstractDiscreteInterval, Interval -from .io import from_data, from_string, to_data, to_string - -__all__ = ["create_api", "inf", "CLOSED", "OPEN", "Interval", "AbstractDiscreteInterval", "open", "closed", "openclosed", "closedopen", "singleton", "empty", "iterate", "from_string", "to_string", "from_data", "to_data", "IntervalDict"] -CLOSED = ... -OPEN = ... -__version__ = ...