Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • hive/helpy
1 result
Show changes
Commits on Source (34)
Showing
with 587 additions and 239 deletions
stages:
- static_code_analysis
- tests
- build
- tests
- deploy
workflow:
......@@ -19,13 +19,22 @@ variables:
TXT_BLUE: "\e[1;34m"
TXT_CLEAR: "\e[0m"
DATA_CACHE_HIVE_PREFIX: "/cache/replay_data_hive"
BLOCK_LOG_SOURCE_DIR_5M: /blockchain/block_log_5m
BLOCK_LOG_SOURCE_DIR_MIRRORNET_5M: /blockchain/block_log_5m_mirrornet
# uses registry.gitlab.syncad.com/hive/hive/ci-base-image:ubuntu22.04-10
CI_BASE_IMAGE_TAG: "@sha256:080b16fd53013aeb9b89b00a8dfc90fecf886173f46448b05f45cee376c43330"
CI_BASE_IMAGE: "registry.gitlab.syncad.com/hive/hive/ci-base-image${CI_BASE_IMAGE_TAG}"
include:
- project: 'hive/hive'
# This has to be the same as the commit checked out in the submodule
ref: 2c5e0bca0a2725bc5e4fbf4280e0a3c862d21bb6
file: '/scripts/ci-helpers/prepare_data_image_job.yml'
- project: 'hive/common-ci-configuration'
ref: 44d0cec8cfa8fb87d0daf5cef1485fb57b9e26fd
# This should be the same version of Common CI defined in /hive/scripts/ci-helpers/prepare_data_image_job.yml
ref: 7ea99b10bbf0f9b1fbb88f52a78ead4c82f15d75
file: '/templates/python_projects.gitlab-ci.yml'
image: ${CI_BASE_IMAGE}
......@@ -54,33 +63,74 @@ type_check_with_mypy:
#<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<| STATIC CODE ANALYSIS |<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>| BUILD |>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
prepare_hived_image:
extends: .prepare_hived_image
needs: []
stage: build
variables:
SUBMODULE_DIR: "$CI_PROJECT_DIR/hive"
REGISTRY_USER: "$HIVED_CI_IMGBUILDER_USER"
REGISTRY_PASS: $HIVED_CI_IMGBUILDER_PASSWORD
BINARY_CACHE_PATH: "$CI_PROJECT_DIR/hived-binaries"
HIVE_NETWORK_TYPE: mainnet
tags:
- public-runner-docker
- hived-for-tests
prepare_hived_data:
extends: .prepare_hived_data_5m
needs:
- job: prepare_hived_image
artifacts: true
stage: build
variables:
SUBMODULE_DIR: "$CI_PROJECT_DIR/hive"
BLOCK_LOG_SOURCE_DIR: $BLOCK_LOG_SOURCE_DIR_5M
CONFIG_INI_SOURCE: "$CI_PROJECT_DIR/hive/docker/config_5M.ini"
tags:
- data-cache-storage
build_wheel:
stage: build
extends: .build_wheel_template
#<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<| BUILD |<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>| TESTS |>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
run_tests:
stage: tests
extends: .project_develop_configuration_template
needs: [ ] # do not wait for previous stage to finish before starting this one
needs:
- job: prepare_hived_data
artifacts: true
services:
- name: $HIVED_IMAGE_NAME
alias: hived-instance
variables:
DATA_SOURCE: "${DATA_CACHE_HIVE_PREFIX}_${HIVED_COMMIT}"
LOG_FILE: $CI_JOB_NAME.log
command: ["--replay-blockchain", "--stop-replay-at-block=5000000"]
variables:
HIVED_COMMIT: $HIVED_COMMIT
HIVED_UID: $HIVED_UID
PATH_TO_REPORT: "$CI_PROJECT_DIR/report.xml"
script:
- echo -e "${TXT_BLUE}Launching tests...${TXT_CLEAR}"
- python3 -m pytest --durations 0 --junitxml=report.xml tests/
- python3 -m pytest --durations 0 --junitxml=report.xml --hived-http-endpoint="http://hived-instance:8091" tests/
artifacts:
when: always
expire_in: 1 week
reports:
junit: $PATH_TO_REPORT
tags:
- data-cache-storage
#<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<| TESTS |<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>| BUILD |>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
build_wheel:
stage: build
extends: .build_wheel_template
#<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<| BUILD |<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>| DEPLOY |>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.deploy_wheel_needs: &deploy_wheel_needs
......
[submodule "hive"]
path = hive
url = ../hive.git
......@@ -11,7 +11,7 @@ repos:
- id: trailing-whitespace
- id: end-of-file-fixer
- repo: https://github.com/python-poetry/poetry
rev: 1.6.1
rev: 1.7.1
hooks:
- id: poetry-lock
name: checking if poetry.lock is consistent with pyproject.toml
......
......@@ -11,7 +11,7 @@ from helpy._handles import (
from helpy._interfaces import wax
from helpy._interfaces.asset import Hf26Asset, LegacyAsset
from helpy._interfaces.stopwatch import Stopwatch
from helpy._interfaces.time import Time, TimeFormats
from helpy._interfaces.time import OffsetTimeControl, SpeedUpRateTimeControl, StartTimeControl, Time, TimeFormats
from helpy._interfaces.url import HttpUrl, P2PUrl, WsUrl
__version__ = "0.0.0"
......@@ -26,7 +26,10 @@ __all__ = [
"HivedNotificationHandler",
"HttpUrl",
"LegacyAsset",
"OffsetTimeControl",
"P2PUrl",
"SpeedUpRateTimeControl",
"StartTimeControl",
"Stopwatch",
"Time",
"TimeFormats",
......
......@@ -3,29 +3,25 @@ from __future__ import annotations
import asyncio
import time
from abc import ABC, abstractmethod
from datetime import timedelta
from typing import TYPE_CHECKING, ClassVar
from typing import TYPE_CHECKING, Any
from helpy.exceptions import HelpyError
from helpy._communication.settings import CommunicationSettings
from helpy.exceptions import CommunicationError
if TYPE_CHECKING:
from helpy._interfaces.url import HttpUrl
class CommunicationError(HelpyError):
"""Base class for all communication related errors."""
class ExceededAmountOfRetriesError(CommunicationError):
"""Raised if exceeded amount of retries."""
class AbstractCommunicator(ABC):
"""Provides basic interface for communicators, which can implement communications using different way."""
max_retries: ClassVar[int] = 5
period_between_retries: ClassVar[timedelta] = timedelta(seconds=1)
timeout: ClassVar[timedelta] = timedelta(seconds=2)
def __init__(self, *args: Any, settings: CommunicationSettings | None = None, **kwargs: Any) -> None:
self.__settings = settings or CommunicationSettings()
super().__init__(*args, **kwargs)
@property
def settings(self) -> CommunicationSettings:
return self.__settings
@abstractmethod
def send(self, url: HttpUrl, data: str) -> str:
......@@ -35,26 +31,17 @@ class AbstractCommunicator(ABC):
async def async_send(self, url: HttpUrl, data: str) -> str:
"""Sends to given url given data asynchronously."""
@classmethod
async def _async_sleep_for_retry(cls) -> None:
async def _async_sleep_for_retry(self) -> None:
"""Sleeps using asyncio.sleep (for asynchronous implementations)."""
await asyncio.sleep(cls.period_between_retries.total_seconds())
await asyncio.sleep(self.settings.period_between_retries.total_seconds())
@classmethod
def _sleep_for_retry(cls) -> None:
def _sleep_for_retry(self) -> None:
"""Sleeps using time.sleep (for synchronous implementations)."""
time.sleep(cls.period_between_retries.total_seconds())
time.sleep(self.settings.period_between_retries.total_seconds())
@classmethod
def _is_amount_of_retries_exceeded(cls, amount: int) -> bool:
def _is_amount_of_retries_exceeded(self, amount: int) -> bool:
"""Returns is given amount of retries exceeds max_retries."""
return amount > cls.max_retries
@classmethod
def _assert_is_amount_of_retries_exceeded(cls, amount: int) -> None:
"""Checks is given amount of retries exceeds max_retries and if so raises ExceededAmountOfRetriesError."""
if cls._is_amount_of_retries_exceeded(amount=amount):
raise ExceededAmountOfRetriesError
return amount > self.settings.max_retries
@classmethod
def _json_headers(cls) -> dict[str, str]:
......
......@@ -6,8 +6,8 @@ import httpx
from helpy._communication.abc.communicator import (
AbstractCommunicator,
CommunicationError,
)
from helpy.exceptions import CommunicationError
if TYPE_CHECKING:
from helpy._interfaces.url import HttpUrl
......@@ -19,7 +19,7 @@ class HttpxCommunicator(AbstractCommunicator):
def __init__(self) -> None:
super().__init__()
self.__async_client: httpx.AsyncClient | None = httpx.AsyncClient(
timeout=self.timeout.total_seconds(), http2=True
timeout=self.settings.timeout.total_seconds(), http2=True
)
async def close(self) -> None:
......@@ -44,7 +44,7 @@ class HttpxCommunicator(AbstractCommunicator):
self._assert_status_code(status_code=response.status_code, sent=data, received=data_received)
return data_received # noqa: TRY300
except httpx.ConnectError as error:
raise CommunicationError(url, data) from error
raise CommunicationError(url=url.as_string(), request=data) from error
except httpx.HTTPError as error:
last_exception = error
await self._async_sleep_for_retry()
......@@ -64,7 +64,7 @@ class HttpxCommunicator(AbstractCommunicator):
self._assert_status_code(status_code=response.status_code, sent=data, received=data_received)
return data_received # noqa: TRY300
except httpx.ConnectError as error:
raise CommunicationError(url, data) from error
raise CommunicationError(url=url.as_string(), request=data) from error
except httpx.HTTPError as error:
last_exception = error
self._sleep_for_retry()
......
......@@ -7,8 +7,8 @@ import requests
from helpy._communication.abc.communicator import (
AbstractCommunicator,
CommunicationError,
)
from helpy.exceptions import CommunicationError
if TYPE_CHECKING:
from helpy._interfaces.url import HttpUrl
......@@ -35,7 +35,7 @@ class RequestCommunicator(AbstractCommunicator):
self._assert_status_code(status_code=response.status_code, sent=data, received=data_received)
return data_received # noqa: TRY300
except httpx.ConnectError as error:
raise CommunicationError(url, data) from error
raise CommunicationError(url=url.as_string(), request=data) from error
except httpx.HTTPError as error:
last_exception = error
self._sleep_for_retry()
......
from __future__ import annotations
from dataclasses import dataclass
from datetime import timedelta
@dataclass
class CommunicationSettings:
max_retries: int = 5
timeout: timedelta = timedelta(seconds=5)
period_between_retries: timedelta = timedelta(seconds=1)
......@@ -5,6 +5,7 @@ import re
from abc import ABC
from collections import defaultdict
from datetime import datetime
from enum import IntEnum
from functools import partial, wraps
from typing import (
TYPE_CHECKING,
......@@ -20,6 +21,7 @@ from helpy._handles.abc.handle import (
AbstractAsyncHandle,
AbstractSyncHandle,
)
from helpy._handles.batch_handle import AsyncBatchHandle, SyncBatchHandle
from schemas._preconfigured_base_model import PreconfiguredBaseModel
from schemas.operations.representations.legacy_representation import LegacyRepresentation
......@@ -30,11 +32,19 @@ if TYPE_CHECKING:
P = ParamSpec("P")
HandleT = TypeVar("HandleT", bound=AbstractAsyncHandle | AbstractSyncHandle)
SyncHandleT = AbstractSyncHandle | SyncBatchHandle
AsyncHandleT = AbstractAsyncHandle | AsyncBatchHandle
HandleT = TypeVar("HandleT", bound=SyncHandleT | AsyncHandleT)
RegisteredApisT = defaultdict[bool, defaultdict[str, set[str]]]
class ApiArgumentSerialization(IntEnum):
OBJECT = 0
ARRAY = 1
DOUBLE_ARRAY = 2
def _convert_pascal_case_to_sneak_case(pascal_case_input: str) -> str:
return re.sub(r"(?<!^)(?=[A-Z])", "_", pascal_case_input).lower()
......@@ -63,9 +73,23 @@ class AbstractApi(ABC, Generic[HandleT]):
return partial(json.dumps, cls=JsonEncoder)
def _serialize_params(self, args: Any, kwargs: dict[str, Any]) -> str: # noqa: ARG002
def _serialize_params(self, args: Any, kwargs: dict[str, Any]) -> str:
"""Return serialized given params. Can be overloaded."""
return AbstractApi.json_dumps()(kwargs)
json_dumps = AbstractApi.json_dumps()
if self.argument_serialization() == ApiArgumentSerialization.ARRAY:
return json_dumps(args)
if self.argument_serialization() == ApiArgumentSerialization.DOUBLE_ARRAY:
return json_dumps([args])
prepared_kwargs = {}
for key, value in kwargs.items():
prepared_kwargs[key.strip("_")] = value
return json_dumps(prepared_kwargs)
def _verify_positional_keyword_args(self, args: Any, kwargs: dict[str, Any]) -> None:
if self.argument_serialization() == ApiArgumentSerialization.OBJECT:
assert len(args) == 0, "This api allows only keyword arguments; Ex.: foo(a=1, b=2, c=3)"
else:
assert len(kwargs) == 0, "This api allows only positional arguments; Ex.: foo(1, 2, 3)"
@classmethod
def _api_name(cls) -> str:
......@@ -90,14 +114,17 @@ class AbstractApi(ABC, Generic[HandleT]):
return pytest_is_running.is_running()
def argument_serialization(self) -> ApiArgumentSerialization:
return ApiArgumentSerialization.OBJECT
def __init__(self, owner: HandleT) -> None:
self._owner = owner
class AbstractSyncApi(AbstractApi[AbstractSyncHandle]):
class AbstractSyncApi(AbstractApi[SyncHandleT]):
"""Base class for all apis, that provides synchronous endpoints."""
def __init__(self, owner: AbstractSyncHandle) -> None:
def __init__(self, owner: SyncHandleT) -> None:
super().__init__(owner)
@classmethod
......@@ -110,6 +137,7 @@ class AbstractSyncApi(AbstractApi[AbstractSyncHandle]):
@wraps(wrapped_function)
def impl(this: AbstractSyncApi, *args: P.args, **kwargs: P.kwargs) -> ExpectResultT:
this._verify_positional_keyword_args(args, kwargs)
return this._owner._send( # type: ignore[no-any-return, union-attr, misc]
endpoint=f"{api_name}.{wrapped_function_name}",
params=this._serialize_params(args=args, kwargs=kwargs),
......@@ -119,10 +147,10 @@ class AbstractSyncApi(AbstractApi[AbstractSyncHandle]):
return impl # type: ignore[return-value]
class AbstractAsyncApi(AbstractApi[AbstractAsyncHandle]):
class AbstractAsyncApi(AbstractApi[AsyncHandleT]):
"""Base class for all apis, that provides asynchronous endpoints."""
def __init__(self, owner: AbstractAsyncHandle) -> None:
def __init__(self, owner: AsyncHandleT) -> None:
super().__init__(owner)
@classmethod
......@@ -137,6 +165,7 @@ class AbstractAsyncApi(AbstractApi[AbstractAsyncHandle]):
@wraps(wrapped_function)
async def impl(this: AbstractAsyncApi, *args: P.args, **kwargs: P.kwargs) -> ExpectResultT:
this._verify_positional_keyword_args(args, kwargs)
return ( # type: ignore[no-any-return]
await this._owner._async_send( # type: ignore[misc]
endpoint=f"{api_name}.{wrapped_function_name}",
......
......@@ -2,11 +2,7 @@ from __future__ import annotations
from typing import Generic
from helpy._handles.abc.api import HandleT
from helpy._handles.abc.handle import (
AbstractAsyncHandle,
AbstractSyncHandle,
)
from helpy._handles.abc.api import AsyncHandleT, HandleT, SyncHandleT
class AbstractApiCollection(Generic[HandleT]):
......@@ -16,15 +12,15 @@ class AbstractApiCollection(Generic[HandleT]):
self._owner = owner
class AbstractAsyncApiCollection(AbstractApiCollection[AbstractAsyncHandle]):
class AbstractAsyncApiCollection(AbstractApiCollection[AsyncHandleT]):
"""Base class for Async Api Collections."""
def __init__(self, owner: AbstractAsyncHandle) -> None:
def __init__(self, owner: AsyncHandleT) -> None:
super().__init__(owner)
class AbstractSyncApiCollection(AbstractApiCollection[AbstractSyncHandle]):
class AbstractSyncApiCollection(AbstractApiCollection[SyncHandleT]):
"""Base class for Sync Api Collections."""
def __init__(self, owner: AbstractSyncHandle) -> None:
def __init__(self, owner: SyncHandleT) -> None:
super().__init__(owner)
......@@ -8,9 +8,10 @@ from loguru import logger
from typing_extensions import Self
from helpy._communication.httpx_communicator import HttpxCommunicator
from helpy._handles.build_json_rpc_call import build_json_rpc_call
from helpy._interfaces.context import ContextAsync, ContextSync
from helpy._interfaces.stopwatch import Stopwatch
from helpy.exceptions import HelpyError, RequestError
from helpy.exceptions import CommunicationError, HelpyError, RequestError
from schemas.jsonrpc import ExpectResultT, JSONRPCResult, get_response_model
if TYPE_CHECKING:
......@@ -19,9 +20,8 @@ if TYPE_CHECKING:
from loguru import Logger
from helpy._communication.abc.communicator import AbstractCommunicator
from helpy._handles.abc.api_collection import (
AbstractApiCollection,
)
from helpy._handles.abc.api_collection import AbstractAsyncApiCollection, AbstractSyncApiCollection
from helpy._handles.batch_handle import AsyncBatchHandle, SyncBatchHandle
from helpy._interfaces.url import HttpUrl
......@@ -65,7 +65,7 @@ class AbstractHandle:
self.__http_endpoint = value
@property
def api(self) -> AbstractApiCollection[AbstractAsyncHandle] | AbstractApiCollection[AbstractSyncHandle]:
def api(self) -> AbstractAsyncApiCollection | AbstractSyncApiCollection:
return self.__api
@property
......@@ -78,7 +78,7 @@ class AbstractHandle:
return self.__logger
@abstractmethod
def _construct_api(self) -> AbstractApiCollection[AbstractAsyncHandle] | AbstractApiCollection[AbstractSyncHandle]:
def _construct_api(self) -> AbstractAsyncApiCollection | AbstractSyncApiCollection:
"""Return api collection."""
@abstractmethod
......@@ -118,20 +118,13 @@ class AbstractHandle:
assert isinstance(serialized_data, JSONRPCResult)
return serialized_data
@classmethod
def _build_json_rpc_call(cls, *, method: str, params: str) -> str:
"""Builds params for jsonrpc call."""
return (
"""{"id": 0, "jsonrpc": "2.0", "method": \""""
+ method
+ '"'
+ (""", "params":""" + params if params else "")
+ "}"
)
def __configure_logger(self) -> Logger:
return logger.bind(**self._logger_extras())
@abstractmethod
def batch(self, *, delay_error_on_data_access: bool = False) -> SyncBatchHandle[Any] | AsyncBatchHandle[Any]:
"""Returns sync batch handle."""
class _SyncCall(Protocol):
def __call__(
......@@ -151,31 +144,38 @@ def _retry_on_unable_to_acquire_database_lock( # noqa: C901
async_version: bool,
) -> Callable[[_SyncCall | _AsyncCall], Callable[..., JSONRPCResult[Any] | Awaitable[JSONRPCResult[Any]]]]:
# inspired by: https://gitlab.syncad.com/hive/test-tools/-/blob/a8290d47ec3638fb31573182a3311137542a6637/package/test_tools/__private/communication.py#L33
def __workaround_communication_problem_with_node(
def __workaround_communication_problem_with_node( # noqa: C901
send_request: _SyncCall | _AsyncCall,
) -> Callable[..., JSONRPCResult[Any]]:
def __handle_exception(exception: RequestError) -> None:
message = exception.error
if isinstance(message, dict):
message = message["message"]
if "Unable to acquire database lock" in message:
logger.debug("Ignored 'Unable to acquire database lock'")
return
def __handle_exception(this: AbstractHandle, exception: RequestError | CommunicationError) -> None:
ignored_messages = [
"Unable to acquire database lock",
"Unable to acquire forkdb lock",
]
message = exception.error if isinstance(exception, RequestError) else str(exception.args)
for imsg in ignored_messages:
if imsg in message:
logger.debug(f"Ignored for {this.http_endpoint}: '{imsg}'")
return
raise exception
def sync_impl(*args: Any, **kwargs: Any) -> JSONRPCResult[Any]:
def sync_impl(this: AbstractHandle, *args: Any, **kwargs: Any) -> JSONRPCResult[Any]:
while True:
try:
return send_request(*args, **kwargs) # type: ignore[return-value]
return send_request(*[this, *args], **kwargs) # type: ignore[return-value]
except CommunicationError as exception:
__handle_exception(this, exception)
except RequestError as exception:
__handle_exception(exception)
__handle_exception(this, exception)
async def async_impl(*args: Any, **kwargs: Any) -> JSONRPCResult[Any]:
async def async_impl(this: AbstractHandle, *args: Any, **kwargs: Any) -> JSONRPCResult[Any]:
while True:
try:
return await send_request(*args, **kwargs) # type: ignore[no-any-return, misc]
return await send_request(*[this, *args], **kwargs) # type: ignore[no-any-return, misc]
except CommunicationError as exception:
__handle_exception(this, exception)
except RequestError as exception:
__handle_exception(exception)
__handle_exception(this, exception)
return async_impl if async_version else sync_impl # type: ignore[return-value]
......@@ -190,11 +190,11 @@ class AbstractAsyncHandle(ABC, AbstractHandle, ContextAsync[Self]): # type: ign
self, *, endpoint: str, params: str, expected_type: type[ExpectResultT]
) -> JSONRPCResult[ExpectResultT]:
"""Sends data asynchronously to handled service basing on jsonrpc."""
request = self._build_json_rpc_call(method=endpoint, params=params)
self.logger.debug(f"sending to `{self.http_endpoint.as_string()}`: `{request}`")
request = build_json_rpc_call(method=endpoint, params=params)
self.logger.trace(f"sending to `{self.http_endpoint.as_string()}`: `{request}`")
with Stopwatch() as record:
response = await self._communicator.async_send(self.http_endpoint, data=request)
self.logger.debug(
self.logger.trace(
f"got response in {record.seconds_delta :.5f}s from `{self.http_endpoint.as_string()}`: `{response}`"
)
return self._response_handle(params=params, response=response, expected_type=expected_type)
......@@ -215,7 +215,7 @@ class AbstractSyncHandle(ABC, AbstractHandle, ContextSync[Self]): # type: ignor
@_retry_on_unable_to_acquire_database_lock(async_version=False) # type: ignore[arg-type]
def _send(self, *, endpoint: str, params: str, expected_type: type[ExpectResultT]) -> JSONRPCResult[ExpectResultT]:
"""Sends data synchronously to handled service basing on jsonrpc."""
request = self._build_json_rpc_call(method=endpoint, params=params)
request = build_json_rpc_call(method=endpoint, params=params)
self.logger.debug(f"sending to `{self.http_endpoint.as_string()}`: `{request}`")
with Stopwatch() as record:
response = self._communicator.send(self.http_endpoint, data=request)
......
from __future__ import annotations
import json
from collections.abc import Callable
from dataclasses import dataclass
from typing import TYPE_CHECKING, Any, Generic, Literal, TypeVar
from helpy._handles.build_json_rpc_call import build_json_rpc_call
from helpy.exceptions import CommunicationError, JsonT, NothingToSendError, ResponseNotReadyError
from schemas.jsonrpc import ExpectResultT, JSONRPCResult, get_response_model
if TYPE_CHECKING:
from types import TracebackType
from typing_extensions import Self
from helpy._communication.abc.communicator import AbstractCommunicator
from helpy._interfaces.url import HttpUrl
class _DelayedResponseWrapper:
def __init__(self, url: HttpUrl, request: str, expected_type: type[ExpectResultT]) -> None:
super().__setattr__("_url", url)
super().__setattr__("_request", request)
super().__setattr__("_response", None)
super().__setattr__("_exception", None)
super().__setattr__("_expected_type", expected_type)
def __check_is_response_available(self) -> None:
if (exception := super().__getattribute__("_exception")) is not None:
raise exception
if self.__get_data() is None:
raise ResponseNotReadyError
def __get_data(self) -> Any:
response = super().__getattribute__("_response")
if response is None:
return None
if isinstance(response, JSONRPCResult):
return response.result
return response
def __setattr__(self, __name: str, __value: Any) -> None:
self.__check_is_response_available()
setattr(self.__get_data(), __name, __value)
def __getattr__(self, __name: str) -> Any:
self.__check_is_response_available()
return getattr(self.__get_data(), __name)
def _set_response(self, **kwargs: Any) -> None:
expected_type = super().__getattribute__("_expected_type")
response = get_response_model(expected_type, **kwargs)
assert isinstance(response, JSONRPCResult)
super().__setattr__("_response", response.result)
def _set_exception(self, exception: BaseException) -> None:
super().__setattr__("_exception", exception)
@dataclass(kw_only=True)
class _BatchRequestResponseItem:
request: str
delayed_result: _DelayedResponseWrapper
class _PostRequestManager:
def __init__(self, owner: _BatchHandle, batch_objs: list[_BatchRequestResponseItem]) -> None:
self.__batch = batch_objs
self.__owner = owner
self.__responses: list[dict[str, Any]] = []
def set_responses(self, responses: str) -> None:
self.__responses = json.loads(responses)
def __enter__(self) -> Self:
return self
def __set_response_or_exception(self, request_id: int, response: dict[str, Any], exception_url: str = "") -> None:
if "error" in response:
# creating a new instance so other responses won't be included in the error
new_error = CommunicationError(
url=exception_url,
request=self.__batch[request_id].request,
response=response,
)
self.__owner._get_batch_delayed_result(request_id)._set_exception(new_error)
if not self.__owner._delay_error_on_data_access:
raise new_error
else:
self.__owner._get_batch_delayed_result(request_id)._set_response(**response)
def __handle_no_exception_case(self) -> None:
self.__validate_response_count(self.__responses)
for response in self.__responses:
self.__set_response_or_exception(request_id=int(response["id"]), response=response)
def __handle_exception_and_no_responses_exists(self, exception: BaseException) -> bool:
for request_id in range(len(self.__batch)):
self.__owner._get_batch_delayed_result(request_id)._set_exception(exception)
if not self.__owner._delay_error_on_data_access:
return False
return True
def __handle_exception_and_responses_exists(self, responses: list[JsonT], url: str) -> bool:
for response in responses:
self.__set_response_or_exception(request_id=int(response["id"]), response=response, exception_url=url)
return not self.__owner._delay_error_on_data_access
def __validate_response_count(self, response: list[JsonT]) -> None:
message = "Invalid amount of responses_from_error"
assert len(response) == len(self.__batch), message
def __handle_exception_case(self, exception: BaseException) -> bool:
if not isinstance(exception, CommunicationError) and isinstance(exception, BaseException):
return False
responses_from_error = exception.get_response()
if responses_from_error is None:
return self.__handle_exception_and_no_responses_exists(exception)
message = f"Invalid error response format: expected list, got {type(responses_from_error)}"
assert isinstance(responses_from_error, list), message
self.__validate_response_count(responses_from_error)
return self.__handle_exception_and_responses_exists(responses_from_error, exception.url)
def __exit__(
self, _: type[BaseException] | None, exception: BaseException | None, traceback: TracebackType | None
) -> bool:
if exception is None and len(self.__responses):
self.__handle_no_exception_case()
return True
assert exception is not None
return self.__handle_exception_case(exception)
class _BatchHandle:
def __init__(
self,
url: HttpUrl,
communicator: AbstractCommunicator,
*args: Any,
delay_error_on_data_access: bool = False,
**kwargs: Any,
) -> None:
super().__init__(*args, **kwargs)
self.__url = url
self.__communicator = communicator
self._delay_error_on_data_access = delay_error_on_data_access
self.__batch: list[_BatchRequestResponseItem] = []
def _impl_handle_request(self, endpoint: str, params: str, *, expect_type: type[ExpectResultT]) -> ExpectResultT:
@dataclass
class DummyResponse:
result: Any
request = build_json_rpc_call(method=endpoint, params=params, id_=len(self.__batch))
delayed_result = _DelayedResponseWrapper(url=self.__url, request=request, expected_type=expect_type)
self.__batch.append(_BatchRequestResponseItem(request=request, delayed_result=delayed_result))
return DummyResponse(result=delayed_result) # type: ignore[return-value]
def __sync_evaluate(self) -> None:
query = self.__prepare_request()
with _PostRequestManager(self, self.__batch) as mgr:
mgr.set_responses(self.__communicator.send(url=self.__url, data=query))
async def __async_evaluate(self) -> None:
query = self.__prepare_request()
with _PostRequestManager(self, self.__batch) as mgr:
mgr.set_responses(await self.__communicator.async_send(url=self.__url, data=query))
def __prepare_request(self) -> str:
return "[" + ",".join([x.request for x in self.__batch]) + "]"
def _get_batch_delayed_result(self, request_id: int) -> _DelayedResponseWrapper:
return self.__batch[request_id].delayed_result
def __is_anything_to_send(self) -> bool:
return bool(self.__batch)
async def __aenter__(self) -> Self:
return self
async def __aexit__(self, _: type[Exception] | None, ex: Exception | None, ___: TracebackType | None) -> None:
if not self.__is_anything_to_send():
raise NothingToSendError
await self.__async_evaluate()
def __enter__(self) -> Self:
return self
def __exit__(self, _: type[Exception] | None, ex: Exception | None, ___: TracebackType | None) -> Literal[False]:
if not self.__is_anything_to_send():
raise NothingToSendError
self.__sync_evaluate()
return False
ApiT = TypeVar("ApiT")
OwnerT = TypeVar("OwnerT")
ApiFactory = Callable[[OwnerT], ApiT]
class SyncBatchHandle(_BatchHandle, Generic[ApiT]):
def __init__(
self,
url: HttpUrl,
communicator: AbstractCommunicator,
api: ApiFactory[Self, ApiT],
*args: Any,
delay_error_on_data_access: bool = False,
**kwargs: Any,
) -> None:
super().__init__(url, communicator, *args, delay_error_on_data_access=delay_error_on_data_access, **kwargs)
self.api: ApiT = api(self)
def _send(self, *, endpoint: str, params: str, expected_type: type[ExpectResultT]) -> JSONRPCResult[ExpectResultT]:
return self._impl_handle_request(endpoint, params, expect_type=expected_type) # type: ignore[arg-type]
class AsyncBatchHandle(_BatchHandle, Generic[ApiT]):
def __init__(
self,
url: HttpUrl,
communicator: AbstractCommunicator,
api: ApiFactory[Self, ApiT],
*args: Any,
delay_error_on_data_access: bool = False,
**kwargs: Any,
) -> None:
super().__init__(url, communicator, *args, delay_error_on_data_access=delay_error_on_data_access, **kwargs)
self.api: ApiT = api(self)
async def _async_send(
self, *, endpoint: str, params: str, expected_type: type[ExpectResultT]
) -> JSONRPCResult[ExpectResultT]:
return self._impl_handle_request(endpoint, params, expect_type=expected_type) # type: ignore[arg-type]
......@@ -9,13 +9,13 @@ from helpy._handles.abc.api_collection import (
from helpy._handles.beekeeper.api import AsyncBeekeeperApi, SyncBeekeeperApi
if TYPE_CHECKING:
from helpy._handles.abc.handle import AbstractAsyncHandle, AbstractSyncHandle
from helpy._handles.abc.api import AsyncHandleT, SyncHandleT
class BeekeeperAsyncApiCollection(AbstractAsyncApiCollection):
"""Beekeepers collection of available apis in async version."""
def __init__(self, owner: AbstractAsyncHandle) -> None:
def __init__(self, owner: AsyncHandleT) -> None:
super().__init__(owner)
self.beekeeper = AsyncBeekeeperApi(owner=self._owner)
......@@ -23,6 +23,6 @@ class BeekeeperAsyncApiCollection(AbstractAsyncApiCollection):
class BeekeeperSyncApiCollection(AbstractSyncApiCollection):
"""Beekeepers collection of available apis in async version."""
def __init__(self, owner: AbstractSyncHandle) -> None:
def __init__(self, owner: SyncHandleT) -> None:
super().__init__(owner)
self.beekeeper = SyncBeekeeperApi(owner=self._owner)
......@@ -3,6 +3,7 @@ from __future__ import annotations
from typing import cast
from helpy._handles.abc.handle import AbstractAsyncHandle, AbstractSyncHandle
from helpy._handles.batch_handle import AsyncBatchHandle, SyncBatchHandle
from helpy._handles.beekeeper.api.api_collection import (
BeekeeperAsyncApiCollection,
BeekeeperSyncApiCollection,
......@@ -27,6 +28,14 @@ class Beekeeper(AbstractSyncHandle):
def _target_service(self) -> str:
return _handle_target_service_name
def batch(self, *, delay_error_on_data_access: bool = False) -> SyncBatchHandle[BeekeeperSyncApiCollection]:
return SyncBatchHandle(
url=self.http_endpoint,
communicator=self._communicator,
api=lambda o: BeekeeperSyncApiCollection(owner=o),
delay_error_on_data_access=delay_error_on_data_access,
)
class AsyncBeekeeper(AbstractAsyncHandle):
"""Asynchronous handle for beekeeper service communication."""
......@@ -43,3 +52,11 @@ class AsyncBeekeeper(AbstractAsyncHandle):
def _target_service(self) -> str:
return _handle_target_service_name
def batch(self, *, delay_error_on_data_access: bool = False) -> AsyncBatchHandle[BeekeeperAsyncApiCollection]:
return AsyncBatchHandle(
url=self.http_endpoint,
communicator=self._communicator,
api=lambda o: BeekeeperAsyncApiCollection(owner=o),
delay_error_on_data_access=delay_error_on_data_access,
)
from __future__ import annotations
def build_json_rpc_call(*, method: str, params: str, id_: int = 0) -> str:
"""Builds params for jsonrpc call."""
return (
"""{"id":"""
+ str(id_)
+ ""","jsonrpc":"2.0","method":\""""
+ method
+ '"'
+ (""","params":""" + params if params else "")
+ "}"
)
......@@ -40,11 +40,11 @@ from helpy._handles.hived.api.wallet_bridge_api import (
)
if TYPE_CHECKING:
from helpy._handles.abc.handle import AbstractAsyncHandle, AbstractSyncHandle
from helpy._handles.abc.api import AsyncHandleT, SyncHandleT
class HivedAsyncApiCollection(AbstractAsyncApiCollection):
def __init__(self, owner: AbstractAsyncHandle) -> None:
def __init__(self, owner: AsyncHandleT) -> None:
super().__init__(owner)
self.account_by_key = AsyncAccountByKeyApi(owner=self._owner)
self.account_history = AsyncAccountHistoryApi(owner=self._owner)
......@@ -63,7 +63,7 @@ class HivedAsyncApiCollection(AbstractAsyncApiCollection):
class HivedSyncApiCollection(AbstractSyncApiCollection):
def __init__(self, owner: AbstractSyncHandle) -> None:
def __init__(self, owner: SyncHandleT) -> None:
super().__init__(owner)
self.account_by_key = SyncAccountByKeyApi(owner=self._owner)
self.account_history = SyncAccountHistoryApi(owner=self._owner)
......
from __future__ import annotations
from datetime import datetime # noqa: TCH003
from typing import Any
from helpy._handles.abc.api import AbstractAsyncApi
from helpy._handles.abc.api import AbstractAsyncApi, ApiArgumentSerialization
from helpy._handles.hived.api.condenser_api.common import CondenserApiCommons
from schemas.apis import condenser_api # noqa: TCH001
from schemas.transaction import TransactionLegacy # noqa: TCH001
......@@ -12,27 +11,27 @@ from schemas.transaction import TransactionLegacy # noqa: TCH001
class CondenserApi(AbstractAsyncApi, CondenserApiCommons):
api = AbstractAsyncApi._endpoint
def _serialize_params(self, args: Any, kwargs: dict[str, Any]) -> str: # noqa: ARG002
return self._legacy_serialization(args=args)
def argument_serialization(self) -> ApiArgumentSerialization:
return ApiArgumentSerialization.ARRAY
@api
async def get_version(self) -> condenser_api.GetVersion:
raise NotImplementedError
@api
async def get_active_witnesses(self, /, include_future: bool = False) -> condenser_api.GetActiveWitnesses:
async def get_active_witnesses(self, include_future: bool = False, /) -> condenser_api.GetActiveWitnesses:
raise NotImplementedError
@api
async def get_block_header(self, /, block_num: int) -> condenser_api.GetBlockHeader:
async def get_block_header(self, block_num: int, /) -> condenser_api.GetBlockHeader:
raise NotImplementedError
@api
async def get_block(self, /, block_num: int) -> condenser_api.GetBlock:
async def get_block(self, block_num: int, /) -> condenser_api.GetBlock:
raise NotImplementedError
@api
async def get_ops_in_block(self, /, block_num: int, only_virtual: bool = False) -> condenser_api.GetOpsInBlock:
async def get_ops_in_block(self, block_num: int, only_virtual: bool = False, /) -> condenser_api.GetOpsInBlock:
raise NotImplementedError
@api
......@@ -56,7 +55,7 @@ class CondenserApi(AbstractAsyncApi, CondenserApiCommons):
raise NotImplementedError
@api
async def get_witness_schedule(self, /, include_future: bool = False) -> condenser_api.GetWitnessSchedule:
async def get_witness_schedule(self, include_future: bool = False, /) -> condenser_api.GetWitnessSchedule:
raise NotImplementedError
@api
......@@ -68,27 +67,27 @@ class CondenserApi(AbstractAsyncApi, CondenserApiCommons):
raise NotImplementedError
@api
async def get_reward_fund(self, /, name: str) -> condenser_api.GetRewardFund:
async def get_reward_fund(self, name: str, /) -> condenser_api.GetRewardFund:
raise NotImplementedError
@api
async def get_key_references(self, /, key: str) -> condenser_api.GetKeyReferences:
async def get_key_references(self, key: str, /) -> condenser_api.GetKeyReferences:
raise NotImplementedError
@api
async def get_accounts(
self, /, accounts: list[str], delayed_votes_active: bool = True
self, accounts: list[str], delayed_votes_active: bool = True, /
) -> condenser_api.GetAccounts:
raise NotImplementedError
@api
async def lookup_account_names(
self, /, accounts: list[str], delayed_votes_active: bool = True
self, accounts: list[str], delayed_votes_active: bool = True, /
) -> condenser_api.LookupAccountNames:
raise NotImplementedError
@api
async def lookup_accounts(self, /, lower_bound_name: str, limit: int) -> condenser_api.LookupAccounts:
async def lookup_accounts(self, lower_bound_name: str, limit: int, /) -> condenser_api.LookupAccounts:
raise NotImplementedError
@api
......@@ -96,69 +95,69 @@ class CondenserApi(AbstractAsyncApi, CondenserApiCommons):
raise NotImplementedError
@api
async def get_owner_history(self, /, owner: str) -> condenser_api.GetOwnerHistory:
async def get_owner_history(self, owner: str, /) -> condenser_api.GetOwnerHistory:
raise NotImplementedError
@api
async def get_recovery_request(self, /, account: str) -> condenser_api.GetRecoveryRequest:
async def get_recovery_request(self, account: str, /) -> condenser_api.GetRecoveryRequest:
raise NotImplementedError
@api
async def get_escrow(
self, /, start: tuple[str, int] | tuple[bool, datetime, int], limit: int, order: CondenserApi.SORT_TYPES
self, start: tuple[str, int] | tuple[bool, datetime, int], limit: int, order: CondenserApi.SORT_TYPES, /
) -> condenser_api.GetEscrow:
raise NotImplementedError
@api
async def get_withdraw_routes(
self, /, account: str, destination: CondenserApi.WITHDRAW_ROUTE_TYPES
self, account: str, destination: CondenserApi.WITHDRAW_ROUTE_TYPES, /
) -> condenser_api.GetWithdrawRoutes:
raise NotImplementedError
@api
async def get_savings_withdraw_from(self, /, account: str) -> condenser_api.GetSavingsWithdrawFrom:
async def get_savings_withdraw_from(self, account: str, /) -> condenser_api.GetSavingsWithdrawFrom:
raise NotImplementedError
@api
async def get_savings_withdraw_to(self, /, account: str) -> condenser_api.GetSavingsWithdrawTo:
async def get_savings_withdraw_to(self, account: str, /) -> condenser_api.GetSavingsWithdrawTo:
raise NotImplementedError
@api
async def get_vesting_delegations(
self, /, account: str, start: str, limit: int = 100
self, account: str, start: str, limit: int = 100, /
) -> condenser_api.GetVestingDelegations:
raise NotImplementedError
@api
async def get_expiring_vesting_delegations(
self, /, account: str, start: str, limit: int = 100
self, account: str, start: str, limit: int = 100, /
) -> condenser_api.GetExpiringVestingDelegations:
raise NotImplementedError
@api
async def get_witnesses(self, /, witness_ids: list[int]) -> condenser_api.GetWitnesses:
async def get_witnesses(self, witness_ids: list[int], /) -> condenser_api.GetWitnesses:
raise NotImplementedError
@api
async def get_conversion_requests(self, /, account: str) -> condenser_api.GetConversionRequests:
async def get_conversion_requests(self, account: str, /) -> condenser_api.GetConversionRequests:
raise NotImplementedError
@api
async def get_collateralized_conversion_requests(
self, /, account: str
self, account: str, /
) -> condenser_api.GetCollateralizedConversionRequests:
raise NotImplementedError
@api
async def get_witness_by_account(self, /, account: str) -> condenser_api.GetWitnessByAccount:
async def get_witness_by_account(self, account: str, /) -> condenser_api.GetWitnessByAccount:
raise NotImplementedError
@api
async def get_witnesses_by_vote(self, /, start_name: str, limit: int) -> condenser_api.GetWitnessesByVote:
async def get_witnesses_by_vote(self, start_name: str, limit: int, /) -> condenser_api.GetWitnessesByVote:
raise NotImplementedError
@api
async def lookup_witness_accounts(self, /, start: str, limit: int) -> condenser_api.LookupWitnessAccounts:
async def lookup_witness_accounts(self, start: str, limit: int, /) -> condenser_api.LookupWitnessAccounts:
raise NotImplementedError
@api
......@@ -166,37 +165,37 @@ class CondenserApi(AbstractAsyncApi, CondenserApiCommons):
raise NotImplementedError
@api
async def get_open_orders(self, /, owner: str) -> condenser_api.GetOpenOrders:
async def get_open_orders(self, owner: str, /) -> condenser_api.GetOpenOrders:
raise NotImplementedError
@api
async def get_transaction_hex(self, /, transaction: TransactionLegacy) -> condenser_api.GetTransactionHex:
async def get_transaction_hex(self, transaction: TransactionLegacy, /) -> condenser_api.GetTransactionHex:
raise NotImplementedError
@api
async def get_transaction(self, /, transaction_id: str) -> condenser_api.GetTransaction:
async def get_transaction(self, transaction_id: str, /) -> condenser_api.GetTransaction:
raise NotImplementedError
@api
async def get_required_signatures(
self, /, transaction: TransactionLegacy, public_key: str
self, transaction: TransactionLegacy, public_key: str, /
) -> condenser_api.GetRequiredSignatures:
raise NotImplementedError
@api
async def get_potential_signatures(self, /, transaction: TransactionLegacy) -> condenser_api.GetPotentialSignatures:
async def get_potential_signatures(self, transaction: TransactionLegacy, /) -> condenser_api.GetPotentialSignatures:
raise NotImplementedError
@api
async def verify_authority(self, /, transaction: TransactionLegacy) -> condenser_api.VerifyAuthority:
async def verify_authority(self, transaction: TransactionLegacy, /) -> condenser_api.VerifyAuthority:
raise NotImplementedError
@api
async def verify_account_authority(self, /, transaction: TransactionLegacy) -> condenser_api.VerifyAccountAuthority:
async def verify_account_authority(self, transaction: TransactionLegacy, /) -> condenser_api.VerifyAccountAuthority:
raise NotImplementedError
@api
async def get_active_votes(self, /, author: str, permlink: str) -> condenser_api.GetActiveVotes:
async def get_active_votes(self, author: str, permlink: str, /) -> condenser_api.GetActiveVotes:
raise NotImplementedError
@api
......@@ -211,17 +210,17 @@ class CondenserApi(AbstractAsyncApi, CondenserApiCommons):
raise NotImplementedError
@api
async def broadcast_transaction(self, /, transaction: TransactionLegacy) -> condenser_api.BroadcastTransaction:
async def broadcast_transaction(self, transaction: TransactionLegacy, /) -> condenser_api.BroadcastTransaction:
raise NotImplementedError
@api
async def broadcast_transaction_synchronous(
self, /, transaction: TransactionLegacy
self, transaction: TransactionLegacy, /
) -> condenser_api.BroadcastTransactionSynchronous:
raise NotImplementedError
@api
async def get_account_reputations(self, /, account: str, limit: int = 1000) -> condenser_api.GetAccountReputations:
async def get_account_reputations(self, account: str, limit: int = 1000, /) -> condenser_api.GetAccountReputations:
raise NotImplementedError
@api
......@@ -233,22 +232,22 @@ class CondenserApi(AbstractAsyncApi, CondenserApiCommons):
raise NotImplementedError
@api
async def get_order_book(self, /, limit: int = 500) -> condenser_api.GetOrderBook:
async def get_order_book(self, limit: int = 500, /) -> condenser_api.GetOrderBook:
raise NotImplementedError
@api
async def get_trade_history(
self, /, start: datetime, stop: datetime, limit: int = 1000
self, start: datetime, stop: datetime, limit: int = 1000, /
) -> condenser_api.GetTradeHistory:
raise NotImplementedError
@api
async def get_recent_trades(self, /, limit: int = 1000) -> condenser_api.GetRecentTrades:
async def get_recent_trades(self, limit: int = 1000, /) -> condenser_api.GetRecentTrades:
raise NotImplementedError
@api
async def get_market_history(
self, /, bucket_seconds: int, start: datetime, stop: datetime
self, bucket_seconds: int, start: datetime, stop: datetime, /
) -> condenser_api.GetMarketHistory:
raise NotImplementedError
......@@ -257,7 +256,7 @@ class CondenserApi(AbstractAsyncApi, CondenserApiCommons):
raise NotImplementedError
@api
async def is_known_transaction(self, /, transaction_id: str) -> condenser_api.IsKnownTransaction:
async def is_known_transaction(self, transaction_id: str, /) -> condenser_api.IsKnownTransaction:
raise NotImplementedError
@api
......@@ -273,7 +272,7 @@ class CondenserApi(AbstractAsyncApi, CondenserApiCommons):
raise NotImplementedError
@api
async def find_proposals(self, /, proposals_ids: list[int]) -> condenser_api.FindProposals:
async def find_proposals(self, proposals_ids: list[int], /) -> condenser_api.FindProposals:
raise NotImplementedError
@api
......@@ -288,19 +287,19 @@ class CondenserApi(AbstractAsyncApi, CondenserApiCommons):
raise NotImplementedError
@api
async def find_recurrent_transfers(self, /, account: str) -> condenser_api.FindRecurrentTransfers:
async def find_recurrent_transfers(self, account: str, /) -> condenser_api.FindRecurrentTransfers:
raise NotImplementedError
@api
async def find_rc_accounts(self, /, accounts: list[str]) -> condenser_api.FindRcAccounts:
async def find_rc_accounts(self, accounts: list[str], /) -> condenser_api.FindRcAccounts:
raise NotImplementedError
@api
async def list_rc_accounts(self, /, start: str, limit: int) -> condenser_api.ListRcAccounts:
async def list_rc_accounts(self, start: str, limit: int, /) -> condenser_api.ListRcAccounts:
raise NotImplementedError
@api
async def list_rc_direct_delegations(
self, /, start: tuple[str, str], limit: int
self, start: tuple[str, str], limit: int, /
) -> condenser_api.ListRcDirectDelegations:
raise NotImplementedError
from __future__ import annotations
from typing import Any, ClassVar, Literal, TypeAlias
from typing import ClassVar, Literal, TypeAlias
from helpy._handles.abc.api import AbstractApi
from helpy._handles.hived.api.database_api.common import DatabaseApiCommons
......@@ -11,7 +10,3 @@ class CondenserApiCommons:
SORT_DIRECTION: ClassVar[TypeAlias] = DatabaseApiCommons.SORT_DIRECTION
PROPOSAL_STATUS: ClassVar[TypeAlias] = DatabaseApiCommons.PROPOSAL_STATUS
WITHDRAW_ROUTE_TYPES = Literal["incoming", "outgoing", "all"]
@classmethod
def _legacy_serialization(cls, args: list[Any]) -> str:
return AbstractApi.json_dumps()(args)
from __future__ import annotations
from datetime import datetime # noqa: TCH003
from typing import Any
from helpy._handles.abc.api import AbstractSyncApi
from helpy._handles.abc.api import AbstractSyncApi, ApiArgumentSerialization
from helpy._handles.hived.api.condenser_api.common import CondenserApiCommons
from schemas.apis import condenser_api # noqa: TCH001
from schemas.transaction import TransactionLegacy # noqa: TCH001
......@@ -12,27 +11,27 @@ from schemas.transaction import TransactionLegacy # noqa: TCH001
class CondenserApi(AbstractSyncApi, CondenserApiCommons):
api = AbstractSyncApi._endpoint
def _serialize_params(self, args: Any, kwargs: dict[str, Any]) -> str: # noqa: ARG002
return self._legacy_serialization(args=args)
def argument_serialization(self) -> ApiArgumentSerialization:
return ApiArgumentSerialization.ARRAY
@api
def get_version(self) -> condenser_api.GetVersion:
raise NotImplementedError
@api
def get_active_witnesses(self, /, include_future: bool = False) -> condenser_api.GetActiveWitnesses:
def get_active_witnesses(self, include_future: bool = False, /) -> condenser_api.GetActiveWitnesses:
raise NotImplementedError
@api
def get_block_header(self, /, block_num: int) -> condenser_api.GetBlockHeader:
def get_block_header(self, block_num: int, /) -> condenser_api.GetBlockHeader:
raise NotImplementedError
@api
def get_block(self, /, block_num: int) -> condenser_api.GetBlock:
def get_block(self, block_num: int, /) -> condenser_api.GetBlock:
raise NotImplementedError
@api
def get_ops_in_block(self, /, block_num: int, only_virtual: bool = False) -> condenser_api.GetOpsInBlock:
def get_ops_in_block(self, block_num: int, only_virtual: bool = False, /) -> condenser_api.GetOpsInBlock:
raise NotImplementedError
@api
......@@ -56,7 +55,7 @@ class CondenserApi(AbstractSyncApi, CondenserApiCommons):
raise NotImplementedError
@api
def get_witness_schedule(self, /, include_future: bool = False) -> condenser_api.GetWitnessSchedule:
def get_witness_schedule(self, include_future: bool = False, /) -> condenser_api.GetWitnessSchedule:
raise NotImplementedError
@api
......@@ -68,25 +67,25 @@ class CondenserApi(AbstractSyncApi, CondenserApiCommons):
raise NotImplementedError
@api
def get_reward_fund(self, /, name: str) -> condenser_api.GetRewardFund:
def get_reward_fund(self, name: str, /) -> condenser_api.GetRewardFund:
raise NotImplementedError
@api
def get_key_references(self, /, key: str) -> condenser_api.GetKeyReferences:
def get_key_references(self, key: str, /) -> condenser_api.GetKeyReferences:
raise NotImplementedError
@api
def get_accounts(self, /, accounts: list[str], delayed_votes_active: bool = True) -> condenser_api.GetAccounts:
def get_accounts(self, accounts: list[str], delayed_votes_active: bool = True, /) -> condenser_api.GetAccounts:
raise NotImplementedError
@api
def lookup_account_names(
self, /, accounts: list[str], delayed_votes_active: bool = True
self, accounts: list[str], delayed_votes_active: bool = True, /
) -> condenser_api.LookupAccountNames:
raise NotImplementedError
@api
def lookup_accounts(self, /, lower_bound_name: str, limit: int) -> condenser_api.LookupAccounts:
def lookup_accounts(self, lower_bound_name: str, limit: int, /) -> condenser_api.LookupAccounts:
raise NotImplementedError
@api
......@@ -94,69 +93,69 @@ class CondenserApi(AbstractSyncApi, CondenserApiCommons):
raise NotImplementedError
@api
def get_owner_history(self, /, owner: str) -> condenser_api.GetOwnerHistory:
def get_owner_history(self, owner: str, /) -> condenser_api.GetOwnerHistory:
raise NotImplementedError
@api
def get_recovery_request(self, /, account: str) -> condenser_api.GetRecoveryRequest:
def get_recovery_request(self, account: str, /) -> condenser_api.GetRecoveryRequest:
raise NotImplementedError
@api
def get_escrow(
self, /, start: tuple[str, int] | tuple[bool, datetime, int], limit: int, order: CondenserApi.SORT_TYPES
self, start: tuple[str, int] | tuple[bool, datetime, int], limit: int, order: CondenserApi.SORT_TYPES, /
) -> condenser_api.GetEscrow:
raise NotImplementedError
@api
def get_withdraw_routes(
self, /, account: str, destination: CondenserApi.WITHDRAW_ROUTE_TYPES
self, account: str, destination: CondenserApi.WITHDRAW_ROUTE_TYPES, /
) -> condenser_api.GetWithdrawRoutes:
raise NotImplementedError
@api
def get_savings_withdraw_from(self, /, account: str) -> condenser_api.GetSavingsWithdrawFrom:
def get_savings_withdraw_from(self, account: str, /) -> condenser_api.GetSavingsWithdrawFrom:
raise NotImplementedError
@api
def get_savings_withdraw_to(self, /, account: str) -> condenser_api.GetSavingsWithdrawTo:
def get_savings_withdraw_to(self, account: str, /) -> condenser_api.GetSavingsWithdrawTo:
raise NotImplementedError
@api
def get_vesting_delegations(
self, /, account: str, start: str, limit: int = 100
self, account: str, start: str, limit: int = 100, /
) -> condenser_api.GetVestingDelegations:
raise NotImplementedError
@api
def get_expiring_vesting_delegations(
self, /, account: str, start: str, limit: int = 100
self, account: str, start: str, limit: int = 100, /
) -> condenser_api.GetExpiringVestingDelegations:
raise NotImplementedError
@api
def get_witnesses(self, /, witness_ids: list[int]) -> condenser_api.GetWitnesses:
def get_witnesses(self, witness_ids: list[int], /) -> condenser_api.GetWitnesses:
raise NotImplementedError
@api
def get_conversion_requests(self, /, account: str) -> condenser_api.GetConversionRequests:
def get_conversion_requests(self, account: str, /) -> condenser_api.GetConversionRequests:
raise NotImplementedError
@api
def get_collateralized_conversion_requests(
self, /, account: str
self, account: str, /
) -> condenser_api.GetCollateralizedConversionRequests:
raise NotImplementedError
@api
def get_witness_by_account(self, /, account: str) -> condenser_api.GetWitnessByAccount:
def get_witness_by_account(self, account: str, /) -> condenser_api.GetWitnessByAccount:
raise NotImplementedError
@api
def get_witnesses_by_vote(self, /, start_name: str, limit: int) -> condenser_api.GetWitnessesByVote:
def get_witnesses_by_vote(self, start_name: str, limit: int, /) -> condenser_api.GetWitnessesByVote:
raise NotImplementedError
@api
def lookup_witness_accounts(self, /, start: str, limit: int) -> condenser_api.LookupWitnessAccounts:
def lookup_witness_accounts(self, start: str, limit: int, /) -> condenser_api.LookupWitnessAccounts:
raise NotImplementedError
@api
......@@ -164,37 +163,37 @@ class CondenserApi(AbstractSyncApi, CondenserApiCommons):
raise NotImplementedError
@api
def get_open_orders(self, /, owner: str) -> condenser_api.GetOpenOrders:
def get_open_orders(self, owner: str, /) -> condenser_api.GetOpenOrders:
raise NotImplementedError
@api
def get_transaction_hex(self, /, transaction: TransactionLegacy) -> condenser_api.GetTransactionHex:
def get_transaction_hex(self, transaction: TransactionLegacy, /) -> condenser_api.GetTransactionHex:
raise NotImplementedError
@api
def get_transaction(self, /, transaction_id: str) -> condenser_api.GetTransaction:
def get_transaction(self, transaction_id: str, /) -> condenser_api.GetTransaction:
raise NotImplementedError
@api
def get_required_signatures(
self, /, transaction: TransactionLegacy, public_key: str
self, transaction: TransactionLegacy, public_key: str, /
) -> condenser_api.GetRequiredSignatures:
raise NotImplementedError
@api
def get_potential_signatures(self, /, transaction: TransactionLegacy) -> condenser_api.GetPotentialSignatures:
def get_potential_signatures(self, transaction: TransactionLegacy, /) -> condenser_api.GetPotentialSignatures:
raise NotImplementedError
@api
def verify_authority(self, /, transaction: TransactionLegacy) -> condenser_api.VerifyAuthority:
def verify_authority(self, transaction: TransactionLegacy, /) -> condenser_api.VerifyAuthority:
raise NotImplementedError
@api
def verify_account_authority(self, /, transaction: TransactionLegacy) -> condenser_api.VerifyAccountAuthority:
def verify_account_authority(self, transaction: TransactionLegacy, /) -> condenser_api.VerifyAccountAuthority:
raise NotImplementedError
@api
def get_active_votes(self, /, author: str, permlink: str) -> condenser_api.GetActiveVotes:
def get_active_votes(self, author: str, permlink: str, /) -> condenser_api.GetActiveVotes:
raise NotImplementedError
@api
......@@ -209,17 +208,17 @@ class CondenserApi(AbstractSyncApi, CondenserApiCommons):
raise NotImplementedError
@api
def broadcast_transaction(self, /, transaction: TransactionLegacy) -> condenser_api.BroadcastTransaction:
def broadcast_transaction(self, transaction: TransactionLegacy, /) -> condenser_api.BroadcastTransaction:
raise NotImplementedError
@api
def broadcast_transaction_synchronous(
self, /, transaction: TransactionLegacy
self, transaction: TransactionLegacy, /
) -> condenser_api.BroadcastTransactionSynchronous:
raise NotImplementedError
@api
def get_account_reputations(self, /, account: str, limit: int = 1000) -> condenser_api.GetAccountReputations:
def get_account_reputations(self, account: str, limit: int = 1000, /) -> condenser_api.GetAccountReputations:
raise NotImplementedError
@api
......@@ -231,20 +230,20 @@ class CondenserApi(AbstractSyncApi, CondenserApiCommons):
raise NotImplementedError
@api
def get_order_book(self, /, limit: int = 500) -> condenser_api.GetOrderBook:
def get_order_book(self, limit: int = 500, /) -> condenser_api.GetOrderBook:
raise NotImplementedError
@api
def get_trade_history(self, /, start: datetime, stop: datetime, limit: int = 1000) -> condenser_api.GetTradeHistory:
def get_trade_history(self, start: datetime, stop: datetime, limit: int = 1000, /) -> condenser_api.GetTradeHistory:
raise NotImplementedError
@api
def get_recent_trades(self, /, limit: int = 1000) -> condenser_api.GetRecentTrades:
def get_recent_trades(self, limit: int = 1000, /) -> condenser_api.GetRecentTrades:
raise NotImplementedError
@api
def get_market_history(
self, /, bucket_seconds: int, start: datetime, stop: datetime
self, bucket_seconds: int, start: datetime, stop: datetime, /
) -> condenser_api.GetMarketHistory:
raise NotImplementedError
......@@ -253,7 +252,7 @@ class CondenserApi(AbstractSyncApi, CondenserApiCommons):
raise NotImplementedError
@api
def is_known_transaction(self, /, transaction_id: str) -> condenser_api.IsKnownTransaction:
def is_known_transaction(self, transaction_id: str, /) -> condenser_api.IsKnownTransaction:
raise NotImplementedError
@api
......@@ -269,7 +268,7 @@ class CondenserApi(AbstractSyncApi, CondenserApiCommons):
raise NotImplementedError
@api
def find_proposals(self, /, proposals_ids: list[int]) -> condenser_api.FindProposals:
def find_proposals(self, proposals_ids: list[int], /) -> condenser_api.FindProposals:
raise NotImplementedError
@api
......@@ -284,19 +283,19 @@ class CondenserApi(AbstractSyncApi, CondenserApiCommons):
raise NotImplementedError
@api
def find_recurrent_transfers(self, /, account: str) -> condenser_api.FindRecurrentTransfers:
def find_recurrent_transfers(self, account: str, /) -> condenser_api.FindRecurrentTransfers:
raise NotImplementedError
@api
def find_rc_accounts(self, /, accounts: list[str]) -> condenser_api.FindRcAccounts:
def find_rc_accounts(self, accounts: list[str], /) -> condenser_api.FindRcAccounts:
raise NotImplementedError
@api
def list_rc_accounts(self, /, start: str, limit: int) -> condenser_api.ListRcAccounts:
def list_rc_accounts(self, start: str, limit: int, /) -> condenser_api.ListRcAccounts:
raise NotImplementedError
@api
def list_rc_direct_delegations(
self, /, start: tuple[str, str], limit: int
self, start: tuple[str, str], limit: int, /
) -> condenser_api.ListRcDirectDelegations:
raise NotImplementedError
from __future__ import annotations
from datetime import datetime # noqa: TCH003
from typing import Any
from helpy._handles.abc.api import AbstractAsyncApi
from helpy._handles.abc.api import AbstractAsyncApi, ApiArgumentSerialization
from helpy._handles.hived.api.wallet_bridge_api.common import WalletBridgeApiCommons
from helpy._interfaces.asset.asset import Hf26Asset # noqa: TCH001
from schemas.apis import wallet_bridge_api # noqa: TCH001
......@@ -13,15 +12,15 @@ from schemas.transaction import Transaction # noqa: TCH001
class WalletBridgeApi(AbstractAsyncApi, WalletBridgeApiCommons):
api = AbstractAsyncApi._endpoint
def _serialize_params(self, args: Any, kwargs: dict[str, Any]) -> str: # noqa: ARG002
return self._legacy_serialization(args=args)
def argument_serialization(self) -> ApiArgumentSerialization:
return ApiArgumentSerialization.DOUBLE_ARRAY
@api
async def get_version(self) -> wallet_bridge_api.GetVersion:
raise NotImplementedError
@api
async def get_block(self, /, block: int) -> wallet_bridge_api.GetBlock:
async def get_block(self, block: int, /) -> wallet_bridge_api.GetBlock:
raise NotImplementedError
@api
......@@ -41,7 +40,7 @@ class WalletBridgeApi(AbstractAsyncApi, WalletBridgeApiCommons):
raise NotImplementedError
@api
async def get_ops_in_block(self, /, block: int, only_virtual: bool = False) -> wallet_bridge_api.GetOpsInBlock:
async def get_ops_in_block(self, block: int, only_virtual: bool = False, /) -> wallet_bridge_api.GetOpsInBlock:
raise NotImplementedError
@api
......@@ -49,21 +48,21 @@ class WalletBridgeApi(AbstractAsyncApi, WalletBridgeApiCommons):
raise NotImplementedError
@api
async def get_active_witnesses(self, /, include_future: bool) -> wallet_bridge_api.GetActiveWitnesses:
async def get_active_witnesses(self, include_future: bool, /) -> wallet_bridge_api.GetActiveWitnesses:
raise NotImplementedError
@api
async def get_withdraw_routes(
self, /, account: str, destination: WalletBridgeApi.WITHDRAW_ROUTE_TYPES
self, account: str, destination: WalletBridgeApi.WITHDRAW_ROUTE_TYPES, /
) -> wallet_bridge_api.GetWithdrawRoutes:
raise NotImplementedError
@api
async def list_my_accounts(self, /, accounts: list[str]) -> wallet_bridge_api.ListMyAccounts:
async def list_my_accounts(self, accounts: list[str], /) -> wallet_bridge_api.ListMyAccounts:
raise NotImplementedError
@api
async def list_accounts(self, /, start: str, limit: int) -> wallet_bridge_api.ListAccounts:
async def list_accounts(self, start: str, limit: int, /) -> wallet_bridge_api.ListAccounts:
raise NotImplementedError
@api
......@@ -71,19 +70,19 @@ class WalletBridgeApi(AbstractAsyncApi, WalletBridgeApiCommons):
raise NotImplementedError
@api
async def get_account(self, /, account: str) -> wallet_bridge_api.GetAccount:
async def get_account(self, account: str, /) -> wallet_bridge_api.GetAccount:
raise NotImplementedError
@api
async def get_accounts(self, /, accounts: list[str]) -> wallet_bridge_api.GetAccounts:
async def get_accounts(self, accounts: list[str], /) -> wallet_bridge_api.GetAccounts:
raise NotImplementedError
@api
async def get_transaction(self, /, transaction_id: str) -> wallet_bridge_api.GetTransaction:
async def get_transaction(self, transaction_id: str, /) -> wallet_bridge_api.GetTransaction:
raise NotImplementedError
@api
async def list_witnesses(self, /, start: str, limit: int) -> wallet_bridge_api.ListWitnesses:
async def list_witnesses(self, start: str, limit: int, /) -> wallet_bridge_api.ListWitnesses:
raise NotImplementedError
@api
......@@ -91,29 +90,29 @@ class WalletBridgeApi(AbstractAsyncApi, WalletBridgeApiCommons):
raise NotImplementedError
@api
async def get_conversion_requests(self, /, account: str) -> wallet_bridge_api.GetConversionRequests:
async def get_conversion_requests(self, account: str, /) -> wallet_bridge_api.GetConversionRequests:
raise NotImplementedError
@api
async def get_collateralized_conversion_requests(
self, /, account: str
self, account: str, /
) -> wallet_bridge_api.GetCollateralizedConversionRequests:
raise NotImplementedError
@api
async def get_order_book(self, /, limit: int) -> wallet_bridge_api.GetOrderBook:
async def get_order_book(self, limit: int, /) -> wallet_bridge_api.GetOrderBook:
raise NotImplementedError
@api
async def get_open_orders(self, /, account: str) -> wallet_bridge_api.GetOpenOrders:
async def get_open_orders(self, account: str, /) -> wallet_bridge_api.GetOpenOrders:
raise NotImplementedError
@api
async def get_owner_history(self, /, account: str) -> wallet_bridge_api.GetOwnerHistory:
async def get_owner_history(self, account: str, /) -> wallet_bridge_api.GetOwnerHistory:
raise NotImplementedError
@api
async def get_account_history(self, /, account: str, start: int, limit: int) -> wallet_bridge_api.GetAccountHistory:
async def get_account_history(self, account: str, start: int, limit: int, /) -> wallet_bridge_api.GetAccountHistory:
raise NotImplementedError
@api
......@@ -128,11 +127,11 @@ class WalletBridgeApi(AbstractAsyncApi, WalletBridgeApiCommons):
raise NotImplementedError
@api
async def find_proposals(self, /, proposal_ids: list[int]) -> wallet_bridge_api.FindProposals:
async def find_proposals(self, proposal_ids: list[int], /) -> wallet_bridge_api.FindProposals:
raise NotImplementedError
@api
async def is_known_transaction(self, /, transaction_id: str) -> wallet_bridge_api.IsKnownTransaction:
async def is_known_transaction(self, transaction_id: str, /) -> wallet_bridge_api.IsKnownTransaction:
raise NotImplementedError
@api
......@@ -147,33 +146,33 @@ class WalletBridgeApi(AbstractAsyncApi, WalletBridgeApiCommons):
raise NotImplementedError
@api
async def get_reward_fund(self, /, reward_fund_account: str) -> wallet_bridge_api.GetRewardFund:
async def get_reward_fund(self, reward_fund_account: str, /) -> wallet_bridge_api.GetRewardFund:
raise NotImplementedError
@api
async def broadcast_transaction_synchronous(
self, /, transaction: Transaction
self, transaction: Transaction, /
) -> wallet_bridge_api.BroadcastTransactionSynchronous:
raise NotImplementedError
@api
async def broadcast_transaction(self, /, transaction: Transaction) -> wallet_bridge_api.BroadcastTransaction:
async def broadcast_transaction(self, transaction: Transaction, /) -> wallet_bridge_api.BroadcastTransaction:
raise NotImplementedError
@api
async def find_recurrent_transfers(self, /, account: str) -> wallet_bridge_api.FindRecurrentTransfers:
async def find_recurrent_transfers(self, account: str, /) -> wallet_bridge_api.FindRecurrentTransfers:
raise NotImplementedError
@api
async def find_rc_accounts(self, /, accounts: list[str]) -> wallet_bridge_api.FindRcAccounts:
async def find_rc_accounts(self, accounts: list[str], /) -> wallet_bridge_api.FindRcAccounts:
raise NotImplementedError
@api
async def list_rc_accounts(self, /, start: str, limit: int) -> wallet_bridge_api.ListRcAccounts:
async def list_rc_accounts(self, start: str, limit: int, /) -> wallet_bridge_api.ListRcAccounts:
raise NotImplementedError
@api
async def list_rc_direct_delegations(
self, /, start: datetime, limit: int
self, start: datetime, limit: int, /
) -> wallet_bridge_api.ListRcDirectDelegations:
raise NotImplementedError