diff --git a/.wordlist.txt b/.wordlist.txt index 69006a43..07460fd0 100644 --- a/.wordlist.txt +++ b/.wordlist.txt @@ -323,3 +323,12 @@ iadd isub repr nanomobx +OnCollision +iter +getitem +getter +delitem +eq +exc +iterable +denoms diff --git a/cosmpy/aerial/coins.py b/cosmpy/aerial/coins.py index 1ae7f5cd..07d09b8a 100644 --- a/cosmpy/aerial/coins.py +++ b/cosmpy/aerial/coins.py @@ -21,7 +21,10 @@ import re from dataclasses import dataclass -from typing import List, Optional, Union +from enum import Enum +from typing import Iterable, List, Optional, Union + +from sortedcontainers import SortedDict from cosmpy.protos.cosmos.base.v1beta1.coin_pb2 import Coin as CoinProto @@ -31,30 +34,53 @@ @dataclass class Coin: - """Coins.""" + """Coins. + + This class does not implicitly ensure that its value represents a valid coin based on Cosmos-SDK requirements. + This is by design to enable operations with the coin instance which might need to pass Coin instance as + by-reference and change coin value the way which will make it invalid from Cosmos-SDK requirements perspective. + For example, mathematical calculations/operations which might need to use Coin to store a relative rather than + an absolute amount value, what might result in to negative coin amount value. + This is to enable flexibility, rather than fail immediately when setting amount or denom values + + The implication is that the validation needs to be executed explicitly by calling the `validate()` method. + """ amount: int denom: str + def __repr__(self) -> str: + """Return Cosmos-SDK string representation of the coin this (self) instance holds.""" + return f"{self.amount}{self.denom}" + def to_proto(self) -> CoinProto: """Convert this type to protobuf schema Coin type.""" return CoinProto(amount=str(self.amount), denom=self.denom) - def __repr__(self) -> str: - """Return string representation of coin.""" - return f"{self.amount}{self.denom}" - def validate(self): - """Validate this type based on Cosmos-SDK requirements for Coin. + """Validate Coin instance based on Cosmos-SDK requirements. + + Throws ValueError exception if coin instance is invalid based on Cosmos-SDK requirements. + """ + self.validate_amount() + self.validate_denom() - :raises ValueError: If amount is negative or denom does not conform to cosmos-sdk requirement for denomination. + def validate_amount(self): + """Validate coin amount value based on Cosmos-SDK requirements. + + :raises ValueError: If coin amount value does not conform to cosmos-sdk requirement. """ if not self.is_amount_valid(): - raise ValueError("Coin amount must be greater than zero") + raise ValueError(f"Coin amount {self.amount} must be greater than zero") + + def validate_denom(self): + """Validate coin denom value based on Cosmos-SDK requirements. + :raises ValueError: If coin denom value does not conform to cosmos-sdk requirement. + """ if not self.is_denom_valid(): raise ValueError( - f'The "{self.denom}" denom does not conform to Cosmos-SDK requirements' + f'Coin denom "{self.denom}" does not conform to Cosmos-SDK requirements.' ) def is_valid(self) -> bool: @@ -67,10 +93,10 @@ def is_valid(self) -> bool: def is_amount_valid(self) -> bool: """Validate amount value based on Cosmos-SDK requirements. - :return: True if the amount conforms to cosmos-sdk requirement for Coin amount (when it is greater than zero), - False otherwise. + :return: True if the amount conforms to cosmos-sdk requirement for Coin amount (when it is greater than + or equal to zero). False otherwise. """ - return self.amount > 0 + return is_coin_amount_valid(self.amount) def is_denom_valid(self) -> bool: """Validate denom value based on Cosmos-SDK requirements. @@ -80,45 +106,33 @@ def is_denom_valid(self) -> bool: return is_denom_valid(self.denom) -class Coins(List[Coin]): - """Coins. +CoinsParamType = Union[ + str, "Coins", Iterable[Coin], Iterable[CoinProto], Coin, CoinProto +] + + +class OnCollision(Enum): + """OnCollision Enum.""" + + Fail = 0 + Override = 1 + - It is required to call the 'canonicalise()' method in order to ensure that the Coins instance conforms to - Cosmos-SDK requirements for Coins type! - This is because one way or another, due to the nature of the base List type, it is possible to create such an - instance of Coins which does not conform to Cosmos-SDK requirements. +class Coins: + """This class implements the behaviour of Coins as defined by Cosmos-SDK. + + Implementation of this class guarantees, that the value it represents/holds is *always* valid (= conforms to + Cosmos-SDK requirements), and all its methods ensure that the value always remains valid, or they fail with + an exception. """ def __init__( self, - coins: Optional[ - Union[str, "Coins", List[Coin], List[CoinProto], Coin, CoinProto] - ] = None, + coins: Optional[CoinsParamType] = None, ): - """Convert any coin representation into Coins.""" - if coins is None: - super().__init__() - return - - if isinstance(coins, str): - _coins = Coins._from_string(coins) - elif isinstance(coins, Coins): - _coins = Coins._from_coins_list(coins) - elif isinstance(coins, Coin): - _coins = [coins] - elif isinstance(coins, CoinProto): - _coins = Coins._from_coins_list([coins]) - elif isinstance(coins, list): - if len(coins) == 0: - _coins = [] - elif isinstance(coins[0], (Coin, CoinProto)): - _coins = Coins._from_coins_list(coins) - else: - raise TypeError(f"Invalid type {type(coins)}") - else: - raise ValueError(f"Invalid type {type(coins)}") - - super().__init__(_coins) + """Instantiate Coins from any of the supported coin(s) representation types.""" + self._amounts: SortedDict[str, int] = SortedDict() + self.assign(coins) def __repr__(self) -> str: """Return cosmos-sdk string representation of Coins. @@ -129,79 +143,47 @@ def __repr__(self) -> str: from cosmpy.aerial.client.coins import Coin, Coins coins = Coins([Coin(1,"afet"), Coin(2,"uatom"), Coin(3,"nanomobx")]) - assert str(coins) == "1afet,2uatom,3nanomobx" - """ - return ",".join([str(c) for c in self[:]]) - - def to_proto(self) -> List[CoinProto]: - """Convert this type to *protobuf schema* Coins type.""" - coins = Coins(self).canonicalise() - return [CoinProto(amount=str(c.amount), denom=c.denom) for c in coins] - - def canonicalise(self) -> "Coins": - """Reorganise the value of the 'self' instance in to canonical form defined by cosmos-sdk for `Coins`. - - This means dropping all coins with zero value, and alphabetically sorting (ascending) the coins based - on denomination. - The algorithm *fails* with exception *if* any of the denominations in the list is *not* unique = if some of the - denominations are present in the coin list more than once, or if validation of any individual coin will fail. - :returns: The 'self' instance. - """ - coins = [c for c in self if c.amount > 0] - - self.clear() - self.extend(coins) - - sort_coins(self) - self.validate() - - return self - - def validate(self): - """Validate whether current value conforms to canonical form for list of coins defined by cosmos-sdk. - - Raises ValueError exception *IF* denominations are not unique, or if validation of individual coins raises an - exception. + assert repr(coins) == "1afet,2uatom,3nanomobx" + assert str(coins) == repr(coins) """ - validate_coins(self) + return ",".join([repr(c) for c in self]) - @classmethod - def _from_coins_list(cls, coins: List[Union[Coin, CoinProto]]) -> List[Coin]: - """Create aerial Coins from List of CoinProto objects."". + def __hash__(self) -> int: + """Hash.""" + return hash(self._amounts) - :param coins: input list of CoinsProto - :return: List of Coin objects - """ - return [Coin(amount=int(coin.amount), denom=coin.denom) for coin in coins] + def __len__(self) -> int: + """Get number of coins.""" + return len(self._amounts) - @classmethod - def _from_string(cls, value: str) -> List[Coin]: - """Parse the coins. + def __eq__(self, right) -> bool: + """Compare if two instances of Coins are equal.""" + if not isinstance(right, Coins): + right = Coins(right) - :param value: coins - :raises RuntimeError: If unable to parse the value - :return: coins - """ - coins = [] + return self._amounts == right._amounts - parts = re.split(r",\s*", value) - for part in parts: - part = part.strip() - if part == "": - continue + def __getitem__(self, denom: str) -> Coin: + """Coins safe getter that prevents modifying of the reference.""" + amount = self._amounts[denom] + return Coin(amount, denom) - match = re.match(r"^(\d+)(.+)$", part) - if match is None: - raise RuntimeError(f"Unable to parse value {part}") + def __contains__(self, denom: str) -> bool: + """Return true if denom is present.""" + return denom in self._amounts - amount, denom = match.groups() - coins.append(Coin(amount=int(amount), denom=denom)) + def __delitem__(self, denom: str): + """Remove denom.""" + del self._amounts[denom] - return coins + def __iter__(self): + """Get coins iterator.""" + for denom, amount in self._amounts.items(): + yield Coin(amount, denom) def __add__(self, other): """Perform algebraic vector addition of two coin lists.""" - result = Coins() + result = Coins(self) for (left, right) in self._math_operation(other, result_inout=result): left.amount += right.amount @@ -209,7 +191,7 @@ def __add__(self, other): def __sub__(self, other): """Perform algebraic vector subtraction of two coin lists, ensuring no coin has negative value.""" - result = Coins() + result = Coins(self) for (left, right) in self._math_operation(other, result_inout=result): if left.amount < right.amount: raise RuntimeError( @@ -239,31 +221,219 @@ def __isub__(self, other): return result - def _math_operation(self, other: List[Coin], result_inout: "Coins"): - self.validate() + def clear(self) -> "Coins": + """Delete all coins.""" + self._amounts.clear() + return self - if isinstance(other, Coins): - other.validate() - else: - Coins(other).validate() + def denoms(self) -> Iterable[str]: + """Return denominations of the coins in this(self) instance in ordered ascending alphabetically. - res_dict = {c.denom: Coin(amount=c.amount, denom=c.denom) for c in self} - for c in other: - left = res_dict.get(c.denom, Coin(amount=0, denom=c.denom)) + :return: iterable of denominations + """ + return self._amounts.keys() - yield left, c + def assign( + self, + coins: Optional[CoinsParamType] = None, + ) -> "Coins": + """Assign passed in `coins` *in to* this ('self') instance. - if left.amount == 0: - if left.denom in res_dict: - del res_dict[left.denom] - elif left.amount > 0: - res_dict[left.denom] = left + This means that the current value of this ('self') instance will be completely *replaced* with the value + carried by the input `coins` parameter. + + :param coins: Input coins in any of the supported types. + + :raises TypeError: If coins or coin in a list has unexpected type + + :return: self + """ + self.clear() + + if coins is None: + return self + + if isinstance(coins, str): + self._from_string(coins) + elif isinstance(coins, Coins): + self._from_coins_list(coins) + elif isinstance(coins, (Coin, CoinProto)): + self._from_coins_list([coins]) + elif isinstance(coins, list): + if len(coins) == 0: + pass + elif isinstance(coins[0], (Coin, CoinProto)): + self._from_coins_list(coins) else: - raise RuntimeError(f"Operation yielded negative amount {left}") + raise TypeError(f"Invalid type {type(coins)}") + else: + raise TypeError(f"Invalid type {type(coins)}") - result_inout.clear() - result_inout.extend(res_dict.values()) - result_inout.canonicalise() + return self + + def merge_from( + self, + coins: CoinsParamType, + on_collision: OnCollision = OnCollision.Fail, + ) -> "Coins": + """Merge passed in coins in to this ('self') coins instance. + + :param coins: Input coins in any of the supported types. + :param on_collision: Instructs what to do in the case of a denom collision = if this (self) already contains + one or more the denomination in the `coins` value: + - if `OnCollision.Override`: then the colliding coin amount in this (self) object will be + *overridden* with the colliding amount value from the `coins` parameter. + - if `OnCollision.Fail`: then the merge will *fail* with the `ValueError` exception when + the first collision is detected + + :return: The `self` instance containing merged coins + """ + cs = Coins(coins) + + for c in cs: + self._merge_coin(c, on_collision) + + return self + + def delete(self, denominations: Iterable[str]) -> "Coins": + """Delete coins from this ('self') instance for each denom listed in `denominations` argument. + + :param denominations: collection of denominations to drop + :return: deleted Coins + """ + removed_coins = Coins() + for denom in denominations: + if denom in self: + removed_coins.merge_from(self[denom]) + + for c in removed_coins: + del self[c.denom] + + return self + + def get(self, denom: str, default_amount: int) -> Coin: + """Return Coin instance for the given `denom`. + + If coin with the given `denom` is not present, the `default` will be returned. + + Runtime complexity: `O(log(n))` + + This method poses the same risk to validity of the Coins value as the `__getitem__(...)` method, + since at the moment it returns Coin instance *by-reference* what allows to change the `Coin.amount` value + from external context and so potentially invalidate the value represented by the `Coins` class/container. + + :param denom: denomination of the coin to query. + :param default_amount: default amount used to construct returned Coin instance if there is *no* coin with + the given `denom` present in this coins instance. + :return: coin instance for the given `denom`, or the `default` value. + + Example:: + >>> from cosmpy.aerial.coins import Coin, Coins + >>> cs = Coins("1aaa,2baa,3caa") + >>> cs.get("baa", 0) + 2baa + >>> cs.get("ggg", 0) + 0ggg + """ + amount = self._amounts[denom] if denom in self._amounts else default_amount + return Coin(amount, denom) + + def get_by_index(self, index: int) -> Coin: + """Return Coin instance at given `index`. + + If the `index` is out of range, raises :exc:`IndexError`. + + Runtime complexity: `O(log(n))` + + This method poses the same risk to validity of the Coins value as the `__getitem__(...)` method, + since at the moment it returns Coin instance *by-reference* what allows to change the `Coin.amount` value + from external context and so potentially invalidate the value represented by the `Coins` class/container. + + Example:: + >>> from cosmpy.aerial.coins import Coin, Coins + >>> cs = Coins("1aaa,2baa,3caa") + >>> cs.get_by_index(0) + 1aaa + >>> cs.get_by_index(2) + 3caa + >>> cs.get_by_index(3) + Traceback (most recent call last): + ... + IndexError: list index out of range + + :param index: int index of item (default -1) + :return: key and value pair + """ + denom, amount = self._amounts.peekitem(index) + return Coin(amount, denom) + + def to_proto(self) -> List[CoinProto]: + """Convert this type to *protobuf schema* Coins type.""" + return [c.to_proto() for c in self] + + def _merge_coin(self, coin: Coin, on_collision: OnCollision = OnCollision.Fail): + """Merge singular aerial Coin in to this object. + + :param coin: input coin to merged. + :param on_collision: If OnCollision.Override then the coin instance in this (self) object will be overridden + if it already contains the denomination, if OnCollision.Fail the merge will fail with + an exception. + :raises ValueError: If there is denom collision and the `on_collision` is set to `OnCollision.Fail`, + or if the `on_collision` has unknown enum value. + """ + if on_collision == OnCollision.Override: + fail_on_collision = False + elif on_collision == OnCollision.Fail: + fail_on_collision = True + else: + raise ValueError(f"Unknown on_collision value: {on_collision}") + + is_already_present = coin.denom in self + + if coin.amount == 0: + if not fail_on_collision and is_already_present: + del self._amounts[coin.denom] + + # Skipping if amount is zero + return + + if fail_on_collision and is_already_present: + raise ValueError( + f'Attempt to merge a coin with the "{coin.denom}" denomination which already exists in the receiving coins instance' + ) + + coin.validate() + self._amounts[coin.denom] = coin.amount + + def _from_coins_list(self, coins: Iterable[Union[Coin, CoinProto]]): + """Create aerial Coins from List of CoinProto objects."". + + :param coins: input list of coins + """ + for c in coins: + self._merge_coin(Coin(int(c.amount), c.denom)) + + def _from_string(self, value: str): + """Parse the coins string and merge it to self. + + :param value: coins + """ + for coin in from_string(value): + self._merge_coin(coin) + + @staticmethod + def _math_operation(other: CoinsParamType, result_inout: "Coins"): + res: Coins = result_inout + + if not isinstance(other, Coins): + other = Coins(other) + + for c in other: + left: Coin = res.get(c.denom, 0) + yield left, c + res._merge_coin( # pylint: disable=protected-access + left, on_collision=OnCollision.Override + ) def parse_coins(value: str) -> List[CoinProto]: @@ -275,97 +445,106 @@ def parse_coins(value: str) -> List[CoinProto]: return Coins(value).to_proto() -CoinsParamType = Union[str, Coins, List[Coin], List[CoinProto], Coin, CoinProto] +def from_string(value: str): + """Parse the coins string and yields individual coins as Coin instances in order of their definition in input `value`. + + :param value: coins + + :yields: Coin objects one by one in the order they are specified in the input `value` string, where validation of + the yielded Coin instance is intentionally *NOT* executed => yielded coin instance might *NOT* be valid + when judged based on cosmos-sdk requirements. + This is by-design to enable just basic parsing focused exclusively on the format of the coins string value. + This leaves a degree of freedom for a caller on how the resulting/parsed coins should be used/consumed, + rather than forcing any checks/validation for individual coins instances, or coins collection as a whole, + here. + + :raises RuntimeError: If unable to parse the value + """ + parts = re.split(r",\s*", value) + for part in parts: + part = part.strip() + if part == "": + continue + + match = re.match(r"^(\d+)(.+)$", part) + if match is None: + raise RuntimeError(f"Unable to parse value {part}") + + amount, denom = match.groups() + yield Coin(int(amount), denom) + + +def is_coin_amount_valid(amount: int) -> bool: + """Check if amount value conforms to Cosmos-SDK requirements. + + :param amount: amount to be checked + :return: True if the amount conforms to cosmos-sdk requirement for Coin amount (when it is greater than zero), + False otherwise. + """ + return amount > 0 def is_denom_valid(denom: str) -> bool: - """Return true if coin denom name is valid. + """Check if denom value conforms to Cosmos-SDK requirements. - :param denom: string denom - :return: bool validity + :param denom: Denom to be checked + :return: True if the denom conforms to cosmos-sdk requirement """ return denom_regex.match(denom) is not None -def is_coins_sorted(coins: Union[str, Coins, List[Coin], List[CoinProto]]) -> bool: +def is_coins_sorted( + coins: Union[str, Coins, Iterable[Coin], Iterable[CoinProto]] +) -> bool: """Return true if given coins representation is sorted in ascending order of denom. :param coins: Any type representing coins :return: bool is_sorted """ + if coins is None: + return False + if not coins: return True if isinstance(coins, str): - coins = Coins(coins) + coins = from_string(coins) + + itr = iter(coins) + coin = next(itr, None) - last_denom = coins[0].denom + if coin is None: + return True + + last_denom = coin.denom + coin = next(itr, None) - for c in coins[1:]: - if last_denom >= c.denom: + while coin is not None: + if last_denom >= coin.denom: return False - last_denom = c.denom + last_denom = coin.denom + coin = next(itr, None) return True -def validate_coins(coins: Union[str, Coins, List[Coin], List[CoinProto]]): +def validate_coins(coins: Union[str, Coins, Iterable[Coin], Iterable[CoinProto]]): """Return true if given coins representation is valid. + raises ValueError if there are multiple coins with the same denom + :param coins: Any type representing coins - :raises ValueError: If there are multiple coins with the same denom - :return: bool validity + :return: True if valid, False otherwise """ if not coins: return - if isinstance(coins, str): - coins = Coins(coins) - - if len(coins) == 0: - return - - def _validate_coin(coin: Union[Coin, CoinProto]): - """Validate coin. - - :param coin: Coin or CoinProto - - """ - if isinstance(coin, CoinProto): - coin = Coin(int(coin.amount), coin.denom) - - coin.validate() - - _validate_coin(coins[0]) - - seen = set() - last_denom = coins[0].denom - seen.add(last_denom) - - for c in coins[1:]: - if c.denom in seen: - raise ValueError(f'Multiple occurrences of the "{c.denom}" denomination') - - if last_denom >= c.denom: - raise ValueError( - "Coins are not sorted as cosmos-sdk expects it (ascending based on denom)" - ) - - _validate_coin(c) - - last_denom = c.denom - seen.add(c.denom) - - -# def sort_coins(coins: Union[Coins, CoinsProto, List[Coin], List[CoinProto]]): -def sort_coins(coins: Union[Coins, List[Coin], List[CoinProto]]): - """Sort the collection of coins based on Cosmos-SDK definition of Coins validity. - - Coins collection is sorted ascending alphabetically based on denomination. - - NOTE: The resulting sorted collection of coins is *NOT* validated by calling the 'Coins.validate()'. - - :param coins: Coins to sort - """ - coins.sort(key=lambda c: c.denom, reverse=False) + if isinstance(coins, Coins): + # Strictly speaking, this is not necessary, since API of the Coins class implicitly ensures validity of + # the value it holds. + for coin in coins: + coin.validate() + else: + # Conversion to Coins will verify everything, no need to do anything else: + _ = Coins(coins) diff --git a/docs/api/aerial/coins.md b/docs/api/aerial/coins.md index d9650f17..d2af0169 100644 --- a/docs/api/aerial/coins.md +++ b/docs/api/aerial/coins.md @@ -15,6 +15,25 @@ class Coin() Coins. +This class does not implicitly ensure that its value represents a valid coin based on Cosmos-SDK requirements. +This is by design to enable operations with the coin instance which might need to pass Coin instance as +by-reference and change coin value the way which will make it invalid from Cosmos-SDK requirements perspective. +For example, mathematical calculations/operations which might need to use Coin to store a relative rather than +an absolute amount value, what might result in to negative coin amount value. +This is to enable flexibility, rather than fail immediately when setting amount or denom values + +The implication is that the validation needs to be executed explicitly by calling the `validate()` method. + + + +#### `__`repr`__` + +```python +def __repr__() -> str +``` + +Return Cosmos-SDK string representation of the coin this (self) instance holds. + #### to`_`proto @@ -25,29 +44,45 @@ def to_proto() -> CoinProto Convert this type to protobuf schema Coin type. - + -#### `__`repr`__` +#### validate ```python -def __repr__() -> str +def validate() ``` -Return string representation of coin. +Validate Coin instance based on Cosmos-SDK requirements. - +Throws ValueError exception if coin instance is invalid based on Cosmos-SDK requirements. -#### validate + + +#### validate`_`amount ```python -def validate() +def validate_amount() ``` -Validate this type based on Cosmos-SDK requirements for Coin. +Validate coin amount value based on Cosmos-SDK requirements. **Raises**: -- `ValueError`: If amount is negative or denom does not conform to cosmos-sdk requirement for denomination. +- `ValueError`: If coin amount value does not conform to cosmos-sdk requirement. + + + +#### validate`_`denom + +```python +def validate_denom() +``` + +Validate coin denom value based on Cosmos-SDK requirements. + +**Raises**: + +- `ValueError`: If coin denom value does not conform to cosmos-sdk requirement. @@ -75,8 +110,8 @@ Validate amount value based on Cosmos-SDK requirements. **Returns**: -True if the amount conforms to cosmos-sdk requirement for Coin amount (when it is greater than zero), -False otherwise. +True if the amount conforms to cosmos-sdk requirement for Coin amount (when it is greater than +or equal to zero). False otherwise. @@ -92,31 +127,39 @@ Validate denom value based on Cosmos-SDK requirements. True if denom conforms to cosmos-sdk requirement for denomination, False otherwise. + + +## OnCollision Objects + +```python +class OnCollision(Enum) +``` + +OnCollision Enum. + ## Coins Objects ```python -class Coins(List[Coin]) +class Coins() ``` -Coins. +This class implements the behaviour of Coins as defined by Cosmos-SDK. -It is required to call the 'canonicalise()' method in order to ensure that the Coins instance conforms to -Cosmos-SDK requirements for Coins type! -This is because one way or another, due to the nature of the base List type, it is possible to create such an -instance of Coins which does not conform to Cosmos-SDK requirements. +Implementation of this class guarantees, that the value it represents/holds is *always* valid (= conforms to +Cosmos-SDK requirements), and all its methods ensure that the value always remains valid, or they fail with +an exception. #### `__`init`__` ```python -def __init__(coins: Optional[Union[str, "Coins", List[Coin], List[CoinProto], - Coin, CoinProto]] = None) +def __init__(coins: Optional[CoinsParamType] = None) ``` -Convert any coin representation into Coins. +Instantiate Coins from any of the supported coin(s) representation types. @@ -135,49 +178,78 @@ Example:: from cosmpy.aerial.client.coins import Coin, Coins coins = Coins([Coin(1,"afet"), Coin(2,"uatom"), Coin(3,"nanomobx")]) -assert str(coins) == "1afet,2uatom,3nanomobx" +assert repr(coins) == "1afet,2uatom,3nanomobx" +assert str(coins) == repr(coins) - + -#### to`_`proto +#### `__`hash`__` ```python -def to_proto() -> List[CoinProto] +def __hash__() -> int ``` -Convert this type to *protobuf schema* Coins type. +Hash. - + -#### canonicalise +#### `__`len`__` ```python -def canonicalise() -> "Coins" +def __len__() -> int ``` -Reorganise the value of the 'self' instance in to canonical form defined by cosmos-sdk for `Coins`. +Get number of coins. -This means dropping all coins with zero value, and alphabetically sorting (ascending) the coins based -on denomination. -The algorithm *fails* with exception *if* any of the denominations in the list is *not* unique = if some of the -denominations are present in the coin list more than once, or if validation of any individual coin will fail. + -**Returns**: +#### `__`eq`__` -The 'self' instance. +```python +def __eq__(right) -> bool +``` - +Compare if two instances of Coins are equal. -#### validate + + +#### `__`getitem`__` ```python -def validate() +def __getitem__(denom: str) -> Coin ``` -Validate whether current value conforms to canonical form for list of coins defined by cosmos-sdk. +Coins safe getter that prevents modifying of the reference. -Raises ValueError exception *IF* denominations are not unique, or if validation of individual coins raises an -exception. + + +#### `__`contains`__` + +```python +def __contains__(denom: str) -> bool +``` + +Return true if denom is present. + + + +#### `__`delitem`__` + +```python +def __delitem__(denom: str) +``` + +Remove denom. + + + +#### `__`iter`__` + +```python +def __iter__() +``` + +Get coins iterator. @@ -219,6 +291,181 @@ def __isub__(other) Perform *in-place* algebraic vector subtraction of two coin lists, ensuring no coin has negative value. + + +#### clear + +```python +def clear() -> "Coins" +``` + +Delete all coins. + + + +#### denoms + +```python +def denoms() -> Iterable[str] +``` + +Return denominations of the coins in this(self) instance in ordered ascending alphabetically. + +**Returns**: + +iterable of denominations + + + +#### assign + +```python +def assign(coins: Optional[CoinsParamType] = None) -> "Coins" +``` + +Assign passed in `coins` *in to* this ('self') instance. + +This means that the current value of this ('self') instance will be completely *replaced* with the value +carried by the input `coins` parameter. + +**Arguments**: + +- `coins`: Input coins in any of the supported types. + +**Raises**: + +- `TypeError`: If coins or coin in a list has unexpected type + +**Returns**: + +self + + + +#### merge`_`from + +```python +def merge_from(coins: CoinsParamType, + on_collision: OnCollision = OnCollision.Fail) -> "Coins" +``` + +Merge passed in coins in to this ('self') coins instance. + +**Arguments**: + +- `coins`: Input coins in any of the supported types. +- `on_collision`: Instructs what to do in the case of a denom collision = if this (self) already contains +one or more the denomination in the `coins` value: +- if `OnCollision.Override`: then the colliding coin amount in this (self) object will be + *overridden* with the colliding amount value from the `coins` parameter. +- if `OnCollision.Fail`: then the merge will *fail* with the `ValueError` exception when + the first collision is detected + +**Returns**: + +The `self` instance containing merged coins + + + +#### delete + +```python +def delete(denominations: Iterable[str]) -> "Coins" +``` + +Delete coins from this ('self') instance for each denom listed in `denominations` argument. + +**Arguments**: + +- `denominations`: collection of denominations to drop + +**Returns**: + +deleted Coins + + + +#### get + +```python +def get(denom: str, default_amount: int) -> Coin +``` + +Return Coin instance for the given `denom`. + +If coin with the given `denom` is not present, the `default` will be returned. + +Runtime complexity: `O(log(n))` + +This method poses the same risk to validity of the Coins value as the `__getitem__(...)` method, +since at the moment it returns Coin instance *by-reference* what allows to change the `Coin.amount` value +from external context and so potentially invalidate the value represented by the `Coins` class/container. + +**Arguments**: + +- `denom`: denomination of the coin to query. +- `default_amount`: default amount used to construct returned Coin instance if there is *no* coin with +the given `denom` present in this coins instance. + +**Returns**: + +coin instance for the given `denom`, or the `default` value. +Example:: +>>> from cosmpy.aerial.coins import Coin, Coins +>>> cs = Coins("1aaa,2baa,3caa") +>>> cs.get("baa", 0) +2baa +>>> cs.get("ggg", 0) +0ggg + + + +#### get`_`by`_`index + +```python +def get_by_index(index: int) -> Coin +``` + +Return Coin instance at given `index`. + +If the `index` is out of range, raises :exc:`IndexError`. + +Runtime complexity: `O(log(n))` + +This method poses the same risk to validity of the Coins value as the `__getitem__(...)` method, +since at the moment it returns Coin instance *by-reference* what allows to change the `Coin.amount` value +from external context and so potentially invalidate the value represented by the `Coins` class/container. + +Example:: +>>> from cosmpy.aerial.coins import Coin, Coins +>>> cs = Coins("1aaa,2baa,3caa") +>>> cs.get_by_index(0) +1aaa +>>> cs.get_by_index(2) +3caa +>>> cs.get_by_index(3) +Traceback (most recent call last): + ... +IndexError: list index out of range + +**Arguments**: + +- `index`: int index of item (default -1) + +**Returns**: + +key and value pair + + + +#### to`_`proto + +```python +def to_proto() -> List[CoinProto] +``` + +Convert this type to *protobuf schema* Coins type. + #### parse`_`coins @@ -237,6 +484,53 @@ Parse the coins. List of CoinProto objects + + +#### from`_`string + +```python +def from_string(value: str) +``` + +Parse the coins string and yields individual coins as Coin instances in order of their definition in input `value`. + +**Arguments**: + +- `value`: coins + +**Raises**: + +- `RuntimeError`: If unable to parse the value + +**Returns**: + +Coin objects one by one in the order they are specified in the input `value` string, where validation of +the yielded Coin instance is intentionally *NOT* executed => yielded coin instance might *NOT* be valid +when judged based on cosmos-sdk requirements. +This is by-design to enable just basic parsing focused exclusively on the format of the coins string value. +This leaves a degree of freedom for a caller on how the resulting/parsed coins should be used/consumed, +rather than forcing any checks/validation for individual coins instances, or coins collection as a whole, +here. + + + +#### is`_`coin`_`amount`_`valid + +```python +def is_coin_amount_valid(amount: int) -> bool +``` + +Check if amount value conforms to Cosmos-SDK requirements. + +**Arguments**: + +- `amount`: amount to be checked + +**Returns**: + +True if the amount conforms to cosmos-sdk requirement for Coin amount (when it is greater than zero), +False otherwise. + #### is`_`denom`_`valid @@ -245,15 +539,15 @@ List of CoinProto objects def is_denom_valid(denom: str) -> bool ``` -Return true if coin denom name is valid. +Check if denom value conforms to Cosmos-SDK requirements. **Arguments**: -- `denom`: string denom +- `denom`: Denom to be checked **Returns**: -bool validity +True if the denom conforms to cosmos-sdk requirement @@ -261,7 +555,7 @@ bool validity ```python def is_coins_sorted( - coins: Union[str, Coins, List[Coin], List[CoinProto]]) -> bool + coins: Union[str, Coins, Iterable[Coin], Iterable[CoinProto]]) -> bool ``` Return true if given coins representation is sorted in ascending order of denom. @@ -279,38 +573,19 @@ bool is_sorted #### validate`_`coins ```python -def validate_coins(coins: Union[str, Coins, List[Coin], List[CoinProto]]) +def validate_coins(coins: Union[str, Coins, Iterable[Coin], + Iterable[CoinProto]]) ``` Return true if given coins representation is valid. +raises ValueError if there are multiple coins with the same denom + **Arguments**: - `coins`: Any type representing coins -**Raises**: - -- `ValueError`: If there are multiple coins with the same denom - **Returns**: -bool validity - - - -#### sort`_`coins - -```python -def sort_coins(coins: Union[Coins, List[Coin], List[CoinProto]]) -``` - -Sort the collection of coins based on Cosmos-SDK definition of Coins validity. - -Coins collection is sorted ascending alphabetically based on denomination. - -NOTE: The resulting sorted collection of coins is *NOT* validated by calling the 'Coins.validate()'. - -**Arguments**: - -- `coins`: Coins to sort +True if valid, False otherwise diff --git a/poetry.lock b/poetry.lock index 0b1d2ff2..5e8081b5 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.2 and should not be changed by hand. [[package]] name = "annotated-types" @@ -6,6 +6,7 @@ version = "0.7.0" description = "Reusable constraint types to use with typing.Annotated" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, @@ -17,6 +18,7 @@ version = "2.13.5" description = "An abstract syntax tree for Python with inference support." optional = false python-versions = ">=3.7.2" +groups = ["dev"] files = [ {file = "astroid-2.13.5-py3-none-any.whl", hash = "sha256:6891f444625b6edb2ac798829b689e95297e100ddf89dbed5a8c610e34901501"}, {file = "astroid-2.13.5.tar.gz", hash = "sha256:df164d5ac811b9f44105a72b8f9d5edfb7b5b2d7e979b04ea377a77b3229114a"}, @@ -36,18 +38,19 @@ version = "25.3.0" description = "Classes Without Boilerplate" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3"}, {file = "attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b"}, ] [package.extras] -benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +benchmark = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +cov = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier"] -tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] +tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\""] [[package]] name = "authlib" @@ -55,6 +58,7 @@ version = "1.5.2" description = "The ultimate Python library in building OAuth and OpenID Connect servers and clients." optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "authlib-1.5.2-py2.py3-none-any.whl", hash = "sha256:8804dd4402ac5e4a0435ac49e0b6e19e395357cfa632a3f624dcb4f6df13b4b1"}, {file = "authlib-1.5.2.tar.gz", hash = "sha256:fe85ec7e50c5f86f1e2603518bb3b4f632985eb4a355e52256530790e326c512"}, @@ -69,13 +73,14 @@ version = "2.17.0" description = "Internationalization utilities" optional = false python-versions = ">=3.8" +groups = ["docs"] files = [ {file = "babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2"}, {file = "babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d"}, ] [package.extras] -dev = ["backports.zoneinfo", "freezegun (>=1.0,<2.0)", "jinja2 (>=3.0)", "pytest (>=6.0)", "pytest-cov", "pytz", "setuptools", "tzdata"] +dev = ["backports.zoneinfo ; python_version < \"3.9\"", "freezegun (>=1.0,<2.0)", "jinja2 (>=3.0)", "pytest (>=6.0)", "pytest-cov", "pytz", "setuptools", "tzdata ; sys_platform == \"win32\""] [[package]] name = "backrefs" @@ -83,6 +88,7 @@ version = "5.8" description = "A wrapper around re and regex that adds additional back references." optional = false python-versions = ">=3.9" +groups = ["docs"] files = [ {file = "backrefs-5.8-py310-none-any.whl", hash = "sha256:c67f6638a34a5b8730812f5101376f9d41dc38c43f1fdc35cb54700f6ed4465d"}, {file = "backrefs-5.8-py311-none-any.whl", hash = "sha256:2e1c15e4af0e12e45c8701bd5da0902d326b2e200cafcd25e49d9f06d44bb61b"}, @@ -101,6 +107,7 @@ version = "1.7.4" description = "Security oriented static analyser for python code." optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "bandit-1.7.4-py3-none-any.whl", hash = "sha256:412d3f259dab4077d0e7f0c11f50f650cc7d10db905d98f6520a95a18049658a"}, {file = "bandit-1.7.4.tar.gz", hash = "sha256:2d63a8c573417bae338962d4b9b06fbc6080f74ecd955a092849e1e65c717bd2"}, @@ -123,6 +130,7 @@ version = "1.2.0" description = "Reference implementation for Bech32 and segwit addresses." optional = false python-versions = ">=3.5" +groups = ["main"] files = [ {file = "bech32-1.2.0-py3-none-any.whl", hash = "sha256:990dc8e5a5e4feabbdf55207b5315fdd9b73db40be294a19b3752cde9e79d981"}, {file = "bech32-1.2.0.tar.gz", hash = "sha256:7d6db8214603bd7871fcfa6c0826ef68b85b0abd90fa21c285a9c5e21d2bd899"}, @@ -134,6 +142,7 @@ version = "22.12.0" description = "The uncompromising code formatter." optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "black-22.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eedd20838bd5d75b80c9f5487dbcb06836a43833a37846cf1d8c1cc01cef59d"}, {file = "black-22.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:159a46a4947f73387b4d83e87ea006dbb2337eab6c879620a3ba52699b1f4351"}, @@ -169,6 +178,7 @@ version = "1.2.2.post1" description = "A simple, correct Python build frontend" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "build-1.2.2.post1-py3-none-any.whl", hash = "sha256:1d61c0887fa860c01971625baae8bdd338e517b836a2f70dd1f7aa3a6b2fc5b5"}, {file = "build-1.2.2.post1.tar.gz", hash = "sha256:b36993e92ca9375a219c99e606a122ff365a760a2d4bba0caa09bd5278b608b7"}, @@ -183,7 +193,7 @@ tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} [package.extras] docs = ["furo (>=2023.08.17)", "sphinx (>=7.0,<8.0)", "sphinx-argparse-cli (>=1.5)", "sphinx-autodoc-typehints (>=1.10)", "sphinx-issues (>=3.0.0)"] -test = ["build[uv,virtualenv]", "filelock (>=3)", "pytest (>=6.2.4)", "pytest-cov (>=2.12)", "pytest-mock (>=2)", "pytest-rerunfailures (>=9.1)", "pytest-xdist (>=1.34)", "setuptools (>=42.0.0)", "setuptools (>=56.0.0)", "setuptools (>=56.0.0)", "setuptools (>=67.8.0)", "wheel (>=0.36.0)"] +test = ["build[uv,virtualenv]", "filelock (>=3)", "pytest (>=6.2.4)", "pytest-cov (>=2.12)", "pytest-mock (>=2)", "pytest-rerunfailures (>=9.1)", "pytest-xdist (>=1.34)", "setuptools (>=42.0.0) ; python_version < \"3.10\"", "setuptools (>=56.0.0) ; python_version == \"3.10\"", "setuptools (>=56.0.0) ; python_version == \"3.11\"", "setuptools (>=67.8.0) ; python_version >= \"3.12\"", "wheel (>=0.36.0)"] typing = ["build[uv]", "importlib-metadata (>=5.1)", "mypy (>=1.9.0,<1.10.0)", "tomli", "typing-extensions (>=3.7.4.3)"] uv = ["uv (>=0.1.18)"] virtualenv = ["virtualenv (>=20.0.35)"] @@ -194,6 +204,7 @@ version = "2025.1.31" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" +groups = ["main", "dev", "docs"] files = [ {file = "certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"}, {file = "certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"}, @@ -205,6 +216,8 @@ version = "1.17.1" description = "Foreign Function Interface for Python calling C code." optional = false python-versions = ">=3.8" +groups = ["dev"] +markers = "platform_python_implementation != \"PyPy\"" files = [ {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, @@ -284,6 +297,7 @@ version = "3.4.1" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7" +groups = ["main", "dev", "docs"] files = [ {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}, {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}, @@ -385,6 +399,7 @@ version = "0.50" description = "Check MANIFEST.in in a Python source package for completeness" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "check_manifest-0.50-py3-none-any.whl", hash = "sha256:6ab3e3aa72a008da3314b432f4c768c9647b4d6d8032f9e1a4672a572118e48c"}, {file = "check_manifest-0.50.tar.gz", hash = "sha256:d300f9f292986aa1a30424af44eb45c5644e0a810e392e62d553b24bb3393494"}, @@ -396,7 +411,7 @@ setuptools = "*" tomli = {version = "*", markers = "python_version < \"3.11\""} [package.extras] -test = ["mock (>=3.0.0)", "pytest", "wheel"] +test = ["mock (>=3.0.0) ; python_version == \"3.7\"", "pytest", "wheel"] [[package]] name = "click" @@ -404,6 +419,7 @@ version = "8.1.8" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" +groups = ["dev", "docs"] files = [ {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, @@ -418,10 +434,12 @@ version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["dev", "docs", "test"] files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +markers = {dev = "os_name == \"nt\" or platform_system == \"Windows\" or sys_platform == \"win32\"", test = "sys_platform == \"win32\""} [[package]] name = "configparser" @@ -429,13 +447,14 @@ version = "7.2.0" description = "Updated configparser from stdlib for earlier Pythons." optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "configparser-7.2.0-py3-none-any.whl", hash = "sha256:fee5e1f3db4156dcd0ed95bc4edfa3580475537711f67a819c966b389d09ce62"}, {file = "configparser-7.2.0.tar.gz", hash = "sha256:b629cc8ae916e3afbd36d1b3d093f34193d851e11998920fdcfc4552218b7b70"}, ] [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] @@ -448,6 +467,7 @@ version = "7.8.0" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.9" +groups = ["test"] files = [ {file = "coverage-7.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2931f66991175369859b5fd58529cd4b73582461877ecfd859b6549869287ffe"}, {file = "coverage-7.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:52a523153c568d2c0ef8826f6cc23031dc86cffb8c6aeab92c4ff776e7951b28"}, @@ -515,7 +535,7 @@ files = [ ] [package.extras] -toml = ["tomli"] +toml = ["tomli ; python_full_version <= \"3.11.0a6\""] [[package]] name = "cryptography" @@ -523,6 +543,7 @@ version = "43.0.3" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "cryptography-43.0.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:bf7a1932ac4176486eab36a19ed4c0492da5d97123f1406cf15e41b05e787d2e"}, {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63efa177ff54aec6e1c0aefaa1a241232dcd37413835a9b674b6e3f0ae2bfd3e"}, @@ -572,6 +593,7 @@ version = "1.8.1" description = "A utility for ensuring Google-style docstrings stay up to date with the source code." optional = false python-versions = ">=3.6,<4.0" +groups = ["dev"] files = [ {file = "darglint-1.8.1-py3-none-any.whl", hash = "sha256:5ae11c259c17b0701618a20c3da343a3eb98b3bc4b5a83d31cdd94f5ebdced8d"}, {file = "darglint-1.8.1.tar.gz", hash = "sha256:080d5106df149b199822e7ee7deb9c012b49891538f14a11be681044f0bb20da"}, @@ -583,6 +605,7 @@ version = "1.5.3" description = "Databind is a library inspired by jackson-databind to de-/serialize Python dataclasses. The `databind` package will install the full suite of databind packages. Compatible with Python 3.7 and newer." optional = false python-versions = ">=3.7,<4.0" +groups = ["docs"] files = [ {file = "databind-1.5.3-py3-none-any.whl", hash = "sha256:43d76e3b931ed9d986b5e35df60e37ccb0bcb2b3b57d6f44cce085c48abe7016"}, {file = "databind-1.5.3.tar.gz", hash = "sha256:a1986c1a2054d28d064cf7900c2d3f03850ace124331fa026e6049f390742122"}, @@ -598,6 +621,7 @@ version = "1.5.3" description = "Databind is a library inspired by jackson-databind to de-/serialize Python dataclasses. Compatible with Python 3.7 and newer." optional = false python-versions = ">=3.7,<4.0" +groups = ["docs"] files = [ {file = "databind.core-1.5.3-py3-none-any.whl", hash = "sha256:105665947e0b0b4709b6dab99cd3cea31d16f5c0d68fe867e4dc2f47585669fb"}, {file = "databind.core-1.5.3.tar.gz", hash = "sha256:663dd0ccfd1b181cbcfb8315226ec8df78bcd65c1b43875ba18d89702d15efbe"}, @@ -614,6 +638,7 @@ version = "1.5.3" description = "De-/serialize Python dataclasses to or from JSON payloads. Compatible with Python 3.7 and newer." optional = false python-versions = ">=3.7,<4.0" +groups = ["docs"] files = [ {file = "databind.json-1.5.3-py3-none-any.whl", hash = "sha256:fd6702ca9f67b15414e573935bf4a5ccb221f3f810553a054b832393c057df3d"}, {file = "databind.json-1.5.3.tar.gz", hash = "sha256:b8785ad9689b98d524d4c96e0756bc41166da0de44cc9d14ed2fa8aea5674507"}, @@ -630,6 +655,7 @@ version = "1.2.18" description = "Python @deprecated decorator to deprecate old python classes, functions or methods." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" +groups = ["docs"] files = [ {file = "Deprecated-1.2.18-py2.py3-none-any.whl", hash = "sha256:bd5011788200372a32418f888e326a09ff80d0214bd961147cfed01b5c018eec"}, {file = "deprecated-1.2.18.tar.gz", hash = "sha256:422b6f6d859da6f2ef57857761bfb392480502a64c3028ca9bbe86085d72115d"}, @@ -639,7 +665,7 @@ files = [ wrapt = ">=1.10,<2" [package.extras] -dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "setuptools", "tox"] +dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "setuptools ; python_version >= \"3.12\"", "tox"] [[package]] name = "dill" @@ -647,6 +673,7 @@ version = "0.4.0" description = "serialize all of Python" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "dill-0.4.0-py3-none-any.whl", hash = "sha256:44f54bf6412c2c8464c14e8243eb163690a9800dbe2c367330883b19c7561049"}, {file = "dill-0.4.0.tar.gz", hash = "sha256:0633f1d2df477324f53a895b02c901fb961bdbf65a17122586ea7019292cbcf0"}, @@ -662,6 +689,7 @@ version = "0.3.9" description = "Distribution utilities" optional = false python-versions = "*" +groups = ["main", "dev"] files = [ {file = "distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87"}, {file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"}, @@ -673,6 +701,7 @@ version = "2.1.2" description = "Docspec is a JSON object specification for representing API documentation of programming languages." optional = false python-versions = ">=3.7,<4.0" +groups = ["docs"] files = [ {file = "docspec-2.1.2-py3-none-any.whl", hash = "sha256:19168242b1e6ca39553feba91b6284479e2bfdf6500949ce63e10c49354d00d7"}, {file = "docspec-2.1.2.tar.gz", hash = "sha256:0edb90f4c54edbd43170875dece7a50750a21e95a4c5faf3f7e655d01d483fa7"}, @@ -688,6 +717,7 @@ version = "2.0.2" description = "A parser based on lib2to3 producing docspec data from Python source code." optional = false python-versions = ">=3.7,<4.0" +groups = ["docs"] files = [ {file = "docspec-python-2.0.2.tar.gz", hash = "sha256:9bb1bcc2b1e6903d8ef00107607255285d1a1f763acb111f9d83c695878cacbc"}, {file = "docspec_python-2.0.2-py3-none-any.whl", hash = "sha256:ffb35751562d13271e2792f4fe3aba9d79aa0493f966897b77ad73ed9cd5f6a6"}, @@ -703,6 +733,7 @@ version = "0.11" description = "\"Parse Python docstrings in reST, Google and Numpydoc format\"" optional = false python-versions = ">=3.6" +groups = ["docs"] files = [ {file = "docstring_parser-0.11.tar.gz", hash = "sha256:93b3f8f481c7d24e37c5d9f30293c89e2933fa209421c8abd731dd3ef0715ecb"}, ] @@ -716,6 +747,7 @@ version = "0.6.4" description = "A parser for Python dependency files" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "dparse-0.6.4-py3-none-any.whl", hash = "sha256:fbab4d50d54d0e739fbb4dedfc3d92771003a5b9aa8545ca7a7045e3b174af57"}, {file = "dparse-0.6.4.tar.gz", hash = "sha256:90b29c39e3edc36c6284c82c4132648eaf28a01863eb3c231c2512196132201a"}, @@ -737,6 +769,7 @@ version = "0.19.1" description = "ECDSA cryptographic signature library (pure python)" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.6" +groups = ["main"] files = [ {file = "ecdsa-0.19.1-py2.py3-none-any.whl", hash = "sha256:30638e27cf77b7e15c4c4cc1973720149e1033827cfd00661ca5c8cc0cdb24c3"}, {file = "ecdsa-0.19.1.tar.gz", hash = "sha256:478cba7b62555866fcb3bb3fe985e06decbdb68ef55713c4e5ab98c57d508e61"}, @@ -755,6 +788,7 @@ version = "2.3.0" description = "Removes commented-out code." optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "eradicate-2.3.0-py3-none-any.whl", hash = "sha256:2b29b3dd27171f209e4ddd8204b70c02f0682ae95eecb353f10e8d72b149c63e"}, {file = "eradicate-2.3.0.tar.gz", hash = "sha256:06df115be3b87d0fc1c483db22a2ebb12bcf40585722810d809cc770f5031c37"}, @@ -766,6 +800,8 @@ version = "1.2.2" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" +groups = ["test"] +markers = "python_version < \"3.11\"" files = [ {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, @@ -780,6 +816,7 @@ version = "3.16.1" description = "A platform independent file lock." optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"}, {file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"}, @@ -788,7 +825,7 @@ files = [ [package.extras] docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4.1)"] testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"] -typing = ["typing-extensions (>=4.12.2)"] +typing = ["typing-extensions (>=4.12.2) ; python_version < \"3.11\""] [[package]] name = "flake8" @@ -796,6 +833,7 @@ version = "5.0.4" description = "the modular source code checker: pep8 pyflakes and co" optional = false python-versions = ">=3.6.1" +groups = ["dev"] files = [ {file = "flake8-5.0.4-py2.py3-none-any.whl", hash = "sha256:7a1cf6b73744f5806ab95e526f6f0d8c01c66d7bbe349562d22dfca20610b248"}, {file = "flake8-5.0.4.tar.gz", hash = "sha256:6fbe320aad8d6b95cec8b8e47bc933004678dc63095be98528b7bdd2a9f510db"}, @@ -812,6 +850,7 @@ version = "22.10.25" description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle." optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "flake8-bugbear-22.10.25.tar.gz", hash = "sha256:89e51284eb929fbb7f23fbd428491e7427f7cdc8b45a77248daffe86a039d696"}, {file = "flake8_bugbear-22.10.25-py3-none-any.whl", hash = "sha256:584631b608dc0d7d3f9201046d5840a45502da4732d5e8df6c7ac1694a91cb9e"}, @@ -830,6 +869,7 @@ version = "0.2.3" description = "Adds copyright checks to flake8" optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "flake8-copyright-0.2.3.tar.gz", hash = "sha256:698840480d377e7c70172672636d898ad7b8af210492f431fff066e75b481d59"}, {file = "flake8_copyright-0.2.3-py3-none-any.whl", hash = "sha256:1f899782bedce6bf086419fd5fb66da6ae0698b0d7faa5269520b4c965d74883"}, @@ -844,6 +884,7 @@ version = "1.6.0" description = "Extension for flake8 which uses pydocstyle to check docstrings" optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "flake8-docstrings-1.6.0.tar.gz", hash = "sha256:9fe7c6a306064af8e62a055c2f61e9eb1da55f84bb39caef2b84ce53708ac34b"}, {file = "flake8_docstrings-1.6.0-py2.py3-none-any.whl", hash = "sha256:99cac583d6c7e32dd28bbfbef120a7c0d1b6dde4adb5a9fd441c4227a6534bde"}, @@ -859,6 +900,7 @@ version = "1.4.0" description = "Flake8 plugin to find commented out code" optional = false python-versions = ">=3.7,<4.0" +groups = ["dev"] files = [ {file = "flake8-eradicate-1.4.0.tar.gz", hash = "sha256:3088cfd6717d1c9c6c3ac45ef2e5f5b6c7267f7504d5a74b781500e95cb9c7e1"}, {file = "flake8_eradicate-1.4.0-py3-none-any.whl", hash = "sha256:e3bbd0871be358e908053c1ab728903c114f062ba596b4d40c852fd18f473d56"}, @@ -875,6 +917,7 @@ version = "5.0.0" description = "flake8 plugin that integrates isort ." optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "flake8-isort-5.0.0.tar.gz", hash = "sha256:e336f928c7edc509684930ab124414194b7f4e237c712af8fcbdf49d8747b10c"}, {file = "flake8_isort-5.0.0-py3-none-any.whl", hash = "sha256:c73f9cbd1bf209887f602a27b827164ccfeba1676801b2aa23cb49051a1be79c"}, @@ -893,6 +936,7 @@ version = "2.1.0" description = "Copy your docs directly to the gh-pages branch." optional = false python-versions = "*" +groups = ["docs"] files = [ {file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"}, {file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"}, @@ -910,6 +954,7 @@ version = "4.0.12" description = "Git Object Database" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf"}, {file = "gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571"}, @@ -924,6 +969,7 @@ version = "3.1.44" description = "GitPython is a Python library used to interact with Git repositories" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "GitPython-3.1.44-py3-none-any.whl", hash = "sha256:9e0e10cda9bed1ee64bc9a6de50e7e38a9c9943241cd7f585f6df3ed28011110"}, {file = "gitpython-3.1.44.tar.gz", hash = "sha256:c87e30b26253bf5418b01b0660f818967f3c503193838337fe5e573331249269"}, @@ -934,7 +980,7 @@ gitdb = ">=4.0.1,<5" [package.extras] doc = ["sphinx (>=7.1.2,<7.2)", "sphinx-autodoc-typehints", "sphinx_rtd_theme"] -test = ["coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock", "mypy", "pre-commit", "pytest (>=7.3.1)", "pytest-cov", "pytest-instafail", "pytest-mock", "pytest-sugar", "typing-extensions"] +test = ["coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock ; python_version < \"3.8\"", "mypy", "pre-commit", "pytest (>=7.3.1)", "pytest-cov", "pytest-instafail", "pytest-mock", "pytest-sugar", "typing-extensions ; python_version < \"3.11\""] [[package]] name = "googleapis-common-protos" @@ -942,6 +988,7 @@ version = "1.70.0" description = "Common protobufs used in Google APIs" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "googleapis_common_protos-1.70.0-py3-none-any.whl", hash = "sha256:b8bfcca8c25a2bb253e0e0b0adaf8c00773e5e6af6fd92397576680b807e0fd8"}, {file = "googleapis_common_protos-1.70.0.tar.gz", hash = "sha256:0e1b44e0ea153e6594f9f394fef15193a68aaaea2d843f83e2742717ca753257"}, @@ -959,6 +1006,7 @@ version = "1.71.0" description = "HTTP/2-based RPC framework" optional = false python-versions = ">=3.9" +groups = ["main", "dev"] files = [ {file = "grpcio-1.71.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:c200cb6f2393468142eb50ab19613229dcc7829b5ccee8b658a36005f6669fdd"}, {file = "grpcio-1.71.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:b2266862c5ad664a380fbbcdbdb8289d71464c42a8c29053820ee78ba0119e5d"}, @@ -1022,6 +1070,7 @@ version = "1.71.0" description = "Protobuf code generator for gRPC" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "grpcio_tools-1.71.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:f4ad7f0d756546902597053d70b3af2606fbd70d7972876cd75c1e241d22ae00"}, {file = "grpcio_tools-1.71.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:64bdb291df61cf570b5256777ad5fe2b1db6d67bc46e55dc56a0a862722ae329"}, @@ -1087,6 +1136,7 @@ version = "3.10" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.6" +groups = ["main", "dev", "docs"] files = [ {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, @@ -1101,21 +1151,23 @@ version = "8.6.1" description = "Read metadata from Python packages" optional = false python-versions = ">=3.9" +groups = ["dev", "docs"] files = [ {file = "importlib_metadata-8.6.1-py3-none-any.whl", hash = "sha256:02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e"}, {file = "importlib_metadata-8.6.1.tar.gz", hash = "sha256:310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580"}, ] +markers = {dev = "python_full_version < \"3.10.2\"", docs = "python_version == \"3.9\""} [package.dependencies] zipp = ">=3.20" [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] perf = ["ipython"] -test = ["flufl.flake8", "importlib_resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] +test = ["flufl.flake8", "importlib_resources (>=1.3) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] type = ["pytest-mypy"] [[package]] @@ -1124,6 +1176,7 @@ version = "2.1.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.8" +groups = ["test"] files = [ {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, @@ -1135,6 +1188,7 @@ version = "5.10.1" description = "A Python utility / library to sort Python imports." optional = false python-versions = ">=3.6.1,<4.0" +groups = ["dev"] files = [ {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"}, {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"}, @@ -1152,6 +1206,7 @@ version = "3.1.6" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" +groups = ["dev", "docs"] files = [ {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, @@ -1169,6 +1224,7 @@ version = "1.4.2" description = "Lightweight pipelining with Python functions" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "joblib-1.4.2-py3-none-any.whl", hash = "sha256:06d478d5674cbc267e7496a410ee875abd68e4340feff4490bcb7afb88060ae6"}, {file = "joblib-1.4.2.tar.gz", hash = "sha256:2382c5816b2636fbd20a09e0f4e9dad4736765fdfb7dca582943b9c1366b3f0e"}, @@ -1180,6 +1236,7 @@ version = "4.23.0" description = "An implementation of JSON Schema validation for Python" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "jsonschema-4.23.0-py3-none-any.whl", hash = "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566"}, {file = "jsonschema-4.23.0.tar.gz", hash = "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4"}, @@ -1201,6 +1258,7 @@ version = "2024.10.1" description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "jsonschema_specifications-2024.10.1-py3-none-any.whl", hash = "sha256:a09a0680616357d9a0ecf05c12ad234479f549239d0f5b55f3deea67475da9bf"}, {file = "jsonschema_specifications-2024.10.1.tar.gz", hash = "sha256:0f38b83639958ce1152d02a7f062902c41c8fd20d558b0c34344292d417ae272"}, @@ -1215,6 +1273,7 @@ version = "1.10.0" description = "A fast and thorough lazy object proxy." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "lazy-object-proxy-1.10.0.tar.gz", hash = "sha256:78247b6d45f43a52ef35c25b5581459e85117225408a4128a3daf8bf9648ac69"}, {file = "lazy_object_proxy-1.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:855e068b0358ab916454464a884779c7ffa312b8925c6f7401e952dcf3b89977"}, @@ -1261,6 +1320,7 @@ version = "0.7.2" description = "Check python packages from requirement.txt and report issues" optional = false python-versions = ">=2.7" +groups = ["dev"] files = [ {file = "liccheck-0.7.2-py2.py3-none-any.whl", hash = "sha256:a50d212b943d119f61d822080e0b7bcefbae4c3b7cc7b54b47b465c191bfc8aa"}, {file = "liccheck-0.7.2.tar.gz", hash = "sha256:1961ee9bd9ae72c21f79da6db3a2048536f0912f4c5fde0f0c062d65ea5fbf0a"}, @@ -1277,6 +1337,7 @@ version = "3.8" description = "Python implementation of John Gruber's Markdown." optional = false python-versions = ">=3.9" +groups = ["docs"] files = [ {file = "markdown-3.8-py3-none-any.whl", hash = "sha256:794a929b79c5af141ef5ab0f2f642d0f7b1872981250230e72682346f7cc90dc"}, {file = "markdown-3.8.tar.gz", hash = "sha256:7df81e63f0df5c4b24b7d156eb81e4690595239b7d70937d0409f1b0de319c6f"}, @@ -1295,6 +1356,7 @@ version = "3.0.0" description = "Python port of markdown-it. Markdown parsing, done right!" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, @@ -1319,6 +1381,7 @@ version = "3.0.2" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.9" +groups = ["dev", "docs"] files = [ {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, @@ -1389,6 +1452,7 @@ version = "3.26.1" description = "A lightweight library for converting complex datatypes to and from native Python datatypes." optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "marshmallow-3.26.1-py3-none-any.whl", hash = "sha256:3350409f20a70a7e4e11a27661187b77cdcaeb20abca41c1454fe33636bea09c"}, {file = "marshmallow-3.26.1.tar.gz", hash = "sha256:e6d8affb6cb61d39d26402096dc0aee12d5a26d490a121f118d2e81dc0719dc6"}, @@ -1408,6 +1472,7 @@ version = "0.7.0" description = "McCabe checker, plugin for flake8" optional = false python-versions = ">=3.6" +groups = ["dev"] files = [ {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, @@ -1419,6 +1484,7 @@ version = "0.1.2" description = "Markdown URL utilities" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, @@ -1430,6 +1496,7 @@ version = "1.3.4" description = "A deep merge function for 🐍." optional = false python-versions = ">=3.6" +groups = ["docs"] files = [ {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"}, {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"}, @@ -1441,6 +1508,7 @@ version = "1.6.1" description = "Project documentation with Markdown." optional = false python-versions = ">=3.8" +groups = ["docs"] files = [ {file = "mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e"}, {file = "mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2"}, @@ -1464,7 +1532,7 @@ watchdog = ">=2.0" [package.extras] i18n = ["babel (>=2.9.0)"] -min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4)", "ghp-import (==1.0)", "importlib-metadata (==4.4)", "jinja2 (==2.11.1)", "markdown (==3.3.6)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "mkdocs-get-deps (==0.2.0)", "packaging (==20.5)", "pathspec (==0.11.1)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "watchdog (==2.0)"] +min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4) ; platform_system == \"Windows\"", "ghp-import (==1.0)", "importlib-metadata (==4.4) ; python_version < \"3.10\"", "jinja2 (==2.11.1)", "markdown (==3.3.6)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "mkdocs-get-deps (==0.2.0)", "packaging (==20.5)", "pathspec (==0.11.1)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "watchdog (==2.0)"] [[package]] name = "mkdocs-get-deps" @@ -1472,6 +1540,7 @@ version = "0.2.0" description = "MkDocs extension that lists all dependencies according to a mkdocs.yml file" optional = false python-versions = ">=3.8" +groups = ["docs"] files = [ {file = "mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134"}, {file = "mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c"}, @@ -1489,6 +1558,7 @@ version = "9.6.11" description = "Documentation that simply works" optional = false python-versions = ">=3.8" +groups = ["docs"] files = [ {file = "mkdocs_material-9.6.11-py3-none-any.whl", hash = "sha256:47f21ef9cbf4f0ebdce78a2ceecaa5d413581a55141e4464902224ebbc0b1263"}, {file = "mkdocs_material-9.6.11.tar.gz", hash = "sha256:0b7f4a0145c5074cdd692e4362d232fb25ef5b23328d0ec1ab287af77cc0deff"}, @@ -1518,6 +1588,7 @@ version = "1.3.1" description = "Extension pack for Python Markdown and MkDocs Material." optional = false python-versions = ">=3.8" +groups = ["docs"] files = [ {file = "mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31"}, {file = "mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443"}, @@ -1525,45 +1596,56 @@ files = [ [[package]] name = "mypy" -version = "0.982" +version = "1.15.0" description = "Optional static typing for Python" optional = false -python-versions = ">=3.7" -files = [ - {file = "mypy-0.982-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5085e6f442003fa915aeb0a46d4da58128da69325d8213b4b35cc7054090aed5"}, - {file = "mypy-0.982-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:41fd1cf9bc0e1c19b9af13a6580ccb66c381a5ee2cf63ee5ebab747a4badeba3"}, - {file = "mypy-0.982-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f793e3dd95e166b66d50e7b63e69e58e88643d80a3dcc3bcd81368e0478b089c"}, - {file = "mypy-0.982-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86ebe67adf4d021b28c3f547da6aa2cce660b57f0432617af2cca932d4d378a6"}, - {file = "mypy-0.982-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:175f292f649a3af7082fe36620369ffc4661a71005aa9f8297ea473df5772046"}, - {file = "mypy-0.982-cp310-cp310-win_amd64.whl", hash = "sha256:8ee8c2472e96beb1045e9081de8e92f295b89ac10c4109afdf3a23ad6e644f3e"}, - {file = "mypy-0.982-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:58f27ebafe726a8e5ccb58d896451dd9a662a511a3188ff6a8a6a919142ecc20"}, - {file = "mypy-0.982-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d6af646bd46f10d53834a8e8983e130e47d8ab2d4b7a97363e35b24e1d588947"}, - {file = "mypy-0.982-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e7aeaa763c7ab86d5b66ff27f68493d672e44c8099af636d433a7f3fa5596d40"}, - {file = "mypy-0.982-cp37-cp37m-win_amd64.whl", hash = "sha256:724d36be56444f569c20a629d1d4ee0cb0ad666078d59bb84f8f887952511ca1"}, - {file = "mypy-0.982-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:14d53cdd4cf93765aa747a7399f0961a365bcddf7855d9cef6306fa41de01c24"}, - {file = "mypy-0.982-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:26ae64555d480ad4b32a267d10cab7aec92ff44de35a7cd95b2b7cb8e64ebe3e"}, - {file = "mypy-0.982-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6389af3e204975d6658de4fb8ac16f58c14e1bacc6142fee86d1b5b26aa52bda"}, - {file = "mypy-0.982-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b35ce03a289480d6544aac85fa3674f493f323d80ea7226410ed065cd46f206"}, - {file = "mypy-0.982-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c6e564f035d25c99fd2b863e13049744d96bd1947e3d3d2f16f5828864506763"}, - {file = "mypy-0.982-cp38-cp38-win_amd64.whl", hash = "sha256:cebca7fd333f90b61b3ef7f217ff75ce2e287482206ef4a8b18f32b49927b1a2"}, - {file = "mypy-0.982-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a705a93670c8b74769496280d2fe6cd59961506c64f329bb179970ff1d24f9f8"}, - {file = "mypy-0.982-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:75838c649290d83a2b83a88288c1eb60fe7a05b36d46cbea9d22efc790002146"}, - {file = "mypy-0.982-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:91781eff1f3f2607519c8b0e8518aad8498af1419e8442d5d0afb108059881fc"}, - {file = "mypy-0.982-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaa97b9ddd1dd9901a22a879491dbb951b5dec75c3b90032e2baa7336777363b"}, - {file = "mypy-0.982-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a692a8e7d07abe5f4b2dd32d731812a0175626a90a223d4b58f10f458747dd8a"}, - {file = "mypy-0.982-cp39-cp39-win_amd64.whl", hash = "sha256:eb7a068e503be3543c4bd329c994103874fa543c1727ba5288393c21d912d795"}, - {file = "mypy-0.982-py3-none-any.whl", hash = "sha256:1021c241e8b6e1ca5a47e4d52601274ac078a89845cfde66c6d5f769819ffa1d"}, - {file = "mypy-0.982.tar.gz", hash = "sha256:85f7a343542dc8b1ed0a888cdd34dca56462654ef23aa673907305b260b3d746"}, -] - -[package.dependencies] -mypy-extensions = ">=0.4.3" +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "mypy-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:979e4e1a006511dacf628e36fadfecbcc0160a8af6ca7dad2f5025529e082c13"}, + {file = "mypy-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c4bb0e1bd29f7d34efcccd71cf733580191e9a264a2202b0239da95984c5b559"}, + {file = "mypy-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be68172e9fd9ad8fb876c6389f16d1c1b5f100ffa779f77b1fb2176fcc9ab95b"}, + {file = "mypy-1.15.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c7be1e46525adfa0d97681432ee9fcd61a3964c2446795714699a998d193f1a3"}, + {file = "mypy-1.15.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2e2c2e6d3593f6451b18588848e66260ff62ccca522dd231cd4dd59b0160668b"}, + {file = "mypy-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:6983aae8b2f653e098edb77f893f7b6aca69f6cffb19b2cc7443f23cce5f4828"}, + {file = "mypy-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2922d42e16d6de288022e5ca321cd0618b238cfc5570e0263e5ba0a77dbef56f"}, + {file = "mypy-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2ee2d57e01a7c35de00f4634ba1bbf015185b219e4dc5909e281016df43f5ee5"}, + {file = "mypy-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:973500e0774b85d9689715feeffcc980193086551110fd678ebe1f4342fb7c5e"}, + {file = "mypy-1.15.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a95fb17c13e29d2d5195869262f8125dfdb5c134dc8d9a9d0aecf7525b10c2c"}, + {file = "mypy-1.15.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1905f494bfd7d85a23a88c5d97840888a7bd516545fc5aaedff0267e0bb54e2f"}, + {file = "mypy-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:c9817fa23833ff189db061e6d2eff49b2f3b6ed9856b4a0a73046e41932d744f"}, + {file = "mypy-1.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:aea39e0583d05124836ea645f412e88a5c7d0fd77a6d694b60d9b6b2d9f184fd"}, + {file = "mypy-1.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f2147ab812b75e5b5499b01ade1f4a81489a147c01585cda36019102538615f"}, + {file = "mypy-1.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce436f4c6d218a070048ed6a44c0bbb10cd2cc5e272b29e7845f6a2f57ee4464"}, + {file = "mypy-1.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8023ff13985661b50a5928fc7a5ca15f3d1affb41e5f0a9952cb68ef090b31ee"}, + {file = "mypy-1.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1124a18bc11a6a62887e3e137f37f53fbae476dc36c185d549d4f837a2a6a14e"}, + {file = "mypy-1.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:171a9ca9a40cd1843abeca0e405bc1940cd9b305eaeea2dda769ba096932bb22"}, + {file = "mypy-1.15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93faf3fdb04768d44bf28693293f3904bbb555d076b781ad2530214ee53e3445"}, + {file = "mypy-1.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:811aeccadfb730024c5d3e326b2fbe9249bb7413553f15499a4050f7c30e801d"}, + {file = "mypy-1.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:98b7b9b9aedb65fe628c62a6dc57f6d5088ef2dfca37903a7d9ee374d03acca5"}, + {file = "mypy-1.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c43a7682e24b4f576d93072216bf56eeff70d9140241f9edec0c104d0c515036"}, + {file = "mypy-1.15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:baefc32840a9f00babd83251560e0ae1573e2f9d1b067719479bfb0e987c6357"}, + {file = "mypy-1.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:b9378e2c00146c44793c98b8d5a61039a048e31f429fb0eb546d93f4b000bedf"}, + {file = "mypy-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e601a7fa172c2131bff456bb3ee08a88360760d0d2f8cbd7a75a65497e2df078"}, + {file = "mypy-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:712e962a6357634fef20412699a3655c610110e01cdaa6180acec7fc9f8513ba"}, + {file = "mypy-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95579473af29ab73a10bada2f9722856792a36ec5af5399b653aa28360290a5"}, + {file = "mypy-1.15.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8f8722560a14cde92fdb1e31597760dc35f9f5524cce17836c0d22841830fd5b"}, + {file = "mypy-1.15.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1fbb8da62dc352133d7d7ca90ed2fb0e9d42bb1a32724c287d3c76c58cbaa9c2"}, + {file = "mypy-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:d10d994b41fb3497719bbf866f227b3489048ea4bbbb5015357db306249f7980"}, + {file = "mypy-1.15.0-py3-none-any.whl", hash = "sha256:5469affef548bd1895d86d3bf10ce2b44e33d86923c29e4d675b3e323437ea3e"}, + {file = "mypy-1.15.0.tar.gz", hash = "sha256:404534629d51d3efea5c800ee7c42b72a6554d6c400e6a79eafe15d11341fd43"}, +] + +[package.dependencies] +mypy_extensions = ">=1.0.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = ">=3.10" +typing_extensions = ">=4.6.0" [package.extras] dmypy = ["psutil (>=4.0)"] -python2 = ["typed-ast (>=1.4.0,<2)"] +faster-cache = ["orjson"] +install-types = ["pip"] +mypyc = ["setuptools (>=50)"] reports = ["lxml"] [[package]] @@ -1572,6 +1654,7 @@ version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." optional = false python-versions = ">=3.5" +groups = ["dev"] files = [ {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, @@ -1583,6 +1666,7 @@ version = "3.9.1" description = "Natural Language Toolkit" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "nltk-3.9.1-py3-none-any.whl", hash = "sha256:4fa26829c5b00715afe3061398a8989dc643b92ce7dd93fb4585a70930d168a1"}, {file = "nltk-3.9.1.tar.gz", hash = "sha256:87d127bd3de4bd89a4f81265e5fa59cb1b199b27440175370f7417d2bc7ae868"}, @@ -1608,6 +1692,7 @@ version = "0.8.12" description = "General purpose Python utility library." optional = false python-versions = ">=3.7,<4.0" +groups = ["docs"] files = [ {file = "nr.util-0.8.12-py3-none-any.whl", hash = "sha256:91da02ac9795eb8e015372275c1efe54bac9051231ee9b0e7e6f96b0b4e7d2bb"}, {file = "nr.util-0.8.12.tar.gz", hash = "sha256:a4549c2033d99d2f0379b3f3d233fd2a8ade286bbf0b3ad0cc7cea16022214f4"}, @@ -1623,6 +1708,7 @@ version = "24.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" +groups = ["dev", "docs", "test"] files = [ {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, @@ -1634,6 +1720,7 @@ version = "0.5.7" description = "Divides large result sets into pages for easier browsing" optional = false python-versions = "*" +groups = ["docs"] files = [ {file = "paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591"}, {file = "paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945"}, @@ -1649,6 +1736,7 @@ version = "0.12.1" description = "Utility library for gitignore style pattern matching of file paths." optional = false python-versions = ">=3.8" +groups = ["dev", "docs"] files = [ {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, @@ -1660,6 +1748,7 @@ version = "6.1.1" description = "Python Build Reasonableness" optional = false python-versions = ">=2.6" +groups = ["dev"] files = [ {file = "pbr-6.1.1-py2.py3-none-any.whl", hash = "sha256:38d4daea5d9fa63b3f626131b9d34947fd0c8be9b05a29276870580050a25a76"}, {file = "pbr-6.1.1.tar.gz", hash = "sha256:93ea72ce6989eb2eed99d0f75721474f69ad88128afdef5ac377eb797c4bf76b"}, @@ -1674,6 +1763,7 @@ version = "4.3.7" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.9" +groups = ["main", "dev", "docs"] files = [ {file = "platformdirs-4.3.7-py3-none-any.whl", hash = "sha256:a03875334331946f13c549dbd8f4bac7a13a50a895a0eb1e8c6a8ace80d40a94"}, {file = "platformdirs-4.3.7.tar.gz", hash = "sha256:eb437d586b6a0986388f0d6f74aa0cde27b48d0e3d66843640bfb6bdcdb6e351"}, @@ -1690,6 +1780,7 @@ version = "1.5.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" +groups = ["dev", "test"] files = [ {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, @@ -1705,6 +1796,7 @@ version = "5.29.4" description = "" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "protobuf-5.29.4-cp310-abi3-win32.whl", hash = "sha256:13eb236f8eb9ec34e63fc8b1d6efd2777d062fa6aaa68268fb67cf77f6839ad7"}, {file = "protobuf-5.29.4-cp310-abi3-win_amd64.whl", hash = "sha256:bcefcdf3976233f8a502d265eb65ea740c989bacc6c30a58290ed0e519eb4b8d"}, @@ -1725,6 +1817,7 @@ version = "6.1.1" description = "Cross-platform lib for process and system monitoring in Python." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +groups = ["dev"] files = [ {file = "psutil-6.1.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:9ccc4316f24409159897799b83004cb1e24f9819b0dcf9c0b68bdcb6cefee6a8"}, {file = "psutil-6.1.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:ca9609c77ea3b8481ab005da74ed894035936223422dc591d6772b147421f777"}, @@ -1755,6 +1848,7 @@ version = "1.11.0" description = "library with cross-python path, ini-parsing, io, code, log facilities" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +groups = ["dev"] files = [ {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, @@ -1766,6 +1860,7 @@ version = "2.9.1" description = "Python style guide checker" optional = false python-versions = ">=3.6" +groups = ["dev"] files = [ {file = "pycodestyle-2.9.1-py2.py3-none-any.whl", hash = "sha256:d1735fc58b418fd7c5f658d28d943854f8a849b01a5d0a1e6f3f3fdd0166804b"}, {file = "pycodestyle-2.9.1.tar.gz", hash = "sha256:2c9607871d58c76354b697b42f5d57e1ada7d261c261efac224b664affdc5785"}, @@ -1777,6 +1872,8 @@ version = "2.22" description = "C parser in Python" optional = false python-versions = ">=3.8" +groups = ["dev"] +markers = "platform_python_implementation != \"PyPy\"" files = [ {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, @@ -1788,6 +1885,7 @@ version = "3.22.0" description = "Cryptographic library for Python" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["main"] files = [ {file = "pycryptodome-3.22.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:96e73527c9185a3d9b4c6d1cfb4494f6ced418573150be170f6580cb975a7f5a"}, {file = "pycryptodome-3.22.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:9e1bb165ea1dc83a11e5dbbe00ef2c378d148f3a2d3834fb5ba4e0f6fd0afe4b"}, @@ -1826,6 +1924,7 @@ version = "2.9.2" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pydantic-2.9.2-py3-none-any.whl", hash = "sha256:f048cec7b26778210e28a0459867920654d48e5e62db0958433636cde4254f12"}, {file = "pydantic-2.9.2.tar.gz", hash = "sha256:d155cef71265d1e9807ed1c32b4c8deec042a44a50a4188b25ac67ecd81a9c0f"}, @@ -1841,7 +1940,7 @@ typing-extensions = [ [package.extras] email = ["email-validator (>=2.0.0)"] -timezone = ["tzdata"] +timezone = ["tzdata ; python_version >= \"3.9\" and sys_platform == \"win32\""] [[package]] name = "pydantic-core" @@ -1849,6 +1948,7 @@ version = "2.23.4" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pydantic_core-2.23.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b10bd51f823d891193d4717448fab065733958bdb6a6b351967bd349d48d5c9b"}, {file = "pydantic_core-2.23.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4fc714bdbfb534f94034efaa6eadd74e5b93c8fa6315565a222f7b6f42ca1166"}, @@ -1950,6 +2050,7 @@ version = "4.6.3" description = "Create Python API documentation in Markdown format." optional = false python-versions = ">=3.7,<4.0" +groups = ["docs"] files = [ {file = "pydoc-markdown-4.6.3.tar.gz", hash = "sha256:82aa424de7e390e4a34ac200b189bb011e33be18bb089e83b15af5742cf21dc7"}, {file = "pydoc_markdown-4.6.3-py3-none-any.whl", hash = "sha256:b79da5b972be5c24db41ed5ce65fedd0031aa9cd023467d42cd47882a5e92f98"}, @@ -1976,6 +2077,7 @@ version = "6.3.0" description = "Python docstring style checker" optional = false python-versions = ">=3.6" +groups = ["dev"] files = [ {file = "pydocstyle-6.3.0-py3-none-any.whl", hash = "sha256:118762d452a49d6b05e194ef344a55822987a462831ade91ec5c06fd2169d019"}, {file = "pydocstyle-6.3.0.tar.gz", hash = "sha256:7ce43f0c0ac87b07494eb9c0b462c0b73e6ff276807f204d6b53edc72b7e44e1"}, @@ -1985,7 +2087,7 @@ files = [ snowballstemmer = ">=2.2.0" [package.extras] -toml = ["tomli (>=1.2.3)"] +toml = ["tomli (>=1.2.3) ; python_version < \"3.11\""] [[package]] name = "pyflakes" @@ -1993,6 +2095,7 @@ version = "2.5.0" description = "passive checker of Python programs" optional = false python-versions = ">=3.6" +groups = ["dev"] files = [ {file = "pyflakes-2.5.0-py2.py3-none-any.whl", hash = "sha256:4579f67d887f804e67edb544428f264b7b24f435b263c4614f384135cea553d2"}, {file = "pyflakes-2.5.0.tar.gz", hash = "sha256:491feb020dca48ccc562a8c0cbe8df07ee13078df59813b83959cbdada312ea3"}, @@ -2004,6 +2107,7 @@ version = "2.19.1" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.8" +groups = ["dev", "docs"] files = [ {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"}, {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"}, @@ -2018,6 +2122,7 @@ version = "2.15.5" description = "python code static checker" optional = false python-versions = ">=3.7.2" +groups = ["dev"] files = [ {file = "pylint-2.15.5-py3-none-any.whl", hash = "sha256:c2108037eb074334d9e874dc3c783752cc03d0796c88c9a9af282d0f161a1004"}, {file = "pylint-2.15.5.tar.gz", hash = "sha256:3b120505e5af1d06a5ad76b55d8660d44bf0f2fc3c59c2bdd94e39188ee3a4df"}, @@ -2044,6 +2149,7 @@ version = "10.14.3" description = "Extension pack for Python Markdown." optional = false python-versions = ">=3.8" +groups = ["docs"] files = [ {file = "pymdown_extensions-10.14.3-py3-none-any.whl", hash = "sha256:05e0bee73d64b9c71a4ae17c72abc2f700e8bc8403755a00580b49a4e9f189e9"}, {file = "pymdown_extensions-10.14.3.tar.gz", hash = "sha256:41e576ce3f5d650be59e900e4ceff231e0aed2a88cf30acaee41e02f063a061b"}, @@ -2062,6 +2168,7 @@ version = "1.2.0" description = "Wrappers to call pyproject.toml-based build backend hooks." optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "pyproject_hooks-1.2.0-py3-none-any.whl", hash = "sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913"}, {file = "pyproject_hooks-1.2.0.tar.gz", hash = "sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8"}, @@ -2073,6 +2180,7 @@ version = "8.3.5" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" +groups = ["test"] files = [ {file = "pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820"}, {file = "pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845"}, @@ -2095,6 +2203,7 @@ version = "15.0" description = "pytest plugin to re-run tests to eliminate flaky failures" optional = false python-versions = ">=3.9" +groups = ["test"] files = [ {file = "pytest-rerunfailures-15.0.tar.gz", hash = "sha256:2d9ac7baf59f4c13ac730b47f6fa80e755d1ba0581da45ce30b72fb3542b4474"}, {file = "pytest_rerunfailures-15.0-py3-none-any.whl", hash = "sha256:dd150c4795c229ef44320adc9a0c0532c51b78bb7a6843a8c53556b9a611df1a"}, @@ -2110,6 +2219,7 @@ version = "2.9.0.post0" description = "Extensions to the standard Python datetime module" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main", "docs"] files = [ {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, @@ -2124,6 +2234,7 @@ version = "5.3.1" description = "YAML parser and emitter for Python" optional = false python-versions = "*" +groups = ["dev", "docs"] files = [ {file = "PyYAML-5.3.1-cp27-cp27m-win32.whl", hash = "sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f"}, {file = "PyYAML-5.3.1-cp27-cp27m-win_amd64.whl", hash = "sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76"}, @@ -2146,6 +2257,7 @@ version = "0.1" description = "A custom YAML tag for referencing environment variables in YAML files. " optional = false python-versions = ">=3.6" +groups = ["docs"] files = [ {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"}, {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"}, @@ -2160,6 +2272,7 @@ version = "0.36.2" description = "JSON Referencing + Python" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0"}, {file = "referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa"}, @@ -2176,6 +2289,7 @@ version = "2024.11.6" description = "Alternative regular expression module, to replace re." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91"}, {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0"}, @@ -2279,6 +2393,7 @@ version = "2.32.3" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" +groups = ["main", "dev", "docs"] files = [ {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, @@ -2300,6 +2415,7 @@ version = "14.0.0" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.8.0" +groups = ["dev"] files = [ {file = "rich-14.0.0-py3-none-any.whl", hash = "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0"}, {file = "rich-14.0.0.tar.gz", hash = "sha256:82f1bc23a6a21ebca4ae0c45af9bdbc492ed20231dcb63f297d6d1021a9d5725"}, @@ -2319,6 +2435,7 @@ version = "0.24.0" description = "Python bindings to Rust's persistent data structures (rpds)" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "rpds_py-0.24.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:006f4342fe729a368c6df36578d7a348c7c716be1da0a1a0f86e3021f8e98724"}, {file = "rpds_py-0.24.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2d53747da70a4e4b17f559569d5f9506420966083a31c5fbd84e764461c4444b"}, @@ -2442,6 +2559,7 @@ version = "0.18.10" description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "ruamel.yaml-0.18.10-py3-none-any.whl", hash = "sha256:30f22513ab2301b3d2b577adc121c6471f28734d3d9728581245f1e76468b4f1"}, {file = "ruamel.yaml-0.18.10.tar.gz", hash = "sha256:20c86ab29ac2153f80a428e1254a8adf686d3383df04490514ca3b79a362db58"}, @@ -2460,6 +2578,8 @@ version = "0.2.12" description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml" optional = false python-versions = ">=3.9" +groups = ["dev"] +markers = "platform_python_implementation == \"CPython\" and python_version < \"3.13\"" files = [ {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:11f891336688faf5156a36293a9c362bdc7c88f03a8a027c2c1d8e0bcde998e5"}, {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:a606ef75a60ecf3d924613892cc603b154178ee25abb3055db5062da811fd969"}, @@ -2515,6 +2635,7 @@ version = "3.3.1" description = "Scan dependencies for known vulnerabilities and licenses." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "safety-3.3.1-py3-none-any.whl", hash = "sha256:243355a961403b873c1504e3e6f79ce36b86881d559722595632d788aa92b7ea"}, {file = "safety-3.3.1.tar.gz", hash = "sha256:679834432d0ad64c54e0b864ac6715d4743a65f8db67512380ee7ee2011c206a"}, @@ -2549,6 +2670,7 @@ version = "0.0.11" description = "Schemas for Safety tools" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "safety_schemas-0.0.11-py3-none-any.whl", hash = "sha256:2af940c1c992d6891a6b84403a7c12fd445e20651752b1818b86c205690b3e03"}, {file = "safety_schemas-0.0.11.tar.gz", hash = "sha256:10804372e077ca5a95e740cc507e73d9173663284006fcaaf5756179ba13ac9d"}, @@ -2567,13 +2689,14 @@ version = "2.10.0" description = "A library implementing the 'SemVer' scheme." optional = false python-versions = ">=2.7" +groups = ["dev"] files = [ {file = "semantic_version-2.10.0-py2.py3-none-any.whl", hash = "sha256:de78a3b8e0feda74cabc54aab2da702113e33ac9d9eb9d2389bcf1f58b7d9177"}, {file = "semantic_version-2.10.0.tar.gz", hash = "sha256:bdabb6d336998cbb378d4b9db3a4b56a1e3235701dc05ea2690d9a997ed5041c"}, ] [package.extras] -dev = ["Django (>=1.11)", "check-manifest", "colorama (<=0.4.1)", "coverage", "flake8", "nose2", "readme-renderer (<25.0)", "tox", "wheel", "zest.releaser[recommended]"] +dev = ["Django (>=1.11)", "check-manifest", "colorama (<=0.4.1) ; python_version == \"3.4\"", "coverage", "flake8", "nose2", "readme-renderer (<25.0) ; python_version == \"3.4\"", "tox", "wheel", "zest.releaser[recommended]"] doc = ["Sphinx", "sphinx-rtd-theme"] [[package]] @@ -2582,19 +2705,20 @@ version = "78.1.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "setuptools-78.1.0-py3-none-any.whl", hash = "sha256:3e386e96793c8702ae83d17b853fb93d3e09ef82ec62722e61da5cd22376dcd8"}, {file = "setuptools-78.1.0.tar.gz", hash = "sha256:18fd474d4a82a5f83dac888df697af65afa82dec7323d09c3e37d1f14288da54"}, ] [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.8.0)"] -core = ["importlib_metadata (>=6)", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\"", "ruff (>=0.8.0) ; sys_platform != \"cygwin\""] +core = ["importlib_metadata (>=6) ; python_version < \"3.10\"", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1) ; python_version < \"3.11\"", "wheel (>=0.43.0)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] enabler = ["pytest-enabler (>=2.2)"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] -type = ["importlib_metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.14.*)", "pytest-mypy"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21) ; python_version >= \"3.9\" and sys_platform != \"cygwin\"", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf ; sys_platform != \"cygwin\"", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] +type = ["importlib_metadata (>=7.0.2) ; python_version < \"3.10\"", "jaraco.develop (>=7.21) ; sys_platform != \"cygwin\"", "mypy (==1.14.*)", "pytest-mypy"] [[package]] name = "shellingham" @@ -2602,6 +2726,7 @@ version = "1.5.4" description = "Tool to Detect Surrounding Shell" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, @@ -2613,6 +2738,7 @@ version = "1.17.0" description = "Python 2 and 3 compatibility utilities" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main", "dev", "docs"] files = [ {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, @@ -2624,6 +2750,7 @@ version = "5.0.2" description = "A pure Python implementation of a sliding window memory map manager" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "smmap-5.0.2-py3-none-any.whl", hash = "sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e"}, {file = "smmap-5.0.2.tar.gz", hash = "sha256:26ea65a03958fa0c8a1c7e8c7a58fdc77221b8910f6be2131affade476898ad5"}, @@ -2635,17 +2762,47 @@ version = "2.2.0" description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, ] +[[package]] +name = "sortedcontainers" +version = "2.4.0" +description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set" +optional = false +python-versions = "*" +groups = ["main", "dev"] +files = [ + {file = "sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0"}, + {file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"}, +] + +[[package]] +name = "sortedcontainers-stubs" +version = "2.4.3" +description = "Type stubs for sortedcontainers" +optional = false +python-versions = "<4.0,>=3.9" +groups = ["dev"] +files = [ + {file = "sortedcontainers_stubs-2.4.3-py3-none-any.whl", hash = "sha256:4496109dfa6645e4b675f57fbc7e42ec4d1bed2c74aab7fa379e0795e49fe406"}, + {file = "sortedcontainers_stubs-2.4.3.tar.gz", hash = "sha256:ba172daceda4bd617d2f6ffd24582261a494da92705409651967faa40ebc75dd"}, +] + +[package.dependencies] +sortedcontainers = ">=2,<3" +typing-extensions = ">=4.1.0,<5.0.0" + [[package]] name = "stevedore" version = "5.4.1" description = "Manage dynamic plugins for Python applications" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "stevedore-5.4.1-py3-none-any.whl", hash = "sha256:d10a31c7b86cba16c1f6e8d15416955fc797052351a56af15e608ad20811fcfe"}, {file = "stevedore-5.4.1.tar.gz", hash = "sha256:3135b5ae50fe12816ef291baff420acb727fcd356106e3e9cbfa9e5985cd6f4b"}, @@ -2660,6 +2817,7 @@ version = "0.10.2" description = "Python Library for Tom's Obvious, Minimal Language" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +groups = ["dev"] files = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, @@ -2671,6 +2829,7 @@ version = "2.2.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" +groups = ["dev", "docs", "test"] files = [ {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, @@ -2705,6 +2864,7 @@ files = [ {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, ] +markers = {dev = "python_version < \"3.11\"", test = "python_version < \"3.11\""} [[package]] name = "tomli-w" @@ -2712,6 +2872,7 @@ version = "1.2.0" description = "A lil' TOML writer" optional = false python-versions = ">=3.9" +groups = ["docs"] files = [ {file = "tomli_w-1.2.0-py3-none-any.whl", hash = "sha256:188306098d013b691fcadc011abd66727d3c414c571bb01b1a174ba8c983cf90"}, {file = "tomli_w-1.2.0.tar.gz", hash = "sha256:2dd14fac5a47c27be9cd4c976af5a12d87fb1f0b4512f81d69cce3b35ae25021"}, @@ -2723,6 +2884,7 @@ version = "0.13.2" description = "Style preserving TOML library" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde"}, {file = "tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79"}, @@ -2734,6 +2896,7 @@ version = "3.28.0" description = "tox is a generic virtualenv management and test command line tool" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +groups = ["dev"] files = [ {file = "tox-3.28.0-py2.py3-none-any.whl", hash = "sha256:57b5ab7e8bb3074edc3c0c0b4b192a4f3799d3723b2c5b76f1fa9f2d40316eea"}, {file = "tox-3.28.0.tar.gz", hash = "sha256:d0d28f3fe6d6d7195c27f8b054c3e99d5451952b54abdae673b71609a581f640"}, @@ -2751,7 +2914,7 @@ virtualenv = ">=16.0.0,<20.0.0 || >20.0.0,<20.0.1 || >20.0.1,<20.0.2 || >20.0.2, [package.extras] docs = ["pygments-github-lexers (>=0.0.5)", "sphinx (>=2.0.0)", "sphinxcontrib-autoprogram (>=0.1.5)", "towncrier (>=18.5.0)"] -testing = ["flaky (>=3.4.0)", "freezegun (>=0.3.11)", "pathlib2 (>=2.3.3)", "psutil (>=5.6.1)", "pytest (>=4.0.0)", "pytest-cov (>=2.5.1)", "pytest-mock (>=1.10.0)", "pytest-randomly (>=1.0.0)"] +testing = ["flaky (>=3.4.0)", "freezegun (>=0.3.11)", "pathlib2 (>=2.3.3) ; python_version < \"3.4\"", "psutil (>=5.6.1) ; platform_python_implementation == \"cpython\"", "pytest (>=4.0.0)", "pytest-cov (>=2.5.1)", "pytest-mock (>=1.10.0)", "pytest-randomly (>=1.0.0)"] [[package]] name = "tqdm" @@ -2759,6 +2922,7 @@ version = "4.67.1" description = "Fast, Extensible Progress Meter" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2"}, {file = "tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2"}, @@ -2780,6 +2944,7 @@ version = "0.15.2" description = "Typer, build great CLIs. Easy to code. Based on Python type hints." optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "typer-0.15.2-py3-none-any.whl", hash = "sha256:46a499c6107d645a9c13f7ee46c5d5096cae6f5fc57dd11eccbbb9ae3e44ddfc"}, {file = "typer-0.15.2.tar.gz", hash = "sha256:ab2fab47533a813c49fe1f16b1a370fd5819099c00b119e0633df65f22144ba5"}, @@ -2797,10 +2962,12 @@ version = "4.13.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" +groups = ["main", "dev", "docs"] files = [ {file = "typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c"}, {file = "typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"}, ] +markers = {main = "python_version < \"3.13\""} [[package]] name = "urllib3" @@ -2808,13 +2975,14 @@ version = "2.4.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.9" +groups = ["main", "dev", "docs"] files = [ {file = "urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813"}, {file = "urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466"}, ] [package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] @@ -2825,6 +2993,7 @@ version = "20.30.0" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "virtualenv-20.30.0-py3-none-any.whl", hash = "sha256:e34302959180fca3af42d1800df014b35019490b119eba981af27f2fa486e5d6"}, {file = "virtualenv-20.30.0.tar.gz", hash = "sha256:800863162bcaa5450a6e4d721049730e7f2dae07720e0902b0e4040bd6f9ada8"}, @@ -2837,7 +3006,7 @@ platformdirs = ">=3.9.1,<5" [package.extras] docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] -test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8) ; platform_python_implementation == \"PyPy\" or platform_python_implementation == \"GraalVM\" or platform_python_implementation == \"CPython\" and sys_platform == \"win32\" and python_version >= \"3.13\"", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10) ; platform_python_implementation == \"CPython\""] [[package]] name = "vulture" @@ -2845,6 +3014,7 @@ version = "2.6" description = "Find dead code" optional = false python-versions = ">=3.6" +groups = ["dev"] files = [ {file = "vulture-2.6-py2.py3-none-any.whl", hash = "sha256:e792e903ccc063ec4873a8979dcf11b51ea3d65a2d3b31c113d47be48f0cdcae"}, {file = "vulture-2.6.tar.gz", hash = "sha256:2515fa848181001dc8a73aba6a01a1a17406f5d372f24ec7f7191866f9f4997e"}, @@ -2859,6 +3029,7 @@ version = "6.0.0" description = "Filesystem events monitoring" optional = false python-versions = ">=3.9" +groups = ["docs"] files = [ {file = "watchdog-6.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d1cdb490583ebd691c012b3d6dae011000fe42edb7a82ece80965b42abd61f26"}, {file = "watchdog-6.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc64ab3bdb6a04d69d4023b29422170b74681784ffb9463ed4870cf2f3e66112"}, @@ -2901,6 +3072,7 @@ version = "1.17.2" description = "Module for decorators, wrappers and monkey patching." optional = false python-versions = ">=3.8" +groups = ["dev", "docs"] files = [ {file = "wrapt-1.17.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3d57c572081fed831ad2d26fd430d565b76aa277ed1d30ff4d40670b1c0dd984"}, {file = "wrapt-1.17.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b5e251054542ae57ac7f3fba5d10bfff615b6c2fb09abeb37d2f1463f841ae22"}, @@ -2989,6 +3161,7 @@ version = "0.43.0" description = "A formatter for Python code" optional = false python-versions = ">=3.7" +groups = ["docs"] files = [ {file = "yapf-0.43.0-py3-none-any.whl", hash = "sha256:224faffbc39c428cb095818cf6ef5511fdab6f7430a10783fdfb292ccf2852ca"}, {file = "yapf-0.43.0.tar.gz", hash = "sha256:00d3aa24bfedff9420b2e0d5d9f5ab6d9d4268e72afbf59bb3fa542781d5218e"}, @@ -3004,20 +3177,22 @@ version = "3.21.0" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.9" +groups = ["dev", "docs"] files = [ {file = "zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931"}, {file = "zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4"}, ] +markers = {dev = "python_full_version < \"3.10.2\"", docs = "python_version == \"3.9\""} [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] -test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] +test = ["big-O", "importlib-resources ; python_version < \"3.9\"", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] type = ["pytest-mypy"] [metadata] -lock-version = "2.0" +lock-version = "2.1" python-versions = "^3.9" -content-hash = "1161c9e59011b9c14ead2344eb61fe846fe0e0b3a07de95f68486446906b7fc9" +content-hash = "9a7d48b66ce5fe817d9f6f310495ae9e71a1bfd0ef3c5a3bdf8b96404d8713ca" diff --git a/pyproject.toml b/pyproject.toml index 502a23d7..478e7ba6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -45,6 +45,7 @@ python-dateutil = "*" pycryptodome = "^3.18.0" googleapis-common-protos = "*" virtualenv = ">=20.26.6" +sortedcontainers = "==2.4.0" [tool.poetry.group.dev] optional = true @@ -54,7 +55,7 @@ check-manifest = "*" tox = "^3.26" flake8 = "==5.0.4" black = "^22.10" -mypy = "==0.982" +mypy = "==1.15.0" bandit = "==1.7.4" safety = "==3.3.1" isort = "==5.10.1" @@ -72,6 +73,7 @@ pyyaml = "==5.3.1" packaging = ">=23.0" virtualenv = ">=20.26.6" marshmallow = "<4.0.0" +sortedcontainers-stubs = "2.4.3" [tool.poetry.group.docs] optional = true @@ -113,7 +115,8 @@ module = [ "certifi.*", "mbedtls.*", "jsonschema.*", - "dateutil.*" + "dateutil.*", + "sortedcontainers.*" ] ignore_missing_imports = true diff --git a/strategy.ini b/strategy.ini index d8b5bb2e..56158dec 100644 --- a/strategy.ini +++ b/strategy.ini @@ -93,4 +93,6 @@ pycryptodome: >=3.15.0 attrs: >=19.2.0 referencing: >=0.28.4 typing-extensions: >=3.0.0 -urllib3: >=1.21.0 \ No newline at end of file +urllib3: >=1.21.0 +jsonschema: >=3.2.0 +jsonschema-specifications: >=2023.3.6 \ No newline at end of file diff --git a/tests/unit/test_aerial/coins_test.py b/tests/unit/test_aerial/coins_test.py index f6ab5466..8300dd2d 100644 --- a/tests/unit/test_aerial/coins_test.py +++ b/tests/unit/test_aerial/coins_test.py @@ -20,7 +20,7 @@ # ------------------------------------------------------------------------------ import pytest -from cosmpy.aerial.coins import Coin, Coins, is_coins_sorted, parse_coins, sort_coins +from cosmpy.aerial.coins import Coin, Coins, OnCollision, parse_coins from cosmpy.protos.cosmos.base.v1beta1.coin_pb2 import Coin as CoinProto @@ -68,112 +68,11 @@ def test_parsing_coins_string(input_coins, expected_result): "input_coins,expected_coins,validate_error", [ ([], [], None), - ("4afet,5afet", [Coin(4, "afet"), Coin(5, "afet")], None), - ("5ccc,2bcc,4acc", [Coin(5, "ccc"), Coin(2, "bcc"), Coin(4, "acc")], None), ( - [ - CoinProto(amount="4", denom="bcc"), - CoinProto(amount="2", denom="ccc"), - CoinProto(amount="5", denom="acc"), - ], - [Coin(4, "bcc"), Coin(2, "ccc"), Coin(5, "acc")], - None, - ), - (CoinProto(amount="4", denom="acc"), [Coin(4, "acc")], None), - (Coin(4, "acc"), [Coin(4, "acc")], None), - ( - [Coin(2, "acc"), CoinProto(amount="4", denom="bcc")], - [Coin(2, "acc"), Coin(4, "bcc")], - None, - ), - ( - "0bcc,2ccc,0acc", - [Coin(0, "bcc"), Coin(2, "ccc"), Coin(0, "acc")], + "4afet,5afet", None, + 'Attempt to merge a coin with the "afet" denomination which already exists in the receiving coins instance', ), - ], -) -def test_coins_ordering_preserved_during_instantiation( - input_coins, expected_coins, validate_error -): - """Test preservation of ordering during initialidation.""" - if validate_error: - with pytest.raises(Exception) as exc_info: - _ = Coins(input_coins) - assert validate_error in str(exc_info.value) - - if not validate_error: - _input_coins = Coins(input_coins) - assert _input_coins == Coins(expected_coins) - - assert len(_input_coins) == len(expected_coins) - for in_c, exp_c in zip(_input_coins, expected_coins): - assert in_c == exp_c - - -@pytest.mark.parametrize( - "input_coins,expected_coins,validate_error", - [ - ([], [], None), - ("4afet,5afet", None, 'Multiple occurrences of the "afet" denomination'), - ("4acc,2bcc,5ccc", [Coin(4, "acc"), Coin(2, "bcc"), Coin(5, "ccc")], None), - ( - [ - CoinProto(amount="4", denom="acc"), - CoinProto(amount="2", denom="bcc"), - CoinProto(amount="5", denom="ccc"), - ], - [Coin(4, "acc"), Coin(2, "bcc"), Coin(5, "ccc")], - None, - ), - (CoinProto(amount="4", denom="acc"), [Coin(4, "acc")], None), - (Coin(4, "acc"), [Coin(4, "acc")], None), - ( - [Coin(2, "acc"), CoinProto(amount="4", denom="bcc")], - [Coin(2, "acc"), Coin(4, "bcc")], - None, - ), - ( - "4acc,2ccc,5bcc", - [Coin(4, "acc"), Coin(2, "ccc"), Coin(5, "bcc")], - "Coins are not sorted", - ), - ( - "4cc", - None, - 'The "cc" denom does not conform to Cosmos-SDK requirements', - ), - ( - "4acc,0bcc,5ccc", - None, - "Coin amount must be greater than zero", - ), - ( - "4acc,1bcc,0ccc", - None, - "Coin amount must be greater than zero", - ), - ], -) -def test_coins_validate(input_coins, expected_coins, validate_error): - """Test Coins validate.""" - if validate_error: - with pytest.raises(Exception) as exc_info: - test_coins = Coins(input_coins) - test_coins.validate() - assert validate_error in str(exc_info.value) - - if not validate_error: - test_coins = Coins(input_coins) - assert test_coins == expected_coins - test_coins.validate() - - -@pytest.mark.parametrize( - "input_coins,expected_coins,validate_error", - [ - ([], [], None), - ("4afet,5afet", None, 'Multiple occurrences of the "afet" denomination'), ("4acc,2bcc,5ccc", [Coin(4, "acc"), Coin(2, "bcc"), Coin(5, "ccc")], None), ( [ @@ -199,7 +98,7 @@ def test_coins_validate(input_coins, expected_coins, validate_error): ( "4cc", None, - 'The "cc" denom does not conform to Cosmos-SDK requirements', + 'Coin denom "cc" does not conform to Cosmos-SDK requirements', ), ( "4acc,5ccc,0bcc", @@ -218,37 +117,16 @@ def test_coins_validate(input_coins, expected_coins, validate_error): ), ], ) -def test_coins_canonicalise(input_coins, expected_coins, validate_error): - """Test Coins canonicalise.""" +def test_coins_instantiate(input_coins, expected_coins, validate_error): + """Test Coins instantiate.""" if validate_error: with pytest.raises(Exception) as exc_info: - _ = Coins(input_coins).canonicalise() + _ = Coins(input_coins) assert validate_error in str(exc_info.value) if not validate_error: - canonicalised_input_coins = Coins(input_coins).canonicalise() - assert canonicalised_input_coins == Coins(expected_coins) - - -@pytest.mark.parametrize( - "input_coins,expected_sorted_coins", - [([], []), ("4acc,2ccc,5bcc", "4acc,5bcc,2ccc")], -) -def test_coins_sort(input_coins, expected_sorted_coins): - """Test Coins sort.""" - sorted_coins = Coins(input_coins) - sort_coins(sorted_coins) - - input_coins = Coins(input_coins) - expected_sorted_coins = Coins(expected_sorted_coins) - - if input_coins != sorted_coins: - assert not is_coins_sorted(input_coins) - - sort_coins(input_coins) - - assert is_coins_sorted(input_coins) - assert input_coins == expected_sorted_coins + instantiated_input_coins = Coins(input_coins) + assert instantiated_input_coins == Coins(expected_coins) @pytest.mark.parametrize( @@ -261,7 +139,7 @@ def test_add(coins_a, coins_b, expected_coins_res): """Test Coins add.""" coins_a = Coins(coins_a) coins_b = Coins(coins_b) - coins_res = coins_a.canonicalise() + coins_b.canonicalise() + coins_res = coins_a + coins_b expected_coins_res = Coins(expected_coins_res) @@ -284,8 +162,8 @@ def test_add(coins_a, coins_b, expected_coins_res): ) def test_subtract(coins_a, coins_b, expected_coins_res, error): """Test Coins subtract.""" - coins_a = Coins(coins_a).canonicalise() - coins_b = Coins(coins_b).canonicalise() + coins_a = Coins(coins_a) + coins_b = Coins(coins_b) if error: with pytest.raises(Exception) as exc_info: @@ -297,3 +175,97 @@ def test_subtract(coins_a, coins_b, expected_coins_res, error): expected_coins_res = Coins(expected_coins_res) assert coins_res == expected_coins_res + + +@pytest.mark.parametrize( + "coins_a,coins_b,expected_coins_res,error", + [ + ("1ccc", "0ccc", "1ccc", None), + ("", "0ccc", "", None), + ("", "1ccc", "1ccc", None), + (None, "3gcc,1ccc", "1ccc,3gcc", None), + ("1ccc", "", "1ccc", None), + ("3gcc,1ccc", None, "1ccc,3gcc", None), + ( + "4acc,2ccc", + "2ccc", + None, + 'Attempt to merge a coin with the "ccc" denomination which already exists in the receiving coins instance', + ), + ("4acc,2ccc", "1bcc", "4acc,1bcc,2ccc", None), + ( + "4acc,2ccc,5ddd", + "1ccc", + None, + 'Attempt to merge a coin with the "ccc" denomination which already exists in the receiving coins instance', + ), + ("4acc,2ccc,5ddd", "1edd", "4acc,2ccc,5ddd,1edd", None), + ("4acc,2ccc,5ddd", "1edd,0acc,0ddd", "4acc,2ccc,5ddd,1edd", None), + ], +) +def test_merge_coins_fail_on_collision(coins_a, coins_b, expected_coins_res, error): + """Test Coins merge with fail on collision.""" + coins_a1 = Coins(coins_a) + coins_a2 = Coins(coins_a) + + if error: + with pytest.raises(Exception) as exc_info: + coins_a1.merge_from(coins_b) + assert error in str(exc_info.value) + + with pytest.raises(Exception) as exc_info: + coins_a2.merge_from(coins_b, on_collision=OnCollision.Fail) + assert error in str(exc_info.value) + else: + expected_coins_res = Coins(expected_coins_res) + + coins_a1.merge_from(coins_b) + assert coins_a1 == expected_coins_res + + coins_a2.merge_from(coins_b, on_collision=OnCollision.Fail) + assert coins_a2 == expected_coins_res + + +@pytest.mark.parametrize( + "coins_a,coins_b,expected_coins_res", + [ + ("1ccc", "0ccc", "1ccc"), + ("", "0ccc", ""), + ("", "1ccc", "1ccc"), + (None, "3gcc,1ccc", "1ccc,3gcc"), + ("1ccc", "", "1ccc"), + ("3gcc,1ccc", None, "1ccc,3gcc"), + ("4acc,2ccc", "3gcc,1ccc", "4acc,1ccc,3gcc"), + ("4acc,2ccc", "1ccc", "4acc,1ccc"), + ("4acc,2ccc,5ddd,7ecc", "1acc,3ccc,6ddd", "1acc,3ccc,6ddd,7ecc"), + ("4acc,2ccc,5ddd", "1acc,3ccc,6ddd,7ecc", "1acc,3ccc,6ddd,7ecc"), + ("4acc,2ccc,5ddd", "1acc,7ecc", "1acc,2ccc,5ddd,7ecc"), + ("4acc,2ccc,5ddd", "1edd,0acc,0ddd", "4acc,2ccc,5ddd,1edd"), + ], +) +def test_merge_coins_override_on_collision(coins_a, coins_b, expected_coins_res): + """Test Coins merge with override on collision.""" + expected_coins_res = Coins(expected_coins_res) + + coins_a1 = Coins(coins_a) + + coins_a1.merge_from(coins_b, on_collision=OnCollision.Override) + assert coins_a1 == expected_coins_res + + +@pytest.mark.parametrize( + "coins_a", + [ + None, + "", + "1ccc", + "4acc,2ccc,5ddd,7ecc", + ], +) +def test_clear(coins_a): + """Test Coins clear.""" + coins = Coins(coins_a) + + x = coins.clear() + assert len(coins) == 0 + assert x == coins