diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9e52a6cede7dd568d7d0d2c9939812820d9d6626..591e9b314fccaa8ba87736b51158a6f5aa566d5e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -18,10 +18,12 @@ variables: GIT_SUBMODULE_STRATEGY: recursive GIT_SUBMODULE_DEPTH: 1 # first party libraries gitlab indexes: + # - 198 -> hive (generated APIs) # - 362 -> schemas # - 419 -> wax # - 434 -> beekeepy FIRST_PARTY_EXTRA_INDEX_ARGS: | + --extra-index https://gitlab.syncad.com/api/v4/projects/198/packages/pypi/simple --extra-index https://gitlab.syncad.com/api/v4/projects/362/packages/pypi/simple --extra-index https://gitlab.syncad.com/api/v4/projects/419/packages/pypi/simple --extra-index https://gitlab.syncad.com/api/v4/projects/434/packages/pypi/simple @@ -52,7 +54,7 @@ variables: include: - project: 'hive/hive' - ref: 9009edf6270db5a0f3bc1d77308bd5cbfb46c1f6 + ref: 846b3d93e5287004961ca3b3b00a6b24c3078067 file: '/scripts/ci-helpers/prepare_data_image_job.yml' # Do not include common-ci-configuration here, it is already referenced by scripts/ci-helpers/prepare_data_image_job.yml included from Hive diff --git a/clive/__private/before_launch.py b/clive/__private/before_launch.py index ff25d8496491fd257d152ab29c11cc302f74b0d0..2b24c51007dcbb47773cc4c6715902637233e0c2 100644 --- a/clive/__private/before_launch.py +++ b/clive/__private/before_launch.py @@ -3,8 +3,6 @@ from __future__ import annotations import shutil from pathlib import Path -from pydantic import Extra - from clive.__private.core.constants.env import ROOT_DIRECTORY from clive.__private.logger import logger from clive.__private.models.schemas import ExtraFieldsPolicy, MissingFieldsInGetConfigPolicy, set_policies @@ -13,7 +11,7 @@ from clive.dev import is_in_dev_mode def _disable_schemas_extra_fields_check() -> None: - set_policies(ExtraFieldsPolicy(policy=Extra.allow), MissingFieldsInGetConfigPolicy(allow=True)) + set_policies(ExtraFieldsPolicy(allow=True), MissingFieldsInGetConfigPolicy(allow=True)) def _create_clive_data_directory() -> None: diff --git a/clive/__private/cli/commands/abc/perform_actions_on_transaction_command.py b/clive/__private/cli/commands/abc/perform_actions_on_transaction_command.py index 160950e0be62cf4049cd6640298ab97ecc647b5f..84e1ff5676e21d5165ab364c3012b87b3fdda8d4 100644 --- a/clive/__private/cli/commands/abc/perform_actions_on_transaction_command.py +++ b/clive/__private/cli/commands/abc/perform_actions_on_transaction_command.py @@ -137,7 +137,7 @@ class PerformActionsOnTransactionCommand(WorldBasedCommand, ForceableCLICommand, return "created" def __print_transaction(self, transaction: Transaction) -> None: - transaction_json = transaction.json(by_alias=True) + transaction_json = transaction.json(order="sorted") message = self._get_transaction_created_message().capitalize() typer.echo(f"{message} transaction:") rich.print_json(transaction_json) diff --git a/clive/__private/cli/commands/beekeeper.py b/clive/__private/cli/commands/beekeeper.py index 30e4b9c52e103017ad49b580273b7ff39e9ddd54..2f8c27eb3a418d31f243dc4261b20c1a8cea27cf 100644 --- a/clive/__private/cli/commands/beekeeper.py +++ b/clive/__private/cli/commands/beekeeper.py @@ -26,7 +26,7 @@ class BeekeeperInfo(WorldBasedCommand): async def _run(self) -> None: session = await self.world.beekeeper_manager.beekeeper.session - info = (await session.get_info()).json(by_alias=True) + info = (await session.get_info()).json(order="sorted") typer.echo(info) diff --git a/clive/__private/cli/commands/process/process_account_update.py b/clive/__private/cli/commands/process/process_account_update.py index bffb7d4eebc13f53173befabda284b658e6d6993..6e5887977ceb946dc73623ba50f12bcbd4b90f2f 100644 --- a/clive/__private/cli/commands/process/process_account_update.py +++ b/clive/__private/cli/commands/process/process_account_update.py @@ -118,7 +118,7 @@ def is_on_auths_list[T: (AccountName, PublicKey)](authority_entry: T, authoritie def add_account(auth: Authority, account: str, weight: int) -> Authority: - if is_on_auths_list(AccountName(account), auth.account_auths): + if is_on_auths_list(account, auth.account_auths): raise CLIPrettyError(f"Account {account} is current account authority") account_weight_tuple = (AccountName(account), HiveInt(weight)) auth.account_auths.append(account_weight_tuple) @@ -126,7 +126,7 @@ def add_account(auth: Authority, account: str, weight: int) -> Authority: def add_key(auth: Authority, key: str, weight: int) -> Authority: - if is_on_auths_list(PublicKey(key), auth.key_auths): + if is_on_auths_list(key, auth.key_auths): raise CLIPrettyError(f"Key {key} is current key authority") key_weight_tuple = (PublicKey(key), HiveInt(weight)) auth.key_auths.append(key_weight_tuple) @@ -134,7 +134,7 @@ def add_key(auth: Authority, key: str, weight: int) -> Authority: def remove_account(auth: Authority, account: str) -> Authority: - if not is_on_auths_list(AccountName(account), auth.account_auths): + if not is_on_auths_list(account, auth.account_auths): raise CLIPrettyError(f"Account {account} is not current account authority") auth.account_auths = [ account_weight_tuple for account_weight_tuple in auth.account_auths if account_weight_tuple[0] != account diff --git a/clive/__private/cli/commands/process/process_custom_json.py b/clive/__private/cli/commands/process/process_custom_json.py index 13fa5d438f952935950b728b6c89505f0e5d84ee..e6655b77c7594aee8441dec1a52ce56ac7393f09 100644 --- a/clive/__private/cli/commands/process/process_custom_json.py +++ b/clive/__private/cli/commands/process/process_custom_json.py @@ -7,7 +7,7 @@ from pathlib import Path from clive.__private.cli.commands.abc.operation_command import OperationCommand from clive.__private.cli.exceptions import CLIPrettyError from clive.__private.core.constants.cli import PERFORM_WORKING_ACCOUNT_LOAD -from clive.__private.models.schemas import CustomJsonOperation +from clive.__private.models.schemas import CustomJsonOperation, JsonString from clive.__private.validators.json_validator import JsonValidator @@ -22,7 +22,7 @@ class ProcessCustomJson(OperationCommand): json_ = self.ensure_json_from_json_string_or_path(self.json_or_path) return CustomJsonOperation( id_=self.id_, - json_=json_, + json_=JsonString(json_), required_auths=self.authorize_by_active, required_posting_auths=self.authorize, ) diff --git a/clive/__private/cli/commands/process/process_transfer_schedule.py b/clive/__private/cli/commands/process/process_transfer_schedule.py index 8d5466ac67af4b28e5e733f29b0ad43459ddbcba..af8212637876ca9c81d4824209aafe6f4696424f 100644 --- a/clive/__private/cli/commands/process/process_transfer_schedule.py +++ b/clive/__private/cli/commands/process/process_transfer_schedule.py @@ -59,10 +59,8 @@ class _ProcessTransferScheduleCommon(OperationCommand, ABC): return [] recurrent_transfer_extension = RecurrentTransferPairIdExtension(pair_id=self.pair_id) - extension = RecurrentTransferPairIdRepresentation( - type=recurrent_transfer_extension.get_name(), value=recurrent_transfer_extension - ) - return [extension.dict(by_alias=True)] + extension = RecurrentTransferPairIdRepresentation(value=recurrent_transfer_extension) + return [extension.dict()] def _identity_check(self, scheduled_transfer: ScheduledTransfer) -> bool: """Determine if a scheduled transfer matches destination and the specified pair ID.""" @@ -124,6 +122,9 @@ class _ProcessTransferScheduleCreateModifyCommon(_ProcessTransferScheduleCommon) raise ProcessTransferScheduleTooLongLifetimeError(requested_lifetime=scheduled_transfer_lifetime) async def _create_operation(self) -> RecurrentTransferOperation: + assert self.repeat is not None, "Value of repeat is None." + assert self.memo is not None, "Value of memo is None." + assert self.amount is not None, "Value of amount is None." return RecurrentTransferOperation( from_=self.from_account, to=self.to, diff --git a/clive/__private/cli/commands/show/show_witness.py b/clive/__private/cli/commands/show/show_witness.py index 7021f02c69a95613f2f526041c5707a3014ba659..e57abb4a17915622ccb404b5b841457b560263d0 100644 --- a/clive/__private/cli/commands/show/show_witness.py +++ b/clive/__private/cli/commands/show/show_witness.py @@ -33,7 +33,7 @@ class ShowWitness(WorldBasedCommand): hbd_savings_apr: str | None = None if witness.props.hbd_interest_rate: hbd_savings_apr = humanize_hbd_savings_apr(hive_percent_to_percent(witness.props.hbd_interest_rate)) - props_as_legacy = witness.props.copy(exclude={"account_creation_fee", "hbd_interest_rate"}, deep=True) + props_as_legacy = witness.props.copy(exclude={"account_creation_fee", "hbd_interest_rate"}) table = Table(title=f"Details of `{self.name}` witness", show_header=False) diff --git a/clive/__private/core/accounts/accounts.py b/clive/__private/core/accounts/accounts.py index eedecc6dd89162ab4c3899b14f096c57f353610c..b3930dbbcb70ae7a05773c94729fc6ec417b73d6 100644 --- a/clive/__private/core/accounts/accounts.py +++ b/clive/__private/core/accounts/accounts.py @@ -3,11 +3,8 @@ from __future__ import annotations from dataclasses import dataclass, field from typing import TYPE_CHECKING -from pydantic import ValidationError - from clive.__private.core.alarms.alarms_storage import AlarmsStorage -from clive.__private.core.validate_schema_field import validate_schema_field -from clive.__private.models.schemas import AccountName +from clive.__private.models.schemas import AccountName, is_matching_model from clive.exceptions import CliveError if TYPE_CHECKING: @@ -78,10 +75,8 @@ class Account: Raises: InvalidAccountNameError: if the given account name is invalid. """ - try: - validate_schema_field(AccountName, name) - except ValidationError as error: - raise InvalidAccountNameError(name) from error + if not is_matching_model(name, AccountName): + raise InvalidAccountNameError(name) @classmethod def is_valid(cls, name: str) -> bool: diff --git a/clive/__private/core/alarms/alarm_identifier.py b/clive/__private/core/alarms/alarm_identifier.py index 5ef1cd4559d0fe305a3d6e24a88c25d44f0bf2bd..4eccfb55e6a72cd3a2e1e0614cacb4a1efaf8f10 100644 --- a/clive/__private/core/alarms/alarm_identifier.py +++ b/clive/__private/core/alarms/alarm_identifier.py @@ -1,12 +1,9 @@ from __future__ import annotations -from abc import ABC +from clive.__private.models.schemas import HiveDateTime, PreconfiguredBaseModel -from clive.__private.models.base import CliveBaseModel -from clive.__private.models.schemas import HiveDateTime # noqa: TC001 - -class AlarmIdentifier(CliveBaseModel, ABC): +class AlarmIdentifier(PreconfiguredBaseModel): """Base class for alarm identifiers.""" diff --git a/clive/__private/core/alarms/specific_alarms/govenance_no_active_votes.py b/clive/__private/core/alarms/specific_alarms/govenance_no_active_votes.py index 5ecf3a8ae0c5a8551ca11c29d66100c0d1d241c1..550749124ee523d01e67b08e024c71d412f730cf 100644 --- a/clive/__private/core/alarms/specific_alarms/govenance_no_active_votes.py +++ b/clive/__private/core/alarms/specific_alarms/govenance_no_active_votes.py @@ -9,6 +9,7 @@ from clive.__private.core.alarms.alarm_data import AlarmDataNeverExpiresWithoutA from clive.__private.core.alarms.alarm_identifier import DateTimeAlarmIdentifier from clive.__private.core.constants.alarm_descriptions import GOVERNANCE_COMMON_ALARM_DESCRIPTION from clive.__private.core.date_utils import is_null_date +from clive.__private.models.schemas import HiveDateTime if TYPE_CHECKING: from clive.__private.core.commands.data_retrieval.update_alarms_data import AccountAlarmsData @@ -26,7 +27,7 @@ class GovernanceNoActiveVotes(Alarm[DateTimeAlarmIdentifier, GovernanceNoActiveV def update_alarm_status(self, data: AccountAlarmsData) -> None: expiration = data.governance_vote_expiration_ts if is_null_date(expiration): - new_identifier = DateTimeAlarmIdentifier(value=expiration) + new_identifier = DateTimeAlarmIdentifier(value=HiveDateTime(expiration)) self.enable_alarm(new_identifier, GovernanceNoActiveVotesAlarmData(expiration_date=expiration)) return diff --git a/clive/__private/core/alarms/specific_alarms/governance_voting_expiration.py b/clive/__private/core/alarms/specific_alarms/governance_voting_expiration.py index c10d75f1953b0718f561ffbaf99e4dede5a12d40..90b049cc989e9cf6f3d458bd4cab33c5a35f517e 100644 --- a/clive/__private/core/alarms/specific_alarms/governance_voting_expiration.py +++ b/clive/__private/core/alarms/specific_alarms/governance_voting_expiration.py @@ -11,6 +11,7 @@ from clive.__private.core.constants.alarm_descriptions import ( GOVERNANCE_VOTING_EXPIRATION_ALARM_DESCRIPTION, ) from clive.__private.core.date_utils import is_null_date +from clive.__private.models.schemas import HiveDateTime if TYPE_CHECKING: from clive.__private.core.commands.data_retrieval.update_alarms_data import AccountAlarmsData @@ -38,7 +39,7 @@ class GovernanceVotingExpiration(Alarm[DateTimeAlarmIdentifier, GovernanceVoting self.disable_alarm() return - new_identifier = DateTimeAlarmIdentifier(value=expiration) + new_identifier = DateTimeAlarmIdentifier(value=HiveDateTime(expiration)) self.enable_alarm(new_identifier, alarm_data) return diff --git a/clive/__private/core/commands/data_retrieval/hive_power_data.py b/clive/__private/core/commands/data_retrieval/hive_power_data.py index f08a881fee9f716d5b1466111e746d19ddc855ec..09014c44b32d7314f7c872ecf38e273b1f8f86fe 100644 --- a/clive/__private/core/commands/data_retrieval/hive_power_data.py +++ b/clive/__private/core/commands/data_retrieval/hive_power_data.py @@ -41,7 +41,7 @@ class SanitizedData: gdpo: DynamicGlobalProperties core_account: Account withdraw_routes: list[WithdrawRoute] - delegations: list[VestingDelegation[Asset.Vests]] + delegations: list[VestingDelegation] @dataclass @@ -52,7 +52,7 @@ class HivePowerData: delegated_balance: HpVestsBalance next_vesting_withdrawal: datetime withdraw_routes: list[WithdrawRoute] - delegations: list[VestingDelegation[Asset.Vests]] + delegations: list[VestingDelegation] to_withdraw: HpVestsBalance withdrawn: HpVestsBalance remaining: HpVestsBalance @@ -143,6 +143,6 @@ class HivePowerDataRetrieval(CommandDataRetrieval[HarvestedDataRaw, SanitizedDat assert data is not None, "ListWithdrawVestingRoutes data is missing" return data.routes - def _assert_delegations(self, data: FindVestingDelegations | None) -> list[VestingDelegation[Asset.Vests]]: + def _assert_delegations(self, data: FindVestingDelegations | None) -> list[VestingDelegation]: assert data is not None, "FindVestingDelegations data is missing" return data.delegations diff --git a/clive/__private/core/commands/load_transaction.py b/clive/__private/core/commands/load_transaction.py index 3f549cc060fbdcfc7b3b936e77a386ef2728d942..44d1940ca1f100006118cee265c107401181dd76 100644 --- a/clive/__private/core/commands/load_transaction.py +++ b/clive/__private/core/commands/load_transaction.py @@ -5,13 +5,12 @@ from dataclasses import dataclass from json import JSONDecodeError from typing import TYPE_CHECKING -from pydantic import ValidationError - from clive.__private.core import iwax from clive.__private.core.commands.abc.command import CommandError from clive.__private.core.commands.abc.command_with_result import CommandWithResult from clive.__private.core.iwax import WaxOperationFailedError from clive.__private.models import Transaction +from clive.__private.models.schemas import DecodeError if TYPE_CHECKING: from pathlib import Path @@ -26,11 +25,11 @@ class LoadTransaction(CommandWithResult[Transaction]): file_path: Path async def _execute(self) -> None: - with contextlib.suppress(JSONDecodeError, ValidationError): - self._result = Transaction.parse_file(self.file_path) + with contextlib.suppress(JSONDecodeError, DecodeError): + self._result = Transaction.parse_file(path=self.file_path) return - with contextlib.suppress(WaxOperationFailedError, JSONDecodeError, ValidationError): + with contextlib.suppress(WaxOperationFailedError, JSONDecodeError, DecodeError): self._result = iwax.deserialize_transaction(self.file_path.read_bytes()) return diff --git a/clive/__private/core/commands/save_transaction.py b/clive/__private/core/commands/save_transaction.py index cd7eca9fc7210839ca3311193e5e74574e80caee..da8769fc423455c5f7e43a891a83d92bc4dfa333 100644 --- a/clive/__private/core/commands/save_transaction.py +++ b/clive/__private/core/commands/save_transaction.py @@ -28,7 +28,7 @@ class SaveTransaction(Command): self.__save_as_binary() if self.__should_save_as_binary() else self.__save_as_json() def __save_as_json(self) -> None: - serialized = self.transaction.json(by_alias=True) + serialized = self.transaction.json(order="sorted", indent=4) self.file_path.write_text(serialized) def __save_as_binary(self) -> None: diff --git a/clive/__private/core/ensure_transaction.py b/clive/__private/core/ensure_transaction.py index c45956a1d0c83a655a876dbdab90991b00f7c612..fa675b36d3ef0cd07c7ec43c47a32f926a6a51ac 100644 --- a/clive/__private/core/ensure_transaction.py +++ b/clive/__private/core/ensure_transaction.py @@ -4,7 +4,7 @@ from collections.abc import Iterable from typing import Any from clive.__private.models import Transaction -from clive.__private.models.schemas import OperationBase, OperationUnion +from clive.__private.models.schemas import OperationBase, convert_to_representation type TransactionConvertibleType = OperationBase | Iterable[OperationBase] | Transaction @@ -26,17 +26,17 @@ def ensure_transaction(content: TransactionConvertibleType) -> Transaction: The transaction. """ - def __ensure_operation(item: Any) -> OperationUnion: # noqa: ANN401 + def __ensure_operation(item: Any) -> OperationBase: # noqa: ANN401 assert isinstance(item, OperationBase) - return item # type: ignore[return-value] + return item if isinstance(content, Transaction): return content if isinstance(content, OperationBase): - operations = [content] + operations = [convert_to_representation(content)] elif isinstance(content, Iterable): - operations = [__ensure_operation(x) for x in content] + operations = [convert_to_representation(__ensure_operation(x)) for x in content] else: raise TypeError(f"Expected a transaction, operation or iterable of operations, got {type(content)}") diff --git a/clive/__private/core/formatters/humanize.py b/clive/__private/core/formatters/humanize.py index 807a6a0cefe90b54fb75ad3fe858b8be420be084..61941f62c21911f3ef28d7f492909e5a6936ff35 100644 --- a/clive/__private/core/formatters/humanize.py +++ b/clive/__private/core/formatters/humanize.py @@ -285,7 +285,8 @@ def humanize_operation_details(operation: OperationBase) -> str: """ out = "" - operation_dict = dict(operation._iter(by_alias=True)) + operation_dict = operation.dict() + for key, value in operation_dict.items(): value_ = value diff --git a/clive/__private/core/get_default_from_model.py b/clive/__private/core/get_default_from_model.py deleted file mode 100644 index 4fb11627dd5de6a2fcbefd3ecf3694f5bd73d8c6..0000000000000000000000000000000000000000 --- a/clive/__private/core/get_default_from_model.py +++ /dev/null @@ -1,41 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Any, overload - -from clive.exceptions import CliveError - -if TYPE_CHECKING: - from pydantic import BaseModel - - -class WrongTypeError(CliveError): - """Raised when the type of the value is not the expected one.""" - - -class NoMatchesError(CliveError): - """Raised when no matches are found.""" - - -@overload -def get_default_from_model(model: type[BaseModel] | BaseModel, field_name: str) -> Any: ... # noqa: ANN401 - - -@overload -def get_default_from_model[T](model: type[BaseModel] | BaseModel, field_name: str, expect_type: type[T]) -> T: ... - - -def get_default_from_model[T]( - model: type[BaseModel] | BaseModel, field_name: str, expect_type: type[T] | None = None -) -> T | Any: - """Get default value from pydantic model.""" - field = model.__fields__.get(field_name, None) - - if field is None: - raise NoMatchesError(f"No matches for {field_name} in {model}") - - default_value = field.default - - if expect_type is not None and not isinstance(default_value, expect_type): - raise WrongTypeError(f"{model}.{field_name} is wrong type; expected {expect_type}, got {type(default_value)}") - - return default_value diff --git a/clive/__private/core/iwax.py b/clive/__private/core/iwax.py index 734edd6a459f85b7a60b183d4c60d3abdcfe4c90..4ef949100e9549a087228c266a0ad55333eb159b 100644 --- a/clive/__private/core/iwax.py +++ b/clive/__private/core/iwax.py @@ -1,13 +1,18 @@ from __future__ import annotations import datetime -from typing import TYPE_CHECKING, Protocol, cast +from collections.abc import Callable +from functools import wraps +from typing import TYPE_CHECKING, Any, Protocol, TypeVar, cast import wax from clive.__private.core.constants.precision import HIVE_PERCENT_PRECISION_DOT_PLACES from clive.__private.core.decimal_conventer import DecimalConverter from clive.__private.core.percent_conversions import hive_percent_to_percent -from clive.__private.models.schemas import convert_to_representation +from clive.__private.models.schemas import ( + HiveInt, + convert_to_representation, +) from clive.exceptions import CliveError if TYPE_CHECKING: @@ -17,6 +22,22 @@ if TYPE_CHECKING: from clive.__private.models import Asset, Transaction from clive.__private.models.schemas import OperationUnion, PriceFeed +F = TypeVar("F", bound=Callable[..., Any]) + + +def cast_hiveint_args(func: F) -> F: + @wraps(func) + def wrapper(*args: Any, **kwargs: Any) -> Any: # noqa: ANN401 + def hiveint_to_int(value: Any) -> Any: # noqa: ANN401 + return int(value) if isinstance(value, HiveInt) else value + + new_args = tuple(hiveint_to_int(arg) for arg in args) + new_kwargs = {k: hiveint_to_int(v) for k, v in kwargs.items()} + + return func(*new_args, **new_kwargs) + + return cast("F", wrapper) + class HpAPRProtocol(Protocol): """ @@ -72,7 +93,7 @@ def to_python_json_asset(asset: Asset.AnyT) -> wax.python_json_asset: case "VESTS": return wax.vests(amount=int(asset.amount)) case _: - raise UnknownAssetTypeError(asset.nai) + raise UnknownAssetTypeError(asset.nai()) def __validate_wax_response(response: wax.python_result) -> None: @@ -83,10 +104,8 @@ def __validate_wax_response(response: wax.python_result) -> None: def __as_binary_json(item: OperationUnion | Transaction) -> bytes: from clive.__private.models import Transaction - if not isinstance(item, Transaction): - item = convert_to_representation(item) - - return item.json(by_alias=True).encode() + to_serialize = item if isinstance(item, Transaction) else convert_to_representation(item) + return to_serialize.json().encode() def validate_transaction(transaction: Transaction) -> None: @@ -139,6 +158,7 @@ def generate_private_key() -> PrivateKey: return PrivateKey(value=result.result.decode()) +@cast_hiveint_args def calculate_manabar_full_regeneration_time( now: int, max_mana: int, current_mana: int, last_update_time: int ) -> datetime.datetime: @@ -149,6 +169,7 @@ def calculate_manabar_full_regeneration_time( return datetime.datetime.fromtimestamp(int(result.result.decode()), tz=datetime.UTC) +@cast_hiveint_args def calculate_current_manabar_value(now: int, max_mana: int, current_mana: int, last_update_time: int) -> int: result = wax.calculate_current_manabar_value( now=now, max_mana=max_mana, current_mana=current_mana, last_update_time=last_update_time @@ -157,6 +178,7 @@ def calculate_current_manabar_value(now: int, max_mana: int, current_mana: int, return int(result.result.decode()) +@cast_hiveint_args def general_asset(asset_num: int, amount: int) -> Asset.AnyT: return from_python_json_asset(wax.general_asset(asset_num=asset_num, amount=amount)) @@ -165,22 +187,25 @@ def get_tapos_data(block_id: str) -> wax.python_ref_block_data: return wax.get_tapos_data(block_id.encode()) +@cast_hiveint_args def hive(amount: int) -> Asset.Hive: return cast("Asset.Hive", from_python_json_asset(wax.hive(amount))) +@cast_hiveint_args def hbd(amount: int) -> Asset.Hbd: return cast("Asset.Hbd", from_python_json_asset(wax.hbd(amount))) +@cast_hiveint_args def vests(amount: int) -> Asset.Vests: return cast("Asset.Vests", from_python_json_asset(wax.vests(amount))) def calculate_hp_apr(data: HpAPRProtocol) -> Decimal: result = wax.calculate_hp_apr( - head_block_num=data.head_block_number, - vesting_reward_percent=data.vesting_reward_percent, + head_block_num=int(data.head_block_number), + vesting_reward_percent=int(data.vesting_reward_percent), virtual_supply=to_python_json_asset(data.virtual_supply), total_vesting_fund_hive=to_python_json_asset(data.total_vesting_fund_hive), ) @@ -197,6 +222,7 @@ def calculate_hbd_to_hive(_hbd: Asset.Hbd, current_price_feed: PriceFeed) -> Ass return cast("Asset.Hive", from_python_json_asset(result)) +@cast_hiveint_args def calculate_vests_to_hp(_vests: int | Asset.Vests, data: TotalVestingProtocol) -> Asset.Hive: vests_json_asset = wax.vests(_vests) if isinstance(_vests, int) else to_python_json_asset(_vests) result = wax.calculate_vests_to_hp( @@ -216,12 +242,14 @@ def calculate_hp_to_vests(_hive: Asset.Hive, data: TotalVestingProtocol) -> Asse return cast("Asset.Vests", from_python_json_asset(result)) +@cast_hiveint_args def calculate_current_inflation_rate(head_block_num: int) -> Decimal: result = wax.calculate_inflation_rate_for_block(head_block_num) __validate_wax_response(result) return hive_percent_to_percent(result.result.decode()) +@cast_hiveint_args def calculate_witness_votes_hp(votes: int, data: TotalVestingProtocol) -> Asset.Hive: result = wax.calculate_witness_votes_hp( votes=votes, diff --git a/clive/__private/core/profile.py b/clive/__private/core/profile.py index 0ba405e09143ba97d0362b2533430d96ebcd4a08..9577713f9de8f63766a954e0ed3d5f892a24199d 100644 --- a/clive/__private/core/profile.py +++ b/clive/__private/core/profile.py @@ -9,10 +9,9 @@ from clive.__private.core.accounts.account_manager import AccountManager from clive.__private.core.constants.tui.themes import DEFAULT_THEME from clive.__private.core.formatters.humanize import humanize_validation_result from clive.__private.core.keys import KeyManager, PublicKeyAliased -from clive.__private.core.validate_schema_field import is_schema_field_valid from clive.__private.logger import logger from clive.__private.models import Transaction -from clive.__private.models.schemas import ChainId, OperationRepresentationUnion, OperationUnion +from clive.__private.models.schemas import ChainId, OperationRepresentationUnion, OperationUnion, is_matching_model from clive.__private.settings import safe_settings from clive.__private.storage.runtime_to_storage_converter import RuntimeToStorageConverter from clive.__private.storage.service import PersistentStorageService @@ -156,7 +155,7 @@ class Profile: Raises: InvalidChainIdError: If the format of the provided chain ID is wrong. """ - if not is_schema_field_valid(ChainId, value): + if not is_matching_model(value, ChainId): raise InvalidChainIdError self._chain_id = value diff --git a/clive/__private/core/validate_schema_field.py b/clive/__private/core/validate_schema_field.py deleted file mode 100644 index 2a2790e5432bba836db92703051fcfcd829827aa..0000000000000000000000000000000000000000 --- a/clive/__private/core/validate_schema_field.py +++ /dev/null @@ -1,35 +0,0 @@ -from __future__ import annotations - -from typing import Any - -from pydantic import BaseModel, ValidationError - - -def validate_schema_field(schema_field: type[Any], value: Any) -> None: # noqa: ANN401 - """ - Validate the given value against the given schema field e.g. one that inherits from pydantic.ConstrainedStr. - - For validating models use `pydantic.validate_model` instead. - - Args: - schema_field: The schema field type to validate against. - value: The value to validate. - - Raises: - pydantic.ValidationError: if the given value is invalid. - """ - - class Model(BaseModel): - value: schema_field # type: ignore[valid-type] - - Model.update_forward_refs(**locals()) - Model(value=value) - - -def is_schema_field_valid(schema_field: type[Any], value: Any) -> bool: # noqa: ANN401 - try: - validate_schema_field(schema_field, value) - except ValidationError: - return False - else: - return True diff --git a/clive/__private/models/asset.py b/clive/__private/models/asset.py index b09fa6f8d0e27b1b8eb91af37c043aa9d53a0883..56ec6326c10d74847a4f658594f59187a62f51bd 100644 --- a/clive/__private/models/asset.py +++ b/clive/__private/models/asset.py @@ -2,24 +2,22 @@ from __future__ import annotations import re from collections.abc import Callable +from dataclasses import dataclass from typing import TYPE_CHECKING, Generic, TypeAlias, TypeVar -from pydantic.generics import GenericModel - from clive.__private.core.decimal_conventer import ( DecimalConversionNotANumberError, DecimalConverter, DecimalConvertible, ) -from clive.__private.models.base import CliveBaseModel -from clive.__private.models.schemas import AssetHbdHF26, AssetHiveHF26, AssetVestsHF26 +from clive.__private.models.schemas import AssetHbd, AssetHive, AssetVests from clive.exceptions import CliveError if TYPE_CHECKING: from decimal import Decimal -AssetT = TypeVar("AssetT", bound=AssetHiveHF26 | AssetHbdHF26 | AssetVestsHF26) -AssetExplicitT = TypeVar("AssetExplicitT", AssetHiveHF26, AssetHbdHF26, AssetVestsHF26) +AssetT = TypeVar("AssetT", bound=AssetHive | AssetHbd | AssetVests) +AssetExplicitT = TypeVar("AssetExplicitT", AssetHive, AssetHbd, AssetVests) AssetAmount = DecimalConvertible AssetFactory = Callable[[AssetAmount], AssetT] @@ -58,7 +56,8 @@ class UnknownAssetNaiError(AssetError): super().__init__(message) -class AssetFactoryHolder(CliveBaseModel, GenericModel, Generic[AssetT]): +@dataclass(frozen=True) +class AssetFactoryHolder(Generic[AssetT]): """ Holds factory for asset. @@ -67,17 +66,14 @@ class AssetFactoryHolder(CliveBaseModel, GenericModel, Generic[AssetT]): asset_factory: Factory function to create an instance of the asset. """ - class Config: - frozen = True - asset_cls: type[AssetT] asset_factory: AssetFactory[AssetT] class Asset: - Hive: TypeAlias = AssetHiveHF26 # noqa: UP040 # used in isinstance check - Hbd: TypeAlias = AssetHbdHF26 # noqa: UP040 # used in isinstance check - Vests: TypeAlias = AssetVestsHF26 # noqa: UP040 # used in isinstance check + Hive: TypeAlias = AssetHive # noqa: UP040 # used in isinstance check + Hbd: TypeAlias = AssetHbd # noqa: UP040 # used in isinstance check + Vests: TypeAlias = AssetVests # noqa: UP040 # used in isinstance check LiquidT: TypeAlias = Hive | Hbd # noqa: UP040 # used in isinstance check VotingT: TypeAlias = Hive | Vests # noqa: UP040 # used in isinstance check AnyT: TypeAlias = Hive | Hbd | Vests # noqa: UP040 # used in isinstance check @@ -200,7 +196,7 @@ class Asset: @classmethod def pretty_amount(cls, asset: Asset.AnyT) -> str: - return f"{int(asset.amount) / 10**asset.precision:.{asset.precision}f}" + return f"{int(asset.amount) / 10 ** asset.precision():.{asset.precision()}f}" @classmethod def as_decimal(cls, asset: Asset.AnyT) -> Decimal: diff --git a/clive/__private/models/base.py b/clive/__private/models/base.py deleted file mode 100644 index e3f78c88475276607f4cd735feafd6d44c79e21c..0000000000000000000000000000000000000000 --- a/clive/__private/models/base.py +++ /dev/null @@ -1,73 +0,0 @@ -from __future__ import annotations - -from datetime import datetime -from typing import TYPE_CHECKING, Any - -from pydantic import BaseModel - -from clive.__private.core.constants.date import TIME_FORMAT_WITH_SECONDS -from clive.__private.models.schemas import Serializable - -if TYPE_CHECKING: - from collections.abc import Callable - - from pydantic.typing import AbstractSetIntStr, DictStrAny, MappingIntStrAny - - -class CliveBaseModel(BaseModel): - class Config: - allow_population_by_field_name = True - json_encoders = { # noqa: RUF012 # pydantic convention - datetime: lambda d: d.strftime(TIME_FORMAT_WITH_SECONDS), - Serializable: lambda x: x.serialize(), - } - - def json( # noqa: PLR0913 - self, - *, - include: AbstractSetIntStr | MappingIntStrAny | None = None, - exclude: AbstractSetIntStr | MappingIntStrAny | None = None, - by_alias: bool = True, # modified, most of the time we want to dump by alias - skip_defaults: bool | None = None, - exclude_unset: bool = False, - exclude_defaults: bool = False, - exclude_none: bool = False, - encoder: Callable[[Any], Any] | None = None, - models_as_dict: bool = True, - ensure_ascii: bool = False, # modified, so unicode characters are not escaped, will properly dump e.g. polish characters # noqa: E501 - **dumps_kwargs: Any, - ) -> str: - return super().json( - include=include, - exclude=exclude, - by_alias=by_alias, - skip_defaults=skip_defaults, - exclude_unset=exclude_unset, - exclude_defaults=exclude_defaults, - exclude_none=exclude_none, - encoder=encoder, - models_as_dict=models_as_dict, - ensure_ascii=ensure_ascii, - **dumps_kwargs, - ) - - def dict( # noqa: PLR0913 - self, - *, - include: AbstractSetIntStr | MappingIntStrAny | None = None, - exclude: AbstractSetIntStr | MappingIntStrAny | None = None, - by_alias: bool = True, # modified, most of the time we want to dump by alias - skip_defaults: bool | None = None, - exclude_unset: bool = False, - exclude_defaults: bool = False, - exclude_none: bool = False, - ) -> DictStrAny: - return super().dict( - include=include, - exclude=exclude, - by_alias=by_alias, - skip_defaults=skip_defaults, - exclude_unset=exclude_unset, - exclude_defaults=exclude_defaults, - exclude_none=exclude_none, - ) diff --git a/clive/__private/models/schemas.py b/clive/__private/models/schemas.py index 70b6ca6b4ef9b253789d8a296866cd598f86a9ff..221b4c6d32ea469367701018b1d85afa844b0bf1 100644 --- a/clive/__private/models/schemas.py +++ b/clive/__private/models/schemas.py @@ -16,9 +16,8 @@ has unnecessary "Fundament" suffix, and is not specialized with HF26 assets. from __future__ import annotations -from schemas._operation_objects import Hf26ApiOperationObject, Hf26ApiVirtualOperationObject -from schemas.apis.account_history_api import EnumVirtualOps, GetAccountHistory, GetOpsInBlock -from schemas.apis.account_history_api.response_schemas import GetTransaction +from schemas._preconfigured_base_model import PreconfiguredBaseModel +from schemas.apis.account_history_api import GetAccountHistory from schemas.apis.database_api import ( FindAccounts, FindProposals, @@ -46,7 +45,6 @@ from schemas.apis.database_api.fundaments_of_reponses import ( FindRecurrentTransfersFundament, ListChangeRecoveryAccountRequestsFundament, ListDeclineVotingRightsRequestsFundament, - OwnerHistoriesFundament, SavingsWithdrawalsFundament, VestingDelegationExpirationsFundament, VestingDelegationsFundament, @@ -54,13 +52,12 @@ from schemas.apis.database_api.fundaments_of_reponses import ( WitnessesFundament, ) from schemas.apis.rc_api import FindRcAccounts as SchemasFindRcAccounts -from schemas.apis.rc_api import GetResourceParams, GetResourcePool, ListRcDirectDelegations -from schemas.apis.rc_api import ListRcAccounts as SchemasListRcAccounts from schemas.apis.rc_api.fundaments_of_responses import RcAccount as SchemasRcAccount -from schemas.apis.reputation_api import GetAccountReputations from schemas.apis.transaction_status_api import FindTransaction -from schemas.fields.assets import AssetHbdHF26, AssetHiveHF26, AssetVestsHF26 -from schemas.fields.assets._base import AssetBase +from schemas.base import field +from schemas.decoders import is_matching_model, validate_schema_field +from schemas.errors import DecodeError, ValidationError +from schemas.fields.assets import AssetHbd, AssetHive, AssetVests from schemas.fields.basic import AccountName, PublicKey from schemas.fields.compound import Authority, Manabar, Price from schemas.fields.compound import HbdExchangeRate as SchemasHbdExchangeRate @@ -68,11 +65,8 @@ from schemas.fields.compound import Proposal as SchemasProposal from schemas.fields.hex import Sha256, Signature, TransactionId from schemas.fields.hive_datetime import HiveDateTime from schemas.fields.hive_int import HiveInt -from schemas.fields.serializable import Serializable -from schemas.jsonrpc import ExpectResultT as JSONRPCExpectResultT -from schemas.jsonrpc import JSONRPCRequest as SchemasJSONRPCRequest -from schemas.jsonrpc import JSONRPCResult -from schemas.jsonrpc import get_response_model as schemas_get_response_model +from schemas.fields.integers import Uint16t, Uint32t +from schemas.fields.resolvables import JsonString from schemas.operation import Operation from schemas.operations import ( AccountCreateOperation, @@ -81,7 +75,6 @@ from schemas.operations import ( AccountUpdateOperation, AccountWitnessProxyOperation, AccountWitnessVoteOperation, - AnyOperation, CancelTransferFromSavingsOperation, ChangeRecoveryAccountOperation, ClaimAccountOperation, @@ -103,6 +96,8 @@ from schemas.operations import ( EscrowReleaseOperation, EscrowTransferOperation, FeedPublishOperation, + Hf26OperationRepresentation, + Hf26Operations, LimitOrderCancelOperation, LimitOrderCreate2Operation, LimitOrderCreateOperation, @@ -125,30 +120,26 @@ from schemas.operations import ( WitnessBlockApproveOperation, WitnessSetPropertiesOperation, WitnessUpdateOperation, + convert_to_representation, ) from schemas.operations.extensions.recurrent_transfer_extensions import RecurrentTransferPairId +from schemas.operations.extensions.representation_types import ( + HF26RepresentationRecurrentTransferPairIdOperationExtension, +) from schemas.operations.recurrent_transfer_operation import RecurrentTransferOperation -from schemas.operations.representation_types import Hf26OperationRepresentationType -from schemas.operations.representations import convert_to_representation -from schemas.operations.representations.hf26_representation import HF26Representation -from schemas.operations.virtual import AnyVirtualOperation -from schemas.operations.virtual.representation_types import Hf26VirtualOperationRepresentationType -from schemas.policies import ExtraFields, MissingFieldsInGetConfig, Policy, set_policies +from schemas.policies import ( + ExtraFieldsPolicy, + MissingFieldsInGetConfigPolicy, + TestnetAssetsPolicy, + set_policies, +) from schemas.transaction import Transaction -from schemas.virtual_operation import VirtualOperation __all__ = [ # noqa: RUF022 # operation BASIC aliases - "ApiOperationObject", "OperationBase", - "OperationRepresentationBase", "OperationRepresentationUnion", "OperationUnion", - # virtual operation BASIC aliases - "ApiVirtualOperationObject", - "VirtualOperationBase", - "VirtualOperationRepresentationUnion", - "VirtualOperationUnion", # list API responses (have nested list property which stores actual model) "ListChangeRecoveryAccountRequests", "ListDeclineVotingRightsRequests", @@ -156,7 +147,6 @@ __all__ = [ # noqa: RUF022 "ListProposalVotes", "ListWitnesses", "ListWitnessVotes", - "ListRcDirectDelegations", "ListWithdrawVestingRoutes", # find API response aliases (have nested list property which stores actual model) "FindAccounts", @@ -169,15 +159,11 @@ __all__ = [ # noqa: RUF022 "FindWitnesses", # get API responses (have unnecessary nested property which stores actual model) "GetAccountHistory", - "GetAccountReputations", - "GetOperationsInBlock", - "GetResourcePool", # get API responses (have no unnecessary nested properties, just the model itself) "Config", "DynamicGlobalProperties", "FeedHistory", "HardforkProperties", - "ResourceParams", "Version", "WitnessSchedule", # operations @@ -235,81 +221,63 @@ __all__ = [ # noqa: RUF022 "RecurrentTransferPairIdExtension", "RecurrentTransferPairIdRepresentation", # assets - "AssetBase", - "AssetHbdHF26", - "AssetHiveHF26", - "AssetVestsHF26", + "AssetHbd", + "AssetHive", + "AssetVests", # basic fields "AccountName", "ChainId", "HiveDateTime", "HiveInt", + "JsonString", "PublicKey", "Signature", "TransactionId", + "Uint16t", + "Uint32t", # compound models "Account", "Authority", "ChangeRecoveryAccountRequest", "DeclineVotingRightsRequest", - "EnumeratedVirtualOperations", "HbdExchangeRate", "Manabar", - "OwnerHistory", "PriceFeed", "Proposal", "RcAccount", "RecurrentTransfer", "SavingsWithdrawal", "Transaction", - "TransactionInBlockchain", "TransactionStatus", "VestingDelegation", "VestingDelegationExpiration", "WithdrawRoute", + "Witness", # policies "ExtraFieldsPolicy", - "JSONRPCExpectResultT", - "JSONRPCResult", "MissingFieldsInGetConfigPolicy", - "Policy", + "TestnetAssetsPolicy", "set_policies", - # jsonrpc - "get_response_model", - "JSONRPCRequest", - "Witness", + # exceptions + "DecodeError", + "ValidationError", # other + "PreconfiguredBaseModel", "convert_to_representation", - "RepresentationBase", - "Serializable", + "is_matching_model", + "validate_schema_field", + "field", ] # operation BASIC aliases -ApiOperationObject = Hf26ApiOperationObject OperationBase = Operation -OperationRepresentationBase = HF26Representation[OperationBase] -OperationRepresentationUnion = Hf26OperationRepresentationType -OperationUnion = AnyOperation - -# virtual operation BASIC aliases - -ApiVirtualOperationObject = Hf26ApiVirtualOperationObject -VirtualOperationBase = VirtualOperation -VirtualOperationRepresentationUnion = Hf26VirtualOperationRepresentationType -VirtualOperationUnion = AnyVirtualOperation - -# list API responses (have nested list property which stores actual model) - -ListRcAccounts = SchemasListRcAccounts[AssetVestsHF26] +OperationRepresentationUnion = Hf26OperationRepresentation +OperationUnion = Hf26Operations # find API response aliases (have nested list property which stores actual model) -FindRcAccounts = SchemasFindRcAccounts[AssetVestsHF26] - -# get API responses (have unnecessary nested property which stores actual model) - -GetOperationsInBlock = GetOpsInBlock +FindRcAccounts = SchemasFindRcAccounts # get API responses (have no unnecessary nested properties, just the model itself) @@ -317,14 +285,13 @@ Config = GetConfig DynamicGlobalProperties = GetDynamicGlobalProperties FeedHistory = GetFeedHistory HardforkProperties = GetHardforkProperties -ResourceParams = GetResourceParams Version = GetVersion WitnessSchedule = GetWitnessSchedule # extensions RecurrentTransferPairIdExtension = RecurrentTransferPairId -RecurrentTransferPairIdRepresentation = HF26Representation[RecurrentTransferPairIdExtension] +RecurrentTransferPairIdRepresentation = HF26RepresentationRecurrentTransferPairIdOperationExtension # basic fields @@ -332,34 +299,17 @@ ChainId = Sha256 # compound models -Account = AccountItemFundament[AssetHiveHF26, AssetHbdHF26, AssetVestsHF26] +Account = AccountItemFundament ChangeRecoveryAccountRequest = ListChangeRecoveryAccountRequestsFundament DeclineVotingRightsRequest = ListDeclineVotingRightsRequestsFundament -EnumeratedVirtualOperations = EnumVirtualOps -HbdExchangeRate = SchemasHbdExchangeRate[AssetHiveHF26, AssetHbdHF26] -OwnerHistory = OwnerHistoriesFundament -PriceFeed = Price[AssetHiveHF26, AssetHbdHF26, AssetVestsHF26] -Proposal = SchemasProposal[AssetHbdHF26] -RcAccount = SchemasRcAccount[AssetVestsHF26] -RecurrentTransfer = FindRecurrentTransfersFundament[AssetHiveHF26, AssetHbdHF26] -SavingsWithdrawal = SavingsWithdrawalsFundament[AssetHiveHF26, AssetHbdHF26] -TransactionInBlockchain = GetTransaction +HbdExchangeRate = SchemasHbdExchangeRate +PriceFeed = Price +Proposal = SchemasProposal +RcAccount = SchemasRcAccount +RecurrentTransfer = FindRecurrentTransfersFundament +SavingsWithdrawal = SavingsWithdrawalsFundament TransactionStatus = FindTransaction VestingDelegation = VestingDelegationsFundament -VestingDelegationExpiration = VestingDelegationExpirationsFundament[AssetVestsHF26] +VestingDelegationExpiration = VestingDelegationExpirationsFundament WithdrawRoute = WithdrawVestingRoutesFundament -Witness = WitnessesFundament[AssetHiveHF26, AssetHbdHF26] - -# policies - -ExtraFieldsPolicy = ExtraFields -MissingFieldsInGetConfigPolicy = MissingFieldsInGetConfig - -# jsonrpc - -get_response_model = schemas_get_response_model -JSONRPCRequest = SchemasJSONRPCRequest - -# other - -RepresentationBase = HF26Representation +Witness = WitnessesFundament diff --git a/clive/__private/models/transaction.py b/clive/__private/models/transaction.py index ef9c7d33a1e34bf1a2d7073e6100de5825f2fa23..596f28e30df64ffbaa824e8a803002f54f78aa9c 100644 --- a/clive/__private/models/transaction.py +++ b/clive/__private/models/transaction.py @@ -4,16 +4,16 @@ from collections.abc import Iterable from datetime import timedelta from typing import TYPE_CHECKING, Any -from pydantic import Field, validator - from clive.__private.models.schemas import ( HiveDateTime, - HiveInt, OperationRepresentationUnion, OperationUnion, Signature, TransactionId, + Uint16t, + Uint32t, convert_to_representation, + field, ) from clive.__private.models.schemas import Transaction as SchemasTransaction @@ -25,23 +25,23 @@ if TYPE_CHECKING: class Transaction(SchemasTransaction): - operations: list[OperationRepresentationUnion] = Field(default_factory=list) - ref_block_num: HiveInt = Field(default_factory=lambda: HiveInt(-1)) - ref_block_prefix: HiveInt = Field(default_factory=lambda: HiveInt(-1)) - expiration: HiveDateTime = Field(default_factory=lambda: HiveDateTime.now() + timedelta(minutes=30)) - extensions: list[Any] = Field(default_factory=list) - signatures: list[Signature] = Field(default_factory=list) + operations: list[OperationRepresentationUnion] = [] # noqa: RUF012 + ref_block_num: Uint16t = 0 + ref_block_prefix: Uint32t = 0 + expiration: HiveDateTime = field(default_factory=lambda: HiveDateTime.now() + timedelta(minutes=30)) + extensions: list[Any] = [] # noqa: RUF012 + signatures: list[Signature] = [] # noqa: RUF012 def __bool__(self) -> bool: """Return True when there are any operations.""" return bool(self.operations) - def __contains__(self, operation: OperationRepresentationUnion | OperationUnion) -> bool: + def __contains__(self, operation: OperationRepresentationUnion | OperationUnion) -> bool: # type: ignore[override] if isinstance(operation, OperationUnion): return operation in self.operations_models return operation in self.operations - def __iter__(self) -> Iterator[OperationUnion]: # type: ignore[override] + def __iter__(self) -> Iterator[OperationUnion]: return iter(self.operations_models) def __len__(self) -> int: @@ -58,9 +58,8 @@ class Transaction(SchemasTransaction): @property def operations_models(self) -> list[OperationUnion]: """Get only the operation models from already stored operations representations.""" - return [op.value for op in self.operations] # type: ignore[attr-defined] + return [op.value for op in self.operations] - @validator("operations", pre=True) @classmethod def convert_operations(cls, value: Any) -> list[OperationRepresentationUnion]: # noqa: ANN401 assert isinstance(value, Iterable) @@ -72,7 +71,7 @@ class Transaction(SchemasTransaction): def remove_operation(self, *operations: OperationUnion) -> None: for op in self.operations: - if op.value in operations: # type: ignore[attr-defined] + if op.value in operations: self.operations.remove(op) return @@ -83,8 +82,8 @@ class Transaction(SchemasTransaction): def reset(self) -> None: self.operations = [] - self.ref_block_num = HiveInt(-1) - self.ref_block_prefix = HiveInt(-1) + self.ref_block_num = Uint16t(0) + self.ref_block_prefix = Uint32t(0) self.expiration = HiveDateTime.now() + timedelta(minutes=30) self.extensions = [] self.signatures = [] @@ -96,7 +95,7 @@ class Transaction(SchemasTransaction): self.signatures.clear() def with_hash(self) -> TransactionWithHash: - return TransactionWithHash(**self.dict(by_alias=True), transaction_id=self.calculate_transaction_id()) + return TransactionWithHash(**self.dict(), transaction_id=self.calculate_transaction_id()) def accept(self, visitor: OperationVisitor) -> None: """ @@ -141,5 +140,5 @@ class Transaction(SchemasTransaction): return visitor.get_unknown_accounts(already_known_accounts) -class TransactionWithHash(Transaction): +class TransactionWithHash(Transaction, kw_only=True): transaction_id: TransactionId diff --git a/clive/__private/settings/_safe_settings.py b/clive/__private/settings/_safe_settings.py index 38e4eca063e6716273b9e558127c81a65134003b..7612bed4ce8d212d04f92f4693678021be227293 100644 --- a/clive/__private/settings/_safe_settings.py +++ b/clive/__private/settings/_safe_settings.py @@ -324,8 +324,7 @@ class SafeSettings: return remote_handle_settings def _get_node_chain_id(self) -> str | None: - from clive.__private.core.validate_schema_field import is_schema_field_valid - from clive.__private.models.schemas import ChainId + from clive.__private.models.schemas import ChainId, is_matching_model setting_name = NODE_CHAIN_ID value = self._parent._get_value_from_settings(setting_name, "") @@ -335,8 +334,8 @@ class SafeSettings: self._parent._assert_is_string(setting_name, value=value) value_ = cast("str", value) - if not is_schema_field_valid(ChainId, value_): - details = f"Chain ID should be {ChainId.max_length} characters long." + if not is_matching_model(value_, ChainId): + details = f"Chain ID should be {ChainId.meta().max_length} characters long." # type: ignore[attr-defined] raise SettingsValueError(setting_name=setting_name, value=value_, details=details) return value_ diff --git a/clive/__private/storage/current_model.py b/clive/__private/storage/current_model.py index ba1d424f041a3f5e126b51d7808ef178dfda154e..99f8a2c7a7372d5c5cfe9a48c660fbc3581b1291 100644 --- a/clive/__private/storage/current_model.py +++ b/clive/__private/storage/current_model.py @@ -14,4 +14,5 @@ def _validate_current_model_alias() -> None: ) +ProfileStorageBase.gather() _validate_current_model_alias() diff --git a/clive/__private/storage/migrations/base.py b/clive/__private/storage/migrations/base.py index e0c586cefd8c17be8b17c622d72a414c573538be..525897f1b5371775d69fcfcbc766336b4c4f2004 100644 --- a/clive/__private/storage/migrations/base.py +++ b/clive/__private/storage/migrations/base.py @@ -1,10 +1,10 @@ from __future__ import annotations -from abc import ABC +import json from hashlib import sha256 from typing import Any, ClassVar, Self, get_type_hints -from clive.__private.models.base import CliveBaseModel +from clive.__private.models.schemas import PreconfiguredBaseModel from clive.exceptions import CliveError type Revision = str @@ -23,22 +23,57 @@ class StorageVersionNotFoundError(CliveError): super().__init__(self.message) -class ProfileStorageBase(CliveBaseModel, ABC): +class ProfileStorageBase(PreconfiguredBaseModel): _REVISIONS: ClassVar[list[Revision]] = [] _REVISION_TO_MODEL_TYPE_MAP: ClassVar[dict[Revision, type[ProfileStorageBase]]] = {} + _REGISTERED_MODELS: ClassVar[list[type[ProfileStorageBase]]] = [] _REVISION_NONCE: ClassVar[int] = 0 def __init_subclass__(cls, *args: Any, **kwargs: Any) -> None: super().__init_subclass__(*args, **kwargs) - revision = cls.get_this_revision() - assert revision not in cls._get_revisions(), f"Revision: {revision} already exists." - cls._REVISIONS.append(revision) - cls._REVISION_TO_MODEL_TYPE_MAP[revision] = cls - cls._validate_upgrade_definition() + cls._REGISTERED_MODELS.append(cls) + + @classmethod + def gather(cls) -> None: + for class_ in cls._REGISTERED_MODELS: + revision = class_.get_this_revision() + assert revision not in cls._get_revisions(), f"Revision: {revision} already exists." + class_._REVISIONS.append(revision) + class_._REVISION_TO_MODEL_TYPE_MAP[revision] = class_ + class_._validate_upgrade_definition() def __hash__(self) -> int: - return hash(self.json(indent=4)) + return hash(self.json(order="deterministic")) + + @classmethod + def create(cls, raw: str) -> Self: + """ + Create a new model instance from data stored on the disk. + + It handles any preprocessing before parsing it into the model. + + Args: + raw: A profile data stored on disk. + + Returns: + A new instance of the model class initialized with the provided data. + """ + data = cls._preprocess_data(json.loads(raw)) + return cls.parse_builtins(data) + + @classmethod + def _preprocess_data(cls, data: dict[str, Any]) -> dict[str, Any]: + """ + Override to preprocess data before parsing it into the model. + + Args: + data: A mapping containing the raw profile data that will be later transformed into the model. + + Returns: + Data that will be used to initialize the model instance. + """ + return data @classmethod def upgrade(cls, old: ProfileStorageBase) -> Self: @@ -93,7 +128,7 @@ class ProfileStorageBase(CliveBaseModel, ABC): @classmethod def _get_revision_seed(cls) -> str: - return cls.schema_json(indent=4) + str(cls._REVISION_NONCE) + return cls.schema_json() + str(cls._REVISION_NONCE) @classmethod def _validate_upgrade_definition(cls) -> None: @@ -114,3 +149,13 @@ class ProfileStorageBase(CliveBaseModel, ABC): assert return_param is Self, ( f"Upgrade function of {cls} should return {Self}, but it returns {return_param} instead." ) + + +class AlarmStorageModelBase(PreconfiguredBaseModel, tag_field="name", kw_only=True): + @classmethod + def get_name(cls) -> str: + assert isinstance(cls.__struct_config__.tag, str), "Alarm storage models must have a string tag." + return cls.__struct_config__.tag + + is_harmless: bool = False + """Identifies the occurrence of specific alarm among other possible alarms of same type. E.g. end date.""" diff --git a/clive/__private/storage/migrations/v0.py b/clive/__private/storage/migrations/v0.py index 3dc1dfd44036caf56392245c5510756ffa6ab88a..46e0ce3386ac0f66ae4e362f562b63730a460da6 100644 --- a/clive/__private/storage/migrations/v0.py +++ b/clive/__private/storage/migrations/v0.py @@ -2,68 +2,29 @@ from __future__ import annotations from collections.abc import Sequence # noqa: TC003 from pathlib import Path # noqa: TC003 -from typing import Any, Self, TypeAlias - +from typing import Any, Self + +from clive.__private.core.alarms.specific_alarms import ( + ChangingRecoveryAccountInProgress, + DecliningVotingRightsInProgress, + GovernanceNoActiveVotes, + GovernanceVotingExpiration, + RecoveryAccountWarningListed, +) from clive.__private.core.date_utils import utc_epoch -from clive.__private.models.base import CliveBaseModel from clive.__private.models.schemas import ( HiveDateTime, - HiveInt, OperationRepresentationUnion, + PreconfiguredBaseModel, Signature, + Uint16t, + Uint32t, + field, ) -from clive.__private.storage.migrations.base import ProfileStorageBase - - -class DateTimeAlarmIdentifierStorageModel(CliveBaseModel): - value: HiveDateTime - - -class RecoveryAccountWarningListedAlarmIdentifierStorageModel(CliveBaseModel): - recovery_account: str - - -AllAlarmIdentifiersStorageModel = ( - DateTimeAlarmIdentifierStorageModel | RecoveryAccountWarningListedAlarmIdentifierStorageModel -) +from clive.__private.storage.migrations.base import AlarmStorageModelBase, ProfileStorageBase -class AlarmStorageModel(CliveBaseModel): - name: str - is_harmless: bool = False - identifier: AllAlarmIdentifiersStorageModel - """Identifies the occurrence of specific alarm among other possible alarms of same type. E.g. end date.""" - - -class TrackedAccountStorageModel(CliveBaseModel): - name: str - alarms: Sequence[AlarmStorageModel] = [] - - -class KeyAliasStorageModel(CliveBaseModel): - alias: str - public_key: str - - -class TransactionCoreStorageModel(CliveBaseModel): - operations: list[OperationRepresentationUnion] = [] # noqa: RUF012 - ref_block_num: HiveInt = HiveInt(-1) - ref_block_prefix: HiveInt = HiveInt(-1) - expiration: HiveDateTime = utc_epoch() # type: ignore[assignment] - extensions: list[Any] = [] # noqa: RUF012 - signatures: list[Signature] = [] # noqa: RUF012 - - @classmethod - def __modify_schema__(cls, field_schema: dict[str, Any]) -> None: - field_schema.update({"type": "object", "description": "This should not be included in revision calculation"}) - - -class TransactionStorageModel(CliveBaseModel): - transaction_core: TransactionCoreStorageModel - transaction_file_path: Path | None = None - - -class ProfileStorageModel(ProfileStorageBase): +class ProfileStorageModel(ProfileStorageBase, kw_only=True): name: str working_account: str | None = None tracked_accounts: Sequence[TrackedAccountStorageModel] = [] @@ -74,17 +35,99 @@ class ProfileStorageModel(ProfileStorageBase): node_address: str should_enable_known_accounts: bool = True - _AlarmStorageModel: TypeAlias = AlarmStorageModel # noqa: UP040 - _TrackedAccountStorageModel: TypeAlias = TrackedAccountStorageModel # noqa: UP040 - _KeyAliasStorageModel: TypeAlias = KeyAliasStorageModel # noqa: UP040 - _TransactionCoreStorageModel: TypeAlias = TransactionCoreStorageModel # noqa: UP040 - _TransactionStorageModel: TypeAlias = TransactionStorageModel # noqa: UP040 - _DateTimeAlarmIdentifierStorageModel: TypeAlias = DateTimeAlarmIdentifierStorageModel # noqa: UP040 - _RecoveryAccountWarningListedAlarmIdentifierStorageModel: TypeAlias = ( # noqa: UP040 - RecoveryAccountWarningListedAlarmIdentifierStorageModel + class DateTimeAlarmIdentifierStorageModel(PreconfiguredBaseModel): + value: HiveDateTime + + class RecoveryAccountWarningListedAlarmIdentifierStorageModel(PreconfiguredBaseModel): + recovery_account: str + + AllAlarmIdentifiersStorageModel = ( + DateTimeAlarmIdentifierStorageModel | RecoveryAccountWarningListedAlarmIdentifierStorageModel ) - _AllAlarmIdentifiersStorageModel: TypeAlias = AllAlarmIdentifiersStorageModel # noqa: UP040 + + class RecoveryAccountWarningListedStorageModel( + AlarmStorageModelBase, tag=RecoveryAccountWarningListed.get_name(), kw_only=True + ): + identifier: ProfileStorageModel.RecoveryAccountWarningListedAlarmIdentifierStorageModel + + class GovernanceVotingExpirationStorageModel( + AlarmStorageModelBase, tag=GovernanceVotingExpiration.get_name(), kw_only=True + ): + identifier: ProfileStorageModel.DateTimeAlarmIdentifierStorageModel + + class GovernanceNoActiveVotesStorageModel( + AlarmStorageModelBase, tag=GovernanceNoActiveVotes.get_name(), kw_only=True + ): + identifier: ProfileStorageModel.DateTimeAlarmIdentifierStorageModel + + class DecliningVotingRightsInProgressStorageModel( + AlarmStorageModelBase, tag=DecliningVotingRightsInProgress.get_name(), kw_only=True + ): + identifier: ProfileStorageModel.DateTimeAlarmIdentifierStorageModel + + class ChangingRecoveryAccountInProgressStorageModel( + AlarmStorageModelBase, tag=ChangingRecoveryAccountInProgress.get_name(), kw_only=True + ): + identifier: ProfileStorageModel.DateTimeAlarmIdentifierStorageModel + + AllAlarmStorageModel = ( + RecoveryAccountWarningListedStorageModel + | GovernanceVotingExpirationStorageModel + | GovernanceNoActiveVotesStorageModel + | DecliningVotingRightsInProgressStorageModel + | ChangingRecoveryAccountInProgressStorageModel + ) + + class TrackedAccountStorageModel(PreconfiguredBaseModel): + name: str + alarms: Sequence[ProfileStorageModel.AllAlarmStorageModel] = [] + + class KeyAliasStorageModel(PreconfiguredBaseModel): + alias: str + public_key: str + + class TransactionCoreStorageModel(PreconfiguredBaseModel): + operations: list[OperationRepresentationUnion] = [] # noqa: RUF012 + ref_block_num: Uint16t = 0 + ref_block_prefix: Uint32t = 0 + expiration: HiveDateTime = field(default_factory=lambda: HiveDateTime(utc_epoch())) + extensions: list[Any] = [] # noqa: RUF012 + signatures: list[Signature] = [] # noqa: RUF012 + + class TransactionStorageModel(PreconfiguredBaseModel): + transaction_core: ProfileStorageModel.TransactionCoreStorageModel + transaction_file_path: Path | None = None @classmethod def upgrade(cls, old: ProfileStorageBase) -> Self: raise NotImplementedError("Upgrade is not not possible for first revision.") + + @classmethod + def _preprocess_data(cls, data: dict[str, Any]) -> dict[str, Any]: + cls._ensure_non_negative_tapos_fields(data) + return data + + @staticmethod + def _ensure_non_negative_tapos_fields(data: dict[str, Any]) -> None: + """ + Ensure that specific TAPoS fields in the transaction data are non-negative. + + This method modifies the data loaded from a disk to ensure that the specified TAPoS + fields, namely 'ref_block_num' and 'ref_block_prefix' within the transaction, are non-negative. + If the fields are absent or their values are negative, they are set to `0`. + + This method exists because in previous versions of storage we included `-1` + by default. + + Args: + data: The profile storage data stored on the disk. + """ + transaction: dict[str, Any] | None = data.get("transaction", {}) + if transaction is None: + # transaction can be also none, so we skip in that case + return + + transaction_core = transaction.get("transaction_core", {}) + for field_ in ("ref_block_num", "ref_block_prefix"): + value = transaction_core.get(field_, 0) + transaction_core[field_] = max(value, 0) diff --git a/clive/__private/storage/migrations/v1.py b/clive/__private/storage/migrations/v1.py index 5c802c6d57cde01fc756e9febd22af91d3e72d54..c5cd69b24689a587546fd9e94633bd1c754fa880 100644 --- a/clive/__private/storage/migrations/v1.py +++ b/clive/__private/storage/migrations/v1.py @@ -1,34 +1,26 @@ from __future__ import annotations from pathlib import Path # noqa: TC003 -from typing import Self, TypeAlias +from typing import Self -from clive.__private.models.base import CliveBaseModel -from clive.__private.models.schemas import Transaction as SchemasTransaction +from clive.__private.models.schemas import PreconfiguredBaseModel, Transaction from clive.__private.storage.migrations import v0 -class Transaction(SchemasTransaction): - __modify_schema__ = v0.TransactionCoreStorageModel.__modify_schema__ - - -class TransactionStorageModel(CliveBaseModel): - transaction_core: Transaction - transaction_file_path: Path | None = None - - class ProfileStorageModel(v0.ProfileStorageModel): _REVISION_NONCE = 1 transaction: TransactionStorageModel | None = None # type: ignore[assignment] # changed storage model - _TransactionStorageModel: TypeAlias = TransactionStorageModel # noqa: UP040 + class TransactionStorageModel(PreconfiguredBaseModel): + transaction_core: Transaction + transaction_file_path: Path | None = None @classmethod def upgrade(cls, old: v0.ProfileStorageModel) -> Self: # type: ignore[override] # should always take previous model old_transaction = old.transaction new_transaction = ( - TransactionStorageModel( + cls.TransactionStorageModel( transaction_core=Transaction( **old_transaction.transaction_core.dict(), ), diff --git a/clive/__private/storage/runtime_to_storage_converter.py b/clive/__private/storage/runtime_to_storage_converter.py index fdb184780eb64480343732148f63639cf4df760b..2a0d5fb483a7ac2897eac11941984dc0c4276d6f 100644 --- a/clive/__private/storage/runtime_to_storage_converter.py +++ b/clive/__private/storage/runtime_to_storage_converter.py @@ -1,7 +1,7 @@ from __future__ import annotations from copy import deepcopy -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, get_args from clive.__private.core.alarms.alarm_identifier import DateTimeAlarmIdentifier from clive.__private.core.alarms.specific_alarms.recovery_account_warning_listed import ( @@ -45,16 +45,16 @@ class RuntimeToStorageConverter: profile = self._profile return profile.accounts.working.name if profile.accounts.has_working_account else None - def _tracked_accounts_to_model_container(self) -> list[ProfileStorageModel._TrackedAccountStorageModel]: + def _tracked_accounts_to_model_container(self) -> list[ProfileStorageModel.TrackedAccountStorageModel]: return [self._tracked_account_to_model(account) for account in self._profile.accounts.tracked] def _known_accounts_to_model_container(self) -> list[str]: return [account.name for account in self._profile.accounts.known] - def _key_aliases_to_model_container(self) -> list[ProfileStorageModel._KeyAliasStorageModel]: + def _key_aliases_to_model_container(self) -> list[ProfileStorageModel.KeyAliasStorageModel]: return [self._key_alias_to_model(key) for key in self._profile.keys] - def _transaction_to_model(self) -> ProfileStorageModel._TransactionStorageModel: + def _transaction_to_model(self) -> ProfileStorageModel.TransactionStorageModel: transaction_core = Transaction( operations=deepcopy(self._profile.operation_representations), ref_block_num=self._profile.transaction.ref_block_num, @@ -63,31 +63,39 @@ class RuntimeToStorageConverter: extensions=deepcopy(self._profile.transaction.extensions), signatures=deepcopy(self._profile.transaction.signatures), ) - return ProfileStorageModel._TransactionStorageModel( + return ProfileStorageModel.TransactionStorageModel( transaction_core=transaction_core, transaction_file_path=self._profile.transaction_file_path ) - def _tracked_account_to_model(self, account: TrackedAccount) -> ProfileStorageModel._TrackedAccountStorageModel: + def _tracked_account_to_model(self, account: TrackedAccount) -> ProfileStorageModel.TrackedAccountStorageModel: alarms = [self._alarm_to_model(alarm) for alarm in account._alarms.all_alarms if alarm.has_identifier] - return ProfileStorageModel._TrackedAccountStorageModel(name=account.name, alarms=alarms) + return ProfileStorageModel.TrackedAccountStorageModel(name=account.name, alarms=alarms) - def _alarm_to_model(self, alarm: AnyAlarm) -> ProfileStorageModel._AlarmStorageModel: - return ProfileStorageModel._AlarmStorageModel( - name=alarm.get_name(), + def _alarm_to_model(self, alarm: AnyAlarm) -> ProfileStorageModel.AllAlarmStorageModel: + alarm_cls = self._get_alarm_storage_model_cls_by_name(alarm.get_name()) + return alarm_cls( is_harmless=alarm.is_harmless, - identifier=self._alarm_identifier_to_model(alarm.identifier_ensure), + identifier=self._alarm_identifier_to_model(alarm.identifier_ensure), # type: ignore[arg-type] ) def _alarm_identifier_to_model( self, identifier: AlarmIdentifier - ) -> ProfileStorageModel._AllAlarmIdentifiersStorageModel: + ) -> ProfileStorageModel.AllAlarmIdentifiersStorageModel: if isinstance(identifier, DateTimeAlarmIdentifier): - return ProfileStorageModel._DateTimeAlarmIdentifierStorageModel(value=identifier.value) + return ProfileStorageModel.DateTimeAlarmIdentifierStorageModel(value=identifier.value) if isinstance(identifier, RecoveryAccountWarningListedAlarmIdentifier): - return ProfileStorageModel._RecoveryAccountWarningListedAlarmIdentifierStorageModel( + return ProfileStorageModel.RecoveryAccountWarningListedAlarmIdentifierStorageModel( recovery_account=identifier.recovery_account ) raise AlarmIdentifierRuntimeToStorageConversionError(f"Unknown alarm identifier type: {type(identifier)}") - def _key_alias_to_model(self, key: PublicKeyAliased) -> ProfileStorageModel._KeyAliasStorageModel: - return ProfileStorageModel._KeyAliasStorageModel(alias=key.alias, public_key=key.value) + def _key_alias_to_model(self, key: PublicKeyAliased) -> ProfileStorageModel.KeyAliasStorageModel: + return ProfileStorageModel.KeyAliasStorageModel(alias=key.alias, public_key=key.value) + + def _get_alarm_storage_model_cls_by_name(self, name: str) -> type[ProfileStorageModel.AllAlarmStorageModel]: + all_alarm_storage_model_classes = get_args(ProfileStorageModel.AllAlarmStorageModel) + name_to_cls: dict[str, type[ProfileStorageModel.AllAlarmStorageModel]] = { + cls.get_name(): cls for cls in all_alarm_storage_model_classes + } + assert name in name_to_cls, f"Alarm class not found for name: {name}" + return name_to_cls[name] diff --git a/clive/__private/storage/service.py b/clive/__private/storage/service.py index 441128c9dc74c469b59a83f67c4a45c12629dd99..fa1c0a3ab1e7d128b19704129db2da6624d7268a 100644 --- a/clive/__private/storage/service.py +++ b/clive/__private/storage/service.py @@ -335,7 +335,7 @@ class PersistentStorageService: profile_directory.mkdir(parents=True, exist_ok=True) try: - encrypted_profile = await self._encryption_service.encrypt(profile_model.json(indent=4)) + encrypted_profile = await self._encryption_service.encrypt(profile_model.json()) except (CommandEncryptError, CommandRequiresUnlockedEncryptionWalletError) as error: raise ProfileEncryptionError from error @@ -369,7 +369,7 @@ class PersistentStorageService: async def _parse_profile_model_from_file(self, profile_filepath: Path) -> ProfileStorageBase: raw = await self._read_profile_file_raw(profile_filepath) model_cls = self._model_cls_from_path(profile_filepath) - return model_cls.parse_raw(raw) + return model_cls.create(raw) async def _read_profile_file_raw(self, profile_filepath: Path) -> str: """ diff --git a/clive/__private/storage/storage_to_runtime_converter.py b/clive/__private/storage/storage_to_runtime_converter.py index afe858a5524949ac63728ea0f2f49ca8214c777f..ae4f0de165692a99bcd039b1ab13b0b5d3b8ce93 100644 --- a/clive/__private/storage/storage_to_runtime_converter.py +++ b/clive/__private/storage/storage_to_runtime_converter.py @@ -98,34 +98,34 @@ class StorageToRuntimeConverter: return transaction_storage_model.transaction_file_path return None - def _working_account_from_model(self, model: ProfileStorageModel._TrackedAccountStorageModel) -> WorkingAccount: + def _working_account_from_model(self, model: ProfileStorageModel.TrackedAccountStorageModel) -> WorkingAccount: return WorkingAccount(model.name, self._alarms_storage_from_model(model)) - def _watched_account_from_model(self, model: ProfileStorageModel._TrackedAccountStorageModel) -> WatchedAccount: + def _watched_account_from_model(self, model: ProfileStorageModel.TrackedAccountStorageModel) -> WatchedAccount: return WatchedAccount(model.name, self._alarms_storage_from_model(model)) - def _alarms_storage_from_model(self, model: ProfileStorageModel._TrackedAccountStorageModel) -> AlarmsStorage: + def _alarms_storage_from_model(self, model: ProfileStorageModel.TrackedAccountStorageModel) -> AlarmsStorage: alarms = [self._alarm_from_model(alarm) for alarm in model.alarms] return AlarmsStorage(alarms) def _known_account_from_model_representation(self, name: str) -> KnownAccount: return KnownAccount(name) - def _alarm_from_model(self, model: ProfileStorageModel._AlarmStorageModel) -> AnyAlarm: - alarm_cls = Alarm.get_alarm_class_by_name(model.name) + def _alarm_from_model(self, model: ProfileStorageModel.AllAlarmStorageModel) -> AnyAlarm: + alarm_cls = Alarm.get_alarm_class_by_name(model.get_name()) identifier = self._alarm_identifier_from_model(model.identifier) return alarm_cls(identifier=identifier, is_harmless=model.is_harmless) def _alarm_identifier_from_model( - self, model: ProfileStorageModel._AllAlarmIdentifiersStorageModel + self, model: ProfileStorageModel.AllAlarmIdentifiersStorageModel ) -> AllAlarmIdentifiers: - if isinstance(model, ProfileStorageModel._DateTimeAlarmIdentifierStorageModel): + if isinstance(model, ProfileStorageModel.DateTimeAlarmIdentifierStorageModel): return DateTimeAlarmIdentifier(value=model.value) - if isinstance(model, ProfileStorageModel._RecoveryAccountWarningListedAlarmIdentifierStorageModel): + if isinstance(model, ProfileStorageModel.RecoveryAccountWarningListedAlarmIdentifierStorageModel): return RecoveryAccountWarningListedAlarmIdentifier(recovery_account=model.recovery_account) raise AlarmIdentifierStorageToRuntimeConversionError( f"Unknown alarm identifier storage model type: {type(model)}" ) - def _key_alias_from_model(self, model: ProfileStorageModel._KeyAliasStorageModel) -> PublicKeyAliased: + def _key_alias_from_model(self, model: ProfileStorageModel.KeyAliasStorageModel) -> PublicKeyAliased: return PublicKeyAliased(value=model.public_key, alias=model.alias) diff --git a/clive/__private/ui/dialogs/operation_summary/remove_delegation_dialog.py b/clive/__private/ui/dialogs/operation_summary/remove_delegation_dialog.py index c34700eec1415628bc06d89e4755cbe38adfdded..f1e7fb8d4de879ba1d160aba0ac2c5d39998f0f5 100644 --- a/clive/__private/ui/dialogs/operation_summary/remove_delegation_dialog.py +++ b/clive/__private/ui/dialogs/operation_summary/remove_delegation_dialog.py @@ -23,7 +23,7 @@ class RemoveDelegationDialog(OperationSummaryBaseDialog): pretty_hp_amount: The formatted amount of HP being removed. """ - def __init__(self, delegation: VestingDelegation[Asset.Vests], pretty_hp_amount: str) -> None: + def __init__(self, delegation: VestingDelegation, pretty_hp_amount: str) -> None: super().__init__("Remove delegation") self._delegation = delegation self._pretty_hp_amount = pretty_hp_amount diff --git a/clive/__private/ui/dialogs/operation_summary/remove_withdraw_vesting_route_dialog.py b/clive/__private/ui/dialogs/operation_summary/remove_withdraw_vesting_route_dialog.py index f94797829cc9ca7084247b03aaf27dd1abdb409b..9ee4a20fbf5f12e01aea9fe34cd5d68467c1f3e1 100644 --- a/clive/__private/ui/dialogs/operation_summary/remove_withdraw_vesting_route_dialog.py +++ b/clive/__private/ui/dialogs/operation_summary/remove_withdraw_vesting_route_dialog.py @@ -42,5 +42,5 @@ class RemoveWithdrawVestingRouteDialog(OperationSummaryBaseDialog): from_account=self.working_account_name, to_account=self._withdraw_route.to_account, auto_vest=self._withdraw_route.auto_vest, - percent=PERCENT_TO_REMOVE_WITHDRAW_ROUTE, + percent=int(PERCENT_TO_REMOVE_WITHDRAW_ROUTE), ) diff --git a/clive/__private/ui/dialogs/raw_json_dialog.py b/clive/__private/ui/dialogs/raw_json_dialog.py index e9527fc7e83c1a228b22c610876d65c64e73c889..213b6d5039972776f085b1b185df876712a75375 100644 --- a/clive/__private/ui/dialogs/raw_json_dialog.py +++ b/clive/__private/ui/dialogs/raw_json_dialog.py @@ -6,14 +6,14 @@ from typing import TYPE_CHECKING from textual.containers import Center from textual.widgets import Pretty +from clive.__private.models.schemas import convert_to_representation from clive.__private.ui.dialogs.clive_base_dialogs import CliveInfoDialog from clive.__private.ui.widgets.section import Section -from schemas.operations.representations import convert_to_representation if TYPE_CHECKING: from textual.app import ComposeResult - from clive.__private.models.schemas import OperationBase, OperationRepresentationBase + from clive.__private.models.schemas import OperationBase class RawJsonDialog(CliveInfoDialog): @@ -27,5 +27,5 @@ class RawJsonDialog(CliveInfoDialog): @staticmethod def _get_operation_representation_json(operation: OperationBase) -> str: - representation: OperationRepresentationBase = convert_to_representation(operation=operation) - return representation.json() + representation = convert_to_representation(operation) + return representation.json(order="sorted") diff --git a/clive/__private/ui/screens/operations/bindings/operation_action_bindings.py b/clive/__private/ui/screens/operations/bindings/operation_action_bindings.py index af1b77771a662a31483383d24f353d472afaebfc..9c15b2496ba17bcf1b6f480fbdd769fd4bacd99c 100644 --- a/clive/__private/ui/screens/operations/bindings/operation_action_bindings.py +++ b/clive/__private/ui/screens/operations/bindings/operation_action_bindings.py @@ -3,12 +3,12 @@ from __future__ import annotations import contextlib from typing import TYPE_CHECKING, Any, ClassVar, Final -from pydantic import ValidationError from textual import on from textual.css.query import NoMatches from clive.__private.abstract_class import AbstractClassMessagePump from clive.__private.core import iwax +from clive.__private.models.schemas import ValidationError from clive.__private.ui.bindings import CLIVE_PREDEFINED_BINDINGS from clive.__private.ui.clive_widget import CliveWidget from clive.__private.ui.dialogs.confirm_action_dialog_with_known_exchange import ConfirmActionDialogWithKnownExchange @@ -65,7 +65,7 @@ class OperationActionBindings(CliveWidget, AbstractClassMessagePump): """ Validate operations from callback result. If any of them is invalid, notifies the user and returns None. - First it checks for any unhandled ValidationError (which may lead to app crash) from pydantic + First it checks for any unhandled ValidationError (which may lead to app crash) from schemas and then performs a wax validation. Args: diff --git a/clive/__private/ui/screens/operations/governance_operations/common_governance/governance_actions.py b/clive/__private/ui/screens/operations/governance_operations/common_governance/governance_actions.py index d1fb4ebf035c1948ed2fa0d9715316367ad6e070..5534e27bdb65cebfbe2901e3a66f3b542f0533aa 100644 --- a/clive/__private/ui/screens/operations/governance_operations/common_governance/governance_actions.py +++ b/clive/__private/ui/screens/operations/governance_operations/common_governance/governance_actions.py @@ -17,7 +17,7 @@ if TYPE_CHECKING: from textual.app import ComposeResult from typing_extensions import TypeIs - from schemas.operations import AccountWitnessVoteOperation, UpdateProposalVotesOperation + from clive.__private.models.schemas import AccountWitnessVoteOperation, UpdateProposalVotesOperation class GovernanceActionRow(Horizontal, AbstractClassMessagePump): diff --git a/clive/__private/ui/screens/operations/governance_operations/proposals/proposals.py b/clive/__private/ui/screens/operations/governance_operations/proposals/proposals.py index 19d2295a1ae92e57be622283d15d385899f6458d..a2fc7ee89f19bd46a65b94877a095f8f961cb490 100644 --- a/clive/__private/ui/screens/operations/governance_operations/proposals/proposals.py +++ b/clive/__private/ui/screens/operations/governance_operations/proposals/proposals.py @@ -270,7 +270,7 @@ class Proposals(GovernanceTabPane): """ def append_operation(operation: UpdateProposalVotesOperation) -> None: - operation.proposal_ids.append(proposal_id) # type: ignore[arg-type] + operation.proposal_ids.append(proposal_id) operation.proposal_ids.sort() # proposal id's must be sorted proposal_id = int(identifier) @@ -302,7 +302,7 @@ class Proposals(GovernanceTabPane): self.app.trigger_profile_watchers() return - operation.proposal_ids.remove(proposal_id) # type: ignore[arg-type] + operation.proposal_ids.remove(proposal_id) self.app.trigger_profile_watchers() def _find_proposal_operation_with_such_id( diff --git a/clive/__private/ui/screens/operations/hive_power_management/delegate_hive_power/delegate_hive_power.py b/clive/__private/ui/screens/operations/hive_power_management/delegate_hive_power/delegate_hive_power.py index 1448e91a54c2829bf156bd88021d5a68176852a9..de4629d571e1b798d380b3cd51b95775ea39bf6c 100644 --- a/clive/__private/ui/screens/operations/hive_power_management/delegate_hive_power/delegate_hive_power.py +++ b/clive/__private/ui/screens/operations/hive_power_management/delegate_hive_power/delegate_hive_power.py @@ -32,7 +32,6 @@ if TYPE_CHECKING: from textual.app import ComposeResult from clive.__private.core.commands.data_retrieval.hive_power_data import HivePowerData - from clive.__private.models import Asset from clive.__private.models.schemas import VestingDelegation @@ -54,9 +53,7 @@ class Delegation(CliveCheckerboardTableRow): aligned_vests_amount: aligned amount of vests to dots. """ - def __init__( - self, delegation: VestingDelegation[Asset.Vests], aligned_hp_amount: str, aligned_vests_amount: str - ) -> None: + def __init__(self, delegation: VestingDelegation, aligned_hp_amount: str, aligned_vests_amount: str) -> None: self._aligned_hp_amount = aligned_hp_amount super().__init__( @@ -80,7 +77,7 @@ class DelegationsTable(CliveCheckerboardTable): def __init__(self) -> None: super().__init__(header=DelegationsTableHeader(), title="Current delegations", init_dynamic=False) - self._previous_delegations: list[VestingDelegation[Asset.Vests]] | NotUpdatedYet = NotUpdatedYet() + self._previous_delegations: list[VestingDelegation] | NotUpdatedYet = NotUpdatedYet() def create_dynamic_rows(self, content: HivePowerData) -> list[Delegation]: aligned_hp, aligned_vests = content.get_delegations_aligned_amounts() diff --git a/clive/__private/ui/screens/operations/savings_operations/savings_operations.py b/clive/__private/ui/screens/operations/savings_operations/savings_operations.py index 27407d028e87c3ed19ef8c1319128468f18129df..f29af921064a6572cafec9a2269ad66b5f97685c 100644 --- a/clive/__private/ui/screens/operations/savings_operations/savings_operations.py +++ b/clive/__private/ui/screens/operations/savings_operations/savings_operations.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Final, Literal +from typing import TYPE_CHECKING, Any, Final, Literal from textual import on from textual.containers import Grid, Horizontal @@ -256,7 +256,7 @@ class SavingsTransfers(TabPane, OperationActionBindings): if not CliveValidatedInput.validate_many(self._to_account_input, self._amount_input, self._memo_input): return None - data = { + data: dict[str, Any] = { "from_": self.default_receiver, "to": self._to_account_input.value_or_error, "amount": self._amount_input.value_or_error, diff --git a/clive/__private/ui/widgets/currency_selector/currency_selector.py b/clive/__private/ui/widgets/currency_selector/currency_selector.py index beca7f63eb2b419d59076ddc785fbad2825bdee9..709920b065ea1245dc39b026d81fd2700d92377b 100644 --- a/clive/__private/ui/widgets/currency_selector/currency_selector.py +++ b/clive/__private/ui/widgets/currency_selector/currency_selector.py @@ -1,7 +1,9 @@ from __future__ import annotations -from clive.__private.models import Asset -from clive.__private.models.asset import AssetFactoryHolder +from clive.__private.models.asset import ( + Asset, + AssetFactoryHolder, +) from clive.__private.ui.widgets.currency_selector.currency_selector_base import ( CurrencySelectorBase, ) diff --git a/clive/__private/ui/widgets/currency_selector/currency_selector_base.py b/clive/__private/ui/widgets/currency_selector/currency_selector_base.py index 3336340d44305ba66bc1e44fdee096de5bf633a1..e176460b28728db5eedb6c138ebdf6de863d14eb 100644 --- a/clive/__private/ui/widgets/currency_selector/currency_selector_base.py +++ b/clive/__private/ui/widgets/currency_selector/currency_selector_base.py @@ -1,7 +1,6 @@ from __future__ import annotations from abc import abstractmethod -from typing import Generic from clive.__private.abstract_class import AbstractClassMessagePump from clive.__private.models.asset import ( @@ -13,7 +12,7 @@ from clive.__private.models.asset import ( from clive.__private.ui.widgets.clive_basic.clive_select import CliveSelect -class CurrencySelectorBase(CliveSelect[AssetFactoryHolder[AssetT]], Generic[AssetT], AbstractClassMessagePump): +class CurrencySelectorBase(CliveSelect[AssetFactoryHolder[AssetT]], AbstractClassMessagePump): """Base Currency Selector for operations, which require to choose type of Assets.""" def __init__(self) -> None: diff --git a/clive/__private/ui/widgets/currency_selector/currency_selector_hive.py b/clive/__private/ui/widgets/currency_selector/currency_selector_hive.py index a98c6e4b9cde350f9a393c5195107e9997616475..e2c362b3f5c4a453262175128d08e719d79be606 100644 --- a/clive/__private/ui/widgets/currency_selector/currency_selector_hive.py +++ b/clive/__private/ui/widgets/currency_selector/currency_selector_hive.py @@ -1,7 +1,9 @@ from __future__ import annotations -from clive.__private.models import Asset -from clive.__private.models.asset import AssetFactoryHolder +from clive.__private.models.asset import ( + Asset, + AssetFactoryHolder, +) from clive.__private.ui.widgets.currency_selector.currency_selector_base import ( CurrencySelectorBase, ) diff --git a/clive/__private/ui/widgets/currency_selector/currency_selector_hp_vests.py b/clive/__private/ui/widgets/currency_selector/currency_selector_hp_vests.py index bbd49f271b7a6809b9eacc55ca08153bdb616876..3a8531f9512c6e7523a1e671dcd8550b69a34489 100644 --- a/clive/__private/ui/widgets/currency_selector/currency_selector_hp_vests.py +++ b/clive/__private/ui/widgets/currency_selector/currency_selector_hp_vests.py @@ -1,7 +1,6 @@ from __future__ import annotations -from clive.__private.models import Asset -from clive.__private.models.asset import AssetFactoryHolder +from clive.__private.models.asset import Asset, AssetFactoryHolder from clive.__private.ui.widgets.currency_selector.currency_selector_base import CurrencySelectorBase diff --git a/clive/__private/ui/widgets/currency_selector/currency_selector_liquid.py b/clive/__private/ui/widgets/currency_selector/currency_selector_liquid.py index b8fa3895753e5461335622307a4ab6a6c17d8cd8..59de3a0f3eca45c50096faa4b862391f26726a06 100644 --- a/clive/__private/ui/widgets/currency_selector/currency_selector_liquid.py +++ b/clive/__private/ui/widgets/currency_selector/currency_selector_liquid.py @@ -1,7 +1,6 @@ from __future__ import annotations -from clive.__private.models import Asset -from clive.__private.models.asset import AssetFactoryHolder +from clive.__private.models.asset import Asset, AssetFactoryHolder from clive.__private.ui.widgets.currency_selector.currency_selector_base import ( CurrencySelectorBase, ) diff --git a/clive/__private/ui/widgets/inputs/account_name_pattern_input.py b/clive/__private/ui/widgets/inputs/account_name_pattern_input.py index ac1b9ca64e9eb59176685569a6b9ef2d04d42812..2028b017b25eb2aa896e42b1f24f326feb8c1098 100644 --- a/clive/__private/ui/widgets/inputs/account_name_pattern_input.py +++ b/clive/__private/ui/widgets/inputs/account_name_pattern_input.py @@ -45,7 +45,7 @@ class AccountNamePatternInput(TextInput): required=required, suggester=suggester, validators=[ - Length(minimum=1, maximum=AccountName.max_length), + Length(minimum=1, maximum=AccountName.meta().max_length), # type: ignore[attr-defined] ], validate_on=validate_on, valid_empty=valid_empty, diff --git a/clive/__private/validators/account_name_validator.py b/clive/__private/validators/account_name_validator.py index 071b3226b1ea5ff7e4cdb6db564927c295438afc..954fd4efc7e7dcecf97e75b5423a6376e4c6bf21 100644 --- a/clive/__private/validators/account_name_validator.py +++ b/clive/__private/validators/account_name_validator.py @@ -4,8 +4,7 @@ from typing import TYPE_CHECKING, Final from textual.validation import Validator -from clive.__private.core.validate_schema_field import is_schema_field_valid -from clive.__private.models.schemas import AccountName +from clive.__private.models.schemas import AccountName, is_matching_model if TYPE_CHECKING: from textual.validation import ValidationResult @@ -18,7 +17,7 @@ class AccountNameValidator(Validator): super().__init__() def validate(self, value: str) -> ValidationResult: - if is_schema_field_valid(AccountName, value): + if is_matching_model(value, AccountName): return self.success() return self.failure(self.INVALID_ACCOUNT_NAME_FAILURE_DESCRIPTION, value) diff --git a/docker/Dockerfile b/docker/Dockerfile index e10d4d69a74314255b03d372cfbada5e2c23cc03..7322f7705770f8fe1614fe1f84e599929bc87bcc 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -72,6 +72,7 @@ WORKDIR /clive SHELL ["/bin/bash", "-c"] # Project IDS: +# - 198 -> hive (generated APIs) # - 362 -> schemas # - 392 -> clive # - 419 -> wax @@ -79,6 +80,7 @@ SHELL ["/bin/bash", "-c"] RUN --mount=type=cache,mode=0777,uid=${CLIVE_UID},target=${PIP_CACHE_DIR} \ source /python_venv/bin/activate && \ pip install clive=="${CLIVE_VERSION}" \ + --extra-index-url https://gitlab.syncad.com/api/v4/projects/198/packages/pypi/simple \ --extra-index-url https://gitlab.syncad.com/api/v4/projects/362/packages/pypi/simple \ --extra-index-url https://gitlab.syncad.com/api/v4/projects/393/packages/pypi/simple \ --extra-index-url https://gitlab.syncad.com/api/v4/projects/419/packages/pypi/simple \ diff --git a/hive b/hive index 9009edf6270db5a0f3bc1d77308bd5cbfb46c1f6..846b3d93e5287004961ca3b3b00a6b24c3078067 160000 --- a/hive +++ b/hive @@ -1 +1 @@ -Subproject commit 9009edf6270db5a0f3bc1d77308bd5cbfb46c1f6 +Subproject commit 846b3d93e5287004961ca3b3b00a6b24c3078067 diff --git a/poetry.lock b/poetry.lock index 316566ae7f97ec184e99c33aa931c93ecd490ae9..732ac4525779da98e587880454819fc1d19a607d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -221,13 +221,13 @@ extras = ["regex"] [[package]] name = "beekeepy" -version = "0.0.1.dev388+171802c" +version = "0.0.1.dev413+0bc0d4b" description = "All in one package for beekeeper interaction via Python interface." optional = false python-versions = ">=3.12,<4.0" groups = ["main", "dev", "embeddedtestnet"] files = [ - {file = "beekeepy-0.0.1.dev388+171802c-py3-none-any.whl", hash = "sha256:e3cdfd5ffa750538a6f4077db3687796165bd8de79858fe2ae5451ac9758ca51"}, + {file = "beekeepy-0.0.1.dev413+0bc0d4b-py3-none-any.whl", hash = "sha256:f9a17976648a43accc91b345fb46989feb93b376616f59dc9808198001d62e47"}, ] [package.dependencies] @@ -237,63 +237,13 @@ loguru = "0.7.2" psutil = "7.0.0" python-dateutil = "2.8.2" requests = "2.32.3" -schemas = "0.0.1.dev345+0b349a2" -setuptools = "77.0.3" -types-setuptools = "76.0.0.20250313" +schemas = "0.0.1.dev424+eeb5c94" [package.source] type = "legacy" url = "https://gitlab.syncad.com/api/v4/projects/434/packages/pypi/simple" reference = "gitlab-beekeepy" -[[package]] -name = "black" -version = "23.3.0" -description = "The uncompromising code formatter." -optional = false -python-versions = ">=3.7" -groups = ["main", "dev", "embeddedtestnet"] -files = [ - {file = "black-23.3.0-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:0945e13506be58bf7db93ee5853243eb368ace1c08a24c65ce108986eac65915"}, - {file = "black-23.3.0-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:67de8d0c209eb5b330cce2469503de11bca4085880d62f1628bd9972cc3366b9"}, - {file = "black-23.3.0-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:7c3eb7cea23904399866c55826b31c1f55bbcd3890ce22ff70466b907b6775c2"}, - {file = "black-23.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32daa9783106c28815d05b724238e30718f34155653d4d6e125dc7daec8e260c"}, - {file = "black-23.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:35d1381d7a22cc5b2be2f72c7dfdae4072a3336060635718cc7e1ede24221d6c"}, - {file = "black-23.3.0-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:a8a968125d0a6a404842fa1bf0b349a568634f856aa08ffaff40ae0dfa52e7c6"}, - {file = "black-23.3.0-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:c7ab5790333c448903c4b721b59c0d80b11fe5e9803d8703e84dcb8da56fec1b"}, - {file = "black-23.3.0-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:a6f6886c9869d4daae2d1715ce34a19bbc4b95006d20ed785ca00fa03cba312d"}, - {file = "black-23.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f3c333ea1dd6771b2d3777482429864f8e258899f6ff05826c3a4fcc5ce3f70"}, - {file = "black-23.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:11c410f71b876f961d1de77b9699ad19f939094c3a677323f43d7a29855fe326"}, - {file = "black-23.3.0-cp37-cp37m-macosx_10_16_x86_64.whl", hash = "sha256:1d06691f1eb8de91cd1b322f21e3bfc9efe0c7ca1f0e1eb1db44ea367dff656b"}, - {file = "black-23.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50cb33cac881766a5cd9913e10ff75b1e8eb71babf4c7104f2e9c52da1fb7de2"}, - {file = "black-23.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:e114420bf26b90d4b9daa597351337762b63039752bdf72bf361364c1aa05925"}, - {file = "black-23.3.0-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:48f9d345675bb7fbc3dd85821b12487e1b9a75242028adad0333ce36ed2a6d27"}, - {file = "black-23.3.0-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:714290490c18fb0126baa0fca0a54ee795f7502b44177e1ce7624ba1c00f2331"}, - {file = "black-23.3.0-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:064101748afa12ad2291c2b91c960be28b817c0c7eaa35bec09cc63aa56493c5"}, - {file = "black-23.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:562bd3a70495facf56814293149e51aa1be9931567474993c7942ff7d3533961"}, - {file = "black-23.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:e198cf27888ad6f4ff331ca1c48ffc038848ea9f031a3b40ba36aced7e22f2c8"}, - {file = "black-23.3.0-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:3238f2aacf827d18d26db07524e44741233ae09a584273aa059066d644ca7b30"}, - {file = "black-23.3.0-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:f0bd2f4a58d6666500542b26354978218a9babcdc972722f4bf90779524515f3"}, - {file = "black-23.3.0-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:92c543f6854c28a3c7f39f4d9b7694f9a6eb9d3c5e2ece488c327b6e7ea9b266"}, - {file = "black-23.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a150542a204124ed00683f0db1f5cf1c2aaaa9cc3495b7a3b5976fb136090ab"}, - {file = "black-23.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:6b39abdfb402002b8a7d030ccc85cf5afff64ee90fa4c5aebc531e3ad0175ddb"}, - {file = "black-23.3.0-py3-none-any.whl", hash = "sha256:ec751418022185b0c1bb7d7736e6933d40bbb14c14a0abcf9123d1b159f98dd4"}, - {file = "black-23.3.0.tar.gz", hash = "sha256:1c7b8d606e728a41ea1ccbd7264677e494e87cf630e399262ced92d4a8dac940"}, -] - -[package.dependencies] -click = ">=8.0.0" -mypy-extensions = ">=0.4.3" -packaging = ">=22.0" -pathspec = ">=0.9.0" -platformdirs = ">=2" - -[package.extras] -colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.7.4)"] -jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] -uvloop = ["uvloop (>=0.15.2)"] - [[package]] name = "certifi" version = "2025.1.31" @@ -426,7 +376,7 @@ version = "8.1.8" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" -groups = ["main", "dev", "docs", "embeddedtestnet"] +groups = ["main", "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"}, @@ -460,7 +410,26 @@ 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 = {main = "sys_platform == \"win32\" or platform_system == \"Windows\"", dev = "sys_platform == \"win32\" or platform_system == \"Windows\"", embeddedtestnet = "sys_platform == \"win32\" or platform_system == \"Windows\""} +markers = {main = "sys_platform == \"win32\" or platform_system == \"Windows\"", dev = "sys_platform == \"win32\" or platform_system == \"Windows\"", embeddedtestnet = "sys_platform == \"win32\""} + +[[package]] +name = "database-api" +version = "1.27.11rc6.dev353+108f17fef" +description = "" +optional = false +python-versions = ">=3.12,<4.0" +groups = ["main", "dev", "embeddedtestnet"] +files = [ + {file = "database_api-1.27.11rc6.dev353+108f17fef-py3-none-any.whl", hash = "sha256:0946e7532a45667a29744866ff1f8c6c539949b07bbb2cafa49bfba2142369af"}, +] + +[package.dependencies] +beekeepy = "0.0.1.dev413+0bc0d4b" + +[package.source] +type = "legacy" +url = "https://gitlab.syncad.com/api/v4/projects/198/packages/pypi/simple" +reference = "gitlab-hive" [[package]] name = "distlib" @@ -1294,6 +1263,59 @@ files = [ {file = "msgpack-1.1.0.tar.gz", hash = "sha256:dd432ccc2c72b914e4cb77afce64aab761c1137cc698be3984eee260bcb2896e"}, ] +[[package]] +name = "msgspec" +version = "0.18.6" +description = "A fast serialization and validation library, with builtin support for JSON, MessagePack, YAML, and TOML." +optional = false +python-versions = ">=3.8" +groups = ["main", "dev", "embeddedtestnet"] +files = [ + {file = "msgspec-0.18.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:77f30b0234eceeff0f651119b9821ce80949b4d667ad38f3bfed0d0ebf9d6d8f"}, + {file = "msgspec-0.18.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1a76b60e501b3932782a9da039bd1cd552b7d8dec54ce38332b87136c64852dd"}, + {file = "msgspec-0.18.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:06acbd6edf175bee0e36295d6b0302c6de3aaf61246b46f9549ca0041a9d7177"}, + {file = "msgspec-0.18.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40a4df891676d9c28a67c2cc39947c33de516335680d1316a89e8f7218660410"}, + {file = "msgspec-0.18.6-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:a6896f4cd5b4b7d688018805520769a8446df911eb93b421c6c68155cdf9dd5a"}, + {file = "msgspec-0.18.6-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3ac4dd63fd5309dd42a8c8c36c1563531069152be7819518be0a9d03be9788e4"}, + {file = "msgspec-0.18.6-cp310-cp310-win_amd64.whl", hash = "sha256:fda4c357145cf0b760000c4ad597e19b53adf01382b711f281720a10a0fe72b7"}, + {file = "msgspec-0.18.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e77e56ffe2701e83a96e35770c6adb655ffc074d530018d1b584a8e635b4f36f"}, + {file = "msgspec-0.18.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d5351afb216b743df4b6b147691523697ff3a2fc5f3d54f771e91219f5c23aaa"}, + {file = "msgspec-0.18.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3232fabacef86fe8323cecbe99abbc5c02f7698e3f5f2e248e3480b66a3596b"}, + {file = "msgspec-0.18.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3b524df6ea9998bbc99ea6ee4d0276a101bcc1aa8d14887bb823914d9f60d07"}, + {file = "msgspec-0.18.6-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:37f67c1d81272131895bb20d388dd8d341390acd0e192a55ab02d4d6468b434c"}, + {file = "msgspec-0.18.6-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d0feb7a03d971c1c0353de1a8fe30bb6579c2dc5ccf29b5f7c7ab01172010492"}, + {file = "msgspec-0.18.6-cp311-cp311-win_amd64.whl", hash = "sha256:41cf758d3f40428c235c0f27bc6f322d43063bc32da7b9643e3f805c21ed57b4"}, + {file = "msgspec-0.18.6-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d86f5071fe33e19500920333c11e2267a31942d18fed4d9de5bc2fbab267d28c"}, + {file = "msgspec-0.18.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce13981bfa06f5eb126a3a5a38b1976bddb49a36e4f46d8e6edecf33ccf11df1"}, + {file = "msgspec-0.18.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e97dec6932ad5e3ee1e3c14718638ba333befc45e0661caa57033cd4cc489466"}, + {file = "msgspec-0.18.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad237100393f637b297926cae1868b0d500f764ccd2f0623a380e2bcfb2809ca"}, + {file = "msgspec-0.18.6-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db1d8626748fa5d29bbd15da58b2d73af25b10aa98abf85aab8028119188ed57"}, + {file = "msgspec-0.18.6-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:d70cb3d00d9f4de14d0b31d38dfe60c88ae16f3182988246a9861259c6722af6"}, + {file = "msgspec-0.18.6-cp312-cp312-win_amd64.whl", hash = "sha256:1003c20bfe9c6114cc16ea5db9c5466e49fae3d7f5e2e59cb70693190ad34da0"}, + {file = "msgspec-0.18.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f7d9faed6dfff654a9ca7d9b0068456517f63dbc3aa704a527f493b9200b210a"}, + {file = "msgspec-0.18.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9da21f804c1a1471f26d32b5d9bc0480450ea77fbb8d9db431463ab64aaac2cf"}, + {file = "msgspec-0.18.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46eb2f6b22b0e61c137e65795b97dc515860bf6ec761d8fb65fdb62aa094ba61"}, + {file = "msgspec-0.18.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8355b55c80ac3e04885d72db515817d9fbb0def3bab936bba104e99ad22cf46"}, + {file = "msgspec-0.18.6-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9080eb12b8f59e177bd1eb5c21e24dd2ba2fa88a1dbc9a98e05ad7779b54c681"}, + {file = "msgspec-0.18.6-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:cc001cf39becf8d2dcd3f413a4797c55009b3a3cdbf78a8bf5a7ca8fdb76032c"}, + {file = "msgspec-0.18.6-cp38-cp38-win_amd64.whl", hash = "sha256:fac5834e14ac4da1fca373753e0c4ec9c8069d1fe5f534fa5208453b6065d5be"}, + {file = "msgspec-0.18.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:974d3520fcc6b824a6dedbdf2b411df31a73e6e7414301abac62e6b8d03791b4"}, + {file = "msgspec-0.18.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fd62e5818731a66aaa8e9b0a1e5543dc979a46278da01e85c3c9a1a4f047ef7e"}, + {file = "msgspec-0.18.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7481355a1adcf1f08dedd9311193c674ffb8bf7b79314b4314752b89a2cf7f1c"}, + {file = "msgspec-0.18.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6aa85198f8f154cf35d6f979998f6dadd3dc46a8a8c714632f53f5d65b315c07"}, + {file = "msgspec-0.18.6-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0e24539b25c85c8f0597274f11061c102ad6b0c56af053373ba4629772b407be"}, + {file = "msgspec-0.18.6-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c61ee4d3be03ea9cd089f7c8e36158786cd06e51fbb62529276452bbf2d52ece"}, + {file = "msgspec-0.18.6-cp39-cp39-win_amd64.whl", hash = "sha256:b5c390b0b0b7da879520d4ae26044d74aeee5144f83087eb7842ba59c02bc090"}, + {file = "msgspec-0.18.6.tar.gz", hash = "sha256:a59fc3b4fcdb972d09138cb516dbde600c99d07c38fd9372a6ef500d2d031b4e"}, +] + +[package.extras] +dev = ["attrs", "coverage", "furo", "gcovr", "ipython", "msgpack", "mypy", "pre-commit", "pyright", "pytest", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "tomli ; python_version < \"3.11\"", "tomli-w"] +doc = ["furo", "ipython", "sphinx", "sphinx-copybutton", "sphinx-design"] +test = ["attrs", "msgpack", "mypy", "pyright", "pytest", "pyyaml", "tomli ; python_version < \"3.11\"", "tomli-w"] +toml = ["tomli ; python_version < \"3.11\"", "tomli-w"] +yaml = ["pyyaml"] + [[package]] name = "multidict" version = "6.4.2" @@ -1467,12 +1489,31 @@ version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." optional = false python-versions = ">=3.5" -groups = ["main", "dev", "embeddedtestnet"] +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"}, ] +[[package]] +name = "network-broadcast-api" +version = "1.27.11rc6.dev353+108f17fef" +description = "" +optional = false +python-versions = ">=3.12,<4.0" +groups = ["main", "dev", "embeddedtestnet"] +files = [ + {file = "network_broadcast_api-1.27.11rc6.dev353+108f17fef-py3-none-any.whl", hash = "sha256:34b4a4925415fd4f0474bc523a8b37caa3faaf716188f9bd47e10792cac0192d"}, +] + +[package.dependencies] +beekeepy = "0.0.1.dev413+0bc0d4b" + +[package.source] +type = "legacy" +url = "https://gitlab.syncad.com/api/v4/projects/198/packages/pypi/simple" +reference = "gitlab-hive" + [[package]] name = "nodeenv" version = "1.9.1" @@ -1491,7 +1532,7 @@ version = "24.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" -groups = ["main", "dev", "docs", "embeddedtestnet"] +groups = ["dev", "docs", "embeddedtestnet"] files = [ {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, @@ -1519,7 +1560,7 @@ version = "0.12.1" description = "Utility library for gitignore style pattern matching of file paths." optional = false python-versions = ">=3.8" -groups = ["main", "dev", "docs", "embeddedtestnet"] +groups = ["docs"] files = [ {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, @@ -1531,7 +1572,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", "embeddedtestnet"] +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"}, @@ -1738,7 +1779,7 @@ version = "1.10.18" description = "Data validation and settings management using python type hints" optional = false python-versions = ">=3.7" -groups = ["main", "dev", "embeddedtestnet"] +groups = ["main"] files = [ {file = "pydantic-1.10.18-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e405ffcc1254d76bb0e760db101ee8916b620893e6edfbfee563b3c6f7a67c02"}, {file = "pydantic-1.10.18-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e306e280ebebc65040034bff1a0a81fd86b2f4f05daac0131f29541cafd80b80"}, @@ -2103,45 +2144,23 @@ files = [ [[package]] name = "schemas" -version = "0.0.1.dev345+0b349a2" +version = "0.0.1.dev424+eeb5c94" description = "Tools for checking if message fits expected format" optional = false python-versions = ">=3.12,<4.0" groups = ["main", "dev", "embeddedtestnet"] files = [ - {file = "schemas-0.0.1.dev345+0b349a2-py3-none-any.whl", hash = "sha256:35487f5bd7ca70556f350f74495bc8e8ef15af829b8c86aee783ab2908fab86a"}, + {file = "schemas-0.0.1.dev424+eeb5c94-py3-none-any.whl", hash = "sha256:eaa87991fbec3878d1f3fbba1cf9562995364b80530787d1eb7598ad62bdea72"}, ] [package.dependencies] -black = "23.3.0" -pydantic = "1.10.18" +msgspec = "0.18.6" [package.source] type = "legacy" url = "https://gitlab.syncad.com/api/v4/projects/362/packages/pypi/simple" reference = "gitlab-schemas" -[[package]] -name = "setuptools" -version = "77.0.3" -description = "Easily download, build, install, upgrade, and uninstall Python packages" -optional = false -python-versions = ">=3.9" -groups = ["main", "dev", "embeddedtestnet"] -files = [ - {file = "setuptools-77.0.3-py3-none-any.whl", hash = "sha256:67122e78221da5cf550ddd04cf8742c8fe12094483749a792d56cd669d6cf58c"}, - {file = "setuptools-77.0.3.tar.gz", hash = "sha256:583b361c8da8de57403743e756609670de6fb2345920e36dc5c2d914c319c945"}, -] - -[package.extras] -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) ; 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" version = "1.5.4" @@ -2192,7 +2211,7 @@ develop = false abstractcp = "0.9.9" loguru = "0.7.2" python-dateutil = "2.8.2" -wax = "0.3.10.dev712+7e6218a0" +wax = "0.3.10.dev841+5ac4412f" [package.source] type = "directory" @@ -2268,21 +2287,6 @@ rich = ">=10.11.0" shellingham = ">=1.3.0" typing-extensions = ">=3.7.4.3" -[[package]] -name = "types-setuptools" -version = "76.0.0.20250313" -description = "Typing stubs for setuptools" -optional = false -python-versions = ">=3.9" -groups = ["main", "dev", "embeddedtestnet"] -files = [ - {file = "types_setuptools-76.0.0.20250313-py3-none-any.whl", hash = "sha256:bf454b2a49b8cfd7ebcf5844d4dd5fe4c8666782df1e3663c5866fd51a47460e"}, - {file = "types_setuptools-76.0.0.20250313.tar.gz", hash = "sha256:b2be66f550f95f3cad2a7d46177b273c7e9c80df7d257fa57addbbcfc8126a9e"}, -] - -[package.dependencies] -setuptools = "*" - [[package]] name = "types-toml" version = "0.10.8.20240310" @@ -2406,19 +2410,20 @@ watchmedo = ["PyYAML (>=3.10)"] [[package]] name = "wax" -version = "0.3.10.dev712+7e6218a0" +version = "0.3.10.dev841+5ac4412f" description = "" optional = false python-versions = ">=3.12,<4.0" groups = ["main", "dev", "embeddedtestnet"] files = [ - {file = "wax-0.3.10.dev712+7e6218a0-cp312-cp312-manylinux_2_39_x86_64.whl", hash = "sha256:d54a51b34036a37363bad13e0e90d501e7b4dbbe5c7ee3c58df16e30553f9811"}, + {file = "wax-0.3.10.dev841+5ac4412f-cp312-cp312-manylinux_2_39_x86_64.whl", hash = "sha256:7b472a060529bcd9fd4e7e4ddde1ab32c0453563b2260bb756da8215c15bd3e3"}, ] [package.dependencies] -beekeepy = "0.0.1.dev388+171802c" +database-api = "1.27.11rc6.dev353+108f17fef" httpx = {version = "0.23.3", extras = ["http2"]} loguru = "0.7.2" +network-broadcast-api = "1.27.11rc6.dev353+108f17fef" protobuf = "4.24.4" python-dateutil = "2.8.2" requests = "2.32.3" @@ -2549,4 +2554,4 @@ propcache = ">=0.2.1" [metadata] lock-version = "2.1" python-versions = ">=3.12,<3.13" -content-hash = "8813b4d16dc3fb1506dd0cb759389c63738c363d440d897f61416120ad4b8991" +content-hash = "b69e729178b8894cfaab8e897f32a9d2a34e83d3016934d3e615ab010673274e" diff --git a/pydoclint-errors-baseline.txt b/pydoclint-errors-baseline.txt index 2725476a567e9362c58eba5826b231bceb3a42ce..2631b937e883af8ed636242955d68fadd0dc03e0 100644 --- a/pydoclint-errors-baseline.txt +++ b/pydoclint-errors-baseline.txt @@ -295,13 +295,6 @@ clive/__private/core/error_handlers/abc/error_notificator.py DOC501: Method `ErrorNotificator.__aexit__` has raise statements, but the docstring does not have a "Raises" section DOC503: Method `ErrorNotificator.__aexit__` exceptions in the "Raises" section in the docstring do not match those in the function body. Raised exceptions in the docstring: []. Raised exceptions in the body: ['CannotNotifyError']. -------------------- -clive/__private/core/get_default_from_model.py - DOC101: Function `get_default_from_model`: Docstring contains fewer arguments than in function signature. - DOC103: Function `get_default_from_model`: Docstring arguments are different from function arguments. (Or could be other formatting issues: https://jsh9.github.io/pydoclint/violation_codes.html#notes-on-doc103 ). Arguments in the function signature but not in the docstring: [expect_type: type[T] | None, field_name: str, model: type[BaseModel] | BaseModel]. - DOC201: Function `get_default_from_model` does not have a return section in docstring - DOC501: Function `get_default_from_model` has raise statements, but the docstring does not have a "Raises" section - DOC503: Function `get_default_from_model` exceptions in the "Raises" section in the docstring do not match those in the function body. Raised exceptions in the docstring: []. Raised exceptions in the body: ['NoMatchesError', 'WrongTypeError']. --------------------- clive/__private/core/known_exchanges.py DOC502: Method `KnownExchanges.get_entity_by_account_name` has a "Raises" section in the docstring, but there are not "raise" statements in the body -------------------- @@ -322,9 +315,6 @@ clive/__private/core/profile.py DOC101: Method `Profile._set_node_address`: Docstring contains fewer arguments than in function signature. DOC103: Method `Profile._set_node_address`: Docstring arguments are different from function arguments. (Or could be other formatting issues: https://jsh9.github.io/pydoclint/violation_codes.html#notes-on-doc103 ). Arguments in the function signature but not in the docstring: [value: HttpUrl]. -------------------- -clive/__private/core/validate_schema_field.py - DOC502: Function `validate_schema_field` has a "Raises" section in the docstring, but there are not "raise" statements in the body --------------------- clive/__private/models/asset.py DOC502: Method `Asset.hive` has a "Raises" section in the docstring, but there are not "raise" statements in the body DOC502: Method `Asset.hbd` has a "Raises" section in the docstring, but there are not "raise" statements in the body diff --git a/pyproject.toml b/pyproject.toml index 93e4a0df78741e523f2ccb0ebfa88aa391b4e0b6..805bd0e51dce288ae0e92b779421043aa6145a73 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,9 +22,9 @@ dependencies = [ 'humanize (==4.12.2)', 'pydantic (==1.10.18)', 'toml (==0.10.2)', - 'schemas (==0.0.1.dev345+0b349a2)', - 'beekeepy (==0.0.1.dev388+171802c)', - 'wax (==0.3.10.dev712+7e6218a0)', + 'schemas (==0.0.1.dev424+eeb5c94)', + 'beekeepy (==0.0.1.dev413+0bc0d4b)', + 'wax (==0.3.10.dev841+5ac4412f)', ] [project.urls] @@ -41,6 +41,7 @@ packages = [ { include = "clive" }, ] source = [ + { name = "gitlab-hive", url = "https://gitlab.syncad.com/api/v4/projects/198/packages/pypi/simple", priority = "supplemental" }, { name = "gitlab-schemas", url = "https://gitlab.syncad.com/api/v4/projects/362/packages/pypi/simple", priority = "supplemental" }, { name = "gitlab-wax", url = "https://gitlab.syncad.com/api/v4/projects/419/packages/pypi/simple", priority = "supplemental" }, { name = "gitlab-beekeepy", url = "https://gitlab.syncad.com/api/v4/projects/434/packages/pypi/simple", priority = "supplemental" }, diff --git a/tests/clive-local-tools/clive_local_tools/checkers/blockchain_checkers.py b/tests/clive-local-tools/clive_local_tools/checkers/blockchain_checkers.py index fc85e0150750f1f8a1515cc1abfeb876e050095f..17c46608e08dcffff784721f69132f0d35fe8bb2 100644 --- a/tests/clive-local-tools/clive_local_tools/checkers/blockchain_checkers.py +++ b/tests/clive-local-tools/clive_local_tools/checkers/blockchain_checkers.py @@ -3,17 +3,16 @@ from __future__ import annotations from typing import TYPE_CHECKING import pytest - -if TYPE_CHECKING: - import test_tools as tt - - from clive.__private.models.schemas import OperationUnion, RepresentationBase - from beekeepy.exceptions import ErrorInResponseError from click.testing import Result from clive_local_tools.helpers import get_transaction_id_from_output +if TYPE_CHECKING: + import test_tools as tt + + from clive.__private.models.schemas import OperationUnion + def _ensure_transaction_id(trx_id_or_result: Result | str) -> str: if isinstance(trx_id_or_result, Result): @@ -47,9 +46,9 @@ def assert_operations_placed_in_blockchain( include_reversible=True, # type: ignore[call-arg] # TODO: id -> id_ after helpy bug fixed ) operations_to_check = list(expected_operations) + for operation_representation in transaction.operations: - _operation_representation: RepresentationBase[OperationUnion] = operation_representation - operation = _operation_representation.value + operation = operation_representation.value if operation in operations_to_check: operations_to_check.remove(operation) diff --git a/tests/clive-local-tools/clive_local_tools/helpers.py b/tests/clive-local-tools/clive_local_tools/helpers.py index a445be0782fb09ff1817aa8353133406e023265b..bbeee8a2cf0028a7c913802bcaedad0a919d388e 100644 --- a/tests/clive-local-tools/clive_local_tools/helpers.py +++ b/tests/clive-local-tools/clive_local_tools/helpers.py @@ -8,8 +8,7 @@ from typer import rich_utils from clive.__private.core.constants.terminal import TERMINAL_HEIGHT, TERMINAL_WIDTH from clive.__private.core.ensure_transaction import TransactionConvertibleType, ensure_transaction -from clive.__private.core.validate_schema_field import validate_schema_field -from clive.__private.models.schemas import TransactionId +from clive.__private.models.schemas import TransactionId, validate_schema_field if TYPE_CHECKING: from pathlib import Path @@ -22,7 +21,7 @@ def get_transaction_id_from_output(output: str) -> str: transaction_id = line.partition('"transaction_id":')[2] if transaction_id: transaction_id_field = transaction_id.strip(' "') - validate_schema_field(TransactionId, transaction_id_field) + validate_schema_field(transaction_id_field, TransactionId) return transaction_id_field pytest.fail(f"Could not find transaction id in output {output}") @@ -48,6 +47,6 @@ def get_formatted_error_message(error: ClickException) -> str: def create_transaction_file(path: Path, content: TransactionConvertibleType) -> Path: transaction_path = path / "trx.json" transaction = ensure_transaction(content) - transaction_serialized = transaction.json(by_alias=True) + transaction_serialized = transaction.json() transaction_path.write_text(transaction_serialized) return transaction_path diff --git a/tests/clive-local-tools/clive_local_tools/storage_migration/helpers.py b/tests/clive-local-tools/clive_local_tools/storage_migration/helpers.py index 92a3ccca179aaf0f643f2023307eaeeecd9df0a7..932b51c06f17c1b9ecc6a9d7ef091b85793f3307 100644 --- a/tests/clive-local-tools/clive_local_tools/storage_migration/helpers.py +++ b/tests/clive-local-tools/clive_local_tools/storage_migration/helpers.py @@ -7,6 +7,7 @@ from clive_local_tools.storage_migration import ( blank_profile_files, with_alarms, with_operations, + with_transaction_containing_negative_tapos, without_alarms_and_operations, ) @@ -30,6 +31,11 @@ def copy_profile_with_operations(destination_dir: Path) -> None: _copy_recursively(package_path, destination_dir) +def copy_profile_with_transaction_containing_negative_tapos(destination_dir: Path) -> None: + package_path = Path(with_transaction_containing_negative_tapos.__file__).parent + _copy_recursively(package_path, destination_dir) + + def copy_profile_without_alarms_and_operations(destination_dir: Path) -> None: package_path = Path(without_alarms_and_operations.__file__).parent _copy_recursively(package_path, destination_dir) diff --git a/tests/clive-local-tools/clive_local_tools/storage_migration/regenerate_prepared_profiles.py b/tests/clive-local-tools/clive_local_tools/storage_migration/regenerate_prepared_profiles.py index f22cb71611184a48d54363933f865d79a3d7802b..2eaeb92f92c18c69a5212a4e716dec9da919b8cb 100644 --- a/tests/clive-local-tools/clive_local_tools/storage_migration/regenerate_prepared_profiles.py +++ b/tests/clive-local-tools/clive_local_tools/storage_migration/regenerate_prepared_profiles.py @@ -18,14 +18,10 @@ from typing import TYPE_CHECKING, Final import test_tools as tt from beekeepy import AsyncBeekeeper -from clive.__private.core.alarms.specific_alarms.recovery_account_warning_listed import ( - RecoveryAccountWarningListedAlarmIdentifier, -) from clive.__private.core.commands.create_encryption_wallet import CreateEncryptionWallet from clive.__private.core.commands.create_user_wallet import CreateUserWallet from clive.__private.core.constants.setting_identifiers import DATA_PATH from clive.__private.core.encryption import EncryptionService -from clive.__private.core.formatters.case import underscore from clive.__private.core.wallet_container import WalletContainer from clive.__private.models.schemas import TransferOperation, convert_to_representation from clive.__private.settings import safe_settings, settings @@ -80,10 +76,10 @@ def create_model_from_scratch() -> ProfileStorageModel: return ProfileStorageModel( name=account_name, working_account=account_name, - tracked_accounts=[ProfileStorageModel._TrackedAccountStorageModel(name=account_name, alarms=[])], + tracked_accounts=[ProfileStorageModel.TrackedAccountStorageModel(name=account_name, alarms=[])], known_accounts=[account_name, ALT_WORKING_ACCOUNT2_DATA.account.name], key_aliases=[ - ProfileStorageModel._KeyAliasStorageModel( + ProfileStorageModel.KeyAliasStorageModel( alias=ALT_WORKING_ACCOUNT1_KEY_ALIAS, public_key=ACCOUNT_DATA.account.public_key, ) @@ -104,6 +100,7 @@ def save_encrypted_profile(encrypted: str) -> None: @contextmanager def copy_profile_files_from_tmp_dir(dst_dir_name: str) -> Generator[None, None]: + init_file_name = "__init__.py" dst_dir = Path(__file__).parent.absolute() / dst_dir_name with tempfile.TemporaryDirectory() as tmp_dir_name: @@ -113,41 +110,56 @@ def copy_profile_files_from_tmp_dir(dst_dir_name: str) -> Generator[None, None]: for path in Path(tmp_dir_name).rglob("*"): if path.suffix in {".wallet", ".profile"}: dst_path = dst_dir / path.relative_to(tmp_dir_name) + dst_last_dir = dst_path.parent + dst_last_dir.mkdir(parents=True, exist_ok=True) shutil.copy(path, dst_path) + all_directories = [dst_dir] + [path for path in dst_dir.rglob("*") if path.is_dir()] + for directory in all_directories: + (directory / init_file_name).touch(exist_ok=True) + async def _main() -> None: with copy_profile_files_from_tmp_dir("without_alarms_and_operations"): async with prepare_encryption_service() as encryption_service: profile_model = create_model_from_scratch() - encrypted = await encryption_service.encrypt(profile_model.json(indent=4)) + encrypted = await encryption_service.encrypt(profile_model.json()) save_encrypted_profile(encrypted) with copy_profile_files_from_tmp_dir("with_alarms"): async with prepare_encryption_service() as encryption_service: profile_model = create_model_from_scratch() profile_model.tracked_accounts[0].alarms = [ - ProfileStorageModel._AlarmStorageModel( - name=underscore("RecoveryAccountWarningListed"), + ProfileStorageModel.RecoveryAccountWarningListedStorageModel( is_harmless=False, - identifier=RecoveryAccountWarningListedAlarmIdentifier( + identifier=ProfileStorageModel.RecoveryAccountWarningListedAlarmIdentifierStorageModel( recovery_account=ALT_WORKING_ACCOUNT2_DATA.account.name ), ) ] - encrypted = await encryption_service.encrypt(profile_model.json(indent=4)) + encrypted = await encryption_service.encrypt(profile_model.json()) save_encrypted_profile(encrypted) with copy_profile_files_from_tmp_dir("with_operations"): async with prepare_encryption_service() as encryption_service: profile_model = create_model_from_scratch() - profile_model.transaction = ProfileStorageModel._TransactionStorageModel( - transaction_core=ProfileStorageModel._TransactionCoreStorageModel( + profile_model.transaction = ProfileStorageModel.TransactionStorageModel( + transaction_core=ProfileStorageModel.TransactionCoreStorageModel( operations=[convert_to_representation(OPERATION)] ), transaction_file_path=Path("example/path"), ) - encrypted = await encryption_service.encrypt(profile_model.json(indent=4)) + encrypted = await encryption_service.encrypt(profile_model.json()) + save_encrypted_profile(encrypted) + + with copy_profile_files_from_tmp_dir("with_transaction_containing_negative_tapos"): + async with prepare_encryption_service() as encryption_service: + transaction_core = ProfileStorageModel.TransactionCoreStorageModel() + transaction_core.ref_block_num = -1 + transaction_core.ref_block_prefix = -1 + profile_model = create_model_from_scratch() + profile_model.transaction = ProfileStorageModel.TransactionStorageModel(transaction_core=transaction_core) + encrypted = await encryption_service.encrypt(profile_model.json()) save_encrypted_profile(encrypted) diff --git a/tests/clive-local-tools/clive_local_tools/storage_migration/with_alarms/data/mary/v0.profile b/tests/clive-local-tools/clive_local_tools/storage_migration/with_alarms/data/mary/v0.profile index 0018bd6bfe84d80d626df66ea980a6556ba6f707..8060c7270d12b6dae68fbd0abd743744d258d940 100644 --- a/tests/clive-local-tools/clive_local_tools/storage_migration/with_alarms/data/mary/v0.profile +++ b/tests/clive-local-tools/clive_local_tools/storage_migration/with_alarms/data/mary/v0.profile @@ -1 +1 @@ -111111113WgftMA1rjZc7TSisRcCGYRGfJH1fscuDCJ9oh9jGswAPYSaqPJCCwhnaLbom1HBq6Ut9BGNsJhUZgKwRYDPUcvRLVrhPHDk1fFHJbH7pyTsxsDvfUD3oF7RRHSFZJXhEkvFHbyySafkmcRY7u6yngoS9fxQgx8cfaipLcHGkt8s3zMstieUgg7LiCzpgknpj4fmDS7hCKLapxzVpnFtnuJnD9ebAJpLRPHw3FdyemZEcT769k1dngfjNvQB45HhZBGPethyNc8336yuDshv3ubSu13G1WvPayRoiW33zeLQFsbx8VtPvotqYp9CjwH8YNyKR98K6cuckYzfZsen4hnKGxuonWj5AVkBc2VR6fZ3iDnUDFdnsUWCqb1h8iTuMruiH6p6YiWt9tgCCoeojbVJJeAiNAQfcW1o58QZ4jKrihh5sTMHfHuFnEwZPX5LDztRwuwqGMLn3Md41mqjs1saTYj7e3azs8tGdRyAHosUeeioJJPUfXgo2EHuHHCEWi41GBmYNzvze2cqrk638ejtPbUpyTQsDDst1GK2xSFzM1si7UdkBByMnkHwq36BMC2sVtGnSRjrop9VC892HFbQyGCkom5yJ7GZRCPPWLZ7k8BKRiJzmw9xfPpjejDeGmFuJAwZgnP4s69hhZ9PTK9yLVazDnBCmBeVYW9yZ7tYomX9LPvETApsD2rmfmFpYXtNiZtN1VJuJpmy765hsGRavr2KUGNWYqfgERwAo6YVaZWctBhQcULBg6CJ4AYj9WTTaBMRhosGtwLYKuiULSyxT8vxXU8WiTg5yfCibJGPrtHo8m3RaCPXD8LcVaMqjVTcamYSvzPJwResXZFjfpr3rrFJ7Lq1yMdEhJorephuNmD5fRwHfZTW7i9Z8EVU2sUhJUYQvMbwVo6rPQgfsJ7eu8SSy6g57rvGP3BKGRKHYVcwoK6z6byCVUSRfLuq9oWhMZie61A8JMqHCbg3s1FymRp8WK85WingGt69Js8v3RdgocNMAj9HNGFfJ4R6ZVuaFQgiWcLWyk3rxStmdWnBfKzcygjpAeajaY1rG12tFbyYhVor2aWruGJoG1ye5nMFXGhkumLr3 \ No newline at end of file +1111111196gsM1k4Nccn3dBJRcj5heBjhZ7zMxi2NayeRiCA2cKpfahX1V1eNojcmSgCKLUeW3aTvbRohjhKqSHSfmgnJwHrXnUCFivmh7YynSxJGWyhHideoWi1grgF2bjRUXzdqcBbHs9KfKDyguQRuaTwJX3pAHuMd8TsnxME1nXhSt1mpk1nXEzoBg7VETeQ5iM2WXH7BeMtK5w2kJGZDFiUgnQVZFy8v9tGywL9KbbZJFAWBQJqcmaLmFQsACy7ZYwecA8mR65YfkHuaoRGUf3yTGrNLnxoYT4hHHWFvZoNnymBoCwhg3Upn7JTAXR24RXrihxi8N69EVAnTnnTZwbki5C54JSQD8RRSx2jVjN9z1Jot4UhC4CJUwtmoi5YSuQNRChjHJSWEc9VppRd4dyygFTSpgfeRXuTZcr2YXcT8jhU5m4YFjupN5THUFCnXrZuHmoLAKCjzgMD2bZwNeiCLEUPuFdJ8U9mEChgHmU8jxzqW22HeZkFwidaQZAZS9Bf3dQHaa2gKUAhHHFCKMxrTmybTjmR8v2dyRJ8eeoRSRD3FNxesRc2jcxfo5uiAum85BVkA231ysrK9w2jGs4oUYr5VbDMVr3jN8vjkbkQZNdm \ No newline at end of file diff --git a/tests/clive-local-tools/clive_local_tools/storage_migration/with_operations/data/mary/v0.profile b/tests/clive-local-tools/clive_local_tools/storage_migration/with_operations/data/mary/v0.profile index d3e32d6071dad0c4ccc6e8632a0869c4db066718..dc7f7558a564444193d0c286e2570e90c65c163e 100644 --- a/tests/clive-local-tools/clive_local_tools/storage_migration/with_operations/data/mary/v0.profile +++ b/tests/clive-local-tools/clive_local_tools/storage_migration/with_operations/data/mary/v0.profile @@ -1 +1 @@ -11111111C8BcgVexmwvtfpyp8qWkKfC4n3zdn4YjCFxfnNuxi4XUtzrcfoZ59LyQyrYVNebZnmMV7kCCHh95AwwpmNZULsGZzyB1cd1Gt4CiDhpjLS8wU2ZyDaTKeWDsXJSaPdbu4657SJj3NMHJkMDc5sugSVH8NXmE3XLo4YZ2HfM9LzwPbSNm9Dvqm5pUexgm4DmQePYVzLJRm9Cx6B6UBPaK4PgfcBGnTZ1jnygB2VHHW2Y8PqjwZxY8KUeSvYWSHrBM1QZN83HTwtEWCoJojJUJeMpU5UyuyboJMuGvVbs9eXmaARKKxkXNVGyotY9KtbHqRQZy5WXTtmvPhJKMDTpVMp6dJvqHEGU4Xs6pE3YdJXf5j5G41EZ5PjafjQ2ixpWmBde76xmGfxhdS5Qkhg1DxnrqYTvbr4NKjJRthJigSzfZTEFEuv3wtrShhZpiCCFW8ebvwYcKKgDBjbtjxjgYxtCqzUuk9yiCd6qYrmxAKjHTBSNQF7LBy8HXdS48qqmN9PzQQyBmXNvhHJptp2AYJJw9CF9SUXGuxjbV2jhUuqqwR27Uy7Njt1TxPvBEm2rQAvU4VwJ5a5Ltoks5nVAw58CBBS5KX5nU2PModhALUtCNJAe8m4nxsYWYVKnAPSnZnqiU8QbHcD6U5pChkdmv5twfbgjWkuyWTjkkVm32THTvbJaJkii6MaQSZpx9rBGc4xyXwDG3bavaNVWySEcf8o5x46hjo5UAXwUnDTstvo3dVTiLsx8uMzv46HuEKhwYK9M6t98rjTq2vsYS6E8zgQFAN8hQHezngEGpuM5deVGMGrm6FHewK7TbueEAs2PAV35tx7KiEeJjQ9yK45xz2TLnf18vMobuUTFNXKoVaCmRezzLg3FBpXna5whfEwmoxJpWEMQtdfAXPCgfuoqQyS7GK83HoBzBqP2SMmvvWXpeAUC2ueN8kQc1iqPaesmZuK7N9ULQidfr7x62rUcwjnd7VpMu1HmF2mnAituFEGKP9XgydVXW9vXMqxVE2we1QqPtzXb1C5ioLKgjxGeLuxrYSJCnGQ2WiNUN9TxUYUY2cfrzPqz9m3QfXf55PFy2RPWEgs13Ay6iLPgEjNmLtsLpnZc21T51C81f8Bhg2fd1sJzBVYDjdsZaF9MeFixLq5AE4Gu3iz8iLwxWVP2X2g16xwTp2BRfTpSoVCFHc2CxjWWQpuPRMYbbHN1zsq6ghYmLdAAiLrE1u6oGfo2uCjmFnPwY4tQtwB32eCQuffJtWcTQPBCisHdvQ1FcqT9a7bkTFxWv6J1FMpCdBfzx9CUmWpUZuFguPXySqBCGUQaWHYYki9oqtFd8Pnc7AZjaWY5CqM2kgypRgH3L5v2FXCfxhtGVgmHZKiSmfgMhBZTThcELEHhqM17HPRtMy5GSh4Hi7WfqcgXoJqrL8FNqDR7XpVWrMxbVymbm9ePcTSs6u3MnjhV2FRtik24mxAanEttjrkndStcmedHYbY1Zyd6pyXKndFwNjjdXnabtimd5aEcoWuriAa1XCraeYtjMxfyBVx8dH6ojsW9vBUUcchxzjAQW3qLwRALSZxgpr9xzhTfhKdLnULWcJvNzsxDcE1Dg9FvMmNii5y3cC9yFRGpwyQCyWCTXsEEPec6jF55n5kE99e6swWGqxrBcsBMtf8qYF5xG5bFn5usksdfgsyh9WjUMuKwG2p7uH7AqoR8DeKsCJ2ArYUvbjaJPnuANUYKcwD56WreJBmyQxSwvGTaATufg7v3pBcXsqG7Rmq1Qz7XHqo \ No newline at end of file +11111111BCo8MUbxzLSryUebhQX2nvqBNZVWREzBzQDzuQsjxEGC3FzSk36S4uF2YD3aDbFERuHh2DNc4yCSFRrByH9AW637PofxbMUohuunJpPb7shxsGa8rv9y6XqE5Rpic5tZrG9MRYBLT47xvZF7jdtENLDye3sMgyS8XeRgFWi4hLACbcKkLyEuQdwTAyhQg13m6kiCtqY9Fujp4VwCxyX26q7C2giacUGU4M433B54UkWvDfrniPjhbku7Ar6QKKvcKNEbDGyYEonHYrTVbQspDSvTcs8nmP5S3urtmGGuVgWZkt4QibbrSj7kS8S3yqhUQn42zHzspRZA77xR1qFU9D7qWRr5XqZbs7iEeL23KqXhihah54yQJhA5qHCveaDpKPqN1eWSuDnYaPfwGvHcvk7HJ9dVkvd7wzDvvDKWnzPeMCu9tZujs1wVxefyFCccVHwJ6y7EmMw3K4FY1YwkCETghFKaaf99w52cH7MhN48nxSPxjfKwa9qKPMPaG4epCE3hwEoeLtBjrbKLfEnAGRstKDyUQcLJJMEMSaoAUwgDHLHQ5whKr78XBtJFCs9Wg1iT7fFgi3M2P7bBw2PwXmpLDV2xqpUQSzDapT4UTGPPfNVYzpxMhD6rVBcvMJEtWmJSr4wAUNA4KzNmZDeph1c8hKu6XC8BhoF1XMoucfYevq59UAQGyYgTiS2TQdCb9XxQz1P9B4uAzUdF5WJGqxJxpLkqBRGaHb2yg1vArZn1zB5jKg6f8zW4ESTUUpjKXe5aLCBniBmVgEd3JnA2vqRySLS4ZY4WeKBg9a6kAFofQdMGoTXcV7qB8bwCxvRi7Wo48LyKgHRJX12jZs3Aidqm5JkmYi9Eg3pUgR6oGj8jufc4DySuDYC8sM3YUUnA8VAoTb22 \ No newline at end of file diff --git a/tests/clive-local-tools/clive_local_tools/storage_migration/with_transaction_containing_negative_tapos/__init__.py b/tests/clive-local-tools/clive_local_tools/storage_migration/with_transaction_containing_negative_tapos/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/clive-local-tools/clive_local_tools/storage_migration/with_transaction_containing_negative_tapos/beekeeper/__init__.py b/tests/clive-local-tools/clive_local_tools/storage_migration/with_transaction_containing_negative_tapos/beekeeper/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/clive-local-tools/clive_local_tools/storage_migration/with_transaction_containing_negative_tapos/beekeeper/mary.wallet b/tests/clive-local-tools/clive_local_tools/storage_migration/with_transaction_containing_negative_tapos/beekeeper/mary.wallet new file mode 100644 index 0000000000000000000000000000000000000000..584944b48d0a7d03ea4b5a596b8efec5f4d87ce5 --- /dev/null +++ b/tests/clive-local-tools/clive_local_tools/storage_migration/with_transaction_containing_negative_tapos/beekeeper/mary.wallet @@ -0,0 +1 @@ +"9a68ec4751ade30194e349360ad813dd621d8be888b01b288b9dd090e24eaec720b54dcf2c993e8d5a0de823b495a3359d2236f9b0bbf098f027d74650bae26c35da6be2086fc438b1780686f0a3a3ca" \ No newline at end of file diff --git a/tests/clive-local-tools/clive_local_tools/storage_migration/with_transaction_containing_negative_tapos/beekeeper/mary_encryption.wallet b/tests/clive-local-tools/clive_local_tools/storage_migration/with_transaction_containing_negative_tapos/beekeeper/mary_encryption.wallet new file mode 100644 index 0000000000000000000000000000000000000000..5f60bebe77b1d8dc853a11385aa7712044e1b285 --- /dev/null +++ b/tests/clive-local-tools/clive_local_tools/storage_migration/with_transaction_containing_negative_tapos/beekeeper/mary_encryption.wallet @@ -0,0 +1 @@ +"9a68ec4751ade30194e349360ad813dd621d8be888b01b288b9dd090e24eaec720b54dcf2c993e8d5a0de823b495a3359d2236f9b0bbf098f027d74650bae26c7cec114e4339b6c038df19c0be255b983e527d80b324c00d514037b99a21e92d251e3b26824894a1a15e020b79749809072a65e1cb4f74f2c84c21478ccf6b7ad7fdc73c59402ea8f5ac3c6c029f836d" \ No newline at end of file diff --git a/tests/clive-local-tools/clive_local_tools/storage_migration/with_transaction_containing_negative_tapos/data/__init__.py b/tests/clive-local-tools/clive_local_tools/storage_migration/with_transaction_containing_negative_tapos/data/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/clive-local-tools/clive_local_tools/storage_migration/with_transaction_containing_negative_tapos/data/mary/__init__.py b/tests/clive-local-tools/clive_local_tools/storage_migration/with_transaction_containing_negative_tapos/data/mary/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/clive-local-tools/clive_local_tools/storage_migration/with_transaction_containing_negative_tapos/data/mary/v0.profile b/tests/clive-local-tools/clive_local_tools/storage_migration/with_transaction_containing_negative_tapos/data/mary/v0.profile new file mode 100644 index 0000000000000000000000000000000000000000..885c87c1d43ac643d3c8a78a6a1f4c7593cb46fc --- /dev/null +++ b/tests/clive-local-tools/clive_local_tools/storage_migration/with_transaction_containing_negative_tapos/data/mary/v0.profile @@ -0,0 +1 @@ +1111111196gsM1k4TnKKYaXvQfGBDHRMRf1Jo6tXE9faxsLj4rfrdkAJQ3aRcd6dnVYXnenquj8AJjAiZEJVuhrvVAcsBnAx57HF8eLeuFBBwgmBpe5oXnuZdwfavx1DDtmUfEFKe9dpoPdkyS88e18a6Efu3yJPsFMDiHqFAfRfLK8au4EbarnKWfQ4C2YLXmg14pe2SU7ZzoqFbUPQHSCgBQFBCShdyWNiLvyr7vPVYa1gWuNELLtij99XKtfctitx7X7sWCub5xGPeh7bAsHMduKUJ5UGnKg8Hh4h4hsmgrdM8PyMknkAdSuiQAsCpSWfKDYGJNJBiNZaNN7P8ztWqUuCkjg2MZogVyMuoTtzeV1a1595eWkTWfZ8LJrF7izegZ43Pe7pfCXf5Y8txcuUE9sXY1jZ5bPWXXaHgEdhW3KjawpQZxtrQCUCkEr2vQtKzhA5eXVyBh3AqCFzyE3eXnj3dLRSJjPbnw5HjnyrXYW4RsGAvUH68Ev8FSwAcEMso4MS8cB2JRZyFbMqvTYhQembhbbBHuPZwna63g5KLteZGoZu59DLZKdv5QkWYSHfAVUueLuNNd7SdghQdhoPiRrZyMZ1HnNmojek5C3aujEHGV29 \ No newline at end of file diff --git a/tests/clive-local-tools/clive_local_tools/storage_migration/without_alarms_and_operations/data/mary/v0.profile b/tests/clive-local-tools/clive_local_tools/storage_migration/without_alarms_and_operations/data/mary/v0.profile index c64e3b4ea8e67c541f2a6b71ea3e24c6bb282a23..0c5aa972fe7fb6858e3216e16cd27f0c76a8b249 100644 --- a/tests/clive-local-tools/clive_local_tools/storage_migration/without_alarms_and_operations/data/mary/v0.profile +++ b/tests/clive-local-tools/clive_local_tools/storage_migration/without_alarms_and_operations/data/mary/v0.profile @@ -1 +1 @@ -11111111PbFcb3vAu3RSWCsUu1iwNS4hyweh6kMqM52HqDXQb4hzKZ4jFLTc1bgtLdunDfKJA7ufr2nWjH82dv4KNJh2YAPVyceTMgmBoj9HUBFXEetq6aiaiKw6aQvtn9MmhVcTFPp25HWhWriobLEBhv7Ru8R2TpSJUQpnLWXFQMSczUaphBd49AhiF1YUpsxXTnELatZUDgYdsumJpc6APgec9cZ9yzGJp5phNWXeBNqJ1pz6q2wVVdD7vgwXRgzHKaL2imaR4TRzLqoi8wBE9N1pZZVUcwjLCmSVqVis3aNh9ZsXMB5yt6NZyt4txSw8ZY79hgjopAGb2M8o5X4tHHpA9HuUdhxJcu6rEyJuRVN6x36CRPmNHNBbUqY4qy8UKSDKNJTDhoGqhTVwpjvFVDDBN9wVTHCta94hcWfSRs4Ua4oLVcwWTFrUjFbSXh33PrgopfBcaRAtBNtCecouTbmrUNPRV6LJDFn5aGcuZdhptjMb29wTqvnb8mBvYPhx7GA6sHJy9r3DFMuNpqdecDJg7cStTH38bFg1yBLygHL2GxzaY89reo8jaCZUuG5eVpeARQJJYGm289eMZRX93hxsWAWGyEj1wHLvEcUeJXzK9Y6oMsxG9SNKctYt4mfFf5SX7Gc51aiDwTLrHJdnEhcksYJ4rXvArt5UAGzjWTcMkSMUhzUKXWtwFiU8yCabxKD1W3oKkDwmajCizFA9mYgMT3wUR \ No newline at end of file +11111111JvuHeRa1EZYQbjN4sKJAyDr7vPizCiPrz3d9JiRmg668L1h4FVgGmTZfUCycmLxg99mPG76hnnDiVqV1VUH36ZYnBerMm4oEWgrwr9i4nCg2uxJgBTQjyTa6MH6aEzoRwN9mben5C4UovE7FAnCHrHcMFBaFgei7qznwx19v7xqusNQXt3Wk7VUMbm4t8NC33TSBwU9DVFD94kJGac9rk3MsApD4GzJkoTwEeXdxhKq5iSP5bV7bjTvJSzFC3y1qsPaRoMtY8Rr5y8DQHpEez9xi9xUB1wzRUCCm3uYRvZbQvJacUy2qxnBnXP1DcZSwo2tdznztWaCL4tMdbJVio3waK52V51SSzsU24t5tgWAH9LMtfJJewYtRUx9tRmQoSHB3CsQKSCQFxANWBYL43a4KH54suyZk92oEPNfnK7egEPCcRNJFw1JzWzTPd \ No newline at end of file diff --git a/tests/conftest.py b/tests/conftest.py index 13219d6e9d77ae7416604fee31557d0310c57a08..354d85c78d9faa40a4beebb574657ff15548455d 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -24,6 +24,10 @@ from clive.__private.core.commands.import_key import ImportKey from clive.__private.core.constants.setting_identifiers import DATA_PATH, LOG_LEVEL_1ST_PARTY, LOG_LEVELS, LOG_PATH from clive.__private.core.world import World from clive.__private.logger import logger +from clive.__private.models.schemas import ( + TestnetAssetsPolicy, + set_policies, +) from clive.__private.settings import safe_settings, settings from clive_local_tools.data.constants import ( BEEKEEPER_REMOTE_ADDRESS_ENV_NAME, @@ -45,6 +49,11 @@ if TYPE_CHECKING: from clive_local_tools.types import EnvContextFactory, GenericEnvContextFactory, SetupWalletsFactory, Wallets +@pytest.fixture(autouse=True) +def _use_testnet_assets() -> None: + set_policies(TestnetAssetsPolicy(use_testnet_assets=False)) + + @pytest.fixture(autouse=True, scope="session") def manage_thread_pool() -> Iterator[None]: with thread_pool: diff --git a/tests/functional/cli/process/test_process_proxy.py b/tests/functional/cli/process/test_process_proxy.py index 76b546e0131e1f47d89bbdba862a5767130dd4c8..736648c443d144fc7f6e4a009fad188589645d74 100644 --- a/tests/functional/cli/process/test_process_proxy.py +++ b/tests/functional/cli/process/test_process_proxy.py @@ -5,6 +5,7 @@ from typing import TYPE_CHECKING, Final import pytest from clive.__private.core.constants.node import CANCEL_PROXY_VALUE +from clive.__private.models.schemas import AccountWitnessProxyOperation if TYPE_CHECKING: import test_tools as tt @@ -15,7 +16,6 @@ from clive_local_tools.checkers.blockchain_checkers import assert_operations_pla from clive_local_tools.cli.exceptions import CLITestCommandError from clive_local_tools.data.constants import WORKING_ACCOUNT_KEY_ALIAS from clive_local_tools.testnet_block_log.constants import WATCHED_ACCOUNTS_NAMES, WORKING_ACCOUNT_NAME -from schemas.operations import AccountWitnessProxyOperation ACCOUNT_NAME: Final[str] = WORKING_ACCOUNT_NAME PROXY_ACCOUNT_NAME: Final[str] = WATCHED_ACCOUNTS_NAMES[0] diff --git a/tests/functional/cli/process/test_process_transaction.py b/tests/functional/cli/process/test_process_transaction.py index e90730c2db39bbfcdea375a501a3fe306c0cf79e..3d3f91326456e4b11007f3bb908eaa112f51fba2 100644 --- a/tests/functional/cli/process/test_process_transaction.py +++ b/tests/functional/cli/process/test_process_transaction.py @@ -6,7 +6,7 @@ import pytest import test_tools as tt from clive.__private.cli.exceptions import CLINoProfileUnlockedError -from clive.__private.models.schemas import CustomJsonOperation +from clive.__private.models.schemas import CustomJsonOperation, JsonString from clive_local_tools.checkers.blockchain_checkers import ( assert_operations_placed_in_blockchain, assert_transaction_in_blockchain, @@ -41,7 +41,7 @@ async def test_load_custom_json_from_file(node: tt.RawNode, cli_tester: CLITeste required_auths=[], required_posting_auths=[WORKING_ACCOUNT_DATA.account.name], id_=ID, - json_=json_, + json_=JsonString(json_), ) # ACT diff --git a/tests/functional/cli/process/test_process_transfer.py b/tests/functional/cli/process/test_process_transfer.py index 9c8f9b91bf4eb4df466afe810fbf62adbce96704..c4546d2b1e2ebacc7ae30e48bb8964cdc71d5d8e 100644 --- a/tests/functional/cli/process/test_process_transfer.py +++ b/tests/functional/cli/process/test_process_transfer.py @@ -9,6 +9,7 @@ from clive.__private.cli.exceptions import ( CLINoProfileUnlockedError, CLITransactionNotSignedError, ) +from clive.__private.models.schemas import TransferOperation from clive_local_tools.checkers.blockchain_checkers import ( assert_operations_placed_in_blockchain, assert_transaction_in_blockchain, @@ -16,7 +17,6 @@ from clive_local_tools.checkers.blockchain_checkers import ( from clive_local_tools.cli.exceptions import CLITestCommandError from clive_local_tools.data.constants import WORKING_ACCOUNT_KEY_ALIAS from clive_local_tools.testnet_block_log.constants import WATCHED_ACCOUNTS_DATA, WORKING_ACCOUNT_NAME -from schemas.operations import TransferOperation if TYPE_CHECKING: from clive_local_tools.cli.cli_tester import CLITester diff --git a/tests/functional/cli/process/test_process_transfer_schedule.py b/tests/functional/cli/process/test_process_transfer_schedule.py index a2ffca215ae9998b0fa9aae0a739c31c88e3b36a..93f324e263d5ad64c871133a6f233a2c73aa770a 100644 --- a/tests/functional/cli/process/test_process_transfer_schedule.py +++ b/tests/functional/cli/process/test_process_transfer_schedule.py @@ -10,10 +10,10 @@ import test_tools as tt from clive.__private.cli.common.parsers import scheduled_transfer_frequency_parser from clive.__private.core.constants.node_special_assets import SCHEDULED_TRANSFER_REMOVE_ASSETS from clive.__private.core.date_utils import timedelta_to_int_hours +from clive.__private.models.schemas import RecurrentTransferOperation from clive_local_tools.checkers.blockchain_checkers import assert_operations_placed_in_blockchain from clive_local_tools.data.constants import WORKING_ACCOUNT_KEY_ALIAS from clive_local_tools.testnet_block_log.constants import WATCHED_ACCOUNTS_NAMES, WORKING_ACCOUNT_NAME -from schemas.operations import RecurrentTransferOperation ACCOUNT_NAME: Final[str] = WORKING_ACCOUNT_NAME RECEIVER: Final[str] = WATCHED_ACCOUNTS_NAMES[0] diff --git a/tests/functional/cli/test_perform_working_account_load_in_regular_operations.py b/tests/functional/cli/test_perform_working_account_load_in_regular_operations.py index fbd287dcea786647b8da4f119a7615db1ceaaad8..989524e51ddbc57171464a67d0be3999e0417d0f 100644 --- a/tests/functional/cli/test_perform_working_account_load_in_regular_operations.py +++ b/tests/functional/cli/test_perform_working_account_load_in_regular_operations.py @@ -6,7 +6,7 @@ import pytest import test_tools as tt from clive.__private.core.keys.keys import PrivateKeyAliased -from clive.__private.models.schemas import CustomJsonOperation, TransferOperation +from clive.__private.models.schemas import CustomJsonOperation, JsonString, TransferOperation from clive_local_tools.checkers.blockchain_checkers import assert_operations_placed_in_blockchain from clive_local_tools.data.constants import ( ALT_WORKING_ACCOUNT2_KEY_ALIAS, @@ -82,7 +82,7 @@ async def test_custom_authority_in_custom_json_operation( required_auths=[], required_posting_auths=[ALT_WORKING_ACCOUNT2_NAME], id_=custom_id, - json_=custom_json, + json_=JsonString(custom_json), ) # ACT diff --git a/tests/functional/commands/test_load_transaction.py b/tests/functional/commands/test_load_transaction.py index 79d67640720db0a274d4634c5260e5dbce7ad37f..e7fb68c4a22ca23b5337d14766f7bb9f50be1b2a 100644 --- a/tests/functional/commands/test_load_transaction.py +++ b/tests/functional/commands/test_load_transaction.py @@ -7,16 +7,18 @@ import pytest from clive.__private.core import iwax from clive.__private.core.commands.load_transaction import LoadTransaction from clive.__private.models import Asset, Transaction -from clive.__private.models.schemas import TransferOperation +from clive.__private.models.schemas import HiveDateTime, TransferOperation, convert_to_representation if TYPE_CHECKING: from pathlib import Path VALID_TRANSACTION: Final[Transaction] = Transaction( - operations=[TransferOperation(from_="alice", to="bob", amount=Asset.hbd(1), memo="test")], + operations=[ + convert_to_representation(TransferOperation(from_="alice", to="bob", amount=Asset.hbd(1), memo="test")) + ], ref_block_num=1, ref_block_prefix=2, - expiration="2021-01-01T00:00:00", + expiration=HiveDateTime("2021-01-01T00:00:00"), extensions=[], signatures=[], ) @@ -30,7 +32,7 @@ async def test_loading_valid_transaction_file(tmp_path: Path, mode: Literal["jso file_path = tmp_path / file_name if mode == "json": - file_path.write_text(expected_transaction.json(by_alias=True)) + file_path.write_text(expected_transaction.json()) else: file_path.write_bytes(iwax.serialize_transaction(expected_transaction)) diff --git a/tests/unit/storage/test_loading_and_migration.py b/tests/unit/storage/test_loading_and_migration.py index e64b337c3aca0b7b6bccd3036d0d2f167cd69d9d..82eb09a340fc270972041fc9542819b6be29073d 100644 --- a/tests/unit/storage/test_loading_and_migration.py +++ b/tests/unit/storage/test_loading_and_migration.py @@ -14,6 +14,7 @@ from clive_local_tools.storage_migration import ( copy_profile_with_operations, copy_profile_without_alarms_and_operations, ) +from clive_local_tools.storage_migration.helpers import copy_profile_with_transaction_containing_negative_tapos from clive_local_tools.testnet_block_log import ALT_WORKING_ACCOUNT1_NAME, ALT_WORKING_ACCOUNT2_NAME PROFILE_NAME: Final[str] = ALT_WORKING_ACCOUNT1_NAME @@ -65,3 +66,16 @@ async def test_migrate_profile_with_operations() -> None: assert profile.operations, "Operations should be loaded from older profile version" operation = profile.operations[0] assert operation == OPERATION, f"Operation should be `{OPERATION}`, but got `{operation}`" + + +async def test_migrate_profile_with_transaction_containing_negative_tapos() -> None: + # ARRANGE + copy_profile_with_transaction_containing_negative_tapos(safe_settings.data_path) + + async with ProfileChecker.from_password(PROFILE_NAME, PROFILE_PASSWORD) as profile_checker: + # ACT + profile = await profile_checker.profile + + # ASSERT + assert profile.transaction.ref_block_num == 0, "ref_block_num should be 0 after migration" + assert profile.transaction.ref_block_prefix == 0, "ref_block_prefix should be 0 after migration" diff --git a/tests/unit/storage/test_storage_revision.py b/tests/unit/storage/test_storage_revision.py index 9b0cb9de86543c72e0be9a1e04908981002941b4..25f650f6afa710ada3c275b58fb6aafc05634c96 100644 --- a/tests/unit/storage/test_storage_revision.py +++ b/tests/unit/storage/test_storage_revision.py @@ -5,7 +5,7 @@ from typing import Final from clive.__private.storage import ProfileStorageModel from clive.__private.storage.storage_history import StorageHistory -REVISIONS: Final[list[str]] = ["ffc97b51", "a721f943", "9c46df0c"] +REVISIONS: Final[list[str]] = ["d8fef2cd", "3b81a04e", "0fc1e8b3"] LATEST_REVISION: Final[str] = REVISIONS[-1] diff --git a/tests/unit/test_chain_id_in_profile.py b/tests/unit/test_chain_id_in_profile.py index 30c934212466b768233007f0df103103c9256928..30178f9a97ff978abdaf1380a8f7b8fa1a24a9d4 100644 --- a/tests/unit/test_chain_id_in_profile.py +++ b/tests/unit/test_chain_id_in_profile.py @@ -7,7 +7,7 @@ import pytest from clive.__private.core.constants.setting_identifiers import NODE_CHAIN_ID from clive.__private.core.profile import InvalidChainIdError, Profile from clive.__private.models import Asset, Transaction -from clive.__private.models.schemas import TransferOperation +from clive.__private.models.schemas import TransferOperation, convert_to_representation from clive.__private.settings import safe_settings, settings from clive_local_tools.data.constants import TESTNET_CHAIN_ID from clive_local_tools.data.generates import generate_wallet_name, generate_wallet_password @@ -91,7 +91,9 @@ async def test_chain_id_is_retrieved_from_api_if_not_set( await world.commands.import_key(key_to_import=wallet.private_key) transaction = Transaction( operations=[ - TransferOperation(from_="doesnt-matter", to="null", amount=Asset.hive(1), memo=""), + convert_to_representation( + TransferOperation(from_="doesnt-matter", to="null", amount=Asset.hive(1), memo="") + ), ] )