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.pyi b/portion/dict.pyi new file mode 100644 index 0000000..a11bfbd --- /dev/null +++ b/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", 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]): + """ + 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/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 462efb6..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/"] +extend-exclude = ["tests/", "**/*.pyi"] [tool.ruff.lint] select = [ @@ -54,3 +54,6 @@ ignore = ["B028"] [tool.setuptools] packages = ["portion"] + +[tool.setuptools.package-data] +portion = ["py.typed", "**/*.pyi"]