From fa4f5cb14e8aae070b37914294998a593123ec1b Mon Sep 17 00:00:00 2001 From: kmochocki <kmochocki@syncad.com> Date: Sat, 22 Mar 2025 19:59:24 +0000 Subject: [PATCH 01/23] Remove notifications --- .gitlab-ci.yml | 16 +- .../abc/http_server_observer.py | 17 -- .../abc/notification_handler.py | 21 -- .../appbase_notification_handler.py | 40 --- .../beekeepy/_communication/async_server.py | 104 ------- .../_communication/notification_decorator.py | 43 --- .../universal_notification_server.py | 87 ------ .../beekeepy/_executable/beekeeper_config.py | 1 - .../_executable/beekeeper_executable.py | 5 +- beekeepy/beekeepy/_executable/defaults.py | 1 - beekeepy/beekeepy/_remote_handle/settings.py | 14 +- .../beekeepy/_runnable_handle/beekeeper.py | 248 ++++------------- .../_runnable_handle/beekeeper_callbacks.py | 42 --- .../beekeeper_notification_handler.py | 70 ----- .../beekeepy/_runnable_handle/match_ports.py | 72 +++++ .../notification_handler_base.py | 29 -- .../_runnable_handle/runnable_handle.py | 256 ++++++++++++++++++ .../beekeepy/_runnable_handle/settings.py | 13 +- beekeepy/beekeepy/exceptions/__init__.py | 12 +- beekeepy/beekeepy/exceptions/base.py | 4 + beekeepy/beekeepy/exceptions/executable.py | 23 ++ beekeepy/poetry.lock | 193 +++++++------ beekeepy/pyproject.toml | 8 +- hive | 2 +- .../api_tests/test_api_close_session.py | 6 +- .../api_tests/test_api_create_session.py | 8 +- .../test_default_values.py | 1 - .../test_notifications_endpoint.py | 37 --- .../test_webserver_http_endpoint.py | 13 +- .../handle/various/test_blocking_unlock.py | 8 +- tests/local-tools/poetry.lock | 210 ++++++++------ tests/local-tools/pyproject.toml | 1 + 32 files changed, 678 insertions(+), 927 deletions(-) delete mode 100644 beekeepy/beekeepy/_communication/abc/http_server_observer.py delete mode 100644 beekeepy/beekeepy/_communication/abc/notification_handler.py delete mode 100644 beekeepy/beekeepy/_communication/appbase_notification_handler.py delete mode 100644 beekeepy/beekeepy/_communication/async_server.py delete mode 100644 beekeepy/beekeepy/_communication/notification_decorator.py delete mode 100644 beekeepy/beekeepy/_communication/universal_notification_server.py delete mode 100644 beekeepy/beekeepy/_runnable_handle/beekeeper_callbacks.py delete mode 100644 beekeepy/beekeepy/_runnable_handle/beekeeper_notification_handler.py create mode 100644 beekeepy/beekeepy/_runnable_handle/match_ports.py delete mode 100644 beekeepy/beekeepy/_runnable_handle/notification_handler_base.py create mode 100644 beekeepy/beekeepy/_runnable_handle/runnable_handle.py create mode 100644 beekeepy/beekeepy/exceptions/executable.py delete mode 100644 tests/beekeepy_test/handle/commandline/application_options/test_notifications_endpoint.py diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index df788639..666fc2b8 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -34,7 +34,7 @@ variables: include: - project: 'hive/hive' # This has to be the same as the commit checked out in the submodule - ref: 18f1d5c753735ddba3ab85baaffc09283d84c652 + ref: 40e6bc384b63fe116f370153a20c15a46888011f file: '/scripts/ci-helpers/prepare_data_image_job.yml' # DO NOT include ccc here. It will be indirectly included by above yaml file. #- project: 'hive/common-ci-configuration' @@ -96,20 +96,6 @@ prepare_hived_image: - public-runner-docker - hived-for-tests -prepare_hived_data: - extends: .prepare_hived_data_5m - needs: - - job: prepare_hived_image - artifacts: true - stage: build - variables: - GIT_SUBMODULE_STRATEGY: normal - 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_beekeepy_wheel: stage: beekeepy extends: .build_wheel_template diff --git a/beekeepy/beekeepy/_communication/abc/http_server_observer.py b/beekeepy/beekeepy/_communication/abc/http_server_observer.py deleted file mode 100644 index 6d4efcee..00000000 --- a/beekeepy/beekeepy/_communication/abc/http_server_observer.py +++ /dev/null @@ -1,17 +0,0 @@ -from __future__ import annotations - -from abc import ABC, abstractmethod -from typing import Any - - -class HttpServerObserver(ABC): - @abstractmethod - async def data_received(self, data: dict[str, Any]) -> None: - """Called when any data is received via PUT method. - - Args: - data: data received as body - - Returns: - Nothing. - """ diff --git a/beekeepy/beekeepy/_communication/abc/notification_handler.py b/beekeepy/beekeepy/_communication/abc/notification_handler.py deleted file mode 100644 index da69e8b5..00000000 --- a/beekeepy/beekeepy/_communication/abc/notification_handler.py +++ /dev/null @@ -1,21 +0,0 @@ -from __future__ import annotations - -from abc import ABC, abstractmethod -from typing import Any - -from beekeepy._communication.abc.http_server_observer import HttpServerObserver -from schemas.notifications import KnownNotificationT, Notification - - -class NotificationHandler(HttpServerObserver, ABC): - async def data_received(self, data: dict[str, Any]) -> None: - deserialized_notification = Notification(**data) - await self.handle_notification(deserialized_notification) - - @abstractmethod - async def handle_notification(self, notification: Notification[KnownNotificationT]) -> None: - """Method called after properly serializing notification. - - Args: - notification: received notification object - """ diff --git a/beekeepy/beekeepy/_communication/appbase_notification_handler.py b/beekeepy/beekeepy/_communication/appbase_notification_handler.py deleted file mode 100644 index c9d0241a..00000000 --- a/beekeepy/beekeepy/_communication/appbase_notification_handler.py +++ /dev/null @@ -1,40 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING - -from beekeepy._communication.notification_decorator import notification -from beekeepy._communication.universal_notification_server import UniversalNotificationHandler -from schemas.notifications import Error, Status, WebserverListening - -if TYPE_CHECKING: - from schemas.notifications import Notification - - -class AppbaseNotificationHandler(UniversalNotificationHandler): - @notification(WebserverListening, condition=lambda n: n.value.type_ == "HTTP") - async def _on_http_webserver_bind(self, notification: Notification[WebserverListening]) -> None: - await self.on_http_webserver_bind(notification) - - @notification(WebserverListening, condition=lambda n: n.value.type_ == "WS") - async def _on_ws_webserver_bind(self, notification: Notification[WebserverListening]) -> None: - await self.on_ws_webserver_bind(notification) - - @notification(Status) - async def _on_status_changed(self, notification: Notification[Status]) -> None: - await self.on_status_changed(notification) - - @notification(Error) - async def _on_error(self, notification: Notification[Error]) -> None: - await self.on_error(notification) - - async def on_http_webserver_bind(self, notification: Notification[WebserverListening]) -> None: - """Called when hived reports http server to be ready.""" - - async def on_ws_webserver_bind(self, notification: Notification[WebserverListening]) -> None: - """Called when hived reports ws server to be ready.""" - - async def on_status_changed(self, notification: Notification[Status]) -> None: - """Called when status of notifier changed.""" - - async def on_error(self, notification: Notification[Error]) -> None: - """Called when notifier reports an error.""" diff --git a/beekeepy/beekeepy/_communication/async_server.py b/beekeepy/beekeepy/_communication/async_server.py deleted file mode 100644 index 75501017..00000000 --- a/beekeepy/beekeepy/_communication/async_server.py +++ /dev/null @@ -1,104 +0,0 @@ -from __future__ import annotations - -import asyncio -from http import HTTPStatus -from typing import TYPE_CHECKING - -from aiohttp import web -from typing_extensions import Self - -from beekeepy._interface.context import ContextAsync -from beekeepy._interface.url import HttpUrl -from beekeepy.exceptions import BeekeepyError - -if TYPE_CHECKING: - from socket import socket - - from beekeepy._communication.abc.http_server_observer import HttpServerObserver - - -class AsyncHttpServerError(BeekeepyError): - pass - - -class ServerNotRunningError(AsyncHttpServerError): - def __init__(self) -> None: - super().__init__("Server is not running. Call run() first.") - - -class ServerAlreadyRunningError(AsyncHttpServerError): - def __init__(self) -> None: - super().__init__("Server is already running. Call close() first.") - - -class ServerSetupError(AsyncHttpServerError): - def __init__(self, message: str) -> None: - super().__init__(message) - - -class AsyncHttpServer(ContextAsync[Self]): # type: ignore[misc] - __ADDRESS = HttpUrl("0.0.0.0:0") - - def __init__(self, observer: HttpServerObserver, notification_endpoint: HttpUrl | None) -> None: - self.__observer = observer - self._app = web.Application() - self.__site: web.TCPSite | None = None - self.__running: bool = False - self.__notification_endpoint = notification_endpoint - self._setup_routes() - - def _setup_routes(self) -> None: - async def handle_put_method(request: web.Request) -> web.Response: - await self.__observer.data_received(await request.json()) - return web.Response(status=HTTPStatus.NO_CONTENT) - - self._app.router.add_route("PUT", "/", handle_put_method) - - @property - def port(self) -> int: - if not self.__site: - raise ServerNotRunningError - server: asyncio.base_events.Server | None = self.__site._server # type: ignore[assignment] - if server is None: - raise ServerSetupError("self.__site.server is None") - - server_socket: socket = server.sockets[0] - address_tuple: tuple[str, int] = server_socket.getsockname() - - if not ( - isinstance(address_tuple, tuple) and isinstance(address_tuple[0], str) and isinstance(address_tuple[1], int) - ): - raise ServerSetupError(f"address_tuple has not recognizable types: {address_tuple}") - - return address_tuple[1] - - async def run(self) -> None: - if self.__site: - raise ServerAlreadyRunningError - - time_between_checks_is_server_running = 0.5 - - runner = web.AppRunner(self._app, access_log=False) - await runner.setup() - address = self.__notification_endpoint or self.__ADDRESS - self.__site = web.TCPSite(runner, address.address, address.port) - await self.__site.start() - self.__running = True - try: - while self.__running: # noqa: ASYNC110 - await asyncio.sleep(time_between_checks_is_server_running) - finally: - await self.__site.stop() - self.__site = None - - def close(self) -> None: - if not self.__site: - raise ServerNotRunningError - self.__running = False - - async def _aenter(self) -> Self: - await self.run() - return self - - async def _afinally(self) -> None: - self.close() diff --git a/beekeepy/beekeepy/_communication/notification_decorator.py b/beekeepy/beekeepy/_communication/notification_decorator.py deleted file mode 100644 index caa22a44..00000000 --- a/beekeepy/beekeepy/_communication/notification_decorator.py +++ /dev/null @@ -1,43 +0,0 @@ -from __future__ import annotations - -from collections.abc import Awaitable, Callable # noqa: TCH003 -from typing import Any, Generic - -from pydantic.generics import GenericModel - -from schemas.notifications import KnownNotificationT, Notification - - -class _NotificationHandlerWrapper(GenericModel, Generic[KnownNotificationT]): - notification_name: str - notification_handler: Callable[[Any, Notification[KnownNotificationT]], Awaitable[None]] - notification_condition: Callable[[Notification[KnownNotificationT]], bool] - - async def call(self, this: Any, notification: Notification[KnownNotificationT]) -> None: - await self.notification_handler(this, notification) - - async def __call__(self, this: Any, notification: Notification[KnownNotificationT]) -> Any: - return self.call(this, notification) - - -def notification( - type_: type[KnownNotificationT], - /, - *, - condition: Callable[[Notification[KnownNotificationT]], bool] | None = None, -) -> Callable[ - [Callable[[Any, Notification[KnownNotificationT]], Awaitable[None]]], - _NotificationHandlerWrapper[KnownNotificationT], -]: - def wrapper( - callback: Callable[[Any, Notification[KnownNotificationT]], Awaitable[None]], - ) -> _NotificationHandlerWrapper[KnownNotificationT]: - result_cls = _NotificationHandlerWrapper[type_] # type: ignore[valid-type] - result_cls.update_forward_refs(**locals()) - return result_cls( # type: ignore[return-value] - notification_name=type_.get_notification_name(), - notification_handler=callback, - notification_condition=condition or (lambda _: True), - ) - - return wrapper diff --git a/beekeepy/beekeepy/_communication/universal_notification_server.py b/beekeepy/beekeepy/_communication/universal_notification_server.py deleted file mode 100644 index 7813e49b..00000000 --- a/beekeepy/beekeepy/_communication/universal_notification_server.py +++ /dev/null @@ -1,87 +0,0 @@ -from __future__ import annotations - -import asyncio -from collections import defaultdict -from threading import Thread -from time import sleep -from typing import TYPE_CHECKING, Any, Final - -from beekeepy._communication.abc.notification_handler import NotificationHandler -from beekeepy._communication.async_server import AsyncHttpServer -from beekeepy._communication.notification_decorator import _NotificationHandlerWrapper -from beekeepy._interface.context import ContextSync -from beekeepy.exceptions import BeekeepyError - -if TYPE_CHECKING: - from beekeepy._interface.url import HttpUrl - from schemas.notifications import KnownNotificationT, Notification - - -class UnhandledNotificationError(BeekeepyError): - def __init__(self, notification: Notification[KnownNotificationT]) -> None: - super().__init__( - f"Notification `{notification.name}` does not have any registered method to be passed to.", notification - ) - - -class UniversalNotificationHandler(NotificationHandler): - def __init__(self, *args: Any, **kwargs: Any) -> None: - self.__registered_notifications: defaultdict[str, list[_NotificationHandlerWrapper[Any]]] = defaultdict(list) - super().__init__(*args, **kwargs) - - def setup(self) -> None: - for member_name in dir(self): - member_value = getattr(self, member_name) - if isinstance(member_value, _NotificationHandlerWrapper): - self.__registered_notifications[member_value.notification_name].append(member_value) - - async def handle_notification(self, notification: Notification[KnownNotificationT]) -> None: - if (callbacks := self.__registered_notifications.get(notification.name)) is not None: - for callback in callbacks: - if callback.notification_condition(notification): - await callback.call(self, notification) - return - - raise UnhandledNotificationError(notification) - - -class UniversalNotificationServer(ContextSync[int]): - def __init__( - self, - event_handler: UniversalNotificationHandler, - notification_endpoint: HttpUrl | None = None, - *, - thread_name: str | None = None, - ) -> None: - self.__event_handler = event_handler - self.__event_handler.setup() - self.__http_server = AsyncHttpServer(self.__event_handler, notification_endpoint=notification_endpoint) - self.__server_thread: Thread | None = None - self.__thread_name = thread_name - - def run(self) -> int: - time_to_launch_notification_server: Final[float] = 0.5 - assert self.__server_thread is None, "Server thread is not None; Is server still running?" - - self.__server_thread = Thread(target=asyncio.run, args=(self.__http_server.run(),), name=self.__thread_name) - self.__server_thread.start() - sleep(time_to_launch_notification_server) - return self.__http_server.port - - def close(self) -> None: - if self.__server_thread is None: - return - - self.__http_server.close() - self.__server_thread.join() - self.__server_thread = None - - @property - def port(self) -> int: - return self.__http_server.port - - def _enter(self) -> int: - return self.run() - - def _finally(self) -> None: - self.close() diff --git a/beekeepy/beekeepy/_executable/beekeeper_config.py b/beekeepy/beekeepy/_executable/beekeeper_config.py index 904ebb7c..3aea8726 100644 --- a/beekeepy/beekeepy/_executable/beekeeper_config.py +++ b/beekeepy/beekeepy/_executable/beekeeper_config.py @@ -23,7 +23,6 @@ class BeekeeperConfig(Config): webserver_ws_endpoint: WsUrl | None = None webserver_ws_deflate: int = 0 webserver_thread_pool_size: int = 1 - notifications_endpoint: HttpUrl | None = BeekeeperDefaults.DEFAULT_NOTIFICATIONS_ENDPOINT backtrace: str = BeekeeperDefaults.DEFAULT_BACKTRACE plugin: list[str] = Field(default_factory=lambda: ["json_rpc", "webserver"]) export_keys_wallet: ExportKeysWalletParams | None = BeekeeperDefaults.DEFAULT_EXPORT_KEYS_WALLET diff --git a/beekeepy/beekeepy/_executable/beekeeper_executable.py b/beekeepy/beekeepy/_executable/beekeeper_executable.py index 38ebcf1b..39e94765 100644 --- a/beekeepy/beekeepy/_executable/beekeeper_executable.py +++ b/beekeepy/beekeepy/_executable/beekeeper_executable.py @@ -25,7 +25,9 @@ class BeekeeperExecutable(Executable[BeekeeperConfig, BeekeeperArguments]): ) def _construct_config(self) -> BeekeeperConfig: - return BeekeeperConfig(wallet_dir=self.working_directory) + config = BeekeeperConfig(wallet_dir=self.working_directory) + config.plugin.append("app_status_api") + return config def _construct_arguments(self) -> BeekeeperArguments: return BeekeeperArguments(data_dir=self.working_directory) @@ -45,7 +47,6 @@ class BeekeeperExecutable(Executable[BeekeeperConfig, BeekeeperArguments]): blocking=True, arguments=BeekeeperArguments( data_dir=tempdir_path, - notifications_endpoint=HttpUrl("0.0.0.0:0"), export_keys_wallet=ExportKeysWalletParams(wallet_name=wallet_name, wallet_password=wallet_password), ), ) diff --git a/beekeepy/beekeepy/_executable/defaults.py b/beekeepy/beekeepy/_executable/defaults.py index c3f2f5d5..955fcf0a 100644 --- a/beekeepy/beekeepy/_executable/defaults.py +++ b/beekeepy/beekeepy/_executable/defaults.py @@ -20,7 +20,6 @@ class BeekeeperDefaults(BaseModel): DEFAULT_DATA_DIR: ClassVar[Path] = Path.cwd() DEFAULT_EXPORT_KEYS_WALLET: ClassVar[ExportKeysWalletParams | None] = None DEFAULT_LOG_JSON_RPC: ClassVar[Path | None] = None - DEFAULT_NOTIFICATIONS_ENDPOINT: ClassVar[HttpUrl | None] = None DEFAULT_UNLOCK_TIMEOUT: ClassVar[int] = 900 DEFAULT_UNLOCK_INTERVAL: ClassVar[int] = 500 DEFAULT_WALLET_DIR: ClassVar[Path] = Path.cwd() diff --git a/beekeepy/beekeepy/_remote_handle/settings.py b/beekeepy/beekeepy/_remote_handle/settings.py index ede77a6a..470e6c1a 100644 --- a/beekeepy/beekeepy/_remote_handle/settings.py +++ b/beekeepy/beekeepy/_remote_handle/settings.py @@ -2,14 +2,16 @@ from __future__ import annotations from typing import ClassVar -from beekeepy._communication.abc.communicator import AbstractCommunicator # noqa: TCH001 -from beekeepy._communication.abc.overseer import AbstractOverseer -from beekeepy._communication.overseers import CommonOverseer -from beekeepy._communication.settings import CommunicationSettings -from beekeepy._interface.url import HttpUrl # noqa: TCH001 +from beekeepy._communication import ( + AbstractCommunicator, + AbstractOverseer, + CommonOverseer, + CommunicationSettings, + HttpUrl, +) -class Settings(CommunicationSettings): +class RemoteHandleSettings(CommunicationSettings): class Defaults(CommunicationSettings.Defaults): OVERSEER: ClassVar[type[AbstractOverseer]] = CommonOverseer diff --git a/beekeepy/beekeepy/_runnable_handle/beekeeper.py b/beekeepy/beekeepy/_runnable_handle/beekeeper.py index 58bc9f66..912ca3df 100644 --- a/beekeepy/beekeepy/_runnable_handle/beekeeper.py +++ b/beekeepy/beekeepy/_runnable_handle/beekeeper.py @@ -1,250 +1,114 @@ from __future__ import annotations -from abc import ABC, abstractmethod -from subprocess import CalledProcessError -from typing import TYPE_CHECKING, Any, TypeVar, cast - -from beekeepy._communication.universal_notification_server import ( - UniversalNotificationServer, -) -from beekeepy._executable import BeekeeperArguments, BeekeeperExecutable -from beekeepy._executable.arguments.beekeeper_arguments import ( - BeekeeperArgumentsDefaults, -) -from beekeepy._interface.url import HttpUrl -from beekeepy._remote_handle import beekeeper as remote_beekeeper -from beekeepy._runnable_handle.beekeeper_callbacks import BeekeeperNotificationCallbacks -from beekeepy._runnable_handle.beekeeper_notification_handler import NotificationHandler +from typing import TYPE_CHECKING, TypeVar + +from beekeepy._executable import BeekeeperArguments, BeekeeperConfig, BeekeeperExecutable +from beekeepy._remote_handle import AsyncBeekeeperTemplate, BeekeeperTemplate +from beekeepy._runnable_handle.runnable_handle import RunnableHandle from beekeepy._runnable_handle.settings import Settings -from beekeepy.exceptions import ( - BeekeeperFailedToStartDuringProcessSpawnError, - BeekeeperFailedToStartNotReadyOnTimeError, - BeekeeperIsNotRunningError, -) +from beekeepy.exceptions import BeekeeperFailedToStartError, ExecutableError if TYPE_CHECKING: from pathlib import Path - from loguru import Logger - - from beekeepy._executable.beekeeper_config import BeekeeperConfig - from beekeepy._interface.key_pair import KeyPair - from schemas.notifications import ( - Error, - Notification, - Status, - WebserverListening, - ) - - -EnterReturnT = TypeVar("EnterReturnT", bound=remote_beekeeper.Beekeeper | remote_beekeeper.AsyncBeekeeper) + from beekeepy._communication import HttpUrl + from beekeepy._executable import KeyPair + from beekeepy._runnable_handle.match_ports import PortMatchingResult __all__ = [ - "SyncRemoteBeekeeper", - "AsyncRemoteBeekeeper", "Beekeeper", "AsyncBeekeeper", ] +RunnableSettingsT = TypeVar("RunnableSettingsT", bound=Settings) -class SyncRemoteBeekeeper(remote_beekeeper.Beekeeper): - pass - - -class AsyncRemoteBeekeeper(remote_beekeeper.AsyncBeekeeper): - pass - - -class BeekeeperCommon(BeekeeperNotificationCallbacks, ABC): - def __init__(self, *args: Any, settings: Settings, logger: Logger, **kwargs: Any) -> None: - super().__init__(*args, settings=settings, logger=logger, **kwargs) - self.__exec = BeekeeperExecutable(settings, logger) - self.__notification_server: UniversalNotificationServer | None = None - self.__notification_event_handler: NotificationHandler | None = None - self.__logger = logger - @property - def pid(self) -> int: - if not self.is_running: - raise BeekeeperIsNotRunningError - return self.__exec.pid +class RunnableBeekeeper(RunnableHandle[BeekeeperExecutable, BeekeeperConfig, BeekeeperArguments, Settings]): + def _construct_executable(self) -> BeekeeperExecutable: + settings = self._get_settings() + return BeekeeperExecutable( + executable_path=settings.binary_path, + working_directory=settings.ensured_working_directory, + logger=self._logger, + ) - @property - def notification_endpoint(self) -> HttpUrl: - endpoint = self._get_settings().notification_endpoint - assert endpoint is not None, "Notification endpoint is not set" - return endpoint + def _get_working_directory_from_cli_arguments(self) -> Path | None: + return self.arguments.data_dir - @property - def config(self) -> BeekeeperConfig: - return self.__exec.config + def _get_http_endpoint_from_cli_arguments(self) -> HttpUrl | None: + return self.arguments.webserver_http_endpoint - @property - def is_running(self) -> bool: - return self.__exec is not None and self.__exec.is_running() + def _get_http_endpoint_from_config(self) -> HttpUrl | None: + return self.config.webserver_http_endpoint - def __setup_notification_server(self, *, address_from_cli_arguments: HttpUrl | None = None) -> None: - assert self.__notification_server is None, "Notification server already exists, previous hasn't been close?" - assert ( - self.__notification_event_handler is None - ), "Notification event handler already exists, previous hasn't been close?" + def _unify_cli_arguments(self, working_directory: Path, http_endpoint: HttpUrl) -> None: + self.arguments.data_dir = working_directory + self.arguments.webserver_http_endpoint = http_endpoint - self.__notification_event_handler = NotificationHandler(self) - self.__notification_server = UniversalNotificationServer( - self.__notification_event_handler, - notification_endpoint=address_from_cli_arguments - or self._get_settings().notification_endpoint, # this has to be accessed directly from settings - ) + def _unify_config(self, working_directory: Path, http_endpoint: HttpUrl) -> None: # noqa: ARG002 + self.config.webserver_http_endpoint = http_endpoint - def __close_notification_server(self) -> None: - if self.__notification_server is not None: - self.__notification_server.close() - self.__notification_server = None - - if self.__notification_event_handler is not None: - self.__notification_event_handler = None - - def __wait_till_ready(self) -> None: - assert self.__notification_event_handler is not None, "Notification event handler hasn't been set" - if not self.__notification_event_handler.http_listening_event.wait( - timeout=self._get_settings().initialization_timeout.total_seconds() - ): - raise TimeoutError("Waiting too long for beekeeper to be up and running") - - def _handle_error(self, error: Error) -> None: - self.__logger.error(f"Beekeepr error: `{error.json()}`") - - def _handle_status_change(self, status: Status) -> None: - self.__logger.info(f"Beekeeper status change to: `{status.current_status}`") - - def _run( - self, - settings: Settings, - additional_cli_arguments: BeekeeperArguments | None = None, - ) -> None: - aca = additional_cli_arguments or BeekeeperArguments() - self.__setup_notification_server(address_from_cli_arguments=aca.notifications_endpoint) - assert self.__notification_server is not None, "Creation of notification server failed" - settings.notification_endpoint = HttpUrl(f"127.0.0.1:{self.__notification_server.run()}", protocol="http") - settings.http_endpoint = ( - aca.webserver_http_endpoint or settings.http_endpoint or HttpUrl("127.0.0.1:0", protocol="http") - ) - settings.working_directory = ( - aca.data_dir - if aca.data_dir != BeekeeperArgumentsDefaults.DEFAULT_DATA_DIR - else self.__exec.working_directory - ) - try: - self._run_application(settings=settings, additional_cli_arguments=aca) - except CalledProcessError as e: - raise BeekeeperFailedToStartDuringProcessSpawnError from e - try: - self.__wait_till_ready() - except (AssertionError, TimeoutError) as e: - self._close() - raise BeekeeperFailedToStartNotReadyOnTimeError from e - - def _run_application(self, settings: Settings, additional_cli_arguments: BeekeeperArguments) -> None: - assert settings.notification_endpoint is not None - self.__exec.run( - blocking=False, - arguments=additional_cli_arguments.copy( - update={ - "notifications_endpoint": settings.notification_endpoint, - "webserver_http_endpoint": settings.ensured_http_endpoint, - "data_dir": settings.ensured_working_directory, - } - ), - propagate_sigint=settings.propagate_sigint, - ) + def run(self, additional_cli_arguments: BeekeeperArguments | None = None) -> None: + with self._exec.restore_arguments(additional_cli_arguments): + try: + self._run() + except ExecutableError as e: + raise BeekeeperFailedToStartError from e - def detach(self) -> int: - pid = self.__exec.detach() - self.__close_notification_server() - return pid + def _write_ports(self, editable_settings: Settings, ports: PortMatchingResult) -> None: + editable_settings.http_endpoint = ports.http + self.config.webserver_http_endpoint = ports.http + self.config.webserver_ws_endpoint = ports.websocket def _close(self) -> None: self._close_application() - self.__close_notification_server() def _close_application(self) -> None: - if self.__exec.is_running(): - self.__exec.close(self._get_settings().close_timeout.total_seconds()) - - def _http_webserver_ready(self, notification: Notification[WebserverListening]) -> None: - """It is converted by _get_http_endpoint_from_event.""" - - def _get_http_endpoint_from_event(self) -> HttpUrl: - assert self.__notification_event_handler is not None, "Notification event handler hasn't been set" - # <###> if you get exception from here, and have consistent way of reproduce please report <###> - # make sure you didn't forget to call beekeeper.run() method - addr = self.__notification_event_handler.http_endpoint_from_event - assert addr is not None, "Endpoint from event was not set" - return addr + if self._exec.is_running(): + self._exec.close(self._get_settings().close_timeout.total_seconds()) def export_keys_wallet( self, wallet_name: str, wallet_password: str, extract_to: Path | None = None ) -> list[KeyPair]: - return self.__exec.export_keys_wallet( + return self._exec.export_keys_wallet( wallet_name=wallet_name, wallet_password=wallet_password, extract_to=extract_to, ) - @abstractmethod - def _get_settings(self) -> Settings: ... - - -class Beekeeper(BeekeeperCommon, SyncRemoteBeekeeper): - def run(self, *, additional_cli_arguments: BeekeeperArguments | None = None) -> None: - self._clear_session() - with self.update_settings() as settings: - self._run( - settings=cast(Settings, settings), - additional_cli_arguments=additional_cli_arguments, - ) - self.http_endpoint = self._get_http_endpoint_from_event() +class Beekeeper(RunnableBeekeeper, BeekeeperTemplate[RunnableSettingsT]): def _get_settings(self) -> Settings: - assert isinstance(self.settings, Settings) return self.settings - @property - def settings(self) -> Settings: - return cast(Settings, super().settings) - - def _enter(self) -> Beekeeper: + def _enter(self) -> Beekeeper[RunnableSettingsT]: self.run() return self def teardown(self) -> None: self._close() + self._clear_session() super().teardown() - -class AsyncBeekeeper(BeekeeperCommon, AsyncRemoteBeekeeper): - def run(self, *, additional_cli_arguments: BeekeeperArguments | None = None) -> None: - self._clear_session() + def _setup_ports(self, ports: PortMatchingResult) -> None: with self.update_settings() as settings: - self._run( - settings=cast(Settings, settings), - additional_cli_arguments=additional_cli_arguments, - ) - self.http_endpoint = self._get_http_endpoint_from_event() + self._write_ports(settings, ports) + +class AsyncBeekeeper(RunnableBeekeeper, AsyncBeekeeperTemplate[RunnableSettingsT]): def _get_settings(self) -> Settings: - assert isinstance(self.settings, Settings) return self.settings - @property - def settings(self) -> Settings: - return cast(Settings, super().settings) - - async def _aenter(self) -> AsyncBeekeeper: + async def _aenter(self) -> AsyncBeekeeper[RunnableSettingsT]: self.run() return self def teardown(self) -> None: - self._close() + self.close() + self._clear_session() super().teardown() + + def _setup_ports(self, ports: PortMatchingResult) -> None: + with self.update_settings() as settings: + self._write_ports(settings, ports) diff --git a/beekeepy/beekeepy/_runnable_handle/beekeeper_callbacks.py b/beekeepy/beekeepy/_runnable_handle/beekeeper_callbacks.py deleted file mode 100644 index 8ca36a4c..00000000 --- a/beekeepy/beekeepy/_runnable_handle/beekeeper_callbacks.py +++ /dev/null @@ -1,42 +0,0 @@ -from __future__ import annotations - -import warnings -from abc import ABC -from typing import TYPE_CHECKING, Any, Protocol - -if TYPE_CHECKING: - from schemas.notifications import ( - AttemptClosingWallets, - Error, - Notification, - OpeningBeekeeperFailed, - Status, - WebserverListening, - ) - - -class NotificationCallback(Protocol): - def __call__(self, notification: Notification[Any]) -> None: ... - - -class BeekeeperNotificationCallbacks(ABC): # noqa: B024 - def __init__(self, *args: Any, **kwargs: Any) -> None: - super().__init__(*args, **kwargs) - - def _http_webserver_ready(self, notification: Notification[WebserverListening]) -> None: - self.__empty_handle_message(notification.value) - - def _handle_error(self, error: Error) -> None: - self.__empty_handle_message(error) - - def _handle_status_change(self, status: Status) -> None: - self.__empty_handle_message(status) - - def _handle_wallets_closed(self, note: AttemptClosingWallets) -> None: - self.__empty_handle_message(note) - - def _handle_opening_beekeeper_failed(self, info: OpeningBeekeeperFailed) -> None: - self.__empty_handle_message(info) - - def __empty_handle_message(self, obj: Any) -> None: - warnings.warn(f"Notification `{type(obj).__name__}` hasn't been handled", category=RuntimeWarning, stacklevel=1) diff --git a/beekeepy/beekeepy/_runnable_handle/beekeeper_notification_handler.py b/beekeepy/beekeepy/_runnable_handle/beekeeper_notification_handler.py deleted file mode 100644 index 74f91779..00000000 --- a/beekeepy/beekeepy/_runnable_handle/beekeeper_notification_handler.py +++ /dev/null @@ -1,70 +0,0 @@ -from __future__ import annotations - -from threading import Event -from typing import TYPE_CHECKING, Any - -from loguru import logger - -from beekeepy._interface.url import HttpUrl -from beekeepy._runnable_handle.notification_handler_base import BeekeeperNotificationHandler - -if TYPE_CHECKING: - from beekeepy._runnable_handle.beekeeper_callbacks import BeekeeperNotificationCallbacks - from schemas.notifications import ( - AttemptClosingWallets, - Error, - KnownNotificationT, - Notification, - OpeningBeekeeperFailed, - Status, - WebserverListening, - ) - - -class NotificationHandler(BeekeeperNotificationHandler): - def __init__(self, owner: BeekeeperNotificationCallbacks, *args: Any, **kwargs: Any) -> None: - super().__init__(*args, **kwargs) - self.__owner = owner - - self.http_listening_event = Event() - self.http_endpoint_from_event: HttpUrl | None = None - - self.already_working_beekeeper_event = Event() - self.already_working_beekeeper_http_address: HttpUrl | None = None - self.already_working_beekeeper_pid: int | None = None - - async def on_attempt_of_closing_wallets(self, notification: Notification[AttemptClosingWallets]) -> None: - self.__owner._handle_wallets_closed(notification.value) - - async def on_opening_beekeeper_failed(self, notification: Notification[OpeningBeekeeperFailed]) -> None: - self.already_working_beekeeper_http_address = HttpUrl( - self.__combine_url_string( - notification.value.connection.address, - notification.value.connection.port, - ), - protocol="http", - ) - self.already_working_beekeeper_pid = int(notification.value.pid) - self.already_working_beekeeper_event.set() - self.__owner._handle_opening_beekeeper_failed(notification.value) - - async def on_error(self, notification: Notification[Error]) -> None: - self.__owner._handle_error(notification.value) - - async def on_status_changed(self, notification: Notification[Status]) -> None: - self.__owner._handle_status_change(notification.value) - - async def on_http_webserver_bind(self, notification: Notification[WebserverListening]) -> None: - self.http_endpoint_from_event = HttpUrl( - self.__combine_url_string(notification.value.address, notification.value.port), - protocol="http", - ) - self.http_listening_event.set() - self.__owner._http_webserver_ready(notification) - - async def handle_notification(self, notification: Notification[KnownNotificationT]) -> None: - logger.debug(f"got notification: {notification.json()}") - return await super().handle_notification(notification) - - def __combine_url_string(self, address: str, port: int) -> str: - return f"{address}:{port}" diff --git a/beekeepy/beekeepy/_runnable_handle/match_ports.py b/beekeepy/beekeepy/_runnable_handle/match_ports.py new file mode 100644 index 00000000..09f46fb6 --- /dev/null +++ b/beekeepy/beekeepy/_runnable_handle/match_ports.py @@ -0,0 +1,72 @@ +from __future__ import annotations + +import socket +import ssl +from dataclasses import dataclass, field +from typing import Final + +from beekeepy._communication import HttpUrl, P2PUrl, WsUrl + +__all__ = ["PortMatchingResult", "match_ports"] + +# https://http.cat/status/426 +WEBSERVER_SPECIFIC_RESPONSE: Final[bytes] = b"426 Upgrade Required" + + +@dataclass +class PortMatchingResult: + http: HttpUrl | None = None + https: HttpUrl | None = None + websocket: WsUrl | None = None + p2p: list[P2PUrl] = field(default_factory=list) + + +def test_http(address: HttpUrl) -> bool: + assert address.port is not None, "HTTP CHECK: Port has to be set" + try: + with socket.create_connection((address.address, address.port), timeout=1) as sock: + sock.sendall(b"GET / HTTP/1.1\r\nHost: localhost\r\n\r\n") + response = sock.recv(1024) + return response.startswith(b"HTTP") and WEBSERVER_SPECIFIC_RESPONSE not in response + except (OSError, socket.timeout, ConnectionRefusedError): + return False + + +def test_https(address: HttpUrl) -> bool: + assert address.port is not None, "HTTPS CHECK: Port has to be set" + try: + context = ssl.create_default_context() + with socket.create_connection((address.address, address.port), timeout=1) as sock, context.wrap_socket( + sock, server_hostname="localhost" + ) as ssock: + ssock.sendall(b"GET / HTTP/1.1\r\nHost: localhost\r\n\r\n") + response = ssock.recv(1024) + return response.startswith(b"HTTP") and WEBSERVER_SPECIFIC_RESPONSE not in response + except (OSError, socket.timeout, ConnectionRefusedError, ssl.SSLError): + return False + + +def test_websocket(address: WsUrl) -> bool: + assert address.port is not None, "WS CHECK: Port has to be set" + try: + with socket.create_connection((address.address, address.port), timeout=1) as sock: + sock.sendall(b"GET / HTTP/1.1\r\nHost: localhost\r\nUpgrade: websocket\r\nConnection: Upgrade\r\n\r\n") + response = sock.recv(1024) + return WEBSERVER_SPECIFIC_RESPONSE in response + except (OSError, socket.timeout, ConnectionRefusedError): + return False + + +def match_ports(ports: list[int], *, address: str = "127.0.0.1") -> PortMatchingResult: + categories = PortMatchingResult() + for port in ports: + if categories.http is None and test_http(http_result := HttpUrl.factory(port=port, address=address)): + categories.http = http_result + elif categories.https is None and test_https(http_result := HttpUrl.factory(port=port, address=address)): + categories.https = http_result + elif categories.websocket is None and test_websocket(ws_result := WsUrl.factory(port=port, address=address)): + categories.websocket = ws_result + else: + categories.p2p.append(P2PUrl.factory(port=port, address=address)) + + return categories diff --git a/beekeepy/beekeepy/_runnable_handle/notification_handler_base.py b/beekeepy/beekeepy/_runnable_handle/notification_handler_base.py deleted file mode 100644 index 1e3195be..00000000 --- a/beekeepy/beekeepy/_runnable_handle/notification_handler_base.py +++ /dev/null @@ -1,29 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING - -from beekeepy._communication.appbase_notification_handler import AppbaseNotificationHandler -from beekeepy._communication.notification_decorator import notification -from schemas.notifications import AttemptClosingWallets, OpeningBeekeeperFailed - -if TYPE_CHECKING: - from schemas.notifications import Notification - - -class BeekeeperNotificationHandler(AppbaseNotificationHandler): - @notification(AttemptClosingWallets) - async def _on_attempt_of_closing_wallets(self, notification: Notification[AttemptClosingWallets]) -> None: - await self.on_attempt_of_closing_wallets(notification) - - @notification(OpeningBeekeeperFailed) - async def _on_opening_beekeeper_failed(self, notification: Notification[OpeningBeekeeperFailed]) -> None: - await self.on_opening_beekeeper_failed(notification) - - async def on_attempt_of_closing_wallets(self, notification: Notification[AttemptClosingWallets]) -> None: - """Called when beekeeper attempts to close wallets in session with given token.""" - - async def on_opening_beekeeper_failed(self, notification: Notification[OpeningBeekeeperFailed]) -> None: - """Called, when beekeeper failed to start. - - That is because of already running other beekeeper in selected working directory. - """ diff --git a/beekeepy/beekeepy/_runnable_handle/runnable_handle.py b/beekeepy/beekeepy/_runnable_handle/runnable_handle.py new file mode 100644 index 00000000..5efd171f --- /dev/null +++ b/beekeepy/beekeepy/_runnable_handle/runnable_handle.py @@ -0,0 +1,256 @@ +from __future__ import annotations + +import time +import warnings +from abc import ABC, abstractmethod +from datetime import timedelta +from pathlib import Path +from subprocess import SubprocessError +from typing import TYPE_CHECKING, Any, Generic, TypeVar, cast + +from loguru import logger as default_logger + +from beekeepy._communication import HttpUrl, P2PUrl, WsUrl +from beekeepy._executable import ArgumentT, ConfigT, Executable +from beekeepy._remote_handle import AppStatusProbe +from beekeepy._runnable_handle.match_ports import PortMatchingResult, match_ports +from beekeepy._runnable_handle.settings import Settings +from beekeepy.exceptions import ( + ApiNotFoundError, + FailedToDetectReservedPortsError, + FailedToStartExecutableError, +) + +if TYPE_CHECKING: + from loguru import Logger + + from schemas.apis.app_status_api import GetAppStatus + + +ExecutableT = TypeVar("ExecutableT", bound=Executable[Any, Any]) +SettingsT = TypeVar("SettingsT", bound=Settings) +T = TypeVar("T") + + +class RunnableHandle(ABC, Generic[ExecutableT, ConfigT, ArgumentT, SettingsT]): + def __init__(self, *args: Any, logger: Logger | None = None, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + self._logger = logger or default_logger + self._exec = self._construct_executable() + + @property + def pid(self) -> int: + """Returns pid of started executable. Note: Proxy method to Executable.pid.""" + return self._exec.pid + + @property + def arguments(self) -> ArgumentT: + """Returns arguments for given binary. Note: Proxy method to Executable.arguments.""" + return cast(ArgumentT, self._exec.arguments) + + @property + def config(self) -> ConfigT: + """Returns config for given binary. Note: Proxy method to Executable.config.""" + return cast(ConfigT, self._exec.config) + + def is_running(self) -> bool: + """Returns is process running. Note: Proxy method to Executable.is_running.""" + return self._exec.is_running() + + def detach(self) -> int: + """Detaches process and allows to keep it after closing python script.""" + return self._exec.detach() + + def close(self) -> None: + """Closes running process. If process is not running, method does nothing.""" + if self.is_running(): + self._exec.close(timeout_secs=self._get_settings().close_timeout.total_seconds()) + + def get_help_text(self) -> str: + """Returns help printed by executable.""" + self.__show_warning_if_executable_already_running() + return self._exec.get_help_text() + + def get_version(self) -> str: + """Returns version string printed by executable.""" + self.__show_warning_if_executable_already_running() + return self._exec.version() + + def generate_default_config_from_executable(self) -> ConfigT: + """Returns config generated by executable.""" + self.__show_warning_if_executable_already_running() + return cast(ConfigT, self._exec.generate_default_config()) + + def _run( + self, + *, + environment_variables: dict[str, str] | None = None, + perform_unification: bool = True, + blocking: bool = False, + save_config: bool = True, + ) -> None: + """ + Runs executable and unifies arguments. + + Note: This method should be called by RunnableHandleChild.run, which is not defined by this interface! + + Keyword Arguments: + environment_variables -- additional environment variables to set before launching executable + additional_cli_arguments -- arguments to add to executable invocation + perform_unification -- if set to true, chosen values will be written to config and cli arguments + """ + settings = self._get_settings().copy() + + settings.working_directory = self.__choose_working_directory(settings=settings) + settings.http_endpoint = self.__choose_http_endpoint(settings=settings) + + if perform_unification: + self._unify_cli_arguments(settings.working_directory, settings.http_endpoint) + self._unify_config(settings.working_directory, settings.http_endpoint) + + try: + self._exec._run( + blocking=blocking, + environ=environment_variables, + propagate_sigint=settings.propagate_sigint, + save_config=save_config, + ) + if blocking: + return + except SubprocessError as e: + raise FailedToStartExecutableError from e + try: + self._wait_for_app_to_start() + except TimeoutError as e: + raise FailedToDetectReservedPortsError from e + self._setup_ports(self.__discover_ports()) + + @abstractmethod + def _construct_executable(self) -> ExecutableT: + """Returns executable instance.""" + + @abstractmethod + def _get_settings(self) -> SettingsT: + """Returns settings hold by child class. Used only for read-only purposes.""" + + def _get_working_directory_from_cli_arguments(self) -> Path | None: + """Returns working directory from specified cli arguments in executable (if specified).""" + return None + + def _get_http_endpoint_from_cli_arguments(self) -> HttpUrl | None: + """Returns http endpoint from specified cli arguments in executable (if specified).""" + return None + + def _get_working_directory_from_config(self) -> Path | None: + """Returns working directory from specified config in executable (if specified).""" + return None + + def _get_http_endpoint_from_config(self) -> HttpUrl | None: + """Returns http endpoint from specified config in executable (if specified).""" + return None + + @abstractmethod + def _unify_cli_arguments(self, working_directory: Path, http_endpoint: HttpUrl) -> None: + """ + Writes selected values to given cli arguments. + + Args: + working_directory -- chosen working path to be set in cli arguments. + http_endpoint -- chosen http endpoint to be set in cli arguments. + """ + + @abstractmethod + def _unify_config(self, working_directory: Path, http_endpoint: HttpUrl) -> None: + """ + Writes selected values to config in executable. + + Args: + working_directory -- chosen working path to be set in config. + http_endpoint -- chosen http endpoint to be set in config. + """ + + def _setup_ports(self, ports: PortMatchingResult) -> None: + """ + Setup ports after startup. + + Args: + ports -- list of ports reserved by started application. + """ + + def _wait_for_app_to_start(self) -> None: + """Waits for application to start.""" + while not self._exec.reserved_ports(): + if not self._exec.is_running(): + raise FailedToStartExecutableError + time.sleep(0.1) + + def __choose_working_directory(self, settings: Settings) -> Path: + return self.__choose_value( + default_value=Path.cwd(), + argument_value=self._get_working_directory_from_cli_arguments(), + config_value=self._get_working_directory_from_config(), + settings_value=settings.working_directory, + ) + + def __choose_http_endpoint(self, settings: Settings) -> HttpUrl: + return self.__choose_value( + default_value=HttpUrl("http://0.0.0.0:0"), + argument_value=self._get_http_endpoint_from_cli_arguments(), + config_value=self._get_http_endpoint_from_config(), + settings_value=settings.http_endpoint, + ) + + def __show_warning_if_executable_already_running(self) -> None: + if self.is_running(): + warnings.warn("Invoking executable that is already running!", stacklevel=2) + + @classmethod + def __choose_value( + cls, + default_value: T, + argument_value: T | None = None, + config_value: T | None = None, + settings_value: T | None = None, + ) -> T: + if argument_value is not None: + return argument_value + if config_value is not None: + return config_value + if settings_value is not None: + return settings_value + return default_value + + def __discover_ports(self) -> PortMatchingResult: + reserved_ports = self._exec.reserved_ports() + matched_ports = match_ports(reserved_ports) + if matched_ports.http is None: + warnings.warn("Given executable probably does not provide http network access", stacklevel=3) + return matched_ports + + handle = AppStatusProbe(settings=Settings(http_endpoint=matched_ports.http, timeout=timedelta(seconds=1))) + status: None | GetAppStatus = None + try: + status = handle.api.get_app_status() + except ApiNotFoundError: + warnings.warn( + "HTTP port detected, but cannot obtain further information. app_status_api plugin is not enabled!", + stacklevel=3, + ) + return matched_ports + + assert status is not None, "Error has not been caught and further port discovery started" + http = status.webservers.HTTP + assert http, "Http cannot be None, as AppStatusProbe is already connected via http" + assert ( + http.port == matched_ports.http.port + ), "Http cannot differ from detected ports, because it is already connected" + + ws = status.webservers.WS + if ws and matched_ports.websocket and ws.port != matched_ports.websocket.port: + matched_ports.websocket = WsUrl.factory(port=ws.port) + + p2p = status.webservers.P2P + if p2p and p2p.port not in [x.port for x in matched_ports.p2p]: + matched_ports.p2p = [P2PUrl.factory(port=p2p.port)] + + return matched_ports diff --git a/beekeepy/beekeepy/_runnable_handle/settings.py b/beekeepy/beekeepy/_runnable_handle/settings.py index c989f3e4..7adf312c 100644 --- a/beekeepy/beekeepy/_runnable_handle/settings.py +++ b/beekeepy/beekeepy/_runnable_handle/settings.py @@ -11,7 +11,7 @@ from beekeepy._remote_handle.settings import Settings as RemoteHandleSettings class Settings(RemoteHandleSettings): - """Defines parameters for beekeeper how to start and behave.""" + """Defines parameters for runnable handles how to start and behave.""" class EnvironNames(RemoteHandleSettings.EnvironNames): WORKING_DIRECTORY: ClassVar[str] = "BEEKEEPY_WORKING_DIRECTORY" @@ -34,16 +34,6 @@ class Settings(RemoteHandleSettings): In case of local beekeeper, this address will be used for beekeeper to start listening on. """ - communicator: type[AbstractCommunicator] | AbstractCommunicator | None = None - """ - Defines class to be used for network handling. Can be given as class or instance. - - Note: If set to none, handles will use preferred communicators - """ - - notification_endpoint: HttpUrl | None = None - """Endpoint to use for reverse communication between beekeeper and python.""" - binary_path: Path | None = None """Alternative path to beekeeper binary.""" @@ -66,6 +56,7 @@ class Settings(RemoteHandleSettings): EnvironNames.INITIALIZATION_TIMEOUT, lambda x: (Settings.Defaults.INITIALIZATION_TIMEOUT if x is None else timedelta(seconds=int(x))), ) + """Affects time handle waits for beekeeper to start.""" @property def ensured_working_directory(self) -> Path: diff --git a/beekeepy/beekeepy/exceptions/__init__.py b/beekeepy/beekeepy/exceptions/__init__.py index 6faeb756..03221948 100644 --- a/beekeepy/beekeepy/exceptions/__init__.py +++ b/beekeepy/beekeepy/exceptions/__init__.py @@ -9,6 +9,7 @@ from beekeepy.exceptions.base import ( CommunicationError, CommunicationResponseT, DetectableError, + ExecutableError, InvalidatedStateError, Json, OverseerError, @@ -27,7 +28,6 @@ from beekeepy.exceptions.common import ( NotPositiveTimeError, ResponseNotReadyError, TimeoutExceededError, - TimeoutReachWhileCloseError, TimeTooBigError, UnknownDecisionPathError, WalletIsLockedError, @@ -46,6 +46,12 @@ from beekeepy.exceptions.detectable import ( NoWalletWithSuchNameError, WalletWithSuchNameAlreadyExistsError, ) +from beekeepy.exceptions.executable import ( + ExecutableIsNotRunningError, + FailedToDetectReservedPortsError, + FailedToStartExecutableError, + TimeoutReachWhileCloseError, +) from beekeepy.exceptions.overseer import ( ApiNotFoundError, DifferenceBetweenAmountOfRequestsAndResponsesError, @@ -79,6 +85,10 @@ __all__ = [ "DetectableError", "DifferenceBetweenAmountOfRequestsAndResponsesError", "ErrorInResponseError", + "ExecutableError", + "ExecutableIsNotRunningError", + "FailedToDetectReservedPortsError", + "FailedToStartExecutableError", "GroupedErrorsError", "InvalidAccountNameError", "InvalidatedStateByClosingBeekeeperError", diff --git a/beekeepy/beekeepy/exceptions/base.py b/beekeepy/beekeepy/exceptions/base.py index 61c77cc9..d59b0d84 100644 --- a/beekeepy/beekeepy/exceptions/base.py +++ b/beekeepy/beekeepy/exceptions/base.py @@ -189,3 +189,7 @@ class OverseerError(CommunicationError, ABC): @abstractmethod def retry(self) -> bool: """Used by overseer to determine if retry should be performed if such error occurs.""" + + +class ExecutableError(BeekeepyError, ABC): + """Base class for errors related to handling executable.""" diff --git a/beekeepy/beekeepy/exceptions/executable.py b/beekeepy/beekeepy/exceptions/executable.py new file mode 100644 index 00000000..fafda8de --- /dev/null +++ b/beekeepy/beekeepy/exceptions/executable.py @@ -0,0 +1,23 @@ +from __future__ import annotations + +from beekeepy.exceptions.base import ExecutableError + + +class TimeoutReachWhileCloseError(ExecutableError): + """Raises when executable did not closed during specified timeout.""" + + def __init__(self) -> None: + """Constructor.""" + super().__init__("Process was force-closed with SIGKILL, because didn't close before timeout") + + +class ExecutableIsNotRunningError(ExecutableError): + """Raises when executable is not running, but user requests action on running instance.""" + + +class FailedToStartExecutableError(ExecutableError): + """Raises when executable failed to start.""" + + +class FailedToDetectReservedPortsError(ExecutableError): + """Raises when port lookup procedure fails.""" diff --git a/beekeepy/poetry.lock b/beekeepy/poetry.lock index feafa092..21c71f37 100644 --- a/beekeepy/poetry.lock +++ b/beekeepy/poetry.lock @@ -13,92 +13,92 @@ files = [ [[package]] name = "aiohttp" -version = "3.11.14" +version = "3.11.16" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.9" files = [ - {file = "aiohttp-3.11.14-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e2bc827c01f75803de77b134afdbf74fa74b62970eafdf190f3244931d7a5c0d"}, - {file = "aiohttp-3.11.14-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e365034c5cf6cf74f57420b57682ea79e19eb29033399dd3f40de4d0171998fa"}, - {file = "aiohttp-3.11.14-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c32593ead1a8c6aabd58f9d7ee706e48beac796bb0cb71d6b60f2c1056f0a65f"}, - {file = "aiohttp-3.11.14-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4e7c7ec4146a94a307ca4f112802a8e26d969018fabed526efc340d21d3e7d0"}, - {file = "aiohttp-3.11.14-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c8b2df9feac55043759aa89f722a967d977d80f8b5865a4153fc41c93b957efc"}, - {file = "aiohttp-3.11.14-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c7571f99525c76a6280f5fe8e194eeb8cb4da55586c3c61c59c33a33f10cfce7"}, - {file = "aiohttp-3.11.14-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b59d096b5537ec7c85954cb97d821aae35cfccce3357a2cafe85660cc6295628"}, - {file = "aiohttp-3.11.14-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b42dbd097abb44b3f1156b4bf978ec5853840802d6eee2784857be11ee82c6a0"}, - {file = "aiohttp-3.11.14-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b05774864c87210c531b48dfeb2f7659407c2dda8643104fb4ae5e2c311d12d9"}, - {file = "aiohttp-3.11.14-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:4e2e8ef37d4bc110917d038807ee3af82700a93ab2ba5687afae5271b8bc50ff"}, - {file = "aiohttp-3.11.14-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e9faafa74dbb906b2b6f3eb9942352e9e9db8d583ffed4be618a89bd71a4e914"}, - {file = "aiohttp-3.11.14-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:7e7abe865504f41b10777ac162c727af14e9f4db9262e3ed8254179053f63e6d"}, - {file = "aiohttp-3.11.14-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:4848ae31ad44330b30f16c71e4f586cd5402a846b11264c412de99fa768f00f3"}, - {file = "aiohttp-3.11.14-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2d0b46abee5b5737cb479cc9139b29f010a37b1875ee56d142aefc10686a390b"}, - {file = "aiohttp-3.11.14-cp310-cp310-win32.whl", hash = "sha256:a0d2c04a623ab83963576548ce098baf711a18e2c32c542b62322a0b4584b990"}, - {file = "aiohttp-3.11.14-cp310-cp310-win_amd64.whl", hash = "sha256:5409a59d5057f2386bb8b8f8bbcfb6e15505cedd8b2445db510563b5d7ea1186"}, - {file = "aiohttp-3.11.14-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f296d637a50bb15fb6a229fbb0eb053080e703b53dbfe55b1e4bb1c5ed25d325"}, - {file = "aiohttp-3.11.14-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ec6cd1954ca2bbf0970f531a628da1b1338f594bf5da7e361e19ba163ecc4f3b"}, - {file = "aiohttp-3.11.14-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:572def4aad0a4775af66d5a2b5923c7de0820ecaeeb7987dcbccda2a735a993f"}, - {file = "aiohttp-3.11.14-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c68e41c4d576cd6aa6c6d2eddfb32b2acfb07ebfbb4f9da991da26633a3db1a"}, - {file = "aiohttp-3.11.14-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99b8bbfc8111826aa8363442c0fc1f5751456b008737ff053570f06a151650b3"}, - {file = "aiohttp-3.11.14-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b0a200e85da5c966277a402736a96457b882360aa15416bf104ca81e6f5807b"}, - {file = "aiohttp-3.11.14-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d173c0ac508a2175f7c9a115a50db5fd3e35190d96fdd1a17f9cb10a6ab09aa1"}, - {file = "aiohttp-3.11.14-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:413fe39fd929329f697f41ad67936f379cba06fcd4c462b62e5b0f8061ee4a77"}, - {file = "aiohttp-3.11.14-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:65c75b14ee74e8eeff2886321e76188cbe938d18c85cff349d948430179ad02c"}, - {file = "aiohttp-3.11.14-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:321238a42ed463848f06e291c4bbfb3d15ba5a79221a82c502da3e23d7525d06"}, - {file = "aiohttp-3.11.14-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:59a05cdc636431f7ce843c7c2f04772437dd816a5289f16440b19441be6511f1"}, - {file = "aiohttp-3.11.14-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:daf20d9c3b12ae0fdf15ed92235e190f8284945563c4b8ad95b2d7a31f331cd3"}, - {file = "aiohttp-3.11.14-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:05582cb2d156ac7506e68b5eac83179faedad74522ed88f88e5861b78740dc0e"}, - {file = "aiohttp-3.11.14-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:12c5869e7ddf6b4b1f2109702b3cd7515667b437da90a5a4a50ba1354fe41881"}, - {file = "aiohttp-3.11.14-cp311-cp311-win32.whl", hash = "sha256:92868f6512714efd4a6d6cb2bfc4903b997b36b97baea85f744229f18d12755e"}, - {file = "aiohttp-3.11.14-cp311-cp311-win_amd64.whl", hash = "sha256:bccd2cb7aa5a3bfada72681bdb91637094d81639e116eac368f8b3874620a654"}, - {file = "aiohttp-3.11.14-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:70ab0f61c1a73d3e0342cedd9a7321425c27a7067bebeeacd509f96695b875fc"}, - {file = "aiohttp-3.11.14-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:602d4db80daf4497de93cb1ce00b8fc79969c0a7cf5b67bec96fa939268d806a"}, - {file = "aiohttp-3.11.14-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3a8a0d127c10b8d89e69bbd3430da0f73946d839e65fec00ae48ca7916a31948"}, - {file = "aiohttp-3.11.14-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca9f835cdfedcb3f5947304e85b8ca3ace31eef6346d8027a97f4de5fb687534"}, - {file = "aiohttp-3.11.14-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8aa5c68e1e68fff7cd3142288101deb4316b51f03d50c92de6ea5ce646e6c71f"}, - {file = "aiohttp-3.11.14-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b512f1de1c688f88dbe1b8bb1283f7fbeb7a2b2b26e743bb2193cbadfa6f307"}, - {file = "aiohttp-3.11.14-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc9253069158d57e27d47a8453d8a2c5a370dc461374111b5184cf2f147a3cc3"}, - {file = "aiohttp-3.11.14-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0b2501f1b981e70932b4a552fc9b3c942991c7ae429ea117e8fba57718cdeed0"}, - {file = "aiohttp-3.11.14-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:28a3d083819741592685762d51d789e6155411277050d08066537c5edc4066e6"}, - {file = "aiohttp-3.11.14-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0df3788187559c262922846087e36228b75987f3ae31dd0a1e5ee1034090d42f"}, - {file = "aiohttp-3.11.14-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9e73fa341d8b308bb799cf0ab6f55fc0461d27a9fa3e4582755a3d81a6af8c09"}, - {file = "aiohttp-3.11.14-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:51ba80d473eb780a329d73ac8afa44aa71dfb521693ccea1dea8b9b5c4df45ce"}, - {file = "aiohttp-3.11.14-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:8d1dd75aa4d855c7debaf1ef830ff2dfcc33f893c7db0af2423ee761ebffd22b"}, - {file = "aiohttp-3.11.14-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41cf0cefd9e7b5c646c2ef529c8335e7eafd326f444cc1cdb0c47b6bc836f9be"}, - {file = "aiohttp-3.11.14-cp312-cp312-win32.whl", hash = "sha256:948abc8952aff63de7b2c83bfe3f211c727da3a33c3a5866a0e2cf1ee1aa950f"}, - {file = "aiohttp-3.11.14-cp312-cp312-win_amd64.whl", hash = "sha256:3b420d076a46f41ea48e5fcccb996f517af0d406267e31e6716f480a3d50d65c"}, - {file = "aiohttp-3.11.14-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d14e274828561db91e4178f0057a915f3af1757b94c2ca283cb34cbb6e00b50"}, - {file = "aiohttp-3.11.14-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f30fc72daf85486cdcdfc3f5e0aea9255493ef499e31582b34abadbfaafb0965"}, - {file = "aiohttp-3.11.14-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4edcbe34e6dba0136e4cabf7568f5a434d89cc9de5d5155371acda275353d228"}, - {file = "aiohttp-3.11.14-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a7169ded15505f55a87f8f0812c94c9412623c744227b9e51083a72a48b68a5"}, - {file = "aiohttp-3.11.14-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad1f2fb9fe9b585ea4b436d6e998e71b50d2b087b694ab277b30e060c434e5db"}, - {file = "aiohttp-3.11.14-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:20412c7cc3720e47a47e63c0005f78c0c2370020f9f4770d7fc0075f397a9fb0"}, - {file = "aiohttp-3.11.14-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6dd9766da617855f7e85f27d2bf9a565ace04ba7c387323cd3e651ac4329db91"}, - {file = "aiohttp-3.11.14-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:599b66582f7276ebefbaa38adf37585e636b6a7a73382eb412f7bc0fc55fb73d"}, - {file = "aiohttp-3.11.14-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b41693b7388324b80f9acfabd479bd1c84f0bc7e8f17bab4ecd9675e9ff9c734"}, - {file = "aiohttp-3.11.14-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:86135c32d06927339c8c5e64f96e4eee8825d928374b9b71a3c42379d7437058"}, - {file = "aiohttp-3.11.14-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:04eb541ce1e03edc1e3be1917a0f45ac703e913c21a940111df73a2c2db11d73"}, - {file = "aiohttp-3.11.14-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:dc311634f6f28661a76cbc1c28ecf3b3a70a8edd67b69288ab7ca91058eb5a33"}, - {file = "aiohttp-3.11.14-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:69bb252bfdca385ccabfd55f4cd740d421dd8c8ad438ded9637d81c228d0da49"}, - {file = "aiohttp-3.11.14-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2b86efe23684b58a88e530c4ab5b20145f102916bbb2d82942cafec7bd36a647"}, - {file = "aiohttp-3.11.14-cp313-cp313-win32.whl", hash = "sha256:b9c60d1de973ca94af02053d9b5111c4fbf97158e139b14f1be68337be267be6"}, - {file = "aiohttp-3.11.14-cp313-cp313-win_amd64.whl", hash = "sha256:0a29be28e60e5610d2437b5b2fed61d6f3dcde898b57fb048aa5079271e7f6f3"}, - {file = "aiohttp-3.11.14-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:14fc03508359334edc76d35b2821832f092c8f092e4b356e74e38419dfe7b6de"}, - {file = "aiohttp-3.11.14-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:92007c89a8cb7be35befa2732b0b32bf3a394c1b22ef2dff0ef12537d98a7bda"}, - {file = "aiohttp-3.11.14-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6d3986112e34eaa36e280dc8286b9dd4cc1a5bcf328a7f147453e188f6fe148f"}, - {file = "aiohttp-3.11.14-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:749f1eb10e51dbbcdba9df2ef457ec060554842eea4d23874a3e26495f9e87b1"}, - {file = "aiohttp-3.11.14-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:781c8bd423dcc4641298c8c5a2a125c8b1c31e11f828e8d35c1d3a722af4c15a"}, - {file = "aiohttp-3.11.14-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:997b57e38aa7dc6caab843c5e042ab557bc83a2f91b7bd302e3c3aebbb9042a1"}, - {file = "aiohttp-3.11.14-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a8b0321e40a833e381d127be993b7349d1564b756910b28b5f6588a159afef3"}, - {file = "aiohttp-3.11.14-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8778620396e554b758b59773ab29c03b55047841d8894c5e335f12bfc45ebd28"}, - {file = "aiohttp-3.11.14-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:e906da0f2bcbf9b26cc2b144929e88cb3bf943dd1942b4e5af066056875c7618"}, - {file = "aiohttp-3.11.14-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:87f0e003fb4dd5810c7fbf47a1239eaa34cd929ef160e0a54c570883125c4831"}, - {file = "aiohttp-3.11.14-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:7f2dadece8b85596ac3ab1ec04b00694bdd62abc31e5618f524648d18d9dd7fa"}, - {file = "aiohttp-3.11.14-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:fe846f0a98aa9913c2852b630cd39b4098f296e0907dd05f6c7b30d911afa4c3"}, - {file = "aiohttp-3.11.14-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ced66c5c6ad5bcaf9be54560398654779ec1c3695f1a9cf0ae5e3606694a000a"}, - {file = "aiohttp-3.11.14-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a40087b82f83bd671cbeb5f582c233d196e9653220404a798798bfc0ee189fff"}, - {file = "aiohttp-3.11.14-cp39-cp39-win32.whl", hash = "sha256:95d7787f2bcbf7cb46823036a8d64ccfbc2ffc7d52016b4044d901abceeba3db"}, - {file = "aiohttp-3.11.14-cp39-cp39-win_amd64.whl", hash = "sha256:22a8107896877212130c58f74e64b77f7007cb03cea8698be317272643602d45"}, - {file = "aiohttp-3.11.14.tar.gz", hash = "sha256:d6edc538c7480fa0a3b2bdd705f8010062d74700198da55d16498e1b49549b9c"}, + {file = "aiohttp-3.11.16-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fb46bb0f24813e6cede6cc07b1961d4b04f331f7112a23b5e21f567da4ee50aa"}, + {file = "aiohttp-3.11.16-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:54eb3aead72a5c19fad07219acd882c1643a1027fbcdefac9b502c267242f955"}, + {file = "aiohttp-3.11.16-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:38bea84ee4fe24ebcc8edeb7b54bf20f06fd53ce4d2cc8b74344c5b9620597fd"}, + {file = "aiohttp-3.11.16-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0666afbe984f6933fe72cd1f1c3560d8c55880a0bdd728ad774006eb4241ecd"}, + {file = "aiohttp-3.11.16-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ba92a2d9ace559a0a14b03d87f47e021e4fa7681dc6970ebbc7b447c7d4b7cd"}, + {file = "aiohttp-3.11.16-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ad1d59fd7114e6a08c4814983bb498f391c699f3c78712770077518cae63ff7"}, + {file = "aiohttp-3.11.16-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b88a2bf26965f2015a771381624dd4b0839034b70d406dc74fd8be4cc053e3"}, + {file = "aiohttp-3.11.16-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:576f5ca28d1b3276026f7df3ec841ae460e0fc3aac2a47cbf72eabcfc0f102e1"}, + {file = "aiohttp-3.11.16-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a2a450bcce4931b295fc0848f384834c3f9b00edfc2150baafb4488c27953de6"}, + {file = "aiohttp-3.11.16-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:37dcee4906454ae377be5937ab2a66a9a88377b11dd7c072df7a7c142b63c37c"}, + {file = "aiohttp-3.11.16-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:4d0c970c0d602b1017e2067ff3b7dac41c98fef4f7472ec2ea26fd8a4e8c2149"}, + {file = "aiohttp-3.11.16-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:004511d3413737700835e949433536a2fe95a7d0297edd911a1e9705c5b5ea43"}, + {file = "aiohttp-3.11.16-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:c15b2271c44da77ee9d822552201180779e5e942f3a71fb74e026bf6172ff287"}, + {file = "aiohttp-3.11.16-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ad9509ffb2396483ceacb1eee9134724443ee45b92141105a4645857244aecc8"}, + {file = "aiohttp-3.11.16-cp310-cp310-win32.whl", hash = "sha256:634d96869be6c4dc232fc503e03e40c42d32cfaa51712aee181e922e61d74814"}, + {file = "aiohttp-3.11.16-cp310-cp310-win_amd64.whl", hash = "sha256:938f756c2b9374bbcc262a37eea521d8a0e6458162f2a9c26329cc87fdf06534"}, + {file = "aiohttp-3.11.16-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8cb0688a8d81c63d716e867d59a9ccc389e97ac7037ebef904c2b89334407180"}, + {file = "aiohttp-3.11.16-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0ad1fb47da60ae1ddfb316f0ff16d1f3b8e844d1a1e154641928ea0583d486ed"}, + {file = "aiohttp-3.11.16-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:df7db76400bf46ec6a0a73192b14c8295bdb9812053f4fe53f4e789f3ea66bbb"}, + {file = "aiohttp-3.11.16-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc3a145479a76ad0ed646434d09216d33d08eef0d8c9a11f5ae5cdc37caa3540"}, + {file = "aiohttp-3.11.16-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d007aa39a52d62373bd23428ba4a2546eed0e7643d7bf2e41ddcefd54519842c"}, + {file = "aiohttp-3.11.16-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f6ddd90d9fb4b501c97a4458f1c1720e42432c26cb76d28177c5b5ad4e332601"}, + {file = "aiohttp-3.11.16-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a2f451849e6b39e5c226803dcacfa9c7133e9825dcefd2f4e837a2ec5a3bb98"}, + {file = "aiohttp-3.11.16-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8df6612df74409080575dca38a5237282865408016e65636a76a2eb9348c2567"}, + {file = "aiohttp-3.11.16-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:78e6e23b954644737e385befa0deb20233e2dfddf95dd11e9db752bdd2a294d3"}, + {file = "aiohttp-3.11.16-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:696ef00e8a1f0cec5e30640e64eca75d8e777933d1438f4facc9c0cdf288a810"}, + {file = "aiohttp-3.11.16-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e3538bc9fe1b902bef51372462e3d7c96fce2b566642512138a480b7adc9d508"}, + {file = "aiohttp-3.11.16-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:3ab3367bb7f61ad18793fea2ef71f2d181c528c87948638366bf1de26e239183"}, + {file = "aiohttp-3.11.16-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:56a3443aca82abda0e07be2e1ecb76a050714faf2be84256dae291182ba59049"}, + {file = "aiohttp-3.11.16-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:61c721764e41af907c9d16b6daa05a458f066015abd35923051be8705108ed17"}, + {file = "aiohttp-3.11.16-cp311-cp311-win32.whl", hash = "sha256:3e061b09f6fa42997cf627307f220315e313ece74907d35776ec4373ed718b86"}, + {file = "aiohttp-3.11.16-cp311-cp311-win_amd64.whl", hash = "sha256:745f1ed5e2c687baefc3c5e7b4304e91bf3e2f32834d07baaee243e349624b24"}, + {file = "aiohttp-3.11.16-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:911a6e91d08bb2c72938bc17f0a2d97864c531536b7832abee6429d5296e5b27"}, + {file = "aiohttp-3.11.16-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6ac13b71761e49d5f9e4d05d33683bbafef753e876e8e5a7ef26e937dd766713"}, + {file = "aiohttp-3.11.16-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fd36c119c5d6551bce374fcb5c19269638f8d09862445f85a5a48596fd59f4bb"}, + {file = "aiohttp-3.11.16-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d489d9778522fbd0f8d6a5c6e48e3514f11be81cb0a5954bdda06f7e1594b321"}, + {file = "aiohttp-3.11.16-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:69a2cbd61788d26f8f1e626e188044834f37f6ae3f937bd9f08b65fc9d7e514e"}, + {file = "aiohttp-3.11.16-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd464ba806e27ee24a91362ba3621bfc39dbbb8b79f2e1340201615197370f7c"}, + {file = "aiohttp-3.11.16-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ce63ae04719513dd2651202352a2beb9f67f55cb8490c40f056cea3c5c355ce"}, + {file = "aiohttp-3.11.16-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09b00dd520d88eac9d1768439a59ab3d145065c91a8fab97f900d1b5f802895e"}, + {file = "aiohttp-3.11.16-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7f6428fee52d2bcf96a8aa7b62095b190ee341ab0e6b1bcf50c615d7966fd45b"}, + {file = "aiohttp-3.11.16-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:13ceac2c5cdcc3f64b9015710221ddf81c900c5febc505dbd8f810e770011540"}, + {file = "aiohttp-3.11.16-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:fadbb8f1d4140825069db3fedbbb843290fd5f5bc0a5dbd7eaf81d91bf1b003b"}, + {file = "aiohttp-3.11.16-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:6a792ce34b999fbe04a7a71a90c74f10c57ae4c51f65461a411faa70e154154e"}, + {file = "aiohttp-3.11.16-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:f4065145bf69de124accdd17ea5f4dc770da0a6a6e440c53f6e0a8c27b3e635c"}, + {file = "aiohttp-3.11.16-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fa73e8c2656a3653ae6c307b3f4e878a21f87859a9afab228280ddccd7369d71"}, + {file = "aiohttp-3.11.16-cp312-cp312-win32.whl", hash = "sha256:f244b8e541f414664889e2c87cac11a07b918cb4b540c36f7ada7bfa76571ea2"}, + {file = "aiohttp-3.11.16-cp312-cp312-win_amd64.whl", hash = "sha256:23a15727fbfccab973343b6d1b7181bfb0b4aa7ae280f36fd2f90f5476805682"}, + {file = "aiohttp-3.11.16-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a3814760a1a700f3cfd2f977249f1032301d0a12c92aba74605cfa6ce9f78489"}, + {file = "aiohttp-3.11.16-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9b751a6306f330801665ae69270a8a3993654a85569b3469662efaad6cf5cc50"}, + {file = "aiohttp-3.11.16-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ad497f38a0d6c329cb621774788583ee12321863cd4bd9feee1effd60f2ad133"}, + {file = "aiohttp-3.11.16-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca37057625693d097543bd88076ceebeb248291df9d6ca8481349efc0b05dcd0"}, + {file = "aiohttp-3.11.16-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a5abcbba9f4b463a45c8ca8b7720891200658f6f46894f79517e6cd11f3405ca"}, + {file = "aiohttp-3.11.16-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f420bfe862fb357a6d76f2065447ef6f484bc489292ac91e29bc65d2d7a2c84d"}, + {file = "aiohttp-3.11.16-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58ede86453a6cf2d6ce40ef0ca15481677a66950e73b0a788917916f7e35a0bb"}, + {file = "aiohttp-3.11.16-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fdec0213244c39973674ca2a7f5435bf74369e7d4e104d6c7473c81c9bcc8c4"}, + {file = "aiohttp-3.11.16-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:72b1b03fb4655c1960403c131740755ec19c5898c82abd3961c364c2afd59fe7"}, + {file = "aiohttp-3.11.16-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:780df0d837276276226a1ff803f8d0fa5f8996c479aeef52eb040179f3156cbd"}, + {file = "aiohttp-3.11.16-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ecdb8173e6c7aa09eee342ac62e193e6904923bd232e76b4157ac0bfa670609f"}, + {file = "aiohttp-3.11.16-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:a6db7458ab89c7d80bc1f4e930cc9df6edee2200127cfa6f6e080cf619eddfbd"}, + {file = "aiohttp-3.11.16-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:2540ddc83cc724b13d1838026f6a5ad178510953302a49e6d647f6e1de82bc34"}, + {file = "aiohttp-3.11.16-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3b4e6db8dc4879015b9955778cfb9881897339c8fab7b3676f8433f849425913"}, + {file = "aiohttp-3.11.16-cp313-cp313-win32.whl", hash = "sha256:493910ceb2764f792db4dc6e8e4b375dae1b08f72e18e8f10f18b34ca17d0979"}, + {file = "aiohttp-3.11.16-cp313-cp313-win_amd64.whl", hash = "sha256:42864e70a248f5f6a49fdaf417d9bc62d6e4d8ee9695b24c5916cb4bb666c802"}, + {file = "aiohttp-3.11.16-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bbcba75fe879ad6fd2e0d6a8d937f34a571f116a0e4db37df8079e738ea95c71"}, + {file = "aiohttp-3.11.16-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:87a6e922b2b2401e0b0cf6b976b97f11ec7f136bfed445e16384fbf6fd5e8602"}, + {file = "aiohttp-3.11.16-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ccf10f16ab498d20e28bc2b5c1306e9c1512f2840f7b6a67000a517a4b37d5ee"}, + {file = "aiohttp-3.11.16-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb3d0cc5cdb926090748ea60172fa8a213cec728bd6c54eae18b96040fcd6227"}, + {file = "aiohttp-3.11.16-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d07502cc14ecd64f52b2a74ebbc106893d9a9717120057ea9ea1fd6568a747e7"}, + {file = "aiohttp-3.11.16-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:776c8e959a01e5e8321f1dec77964cb6101020a69d5a94cd3d34db6d555e01f7"}, + {file = "aiohttp-3.11.16-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0902e887b0e1d50424112f200eb9ae3dfed6c0d0a19fc60f633ae5a57c809656"}, + {file = "aiohttp-3.11.16-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e87fd812899aa78252866ae03a048e77bd11b80fb4878ce27c23cade239b42b2"}, + {file = "aiohttp-3.11.16-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0a950c2eb8ff17361abd8c85987fd6076d9f47d040ebffce67dce4993285e973"}, + {file = "aiohttp-3.11.16-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:c10d85e81d0b9ef87970ecbdbfaeec14a361a7fa947118817fcea8e45335fa46"}, + {file = "aiohttp-3.11.16-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:7951decace76a9271a1ef181b04aa77d3cc309a02a51d73826039003210bdc86"}, + {file = "aiohttp-3.11.16-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:14461157d8426bcb40bd94deb0450a6fa16f05129f7da546090cebf8f3123b0f"}, + {file = "aiohttp-3.11.16-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9756d9b9d4547e091f99d554fbba0d2a920aab98caa82a8fb3d3d9bee3c9ae85"}, + {file = "aiohttp-3.11.16-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:87944bd16b7fe6160607f6a17808abd25f17f61ae1e26c47a491b970fb66d8cb"}, + {file = "aiohttp-3.11.16-cp39-cp39-win32.whl", hash = "sha256:92b7ee222e2b903e0a4b329a9943d432b3767f2d5029dbe4ca59fb75223bbe2e"}, + {file = "aiohttp-3.11.16-cp39-cp39-win_amd64.whl", hash = "sha256:17ae4664031aadfbcb34fd40ffd90976671fa0c0286e6c4113989f78bebab37a"}, + {file = "aiohttp-3.11.16.tar.gz", hash = "sha256:16f8a2c9538c14a557b4d309ed4d0a7c60f0253e8ed7b6c9a2859a7582f8b1b8"}, ] [package.dependencies] @@ -621,6 +621,29 @@ files = [ {file = "propcache-0.2.1.tar.gz", hash = "sha256:3f77ce728b19cb537714499928fe800c3dda29e8d9428778fc7c186da4c09a64"}, ] +[[package]] +name = "psutil" +version = "7.0.0" +description = "Cross-platform lib for process and system monitoring in Python. NOTE: the syntax of this script MUST be kept compatible with Python 2.7." +optional = false +python-versions = ">=3.6" +files = [ + {file = "psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25"}, + {file = "psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da"}, + {file = "psutil-7.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fcee592b4c6f146991ca55919ea3d1f8926497a713ed7faaf8225e174581e91"}, + {file = "psutil-7.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b1388a4f6875d7e2aff5c4ca1cc16c545ed41dd8bb596cefea80111db353a34"}, + {file = "psutil-7.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f098451abc2828f7dc6b58d44b532b22f2088f4999a937557b603ce72b1993"}, + {file = "psutil-7.0.0-cp36-cp36m-win32.whl", hash = "sha256:84df4eb63e16849689f76b1ffcb36db7b8de703d1bc1fe41773db487621b6c17"}, + {file = "psutil-7.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:1e744154a6580bc968a0195fd25e80432d3afec619daf145b9e5ba16cc1d688e"}, + {file = "psutil-7.0.0-cp37-abi3-win32.whl", hash = "sha256:ba3fcef7523064a6c9da440fc4d6bd07da93ac726b5733c29027d7dc95b39d99"}, + {file = "psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553"}, + {file = "psutil-7.0.0.tar.gz", hash = "sha256:7be9c3eba38beccb6495ea33afd982a44074b78f28c434a1f51cc07fd315c456"}, +] + +[package.extras] +dev = ["abi3audit", "black (==24.10.0)", "check-manifest", "coverage", "packaging", "pylint", "pyperf", "pypinfo", "pytest", "pytest-cov", "pytest-xdist", "requests", "rstcheck", "ruff", "setuptools", "sphinx", "sphinx_rtd_theme", "toml-sort", "twine", "virtualenv", "vulture", "wheel"] +test = ["pytest", "pytest-xdist", "setuptools"] + [[package]] name = "pydantic" version = "1.10.18" @@ -734,12 +757,12 @@ idna2008 = ["idna"] [[package]] name = "schemas" -version = "0.0.1.dev331+7181389" +version = "0.0.1.dev336+c6f2974" description = "Tools for checking if message fits expected format" optional = false python-versions = ">=3.12,<4.0" files = [ - {file = "schemas-0.0.1.dev331+7181389-py3-none-any.whl", hash = "sha256:f0b51978f6a440bbc2e19b32aeec103cd5621d17902e9a4b3703d32ddd3f3ec3"}, + {file = "schemas-0.0.1.dev336+c6f2974-py3-none-any.whl", hash = "sha256:9a2148f014bdf82e05d138629df11cb20f53799eb32dcfff14942b4b20a76cc6"}, ] [package.dependencies] @@ -946,4 +969,4 @@ propcache = ">=0.2.0" [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "eb1a2c4d9e1883bc79fd8c47f11be5b8fc25b8a75837ef1fb9beeabe12574229" +content-hash = "82e955944afb54dfa4727975524023ccae164af25866f36871b7ae34129ccdd8" diff --git a/beekeepy/pyproject.toml b/beekeepy/pyproject.toml index 8f6c68c3..64d90262 100644 --- a/beekeepy/pyproject.toml +++ b/beekeepy/pyproject.toml @@ -23,17 +23,17 @@ source = [ version = "0.0.0" [tool.poetry.dependencies] -aiohttp = "3.11.14" +python = "^3.12" +aiohttp = "3.11.16" httpx = {extras = ["http2"], version = "0.23.3"} loguru = "0.7.2" -python = "^3.12" +psutil = "7.0.0" python-dateutil = "2.8.2" -pydantic="1.10.18" requests = "2.32.3" setuptools = "77.0.3" types-setuptools = "76.0.0.20250313" -schemas = "0.0.1.dev331+7181389" +schemas = "0.0.1.dev336+c6f2974" [tool.poetry-dynamic-versioning] enable = true diff --git a/hive b/hive index 18f1d5c7..40e6bc38 160000 --- a/hive +++ b/hive @@ -1 +1 @@ -Subproject commit 18f1d5c753735ddba3ab85baaffc09283d84c652 +Subproject commit 40e6bc384b63fe116f370153a20c15a46888011f diff --git a/tests/beekeepy_test/handle/api_tests/test_api_close_session.py b/tests/beekeepy_test/handle/api_tests/test_api_close_session.py index 81168d1f..a3a99f52 100644 --- a/tests/beekeepy_test/handle/api_tests/test_api_close_session.py +++ b/tests/beekeepy_test/handle/api_tests/test_api_close_session.py @@ -55,11 +55,7 @@ def test_api_close_session_not_existing(create_session: bool, beekeeper: Beekeep """Test test_api_close_session_not_existing will test possibility of closing not existing session.""" # ARRANGE if create_session: - assert beekeeper.settings.notification_endpoint is not None - beekeeper.api.create_session( - notifications_endpoint=beekeeper.settings.notification_endpoint.as_string(with_protocol=False), - salt="salt", - ) + beekeeper.api.create_session(salt="salt") # ACT & ASSERT beekeeper.set_session_token(WRONG_TOKEN) diff --git a/tests/beekeepy_test/handle/api_tests/test_api_create_session.py b/tests/beekeepy_test/handle/api_tests/test_api_create_session.py index 7201ab68..b206c0d0 100644 --- a/tests/beekeepy_test/handle/api_tests/test_api_create_session.py +++ b/tests/beekeepy_test/handle/api_tests/test_api_create_session.py @@ -14,17 +14,11 @@ if TYPE_CHECKING: def create_session(beekeeper: Beekeeper, salt: str) -> None: # ARRANGE - assert beekeeper.settings.notification_endpoint is not None - notification_endpoint = beekeeper.settings.notification_endpoint.as_string(with_protocol=False) - message_to_check = ( - '"id":0,"jsonrpc":"2.0","method":"beekeeper_api.create_session",' - f'"params":{{"notifications_endpoint":"{notification_endpoint}","salt":"{salt}"}}' - ) + message_to_check = '"id":0,"jsonrpc":"2.0","method":"beekeeper_api.create_session",' f'"params":{{"salt":"{salt}"}}' # ACT token = ( beekeeper.api.create_session( - notifications_endpoint=notification_endpoint, salt=salt, ) ).token diff --git a/tests/beekeepy_test/handle/commandline/application_options/test_default_values.py b/tests/beekeepy_test/handle/commandline/application_options/test_default_values.py index 4d7f9eac..53b4c75d 100644 --- a/tests/beekeepy_test/handle/commandline/application_options/test_default_values.py +++ b/tests/beekeepy_test/handle/commandline/application_options/test_default_values.py @@ -18,7 +18,6 @@ def check_default_values_from_config(default_config: BeekeeperConfig) -> None: assert default_config.log_json_rpc == BeekeeperDefaults.DEFAULT_LOG_JSON_RPC assert default_config.webserver_http_endpoint == http_webserver_default() assert default_config.webserver_thread_pool_size == BeekeeperDefaults.DEFAULT_WEBSERVER_THREAD_POOL_SIZE - assert default_config.notifications_endpoint == BeekeeperDefaults.DEFAULT_NOTIFICATIONS_ENDPOINT assert default_config.backtrace == BeekeeperDefaults.DEFAULT_BACKTRACE assert default_config.export_keys_wallet == BeekeeperDefaults.DEFAULT_EXPORT_KEYS_WALLET diff --git a/tests/beekeepy_test/handle/commandline/application_options/test_notifications_endpoint.py b/tests/beekeepy_test/handle/commandline/application_options/test_notifications_endpoint.py deleted file mode 100644 index 377251ac..00000000 --- a/tests/beekeepy_test/handle/commandline/application_options/test_notifications_endpoint.py +++ /dev/null @@ -1,37 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING - -import pytest - -from beekeepy.handle.runnable import BeekeeperArguments -from beekeepy.interfaces import HttpUrl - -if TYPE_CHECKING: - from beekeepy.handle.runnable import Beekeeper - - -@pytest.mark.parametrize( - "notifications_endpoint", - [HttpUrl("0.0.0.0:0", protocol="http"), HttpUrl("127.0.0.1:0", protocol="http")], -) -def test_notifications_endpoint(beekeeper_not_started: Beekeeper, notifications_endpoint: HttpUrl) -> None: - """ - Test will check command line flag --notifications-endpoint. - - In this test we will re-use built-in notification server. We will not pass a port here, - because built-in notification server will get free port and use it. - - In order to test flag notifications-endpoint we will pass this flag, and internal - we will pass port that already has been taken by notification http server to beekeeper - executable. - """ - # 1 pass notification flag - # 2 inside start function there is special if, that will check if we have explicitly pass - # notification-endpoint flag, and append to it already taken port. This way we will - # point beekeeper where to send notifications. - - # ARRANGE & ACT & ASSERT - beekeeper_not_started.run( - additional_cli_arguments=BeekeeperArguments(notifications_endpoint=notifications_endpoint) - ) diff --git a/tests/beekeepy_test/handle/commandline/application_options/test_webserver_http_endpoint.py b/tests/beekeepy_test/handle/commandline/application_options/test_webserver_http_endpoint.py index 7a8e0ce1..97793b53 100644 --- a/tests/beekeepy_test/handle/commandline/application_options/test_webserver_http_endpoint.py +++ b/tests/beekeepy_test/handle/commandline/application_options/test_webserver_http_endpoint.py @@ -16,15 +16,12 @@ if TYPE_CHECKING: from beekeepy.handle.runnable import Beekeeper -def check_webserver_http_endpoint(*, nofification_endpoint: HttpUrl, webserver_http_endpoint: HttpUrl) -> None: +def check_webserver_http_endpoint(*, webserver_http_endpoint: HttpUrl) -> None: """Check if beekeeper is listening on given endpoint.""" data = { "jsonrpc": "2.0", "method": "beekeeper_api.create_session", - "params": { - "salt": "avocado", - "notifications_endpoint": nofification_endpoint.as_string(with_protocol=False), - }, + "params": {"salt": "avocado"}, "id": 1, } @@ -49,8 +46,4 @@ def test_webserver_http_endpoint(beekeeper_not_started: Beekeeper, webserver_htt ) # ASSERT - assert beekeeper_not_started.settings.notification_endpoint is not None - check_webserver_http_endpoint( - nofification_endpoint=beekeeper_not_started.settings.notification_endpoint, - webserver_http_endpoint=webserver_http_endpoint, - ) + check_webserver_http_endpoint(webserver_http_endpoint=webserver_http_endpoint) diff --git a/tests/beekeepy_test/handle/various/test_blocking_unlock.py b/tests/beekeepy_test/handle/various/test_blocking_unlock.py index c36d4e58..821630bb 100644 --- a/tests/beekeepy_test/handle/various/test_blocking_unlock.py +++ b/tests/beekeepy_test/handle/various/test_blocking_unlock.py @@ -98,14 +98,8 @@ async def test_wallet_blocking_timeout(beekeeper: AsyncBeekeeper, wallet: Wallet assert wallets[0].name == wallet.name unlock_jsons = [] - assert beekeeper.settings.notification_endpoint is not None for i in range(5): - session = ( - await beekeeper.api.create_session( - notifications_endpoint=beekeeper.settings.notification_endpoint.as_string(with_protocol=False), - salt=f"salt-{i}", - ) - ).token + session = (await beekeeper.api.create_session(salt=f"salt-{i}")).token unlock_json = JSONRPCRequest( method="beekeeper_api.unlock", params={ diff --git a/tests/local-tools/poetry.lock b/tests/local-tools/poetry.lock index 87469350..9db12e17 100644 --- a/tests/local-tools/poetry.lock +++ b/tests/local-tools/poetry.lock @@ -13,92 +13,92 @@ files = [ [[package]] name = "aiohttp" -version = "3.11.14" +version = "3.11.16" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.9" files = [ - {file = "aiohttp-3.11.14-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e2bc827c01f75803de77b134afdbf74fa74b62970eafdf190f3244931d7a5c0d"}, - {file = "aiohttp-3.11.14-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e365034c5cf6cf74f57420b57682ea79e19eb29033399dd3f40de4d0171998fa"}, - {file = "aiohttp-3.11.14-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c32593ead1a8c6aabd58f9d7ee706e48beac796bb0cb71d6b60f2c1056f0a65f"}, - {file = "aiohttp-3.11.14-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4e7c7ec4146a94a307ca4f112802a8e26d969018fabed526efc340d21d3e7d0"}, - {file = "aiohttp-3.11.14-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c8b2df9feac55043759aa89f722a967d977d80f8b5865a4153fc41c93b957efc"}, - {file = "aiohttp-3.11.14-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c7571f99525c76a6280f5fe8e194eeb8cb4da55586c3c61c59c33a33f10cfce7"}, - {file = "aiohttp-3.11.14-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b59d096b5537ec7c85954cb97d821aae35cfccce3357a2cafe85660cc6295628"}, - {file = "aiohttp-3.11.14-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b42dbd097abb44b3f1156b4bf978ec5853840802d6eee2784857be11ee82c6a0"}, - {file = "aiohttp-3.11.14-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b05774864c87210c531b48dfeb2f7659407c2dda8643104fb4ae5e2c311d12d9"}, - {file = "aiohttp-3.11.14-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:4e2e8ef37d4bc110917d038807ee3af82700a93ab2ba5687afae5271b8bc50ff"}, - {file = "aiohttp-3.11.14-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e9faafa74dbb906b2b6f3eb9942352e9e9db8d583ffed4be618a89bd71a4e914"}, - {file = "aiohttp-3.11.14-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:7e7abe865504f41b10777ac162c727af14e9f4db9262e3ed8254179053f63e6d"}, - {file = "aiohttp-3.11.14-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:4848ae31ad44330b30f16c71e4f586cd5402a846b11264c412de99fa768f00f3"}, - {file = "aiohttp-3.11.14-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2d0b46abee5b5737cb479cc9139b29f010a37b1875ee56d142aefc10686a390b"}, - {file = "aiohttp-3.11.14-cp310-cp310-win32.whl", hash = "sha256:a0d2c04a623ab83963576548ce098baf711a18e2c32c542b62322a0b4584b990"}, - {file = "aiohttp-3.11.14-cp310-cp310-win_amd64.whl", hash = "sha256:5409a59d5057f2386bb8b8f8bbcfb6e15505cedd8b2445db510563b5d7ea1186"}, - {file = "aiohttp-3.11.14-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f296d637a50bb15fb6a229fbb0eb053080e703b53dbfe55b1e4bb1c5ed25d325"}, - {file = "aiohttp-3.11.14-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ec6cd1954ca2bbf0970f531a628da1b1338f594bf5da7e361e19ba163ecc4f3b"}, - {file = "aiohttp-3.11.14-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:572def4aad0a4775af66d5a2b5923c7de0820ecaeeb7987dcbccda2a735a993f"}, - {file = "aiohttp-3.11.14-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c68e41c4d576cd6aa6c6d2eddfb32b2acfb07ebfbb4f9da991da26633a3db1a"}, - {file = "aiohttp-3.11.14-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99b8bbfc8111826aa8363442c0fc1f5751456b008737ff053570f06a151650b3"}, - {file = "aiohttp-3.11.14-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b0a200e85da5c966277a402736a96457b882360aa15416bf104ca81e6f5807b"}, - {file = "aiohttp-3.11.14-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d173c0ac508a2175f7c9a115a50db5fd3e35190d96fdd1a17f9cb10a6ab09aa1"}, - {file = "aiohttp-3.11.14-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:413fe39fd929329f697f41ad67936f379cba06fcd4c462b62e5b0f8061ee4a77"}, - {file = "aiohttp-3.11.14-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:65c75b14ee74e8eeff2886321e76188cbe938d18c85cff349d948430179ad02c"}, - {file = "aiohttp-3.11.14-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:321238a42ed463848f06e291c4bbfb3d15ba5a79221a82c502da3e23d7525d06"}, - {file = "aiohttp-3.11.14-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:59a05cdc636431f7ce843c7c2f04772437dd816a5289f16440b19441be6511f1"}, - {file = "aiohttp-3.11.14-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:daf20d9c3b12ae0fdf15ed92235e190f8284945563c4b8ad95b2d7a31f331cd3"}, - {file = "aiohttp-3.11.14-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:05582cb2d156ac7506e68b5eac83179faedad74522ed88f88e5861b78740dc0e"}, - {file = "aiohttp-3.11.14-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:12c5869e7ddf6b4b1f2109702b3cd7515667b437da90a5a4a50ba1354fe41881"}, - {file = "aiohttp-3.11.14-cp311-cp311-win32.whl", hash = "sha256:92868f6512714efd4a6d6cb2bfc4903b997b36b97baea85f744229f18d12755e"}, - {file = "aiohttp-3.11.14-cp311-cp311-win_amd64.whl", hash = "sha256:bccd2cb7aa5a3bfada72681bdb91637094d81639e116eac368f8b3874620a654"}, - {file = "aiohttp-3.11.14-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:70ab0f61c1a73d3e0342cedd9a7321425c27a7067bebeeacd509f96695b875fc"}, - {file = "aiohttp-3.11.14-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:602d4db80daf4497de93cb1ce00b8fc79969c0a7cf5b67bec96fa939268d806a"}, - {file = "aiohttp-3.11.14-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3a8a0d127c10b8d89e69bbd3430da0f73946d839e65fec00ae48ca7916a31948"}, - {file = "aiohttp-3.11.14-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca9f835cdfedcb3f5947304e85b8ca3ace31eef6346d8027a97f4de5fb687534"}, - {file = "aiohttp-3.11.14-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8aa5c68e1e68fff7cd3142288101deb4316b51f03d50c92de6ea5ce646e6c71f"}, - {file = "aiohttp-3.11.14-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b512f1de1c688f88dbe1b8bb1283f7fbeb7a2b2b26e743bb2193cbadfa6f307"}, - {file = "aiohttp-3.11.14-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc9253069158d57e27d47a8453d8a2c5a370dc461374111b5184cf2f147a3cc3"}, - {file = "aiohttp-3.11.14-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0b2501f1b981e70932b4a552fc9b3c942991c7ae429ea117e8fba57718cdeed0"}, - {file = "aiohttp-3.11.14-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:28a3d083819741592685762d51d789e6155411277050d08066537c5edc4066e6"}, - {file = "aiohttp-3.11.14-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0df3788187559c262922846087e36228b75987f3ae31dd0a1e5ee1034090d42f"}, - {file = "aiohttp-3.11.14-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9e73fa341d8b308bb799cf0ab6f55fc0461d27a9fa3e4582755a3d81a6af8c09"}, - {file = "aiohttp-3.11.14-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:51ba80d473eb780a329d73ac8afa44aa71dfb521693ccea1dea8b9b5c4df45ce"}, - {file = "aiohttp-3.11.14-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:8d1dd75aa4d855c7debaf1ef830ff2dfcc33f893c7db0af2423ee761ebffd22b"}, - {file = "aiohttp-3.11.14-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41cf0cefd9e7b5c646c2ef529c8335e7eafd326f444cc1cdb0c47b6bc836f9be"}, - {file = "aiohttp-3.11.14-cp312-cp312-win32.whl", hash = "sha256:948abc8952aff63de7b2c83bfe3f211c727da3a33c3a5866a0e2cf1ee1aa950f"}, - {file = "aiohttp-3.11.14-cp312-cp312-win_amd64.whl", hash = "sha256:3b420d076a46f41ea48e5fcccb996f517af0d406267e31e6716f480a3d50d65c"}, - {file = "aiohttp-3.11.14-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d14e274828561db91e4178f0057a915f3af1757b94c2ca283cb34cbb6e00b50"}, - {file = "aiohttp-3.11.14-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f30fc72daf85486cdcdfc3f5e0aea9255493ef499e31582b34abadbfaafb0965"}, - {file = "aiohttp-3.11.14-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4edcbe34e6dba0136e4cabf7568f5a434d89cc9de5d5155371acda275353d228"}, - {file = "aiohttp-3.11.14-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a7169ded15505f55a87f8f0812c94c9412623c744227b9e51083a72a48b68a5"}, - {file = "aiohttp-3.11.14-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad1f2fb9fe9b585ea4b436d6e998e71b50d2b087b694ab277b30e060c434e5db"}, - {file = "aiohttp-3.11.14-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:20412c7cc3720e47a47e63c0005f78c0c2370020f9f4770d7fc0075f397a9fb0"}, - {file = "aiohttp-3.11.14-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6dd9766da617855f7e85f27d2bf9a565ace04ba7c387323cd3e651ac4329db91"}, - {file = "aiohttp-3.11.14-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:599b66582f7276ebefbaa38adf37585e636b6a7a73382eb412f7bc0fc55fb73d"}, - {file = "aiohttp-3.11.14-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b41693b7388324b80f9acfabd479bd1c84f0bc7e8f17bab4ecd9675e9ff9c734"}, - {file = "aiohttp-3.11.14-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:86135c32d06927339c8c5e64f96e4eee8825d928374b9b71a3c42379d7437058"}, - {file = "aiohttp-3.11.14-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:04eb541ce1e03edc1e3be1917a0f45ac703e913c21a940111df73a2c2db11d73"}, - {file = "aiohttp-3.11.14-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:dc311634f6f28661a76cbc1c28ecf3b3a70a8edd67b69288ab7ca91058eb5a33"}, - {file = "aiohttp-3.11.14-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:69bb252bfdca385ccabfd55f4cd740d421dd8c8ad438ded9637d81c228d0da49"}, - {file = "aiohttp-3.11.14-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2b86efe23684b58a88e530c4ab5b20145f102916bbb2d82942cafec7bd36a647"}, - {file = "aiohttp-3.11.14-cp313-cp313-win32.whl", hash = "sha256:b9c60d1de973ca94af02053d9b5111c4fbf97158e139b14f1be68337be267be6"}, - {file = "aiohttp-3.11.14-cp313-cp313-win_amd64.whl", hash = "sha256:0a29be28e60e5610d2437b5b2fed61d6f3dcde898b57fb048aa5079271e7f6f3"}, - {file = "aiohttp-3.11.14-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:14fc03508359334edc76d35b2821832f092c8f092e4b356e74e38419dfe7b6de"}, - {file = "aiohttp-3.11.14-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:92007c89a8cb7be35befa2732b0b32bf3a394c1b22ef2dff0ef12537d98a7bda"}, - {file = "aiohttp-3.11.14-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6d3986112e34eaa36e280dc8286b9dd4cc1a5bcf328a7f147453e188f6fe148f"}, - {file = "aiohttp-3.11.14-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:749f1eb10e51dbbcdba9df2ef457ec060554842eea4d23874a3e26495f9e87b1"}, - {file = "aiohttp-3.11.14-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:781c8bd423dcc4641298c8c5a2a125c8b1c31e11f828e8d35c1d3a722af4c15a"}, - {file = "aiohttp-3.11.14-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:997b57e38aa7dc6caab843c5e042ab557bc83a2f91b7bd302e3c3aebbb9042a1"}, - {file = "aiohttp-3.11.14-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a8b0321e40a833e381d127be993b7349d1564b756910b28b5f6588a159afef3"}, - {file = "aiohttp-3.11.14-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8778620396e554b758b59773ab29c03b55047841d8894c5e335f12bfc45ebd28"}, - {file = "aiohttp-3.11.14-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:e906da0f2bcbf9b26cc2b144929e88cb3bf943dd1942b4e5af066056875c7618"}, - {file = "aiohttp-3.11.14-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:87f0e003fb4dd5810c7fbf47a1239eaa34cd929ef160e0a54c570883125c4831"}, - {file = "aiohttp-3.11.14-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:7f2dadece8b85596ac3ab1ec04b00694bdd62abc31e5618f524648d18d9dd7fa"}, - {file = "aiohttp-3.11.14-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:fe846f0a98aa9913c2852b630cd39b4098f296e0907dd05f6c7b30d911afa4c3"}, - {file = "aiohttp-3.11.14-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ced66c5c6ad5bcaf9be54560398654779ec1c3695f1a9cf0ae5e3606694a000a"}, - {file = "aiohttp-3.11.14-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a40087b82f83bd671cbeb5f582c233d196e9653220404a798798bfc0ee189fff"}, - {file = "aiohttp-3.11.14-cp39-cp39-win32.whl", hash = "sha256:95d7787f2bcbf7cb46823036a8d64ccfbc2ffc7d52016b4044d901abceeba3db"}, - {file = "aiohttp-3.11.14-cp39-cp39-win_amd64.whl", hash = "sha256:22a8107896877212130c58f74e64b77f7007cb03cea8698be317272643602d45"}, - {file = "aiohttp-3.11.14.tar.gz", hash = "sha256:d6edc538c7480fa0a3b2bdd705f8010062d74700198da55d16498e1b49549b9c"}, + {file = "aiohttp-3.11.16-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fb46bb0f24813e6cede6cc07b1961d4b04f331f7112a23b5e21f567da4ee50aa"}, + {file = "aiohttp-3.11.16-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:54eb3aead72a5c19fad07219acd882c1643a1027fbcdefac9b502c267242f955"}, + {file = "aiohttp-3.11.16-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:38bea84ee4fe24ebcc8edeb7b54bf20f06fd53ce4d2cc8b74344c5b9620597fd"}, + {file = "aiohttp-3.11.16-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0666afbe984f6933fe72cd1f1c3560d8c55880a0bdd728ad774006eb4241ecd"}, + {file = "aiohttp-3.11.16-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ba92a2d9ace559a0a14b03d87f47e021e4fa7681dc6970ebbc7b447c7d4b7cd"}, + {file = "aiohttp-3.11.16-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ad1d59fd7114e6a08c4814983bb498f391c699f3c78712770077518cae63ff7"}, + {file = "aiohttp-3.11.16-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b88a2bf26965f2015a771381624dd4b0839034b70d406dc74fd8be4cc053e3"}, + {file = "aiohttp-3.11.16-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:576f5ca28d1b3276026f7df3ec841ae460e0fc3aac2a47cbf72eabcfc0f102e1"}, + {file = "aiohttp-3.11.16-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a2a450bcce4931b295fc0848f384834c3f9b00edfc2150baafb4488c27953de6"}, + {file = "aiohttp-3.11.16-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:37dcee4906454ae377be5937ab2a66a9a88377b11dd7c072df7a7c142b63c37c"}, + {file = "aiohttp-3.11.16-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:4d0c970c0d602b1017e2067ff3b7dac41c98fef4f7472ec2ea26fd8a4e8c2149"}, + {file = "aiohttp-3.11.16-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:004511d3413737700835e949433536a2fe95a7d0297edd911a1e9705c5b5ea43"}, + {file = "aiohttp-3.11.16-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:c15b2271c44da77ee9d822552201180779e5e942f3a71fb74e026bf6172ff287"}, + {file = "aiohttp-3.11.16-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ad9509ffb2396483ceacb1eee9134724443ee45b92141105a4645857244aecc8"}, + {file = "aiohttp-3.11.16-cp310-cp310-win32.whl", hash = "sha256:634d96869be6c4dc232fc503e03e40c42d32cfaa51712aee181e922e61d74814"}, + {file = "aiohttp-3.11.16-cp310-cp310-win_amd64.whl", hash = "sha256:938f756c2b9374bbcc262a37eea521d8a0e6458162f2a9c26329cc87fdf06534"}, + {file = "aiohttp-3.11.16-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8cb0688a8d81c63d716e867d59a9ccc389e97ac7037ebef904c2b89334407180"}, + {file = "aiohttp-3.11.16-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0ad1fb47da60ae1ddfb316f0ff16d1f3b8e844d1a1e154641928ea0583d486ed"}, + {file = "aiohttp-3.11.16-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:df7db76400bf46ec6a0a73192b14c8295bdb9812053f4fe53f4e789f3ea66bbb"}, + {file = "aiohttp-3.11.16-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc3a145479a76ad0ed646434d09216d33d08eef0d8c9a11f5ae5cdc37caa3540"}, + {file = "aiohttp-3.11.16-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d007aa39a52d62373bd23428ba4a2546eed0e7643d7bf2e41ddcefd54519842c"}, + {file = "aiohttp-3.11.16-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f6ddd90d9fb4b501c97a4458f1c1720e42432c26cb76d28177c5b5ad4e332601"}, + {file = "aiohttp-3.11.16-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a2f451849e6b39e5c226803dcacfa9c7133e9825dcefd2f4e837a2ec5a3bb98"}, + {file = "aiohttp-3.11.16-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8df6612df74409080575dca38a5237282865408016e65636a76a2eb9348c2567"}, + {file = "aiohttp-3.11.16-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:78e6e23b954644737e385befa0deb20233e2dfddf95dd11e9db752bdd2a294d3"}, + {file = "aiohttp-3.11.16-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:696ef00e8a1f0cec5e30640e64eca75d8e777933d1438f4facc9c0cdf288a810"}, + {file = "aiohttp-3.11.16-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e3538bc9fe1b902bef51372462e3d7c96fce2b566642512138a480b7adc9d508"}, + {file = "aiohttp-3.11.16-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:3ab3367bb7f61ad18793fea2ef71f2d181c528c87948638366bf1de26e239183"}, + {file = "aiohttp-3.11.16-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:56a3443aca82abda0e07be2e1ecb76a050714faf2be84256dae291182ba59049"}, + {file = "aiohttp-3.11.16-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:61c721764e41af907c9d16b6daa05a458f066015abd35923051be8705108ed17"}, + {file = "aiohttp-3.11.16-cp311-cp311-win32.whl", hash = "sha256:3e061b09f6fa42997cf627307f220315e313ece74907d35776ec4373ed718b86"}, + {file = "aiohttp-3.11.16-cp311-cp311-win_amd64.whl", hash = "sha256:745f1ed5e2c687baefc3c5e7b4304e91bf3e2f32834d07baaee243e349624b24"}, + {file = "aiohttp-3.11.16-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:911a6e91d08bb2c72938bc17f0a2d97864c531536b7832abee6429d5296e5b27"}, + {file = "aiohttp-3.11.16-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6ac13b71761e49d5f9e4d05d33683bbafef753e876e8e5a7ef26e937dd766713"}, + {file = "aiohttp-3.11.16-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fd36c119c5d6551bce374fcb5c19269638f8d09862445f85a5a48596fd59f4bb"}, + {file = "aiohttp-3.11.16-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d489d9778522fbd0f8d6a5c6e48e3514f11be81cb0a5954bdda06f7e1594b321"}, + {file = "aiohttp-3.11.16-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:69a2cbd61788d26f8f1e626e188044834f37f6ae3f937bd9f08b65fc9d7e514e"}, + {file = "aiohttp-3.11.16-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd464ba806e27ee24a91362ba3621bfc39dbbb8b79f2e1340201615197370f7c"}, + {file = "aiohttp-3.11.16-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ce63ae04719513dd2651202352a2beb9f67f55cb8490c40f056cea3c5c355ce"}, + {file = "aiohttp-3.11.16-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09b00dd520d88eac9d1768439a59ab3d145065c91a8fab97f900d1b5f802895e"}, + {file = "aiohttp-3.11.16-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7f6428fee52d2bcf96a8aa7b62095b190ee341ab0e6b1bcf50c615d7966fd45b"}, + {file = "aiohttp-3.11.16-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:13ceac2c5cdcc3f64b9015710221ddf81c900c5febc505dbd8f810e770011540"}, + {file = "aiohttp-3.11.16-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:fadbb8f1d4140825069db3fedbbb843290fd5f5bc0a5dbd7eaf81d91bf1b003b"}, + {file = "aiohttp-3.11.16-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:6a792ce34b999fbe04a7a71a90c74f10c57ae4c51f65461a411faa70e154154e"}, + {file = "aiohttp-3.11.16-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:f4065145bf69de124accdd17ea5f4dc770da0a6a6e440c53f6e0a8c27b3e635c"}, + {file = "aiohttp-3.11.16-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fa73e8c2656a3653ae6c307b3f4e878a21f87859a9afab228280ddccd7369d71"}, + {file = "aiohttp-3.11.16-cp312-cp312-win32.whl", hash = "sha256:f244b8e541f414664889e2c87cac11a07b918cb4b540c36f7ada7bfa76571ea2"}, + {file = "aiohttp-3.11.16-cp312-cp312-win_amd64.whl", hash = "sha256:23a15727fbfccab973343b6d1b7181bfb0b4aa7ae280f36fd2f90f5476805682"}, + {file = "aiohttp-3.11.16-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a3814760a1a700f3cfd2f977249f1032301d0a12c92aba74605cfa6ce9f78489"}, + {file = "aiohttp-3.11.16-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9b751a6306f330801665ae69270a8a3993654a85569b3469662efaad6cf5cc50"}, + {file = "aiohttp-3.11.16-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ad497f38a0d6c329cb621774788583ee12321863cd4bd9feee1effd60f2ad133"}, + {file = "aiohttp-3.11.16-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca37057625693d097543bd88076ceebeb248291df9d6ca8481349efc0b05dcd0"}, + {file = "aiohttp-3.11.16-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a5abcbba9f4b463a45c8ca8b7720891200658f6f46894f79517e6cd11f3405ca"}, + {file = "aiohttp-3.11.16-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f420bfe862fb357a6d76f2065447ef6f484bc489292ac91e29bc65d2d7a2c84d"}, + {file = "aiohttp-3.11.16-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58ede86453a6cf2d6ce40ef0ca15481677a66950e73b0a788917916f7e35a0bb"}, + {file = "aiohttp-3.11.16-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fdec0213244c39973674ca2a7f5435bf74369e7d4e104d6c7473c81c9bcc8c4"}, + {file = "aiohttp-3.11.16-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:72b1b03fb4655c1960403c131740755ec19c5898c82abd3961c364c2afd59fe7"}, + {file = "aiohttp-3.11.16-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:780df0d837276276226a1ff803f8d0fa5f8996c479aeef52eb040179f3156cbd"}, + {file = "aiohttp-3.11.16-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ecdb8173e6c7aa09eee342ac62e193e6904923bd232e76b4157ac0bfa670609f"}, + {file = "aiohttp-3.11.16-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:a6db7458ab89c7d80bc1f4e930cc9df6edee2200127cfa6f6e080cf619eddfbd"}, + {file = "aiohttp-3.11.16-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:2540ddc83cc724b13d1838026f6a5ad178510953302a49e6d647f6e1de82bc34"}, + {file = "aiohttp-3.11.16-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3b4e6db8dc4879015b9955778cfb9881897339c8fab7b3676f8433f849425913"}, + {file = "aiohttp-3.11.16-cp313-cp313-win32.whl", hash = "sha256:493910ceb2764f792db4dc6e8e4b375dae1b08f72e18e8f10f18b34ca17d0979"}, + {file = "aiohttp-3.11.16-cp313-cp313-win_amd64.whl", hash = "sha256:42864e70a248f5f6a49fdaf417d9bc62d6e4d8ee9695b24c5916cb4bb666c802"}, + {file = "aiohttp-3.11.16-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bbcba75fe879ad6fd2e0d6a8d937f34a571f116a0e4db37df8079e738ea95c71"}, + {file = "aiohttp-3.11.16-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:87a6e922b2b2401e0b0cf6b976b97f11ec7f136bfed445e16384fbf6fd5e8602"}, + {file = "aiohttp-3.11.16-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ccf10f16ab498d20e28bc2b5c1306e9c1512f2840f7b6a67000a517a4b37d5ee"}, + {file = "aiohttp-3.11.16-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb3d0cc5cdb926090748ea60172fa8a213cec728bd6c54eae18b96040fcd6227"}, + {file = "aiohttp-3.11.16-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d07502cc14ecd64f52b2a74ebbc106893d9a9717120057ea9ea1fd6568a747e7"}, + {file = "aiohttp-3.11.16-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:776c8e959a01e5e8321f1dec77964cb6101020a69d5a94cd3d34db6d555e01f7"}, + {file = "aiohttp-3.11.16-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0902e887b0e1d50424112f200eb9ae3dfed6c0d0a19fc60f633ae5a57c809656"}, + {file = "aiohttp-3.11.16-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e87fd812899aa78252866ae03a048e77bd11b80fb4878ce27c23cade239b42b2"}, + {file = "aiohttp-3.11.16-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0a950c2eb8ff17361abd8c85987fd6076d9f47d040ebffce67dce4993285e973"}, + {file = "aiohttp-3.11.16-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:c10d85e81d0b9ef87970ecbdbfaeec14a361a7fa947118817fcea8e45335fa46"}, + {file = "aiohttp-3.11.16-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:7951decace76a9271a1ef181b04aa77d3cc309a02a51d73826039003210bdc86"}, + {file = "aiohttp-3.11.16-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:14461157d8426bcb40bd94deb0450a6fa16f05129f7da546090cebf8f3123b0f"}, + {file = "aiohttp-3.11.16-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9756d9b9d4547e091f99d554fbba0d2a920aab98caa82a8fb3d3d9bee3c9ae85"}, + {file = "aiohttp-3.11.16-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:87944bd16b7fe6160607f6a17808abd25f17f61ae1e26c47a491b970fb66d8cb"}, + {file = "aiohttp-3.11.16-cp39-cp39-win32.whl", hash = "sha256:92b7ee222e2b903e0a4b329a9943d432b3767f2d5029dbe4ca59fb75223bbe2e"}, + {file = "aiohttp-3.11.16-cp39-cp39-win_amd64.whl", hash = "sha256:17ae4664031aadfbcb34fd40ffd90976671fa0c0286e6c4113989f78bebab37a"}, + {file = "aiohttp-3.11.16.tar.gz", hash = "sha256:16f8a2c9538c14a557b4d309ed4d0a7c60f0253e8ed7b6c9a2859a7582f8b1b8"}, ] [package.dependencies] @@ -176,13 +176,13 @@ files = [] develop = true [package.dependencies] -aiohttp = "3.11.14" +aiohttp = "3.11.16" httpx = {version = "0.23.3", extras = ["http2"]} loguru = "0.7.2" -pydantic = "1.10.18" +psutil = "7.0.0" python-dateutil = "2.8.2" requests = "2.32.3" -schemas = "0.0.1.dev331+7181389" +schemas = "0.0.1.dev336+c6f2974" setuptools = "77.0.3" types-setuptools = "76.0.0.20250313" @@ -850,6 +850,29 @@ files = [ {file = "propcache-0.3.0.tar.gz", hash = "sha256:a8fd93de4e1d278046345f49e2238cdb298589325849b2645d4a94c53faeffc5"}, ] +[[package]] +name = "psutil" +version = "7.0.0" +description = "Cross-platform lib for process and system monitoring in Python. NOTE: the syntax of this script MUST be kept compatible with Python 2.7." +optional = false +python-versions = ">=3.6" +files = [ + {file = "psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25"}, + {file = "psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da"}, + {file = "psutil-7.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fcee592b4c6f146991ca55919ea3d1f8926497a713ed7faaf8225e174581e91"}, + {file = "psutil-7.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b1388a4f6875d7e2aff5c4ca1cc16c545ed41dd8bb596cefea80111db353a34"}, + {file = "psutil-7.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f098451abc2828f7dc6b58d44b532b22f2088f4999a937557b603ce72b1993"}, + {file = "psutil-7.0.0-cp36-cp36m-win32.whl", hash = "sha256:84df4eb63e16849689f76b1ffcb36db7b8de703d1bc1fe41773db487621b6c17"}, + {file = "psutil-7.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:1e744154a6580bc968a0195fd25e80432d3afec619daf145b9e5ba16cc1d688e"}, + {file = "psutil-7.0.0-cp37-abi3-win32.whl", hash = "sha256:ba3fcef7523064a6c9da440fc4d6bd07da93ac726b5733c29027d7dc95b39d99"}, + {file = "psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553"}, + {file = "psutil-7.0.0.tar.gz", hash = "sha256:7be9c3eba38beccb6495ea33afd982a44074b78f28c434a1f51cc07fd315c456"}, +] + +[package.extras] +dev = ["abi3audit", "black (==24.10.0)", "check-manifest", "coverage", "packaging", "pylint", "pyperf", "pypinfo", "pytest", "pytest-cov", "pytest-xdist", "requests", "rstcheck", "ruff", "setuptools", "sphinx", "sphinx_rtd_theme", "toml-sort", "twine", "virtualenv", "vulture", "wheel"] +test = ["pytest", "pytest-xdist", "setuptools"] + [[package]] name = "pydantic" version = "1.10.18" @@ -1110,12 +1133,12 @@ files = [ [[package]] name = "schemas" -version = "0.0.1.dev331+7181389" +version = "0.0.1.dev336+c6f2974" description = "Tools for checking if message fits expected format" optional = false python-versions = ">=3.12,<4.0" files = [ - {file = "schemas-0.0.1.dev331+7181389-py3-none-any.whl", hash = "sha256:f0b51978f6a440bbc2e19b32aeec103cd5621d17902e9a4b3703d32ddd3f3ec3"}, + {file = "schemas-0.0.1.dev336+c6f2974-py3-none-any.whl", hash = "sha256:9a2148f014bdf82e05d138629df11cb20f53799eb32dcfff14942b4b20a76cc6"}, ] [package.dependencies] @@ -1168,6 +1191,17 @@ files = [ {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, ] +[[package]] +name = "types-psutil" +version = "6.0.0.20240901" +description = "Typing stubs for psutil" +optional = false +python-versions = ">=3.8" +files = [ + {file = "types-psutil-6.0.0.20240901.tar.gz", hash = "sha256:437affa76670363db9ffecfa4f153cc6900bf8a7072b3420f3bc07a593f92226"}, + {file = "types_psutil-6.0.0.20240901-py3-none-any.whl", hash = "sha256:20af311bfb0386a018a27ae47dc952119d7c0e849ff72b6aa24fc0433afb92a6"}, +] + [[package]] name = "types-python-dateutil" version = "2.8.19.14" @@ -1389,4 +1423,4 @@ propcache = ">=0.2.0" [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "9bedefe3bd538cea6a562fd77dc85d29aa3413a3f0900bfdfb21357dc5908f38" +content-hash = "a7125ab9d77d3431d5076460d8ebb6d574bc96408318c0f6fa02df550b633eab" diff --git a/tests/local-tools/pyproject.toml b/tests/local-tools/pyproject.toml index 2f7bb48a..4c634eba 100644 --- a/tests/local-tools/pyproject.toml +++ b/tests/local-tools/pyproject.toml @@ -36,6 +36,7 @@ ruff = "0.6.5" types-python-dateutil = "2.8.19.14" types-pyyaml = "6.0.12.4" types-requests = "2.31.0.2" +types-psutil = "6.0.0.20240901" [tool.mypy] -- GitLab From 45454f369fb461c9cab68e754897ee0c949d0bbd Mon Sep 17 00:00:00 2001 From: kmochocki <kmochocki@syncad.com> Date: Sat, 22 Mar 2025 20:42:34 +0000 Subject: [PATCH 02/23] Introduce app_status_probe --- .../_remote_handle/app_status_probe.py | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 beekeepy/beekeepy/_remote_handle/app_status_probe.py diff --git a/beekeepy/beekeepy/_remote_handle/app_status_probe.py b/beekeepy/beekeepy/_remote_handle/app_status_probe.py new file mode 100644 index 00000000..432901fb --- /dev/null +++ b/beekeepy/beekeepy/_remote_handle/app_status_probe.py @@ -0,0 +1,28 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from beekeepy._apis import AppStatusProbeSyncApiCollection +from beekeepy._remote_handle.abc.handle import AbstractSyncHandle +from beekeepy._remote_handle.settings import RemoteHandleSettings + +if TYPE_CHECKING: + from beekeepy._apis import SyncAppStatusApi + from beekeepy._remote_handle.abc.batch_handle import SyncBatchHandle + + +class AppStatusProbe(AbstractSyncHandle[RemoteHandleSettings, AppStatusProbeSyncApiCollection]): + """Synchronous handle for probing.""" + + def _construct_api(self) -> AppStatusProbeSyncApiCollection: + return AppStatusProbeSyncApiCollection(owner=self) + + @property + def api(self) -> SyncAppStatusApi: # type: ignore[override] + return self.apis.app_status + + def _target_service(self) -> str: + return "app_status_probe" + + def batch(self, *, delay_error_on_data_access: bool = False) -> SyncBatchHandle[AppStatusProbeSyncApiCollection]: + raise NotImplementedError -- GitLab From 76c160b4bb55415616ec0ee2ffda9be52a186c6f Mon Sep 17 00:00:00 2001 From: kmochocki <kmochocki@syncad.com> Date: Sat, 22 Mar 2025 20:01:19 +0000 Subject: [PATCH 03/23] Move apis to seperate sub-package --- beekeepy/beekeepy/_apis/__init__.py | 30 +++++++ beekeepy/beekeepy/_apis/abc/__init__.py | 33 +++++++ .../{_remote_handle => _apis}/abc/api.py | 15 +--- .../abc/api_collection.py | 11 +-- beekeepy/beekeepy/_apis/abc/sendable.py | 21 +++++ .../api => _apis/abc}/session_holder.py | 7 +- .../beekeepy/_apis/app_status_api/__init__.py | 19 ++++ .../_apis/app_status_api/api_collection.py | 32 +++++++ .../_apis/app_status_api/async_api.py | 12 +++ .../beekeepy/_apis/app_status_api/sync_api.py | 12 +++ .../api => _apis}/apply_session_token.py | 3 +- .../beekeepy/_apis/beekeeper_api/__init__.py | 14 +++ .../_apis/beekeeper_api/api_collection.py | 32 +++++++ .../api => _apis/beekeeper_api}/async_api.py | 21 ++--- .../beekeeper_api}/beekeeper_api_commons.py | 4 +- .../api => _apis/beekeeper_api}/sync_api.py | 22 ++--- beekeepy/beekeepy/_executable/streams.py | 87 ------------------- .../beekeepy/_remote_handle/api/__init__.py | 6 -- .../_remote_handle/api/api_collection.py | 39 --------- 19 files changed, 237 insertions(+), 183 deletions(-) create mode 100644 beekeepy/beekeepy/_apis/__init__.py create mode 100644 beekeepy/beekeepy/_apis/abc/__init__.py rename beekeepy/beekeepy/{_remote_handle => _apis}/abc/api.py (93%) rename beekeepy/beekeepy/{_remote_handle => _apis}/abc/api_collection.py (52%) create mode 100644 beekeepy/beekeepy/_apis/abc/sendable.py rename beekeepy/beekeepy/{_remote_handle/api => _apis/abc}/session_holder.py (88%) create mode 100644 beekeepy/beekeepy/_apis/app_status_api/__init__.py create mode 100644 beekeepy/beekeepy/_apis/app_status_api/api_collection.py create mode 100644 beekeepy/beekeepy/_apis/app_status_api/async_api.py create mode 100644 beekeepy/beekeepy/_apis/app_status_api/sync_api.py rename beekeepy/beekeepy/{_remote_handle/api => _apis}/apply_session_token.py (76%) create mode 100644 beekeepy/beekeepy/_apis/beekeeper_api/__init__.py create mode 100644 beekeepy/beekeepy/_apis/beekeeper_api/api_collection.py rename beekeepy/beekeepy/{_remote_handle/api => _apis/beekeeper_api}/async_api.py (90%) rename beekeepy/beekeepy/{_remote_handle/api => _apis/beekeeper_api}/beekeeper_api_commons.py (86%) rename beekeepy/beekeepy/{_remote_handle/api => _apis/beekeeper_api}/sync_api.py (81%) delete mode 100644 beekeepy/beekeepy/_executable/streams.py delete mode 100644 beekeepy/beekeepy/_remote_handle/api/__init__.py delete mode 100644 beekeepy/beekeepy/_remote_handle/api/api_collection.py diff --git a/beekeepy/beekeepy/_apis/__init__.py b/beekeepy/beekeepy/_apis/__init__.py new file mode 100644 index 00000000..c9d688e0 --- /dev/null +++ b/beekeepy/beekeepy/_apis/__init__.py @@ -0,0 +1,30 @@ +from __future__ import annotations + +from beekeepy._apis import abc +from beekeepy._apis.app_status_api import ( + AppStatusProbeAsyncApiCollection, + AppStatusProbeSyncApiCollection, + AsyncAppStatusApi, + SyncAppStatusApi, +) +from beekeepy._apis.apply_session_token import async_apply_session_token, sync_apply_session_token +from beekeepy._apis.beekeeper_api import ( + AsyncBeekeeperApi, + BeekeeperAsyncApiCollection, + BeekeeperSyncApiCollection, + SyncBeekeeperApi, +) + +__all__ = [ + "abc", + "AppStatusProbeAsyncApiCollection", + "AppStatusProbeSyncApiCollection", + "AsyncAppStatusApi", + "SyncAppStatusApi", + "SyncBeekeeperApi", + "AsyncBeekeeperApi", + "BeekeeperSyncApiCollection", + "BeekeeperAsyncApiCollection", + "sync_apply_session_token", + "async_apply_session_token", +] diff --git a/beekeepy/beekeepy/_apis/abc/__init__.py b/beekeepy/beekeepy/_apis/abc/__init__.py new file mode 100644 index 00000000..558e1c1c --- /dev/null +++ b/beekeepy/beekeepy/_apis/abc/__init__.py @@ -0,0 +1,33 @@ +from __future__ import annotations + +from beekeepy._apis.abc.api import ( + AbstractApi, + AbstractAsyncApi, + AbstractSyncApi, + ApiArgumentSerialization, + ApiArgumentsToSerialize, + HandleT, + RegisteredApisT, +) +from beekeepy._apis.abc.api_collection import AbstractAsyncApiCollection, AbstractSyncApiCollection +from beekeepy._apis.abc.sendable import ( + AsyncSendable, + SyncSendable, +) +from beekeepy._apis.abc.session_holder import AsyncSessionHolder, SyncSessionHolder + +__all__ = [ + "AbstractApi", + "AbstractAsyncApi", + "AbstractAsyncApiCollection", + "AbstractSyncApi", + "AbstractSyncApiCollection", + "ApiArgumentSerialization", + "ApiArgumentsToSerialize", + "AsyncSendable", + "AsyncSessionHolder", + "HandleT", + "RegisteredApisT", + "SyncSendable", + "SyncSessionHolder", +] diff --git a/beekeepy/beekeepy/_remote_handle/abc/api.py b/beekeepy/beekeepy/_apis/abc/api.py similarity index 93% rename from beekeepy/beekeepy/_remote_handle/abc/api.py rename to beekeepy/beekeepy/_apis/abc/api.py index af7bfc9e..4dcbd68c 100644 --- a/beekeepy/beekeepy/_remote_handle/abc/api.py +++ b/beekeepy/beekeepy/_apis/abc/api.py @@ -13,16 +13,11 @@ from typing import ( ClassVar, Generic, ParamSpec, - TypeAlias, TypeVar, get_type_hints, ) -from beekeepy._remote_handle.abc.handle import ( - AbstractAsyncHandle, - AbstractSyncHandle, -) -from beekeepy._remote_handle.batch_handle import AsyncBatchHandle, SyncBatchHandle +from beekeepy._apis.abc.sendable import AsyncSendable, SyncSendable from schemas._preconfigured_base_model import PreconfiguredBaseModel from schemas.fields.serializable import Serializable from schemas.operations.representations.legacy_representation import LegacyRepresentation @@ -34,9 +29,7 @@ if TYPE_CHECKING: P = ParamSpec("P") -SyncHandleT: TypeAlias = AbstractSyncHandle[Any] | SyncBatchHandle[Any] -AsyncHandleT: TypeAlias = AbstractAsyncHandle[Any] | AsyncBatchHandle[Any] -HandleT = TypeVar("HandleT", bound=SyncHandleT | AsyncHandleT) +HandleT = TypeVar("HandleT", bound=SyncSendable | AsyncSendable) RegisteredApisT = defaultdict[bool, defaultdict[str, set[str]]] ApiArgumentsToSerialize = tuple[tuple[Any, ...], dict[str, Any]] @@ -121,7 +114,7 @@ class AbstractApi(ABC, Generic[HandleT]): self._owner = owner -class AbstractSyncApi(AbstractApi[SyncHandleT]): +class AbstractSyncApi(AbstractApi[SyncSendable]): """Base class for all apis, that provides synchronous endpoints.""" def _additional_arguments_actions( @@ -153,7 +146,7 @@ class AbstractSyncApi(AbstractApi[SyncHandleT]): return impl # type: ignore[return-value] -class AbstractAsyncApi(AbstractApi[AsyncHandleT]): +class AbstractAsyncApi(AbstractApi[AsyncSendable]): """Base class for all apis, that provides asynchronous endpoints.""" async def _additional_arguments_actions( diff --git a/beekeepy/beekeepy/_remote_handle/abc/api_collection.py b/beekeepy/beekeepy/_apis/abc/api_collection.py similarity index 52% rename from beekeepy/beekeepy/_remote_handle/abc/api_collection.py rename to beekeepy/beekeepy/_apis/abc/api_collection.py index d8836883..8838e34b 100644 --- a/beekeepy/beekeepy/_remote_handle/abc/api_collection.py +++ b/beekeepy/beekeepy/_apis/abc/api_collection.py @@ -2,7 +2,8 @@ from __future__ import annotations from typing import Generic -from beekeepy._remote_handle.abc.api import AsyncHandleT, HandleT, SyncHandleT +from beekeepy._apis.abc.api import HandleT +from beekeepy._apis.abc.sendable import AsyncSendable, SyncSendable class AbstractApiCollection(Generic[HandleT]): @@ -12,15 +13,15 @@ class AbstractApiCollection(Generic[HandleT]): self._owner = owner -class AbstractAsyncApiCollection(AbstractApiCollection[AsyncHandleT]): +class AbstractAsyncApiCollection(AbstractApiCollection[AsyncSendable]): """Base class for Async Api Collections.""" - def __init__(self, owner: AsyncHandleT) -> None: + def __init__(self, owner: AsyncSendable) -> None: super().__init__(owner) -class AbstractSyncApiCollection(AbstractApiCollection[SyncHandleT]): +class AbstractSyncApiCollection(AbstractApiCollection[SyncSendable]): """Base class for Sync Api Collections.""" - def __init__(self, owner: SyncHandleT) -> None: + def __init__(self, owner: SyncSendable) -> None: super().__init__(owner) diff --git a/beekeepy/beekeepy/_apis/abc/sendable.py b/beekeepy/beekeepy/_apis/abc/sendable.py new file mode 100644 index 00000000..3f2e5ac1 --- /dev/null +++ b/beekeepy/beekeepy/_apis/abc/sendable.py @@ -0,0 +1,21 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from schemas.jsonrpc import ExpectResultT, JSONRPCResult + + +class SyncSendable(ABC): + @abstractmethod + def _send( + self, *, endpoint: str, params: str, expected_type: type[ExpectResultT] + ) -> JSONRPCResult[ExpectResultT]: ... + + +class AsyncSendable(ABC): + @abstractmethod + async def _async_send( + self, *, endpoint: str, params: str, expected_type: type[ExpectResultT] + ) -> JSONRPCResult[ExpectResultT]: ... diff --git a/beekeepy/beekeepy/_remote_handle/api/session_holder.py b/beekeepy/beekeepy/_apis/abc/session_holder.py similarity index 88% rename from beekeepy/beekeepy/_remote_handle/api/session_holder.py rename to beekeepy/beekeepy/_apis/abc/session_holder.py index 2c5b3d12..9e314b8b 100644 --- a/beekeepy/beekeepy/_remote_handle/api/session_holder.py +++ b/beekeepy/beekeepy/_apis/abc/session_holder.py @@ -1,8 +1,9 @@ from __future__ import annotations -from abc import abstractmethod +from abc import ABC, abstractmethod from typing import Any +from beekeepy._apis.abc.sendable import AsyncSendable, SyncSendable from schemas.apis.beekeeper_api import CreateSession __all__ = ["SyncSessionHolder", "AsyncSessionHolder"] @@ -37,7 +38,7 @@ class SessionHolder: return self.__session -class SyncSessionHolder(SessionHolder): +class SyncSessionHolder(SyncSendable, SessionHolder, ABC): @abstractmethod def _acquire_session_token(self) -> str: ... @@ -48,7 +49,7 @@ class SyncSessionHolder(SessionHolder): return self._check_and_return_session() -class AsyncSessionHolder(SessionHolder): +class AsyncSessionHolder(AsyncSendable, SessionHolder, ABC): @abstractmethod async def _acquire_session_token(self) -> str: ... diff --git a/beekeepy/beekeepy/_apis/app_status_api/__init__.py b/beekeepy/beekeepy/_apis/app_status_api/__init__.py new file mode 100644 index 00000000..58d93619 --- /dev/null +++ b/beekeepy/beekeepy/_apis/app_status_api/__init__.py @@ -0,0 +1,19 @@ +from __future__ import annotations + +from beekeepy._apis.app_status_api.api_collection import ( + AppStatusProbeAsyncApiCollection, + AppStatusProbeSyncApiCollection, +) +from beekeepy._apis.app_status_api.async_api import ( + AppStatusApi as AsyncAppStatusApi, +) +from beekeepy._apis.app_status_api.sync_api import ( + AppStatusApi as SyncAppStatusApi, +) + +__all__ = [ + "AsyncAppStatusApi", + "SyncAppStatusApi", + "AppStatusProbeAsyncApiCollection", + "AppStatusProbeSyncApiCollection", +] diff --git a/beekeepy/beekeepy/_apis/app_status_api/api_collection.py b/beekeepy/beekeepy/_apis/app_status_api/api_collection.py new file mode 100644 index 00000000..a61fc45d --- /dev/null +++ b/beekeepy/beekeepy/_apis/app_status_api/api_collection.py @@ -0,0 +1,32 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from beekeepy._apis.abc.api_collection import AbstractAsyncApiCollection, AbstractSyncApiCollection +from beekeepy._apis.app_status_api.async_api import AppStatusApi as AsyncAppStatusApi +from beekeepy._apis.app_status_api.sync_api import AppStatusApi as SyncAppStatusApi + +if TYPE_CHECKING: + from beekeepy._apis.abc.sendable import AsyncSendable, SyncSendable + + +class AppStatusProbeSyncApiCollection(AbstractSyncApiCollection): + """Beekeepers collection of available apis in async version.""" + + _owner: SyncSendable + + def __init__(self, owner: SyncSendable) -> None: + super().__init__(owner) + self.app_status = SyncAppStatusApi(owner=self._owner) + self.app_status_api = self.app_status + + +class AppStatusProbeAsyncApiCollection(AbstractAsyncApiCollection): + """Beekeepers collection of available apis in async version.""" + + _owner: AsyncSendable + + def __init__(self, owner: AsyncSendable) -> None: + super().__init__(owner) + self.app_status = AsyncAppStatusApi(owner=self._owner) + self.app_status_api = self.app_status diff --git a/beekeepy/beekeepy/_apis/app_status_api/async_api.py b/beekeepy/beekeepy/_apis/app_status_api/async_api.py new file mode 100644 index 00000000..b6c36b7f --- /dev/null +++ b/beekeepy/beekeepy/_apis/app_status_api/async_api.py @@ -0,0 +1,12 @@ +from __future__ import annotations + +from beekeepy._apis.abc.api import AbstractAsyncApi +from schemas.apis import app_status_api # noqa: TCH001 + + +class AppStatusApi(AbstractAsyncApi): + api = AbstractAsyncApi._endpoint + + @api + async def get_app_status(self) -> app_status_api.GetAppStatus: + raise NotImplementedError diff --git a/beekeepy/beekeepy/_apis/app_status_api/sync_api.py b/beekeepy/beekeepy/_apis/app_status_api/sync_api.py new file mode 100644 index 00000000..50633a60 --- /dev/null +++ b/beekeepy/beekeepy/_apis/app_status_api/sync_api.py @@ -0,0 +1,12 @@ +from __future__ import annotations + +from beekeepy._apis.abc.api import AbstractSyncApi +from schemas.apis import app_status_api # noqa: TCH001 + + +class AppStatusApi(AbstractSyncApi): + api = AbstractSyncApi._endpoint + + @api + def get_app_status(self) -> app_status_api.GetAppStatus: + raise NotImplementedError diff --git a/beekeepy/beekeepy/_remote_handle/api/apply_session_token.py b/beekeepy/beekeepy/_apis/apply_session_token.py similarity index 76% rename from beekeepy/beekeepy/_remote_handle/api/apply_session_token.py rename to beekeepy/beekeepy/_apis/apply_session_token.py index e7ed3274..a440a918 100644 --- a/beekeepy/beekeepy/_remote_handle/api/apply_session_token.py +++ b/beekeepy/beekeepy/_apis/apply_session_token.py @@ -3,8 +3,7 @@ from __future__ import annotations from typing import TYPE_CHECKING if TYPE_CHECKING: - from beekeepy._remote_handle.abc.api import ApiArgumentsToSerialize - from beekeepy._remote_handle.api.session_holder import AsyncSessionHolder, SyncSessionHolder + from beekeepy._apis.abc import ApiArgumentsToSerialize, AsyncSessionHolder, SyncSessionHolder def sync_apply_session_token(owner: SyncSessionHolder, arguments: ApiArgumentsToSerialize) -> ApiArgumentsToSerialize: diff --git a/beekeepy/beekeepy/_apis/beekeeper_api/__init__.py b/beekeepy/beekeepy/_apis/beekeeper_api/__init__.py new file mode 100644 index 00000000..dca7d2cf --- /dev/null +++ b/beekeepy/beekeepy/_apis/beekeeper_api/__init__.py @@ -0,0 +1,14 @@ +from __future__ import annotations + +from beekeepy._apis.beekeeper_api.api_collection import ( + BeekeeperAsyncApiCollection, + BeekeeperSyncApiCollection, +) +from beekeepy._apis.beekeeper_api.async_api import ( + BeekeeperApi as AsyncBeekeeperApi, +) +from beekeepy._apis.beekeeper_api.sync_api import ( + BeekeeperApi as SyncBeekeeperApi, +) + +__all__ = ["AsyncBeekeeperApi", "SyncBeekeeperApi", "BeekeeperAsyncApiCollection", "BeekeeperSyncApiCollection"] diff --git a/beekeepy/beekeepy/_apis/beekeeper_api/api_collection.py b/beekeepy/beekeepy/_apis/beekeeper_api/api_collection.py new file mode 100644 index 00000000..26744fc9 --- /dev/null +++ b/beekeepy/beekeepy/_apis/beekeeper_api/api_collection.py @@ -0,0 +1,32 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from beekeepy._apis.app_status_api import AppStatusProbeAsyncApiCollection, AppStatusProbeSyncApiCollection +from beekeepy._apis.beekeeper_api.async_api import BeekeeperApi as AsyncBeekeeperApi +from beekeepy._apis.beekeeper_api.sync_api import BeekeeperApi as SyncBeekeeperApi + +if TYPE_CHECKING: + from beekeepy._apis.abc import AsyncSessionHolder, SyncSessionHolder + + +class BeekeeperAsyncApiCollection(AppStatusProbeAsyncApiCollection): + """Beekeepers collection of available apis in async version.""" + + _owner: AsyncSessionHolder + + def __init__(self, owner: AsyncSessionHolder) -> None: + super().__init__(owner) + self.beekeeper = AsyncBeekeeperApi(owner=self._owner) + self.beekeeper_api = self.beekeeper + + +class BeekeeperSyncApiCollection(AppStatusProbeSyncApiCollection): + """Beekeepers collection of available apis in async version.""" + + _owner: SyncSessionHolder + + def __init__(self, owner: SyncSessionHolder) -> None: + super().__init__(owner) + self.beekeeper = SyncBeekeeperApi(owner=self._owner) + self.beekeeper_api = self.beekeeper diff --git a/beekeepy/beekeepy/_remote_handle/api/async_api.py b/beekeepy/beekeepy/_apis/beekeeper_api/async_api.py similarity index 90% rename from beekeepy/beekeepy/_remote_handle/api/async_api.py rename to beekeepy/beekeepy/_apis/beekeeper_api/async_api.py index 336e20c1..ba109ee1 100644 --- a/beekeepy/beekeepy/_remote_handle/api/async_api.py +++ b/beekeepy/beekeepy/_apis/beekeeper_api/async_api.py @@ -1,24 +1,18 @@ from __future__ import annotations -from typing import TYPE_CHECKING - -from beekeepy._remote_handle.abc.api import AbstractAsyncApi, ApiArgumentsToSerialize, AsyncHandleT -from beekeepy._remote_handle.api.apply_session_token import async_apply_session_token -from beekeepy._remote_handle.api.beekeeper_api_commons import BeekeeperApiCommons -from beekeepy._remote_handle.api.session_holder import AsyncSessionHolder +from beekeepy._apis.abc import AbstractAsyncApi, ApiArgumentsToSerialize, AsyncSendable, AsyncSessionHolder +from beekeepy._apis.apply_session_token import async_apply_session_token +from beekeepy._apis.beekeeper_api.beekeeper_api_commons import BeekeeperApiCommons from schemas.apis import beekeeper_api # noqa: TCH001 -if TYPE_CHECKING: - from beekeepy._remote_handle.beekeeper import AsyncBeekeeper, _AsyncSessionBatchHandle - -class BeekeeperApi(AbstractAsyncApi, BeekeeperApiCommons[AsyncHandleT]): +class BeekeeperApi(AbstractAsyncApi, BeekeeperApiCommons[AsyncSendable]): """Set of endpoints, that allows asynchronous communication with beekeeper service.""" api = AbstractAsyncApi._endpoint - _owner: AsyncBeekeeper | _AsyncSessionBatchHandle + _owner: AsyncSessionHolder - def __init__(self, owner: AsyncBeekeeper | _AsyncSessionBatchHandle) -> None: + def __init__(self, owner: AsyncSessionHolder) -> None: self._verify_is_owner_can_hold_session_token(owner=owner) super().__init__(owner=owner) @@ -216,14 +210,13 @@ class BeekeeperApi(AbstractAsyncApi, BeekeeperApiCommons[AsyncHandleT]): raise NotImplementedError @api - async def create_session(self, *, notifications_endpoint: str = "", salt: str = "") -> beekeeper_api.CreateSession: + async def create_session(self, *, salt: str = "") -> beekeeper_api.CreateSession: """Creates session. Note: This is called automatically when connection with beekeeper is establish, no need to call it explicitly. Args: - notifications_endpoint: endpoint on which notifications of status will be broadcasted. (defaults: "") salt: used for generation of session token Returns: diff --git a/beekeepy/beekeepy/_remote_handle/api/beekeeper_api_commons.py b/beekeepy/beekeepy/_apis/beekeeper_api/beekeeper_api_commons.py similarity index 86% rename from beekeepy/beekeepy/_remote_handle/api/beekeeper_api_commons.py rename to beekeepy/beekeepy/_apis/beekeeper_api/beekeeper_api_commons.py index 16b55fb4..718cc059 100644 --- a/beekeepy/beekeepy/_remote_handle/api/beekeeper_api_commons.py +++ b/beekeepy/beekeepy/_apis/beekeeper_api/beekeeper_api_commons.py @@ -3,10 +3,10 @@ from __future__ import annotations from abc import abstractmethod from typing import TYPE_CHECKING, Any, Generic, Protocol -from beekeepy._remote_handle.abc.api import HandleT +from beekeepy._apis.abc.api import HandleT if TYPE_CHECKING: - from beekeepy._remote_handle.api.session_holder import AsyncSessionHolder, SyncSessionHolder + from beekeepy._apis.abc import AsyncSessionHolder, SyncSessionHolder class CreateSessionActionProtocol(Protocol): diff --git a/beekeepy/beekeepy/_remote_handle/api/sync_api.py b/beekeepy/beekeepy/_apis/beekeeper_api/sync_api.py similarity index 81% rename from beekeepy/beekeepy/_remote_handle/api/sync_api.py rename to beekeepy/beekeepy/_apis/beekeeper_api/sync_api.py index 1360bf52..9b236f63 100644 --- a/beekeepy/beekeepy/_remote_handle/api/sync_api.py +++ b/beekeepy/beekeepy/_apis/beekeeper_api/sync_api.py @@ -1,23 +1,17 @@ from __future__ import annotations -from typing import TYPE_CHECKING, cast - -from beekeepy._remote_handle.abc.api import AbstractSyncApi, ApiArgumentsToSerialize, SyncHandleT -from beekeepy._remote_handle.api.apply_session_token import sync_apply_session_token -from beekeepy._remote_handle.api.beekeeper_api_commons import BeekeeperApiCommons -from beekeepy._remote_handle.api.session_holder import SyncSessionHolder +from beekeepy._apis.abc import AbstractSyncApi, ApiArgumentsToSerialize, SyncSendable, SyncSessionHolder +from beekeepy._apis.apply_session_token import sync_apply_session_token +from beekeepy._apis.beekeeper_api.beekeeper_api_commons import BeekeeperApiCommons from schemas.apis import beekeeper_api # noqa: TCH001 -if TYPE_CHECKING: - from beekeepy._remote_handle.beekeeper import Beekeeper, _SyncSessionBatchHandle - -class BeekeeperApi(AbstractSyncApi, BeekeeperApiCommons[SyncHandleT]): +class BeekeeperApi(AbstractSyncApi, BeekeeperApiCommons[SyncSendable]): api = AbstractSyncApi._endpoint - _owner: Beekeeper | _SyncSessionBatchHandle + _owner: SyncSessionHolder - def __init__(self, owner: Beekeeper | _SyncSessionBatchHandle) -> None: + def __init__(self, owner: SyncSessionHolder) -> None: self._verify_is_owner_can_hold_session_token(owner=owner) super().__init__(owner=owner) @@ -26,7 +20,7 @@ class BeekeeperApi(AbstractSyncApi, BeekeeperApiCommons[SyncHandleT]): ) -> ApiArgumentsToSerialize: if not self._token_required(endpoint_name): return super()._additional_arguments_actions(endpoint_name, arguments) - return sync_apply_session_token(cast(SyncSessionHolder, self._owner), arguments) + return sync_apply_session_token(self._owner, arguments) def _get_requires_session_holder_type(self) -> type[SyncSessionHolder]: return SyncSessionHolder @@ -94,7 +88,7 @@ class BeekeeperApi(AbstractSyncApi, BeekeeperApiCommons[SyncHandleT]): raise NotImplementedError @api - def create_session(self, *, notifications_endpoint: str = "", salt: str = "") -> beekeeper_api.CreateSession: + def create_session(self, *, salt: str = "") -> beekeeper_api.CreateSession: raise NotImplementedError @api diff --git a/beekeepy/beekeepy/_executable/streams.py b/beekeepy/beekeepy/_executable/streams.py deleted file mode 100644 index 27b5eef1..00000000 --- a/beekeepy/beekeepy/_executable/streams.py +++ /dev/null @@ -1,87 +0,0 @@ -from __future__ import annotations - -from dataclasses import dataclass, field -from typing import TYPE_CHECKING, TextIO, cast - -from beekeepy._interface.context import ContextSync - -if TYPE_CHECKING: - from pathlib import Path - - -@dataclass -class StreamRepresentation(ContextSync[TextIO]): - filename: str - dirpath: Path | None = None - stream: TextIO | None = None - _backup_count: int = 0 - _current_filename: str | None = None - - def __get_path(self) -> Path: - assert self.dirpath is not None, "Path is not specified" - if self._current_filename is None: - self._current_filename = self.__next_filename() - return self.dirpath / self._current_filename - - def __get_stream(self) -> TextIO: - assert self.stream is not None, "Unable to get stream, as it is not opened" - return self.stream - - def open_stream(self, mode: str = "wt") -> TextIO: - assert self.stream is None, "Stream is already opened" - self.__next_filename() - self.__create_user_friendly_link() - self.stream = cast(TextIO, self.__get_path().open(mode)) - assert not self.stream.closed, f"Failed to open stream: `{self.stream.errors}`" - return self.stream - - def close_stream(self) -> None: - self.__get_stream().close() - self.stream = None - - def set_path_for_dir(self, dir_path: Path) -> None: - self.dirpath = dir_path - - def _enter(self) -> TextIO: - return self.open_stream() - - def _finally(self) -> None: - self.close_stream() - - def __create_user_friendly_link(self) -> None: - assert self.dirpath is not None, "dirpath is not set" - user_friendly_link_dst = self.dirpath / f"{self.filename}.log" - user_friendly_link_dst.unlink(missing_ok=True) - user_friendly_link_dst.symlink_to(self.__get_path()) - - def __next_filename(self) -> str: - self._current_filename = f"{self.filename}_{self._backup_count}.log" - self._backup_count += 1 - return self._current_filename - - def __contains__(self, text: str) -> bool: - if not self.__get_path().exists(): - return False - - with self.open_stream("rt") as file: - for line in file: - if text in line: - return True - return False - - -@dataclass -class StreamsHolder: - stdout: StreamRepresentation = field(default_factory=lambda: StreamRepresentation("stdout")) - stderr: StreamRepresentation = field(default_factory=lambda: StreamRepresentation("stderr")) - - def set_paths_for_dir(self, dir_path: Path) -> None: - self.stdout.set_path_for_dir(dir_path) - self.stderr.set_path_for_dir(dir_path) - - def close(self) -> None: - self.stdout.close_stream() - self.stderr.close_stream() - - def __contains__(self, text: str) -> bool: - return (text in self.stderr) or (text in self.stdout) diff --git a/beekeepy/beekeepy/_remote_handle/api/__init__.py b/beekeepy/beekeepy/_remote_handle/api/__init__.py deleted file mode 100644 index 6d4be492..00000000 --- a/beekeepy/beekeepy/_remote_handle/api/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -from __future__ import annotations - -from beekeepy._remote_handle.api.async_api import BeekeeperApi as AsyncBeekeeperApi -from beekeepy._remote_handle.api.sync_api import BeekeeperApi as SyncBeekeeperApi - -__all__ = ["AsyncBeekeeperApi", "SyncBeekeeperApi"] diff --git a/beekeepy/beekeepy/_remote_handle/api/api_collection.py b/beekeepy/beekeepy/_remote_handle/api/api_collection.py deleted file mode 100644 index 37b2a0ff..00000000 --- a/beekeepy/beekeepy/_remote_handle/api/api_collection.py +++ /dev/null @@ -1,39 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING - -from beekeepy._remote_handle.abc.api_collection import ( - AbstractAsyncApiCollection, - AbstractSyncApiCollection, -) -from beekeepy._remote_handle.api import AsyncBeekeeperApi, SyncBeekeeperApi - -if TYPE_CHECKING: - from beekeepy._remote_handle.beekeeper import ( - AsyncBeekeeper, - Beekeeper, - _AsyncSessionBatchHandle, - _SyncSessionBatchHandle, - ) - - -class BeekeeperAsyncApiCollection(AbstractAsyncApiCollection): - """Beekeepers collection of available apis in async version.""" - - _owner: AsyncBeekeeper | _AsyncSessionBatchHandle - - def __init__(self, owner: AsyncBeekeeper | _AsyncSessionBatchHandle) -> None: - super().__init__(owner) - self.beekeeper = AsyncBeekeeperApi(owner=self._owner) - self.beekeeper_api = self.beekeeper - - -class BeekeeperSyncApiCollection(AbstractSyncApiCollection): - """Beekeepers collection of available apis in async version.""" - - _owner: Beekeeper | _SyncSessionBatchHandle - - def __init__(self, owner: Beekeeper | _SyncSessionBatchHandle) -> None: - super().__init__(owner) - self.beekeeper = SyncBeekeeperApi(owner=self._owner) - self.beekeeper_api = self.beekeeper -- GitLab From 9c97dcca2dbfedb2912c56ba04520db6b7296400 Mon Sep 17 00:00:00 2001 From: kmochocki <kmochocki@syncad.com> Date: Sat, 22 Mar 2025 20:03:30 +0000 Subject: [PATCH 04/23] Move utility classes and functions to separate sub-package --- beekeepy/beekeepy/_utilities/__init__.py | 0 .../{_remote_handle => _utilities}/build_json_rpc_call.py | 0 beekeepy/beekeepy/{_interface => _utilities}/context.py | 0 .../{_interface => _utilities}/context_settings_updater.py | 4 ++-- beekeepy/beekeepy/{_interface => _utilities}/delay_guard.py | 2 +- beekeepy/beekeepy/{_interface => _utilities}/error_logger.py | 4 ++-- beekeepy/beekeepy/{_interface => _utilities}/key_pair.py | 0 .../{_interface/_sanitize.py => _utilities/sanitize.py} | 0 .../beekeepy/{_interface => _utilities}/settings_holder.py | 2 +- .../beekeepy/{_interface => _utilities}/state_invalidator.py | 0 beekeepy/beekeepy/{_interface => _utilities}/stopwatch.py | 2 +- .../suppress_api_not_found.py} | 2 +- 12 files changed, 8 insertions(+), 8 deletions(-) create mode 100644 beekeepy/beekeepy/_utilities/__init__.py rename beekeepy/beekeepy/{_remote_handle => _utilities}/build_json_rpc_call.py (100%) rename beekeepy/beekeepy/{_interface => _utilities}/context.py (100%) rename beekeepy/beekeepy/{_interface => _utilities}/context_settings_updater.py (93%) rename beekeepy/beekeepy/{_interface => _utilities}/delay_guard.py (97%) rename beekeepy/beekeepy/{_interface => _utilities}/error_logger.py (93%) rename beekeepy/beekeepy/{_interface => _utilities}/key_pair.py (100%) rename beekeepy/beekeepy/{_interface/_sanitize.py => _utilities/sanitize.py} (100%) rename beekeepy/beekeepy/{_interface => _utilities}/settings_holder.py (97%) rename beekeepy/beekeepy/{_interface => _utilities}/state_invalidator.py (100%) rename beekeepy/beekeepy/{_interface => _utilities}/stopwatch.py (95%) rename beekeepy/beekeepy/{_interface/_suppress_api_not_found.py => _utilities/suppress_api_not_found.py} (96%) diff --git a/beekeepy/beekeepy/_utilities/__init__.py b/beekeepy/beekeepy/_utilities/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/beekeepy/beekeepy/_remote_handle/build_json_rpc_call.py b/beekeepy/beekeepy/_utilities/build_json_rpc_call.py similarity index 100% rename from beekeepy/beekeepy/_remote_handle/build_json_rpc_call.py rename to beekeepy/beekeepy/_utilities/build_json_rpc_call.py diff --git a/beekeepy/beekeepy/_interface/context.py b/beekeepy/beekeepy/_utilities/context.py similarity index 100% rename from beekeepy/beekeepy/_interface/context.py rename to beekeepy/beekeepy/_utilities/context.py diff --git a/beekeepy/beekeepy/_interface/context_settings_updater.py b/beekeepy/beekeepy/_utilities/context_settings_updater.py similarity index 93% rename from beekeepy/beekeepy/_interface/context_settings_updater.py rename to beekeepy/beekeepy/_utilities/context_settings_updater.py index a10bbc93..ca41424a 100644 --- a/beekeepy/beekeepy/_interface/context_settings_updater.py +++ b/beekeepy/beekeepy/_utilities/context_settings_updater.py @@ -4,12 +4,12 @@ from abc import abstractmethod from contextlib import contextmanager from typing import TYPE_CHECKING, Generic, TypeVar -from beekeepy._communication.settings import CommunicationSettings +from pydantic import BaseModel if TYPE_CHECKING: from collections.abc import Iterator -SettingsT = TypeVar("SettingsT", bound=CommunicationSettings) +SettingsT = TypeVar("SettingsT", bound=BaseModel) class ContextSettingsUpdater(Generic[SettingsT]): diff --git a/beekeepy/beekeepy/_interface/delay_guard.py b/beekeepy/beekeepy/_utilities/delay_guard.py similarity index 97% rename from beekeepy/beekeepy/_interface/delay_guard.py rename to beekeepy/beekeepy/_utilities/delay_guard.py index 1f82840d..89754405 100644 --- a/beekeepy/beekeepy/_interface/delay_guard.py +++ b/beekeepy/beekeepy/_utilities/delay_guard.py @@ -5,7 +5,7 @@ import time from datetime import datetime, timedelta, timezone from typing import TYPE_CHECKING, Final -from beekeepy._interface.context import ContextAsync, ContextSync +from beekeepy._utilities.context import ContextAsync, ContextSync from beekeepy.exceptions import UnlockIsNotAccessibleError if TYPE_CHECKING: diff --git a/beekeepy/beekeepy/_interface/error_logger.py b/beekeepy/beekeepy/_utilities/error_logger.py similarity index 93% rename from beekeepy/beekeepy/_interface/error_logger.py rename to beekeepy/beekeepy/_utilities/error_logger.py index 76037104..dd2872cc 100644 --- a/beekeepy/beekeepy/_interface/error_logger.py +++ b/beekeepy/beekeepy/_utilities/error_logger.py @@ -4,7 +4,7 @@ from typing import TYPE_CHECKING from loguru import logger as loguru_logger -from beekeepy._interface.context import ContextSync +from beekeepy._utilities.context import ContextSync if TYPE_CHECKING: from types import TracebackType @@ -13,7 +13,7 @@ if TYPE_CHECKING: class ErrorLogger(ContextSync[None]): - def __init__(self, logger: Logger | None = None, *exceptions: type[Exception]) -> None: + def __init__(self, logger: Logger | None = None, *exceptions: type[BaseException]) -> None: super().__init__() self.__logger = logger or loguru_logger self.__exception_whitelist = list(exceptions) diff --git a/beekeepy/beekeepy/_interface/key_pair.py b/beekeepy/beekeepy/_utilities/key_pair.py similarity index 100% rename from beekeepy/beekeepy/_interface/key_pair.py rename to beekeepy/beekeepy/_utilities/key_pair.py diff --git a/beekeepy/beekeepy/_interface/_sanitize.py b/beekeepy/beekeepy/_utilities/sanitize.py similarity index 100% rename from beekeepy/beekeepy/_interface/_sanitize.py rename to beekeepy/beekeepy/_utilities/sanitize.py diff --git a/beekeepy/beekeepy/_interface/settings_holder.py b/beekeepy/beekeepy/_utilities/settings_holder.py similarity index 97% rename from beekeepy/beekeepy/_interface/settings_holder.py rename to beekeepy/beekeepy/_utilities/settings_holder.py index ae98400d..e4a3ccae 100644 --- a/beekeepy/beekeepy/_interface/settings_holder.py +++ b/beekeepy/beekeepy/_utilities/settings_holder.py @@ -4,7 +4,7 @@ from abc import ABC, abstractmethod from contextlib import contextmanager from typing import TYPE_CHECKING, Any -from beekeepy._interface.context_settings_updater import ContextSettingsUpdater, SettingsT +from beekeepy._utilities.context_settings_updater import ContextSettingsUpdater, SettingsT if TYPE_CHECKING: from collections.abc import Iterator diff --git a/beekeepy/beekeepy/_interface/state_invalidator.py b/beekeepy/beekeepy/_utilities/state_invalidator.py similarity index 100% rename from beekeepy/beekeepy/_interface/state_invalidator.py rename to beekeepy/beekeepy/_utilities/state_invalidator.py diff --git a/beekeepy/beekeepy/_interface/stopwatch.py b/beekeepy/beekeepy/_utilities/stopwatch.py similarity index 95% rename from beekeepy/beekeepy/_interface/stopwatch.py rename to beekeepy/beekeepy/_utilities/stopwatch.py index 094f09a4..61b751fe 100644 --- a/beekeepy/beekeepy/_interface/stopwatch.py +++ b/beekeepy/beekeepy/_utilities/stopwatch.py @@ -4,7 +4,7 @@ from dataclasses import dataclass from datetime import datetime, timedelta, timezone from typing import Any -from beekeepy._interface.context import ContextSync +from beekeepy._utilities.context import ContextSync @dataclass diff --git a/beekeepy/beekeepy/_interface/_suppress_api_not_found.py b/beekeepy/beekeepy/_utilities/suppress_api_not_found.py similarity index 96% rename from beekeepy/beekeepy/_interface/_suppress_api_not_found.py rename to beekeepy/beekeepy/_utilities/suppress_api_not_found.py index 4b7758cd..2fb37cbf 100644 --- a/beekeepy/beekeepy/_interface/_suppress_api_not_found.py +++ b/beekeepy/beekeepy/_utilities/suppress_api_not_found.py @@ -2,7 +2,7 @@ from __future__ import annotations from typing import TYPE_CHECKING -from beekeepy._interface.context import SelfContextSync +from beekeepy._utilities.context import SelfContextSync from beekeepy.exceptions import ApiNotFoundError, GroupedErrorsError if TYPE_CHECKING: -- GitLab From ba4c8f7264be9bf06f47768742bbaf58889e3299 Mon Sep 17 00:00:00 2001 From: kmochocki <kmochocki@syncad.com> Date: Sat, 22 Mar 2025 20:04:35 +0000 Subject: [PATCH 05/23] Move url to communication and add sub-package interface --- beekeepy/beekeepy/_communication/__init__.py | 28 +++++++++++++++++++ .../{_interface => _communication}/url.py | 11 ++++++++ 2 files changed, 39 insertions(+) rename beekeepy/beekeepy/{_interface => _communication}/url.py (88%) diff --git a/beekeepy/beekeepy/_communication/__init__.py b/beekeepy/beekeepy/_communication/__init__.py index e69de29b..f56725c6 100644 --- a/beekeepy/beekeepy/_communication/__init__.py +++ b/beekeepy/beekeepy/_communication/__init__.py @@ -0,0 +1,28 @@ +from __future__ import annotations + +from beekeepy._communication import rules +from beekeepy._communication.abc.communicator import AbstractCommunicator +from beekeepy._communication.abc.overseer import AbstractOverseer +from beekeepy._communication.aiohttp_communicator import AioHttpCommunicator +from beekeepy._communication.httpx_communicator import HttpxCommunicator +from beekeepy._communication.overseers import CommonOverseer, StrictOverseer +from beekeepy._communication.request_communicator import RequestCommunicator +from beekeepy._communication.settings import CommunicationSettings +from beekeepy._communication.url import AnyUrl, HttpUrl, P2PUrl, Url, WsUrl + +__all__ = [ + "CommunicationSettings", + "CommonOverseer", + "StrictOverseer", + "AioHttpCommunicator", + "HttpxCommunicator", + "RequestCommunicator", + "AnyUrl", + "HttpUrl", + "P2PUrl", + "Url", + "WsUrl", + "AbstractCommunicator", + "AbstractOverseer", + "rules", +] diff --git a/beekeepy/beekeepy/_interface/url.py b/beekeepy/beekeepy/_communication/url.py similarity index 88% rename from beekeepy/beekeepy/_interface/url.py rename to beekeepy/beekeepy/_communication/url.py index f7a8937f..c7ce7d39 100644 --- a/beekeepy/beekeepy/_interface/url.py +++ b/beekeepy/beekeepy/_communication/url.py @@ -3,6 +3,8 @@ from __future__ import annotations from typing import Generic, Literal, TypeVar, get_args from urllib.parse import urlparse +from typing_extensions import Self + P2PProtocolT = Literal[""] HttpProtocolT = Literal["http", "https"] WsProtocolT = Literal["ws", "wss"] @@ -18,6 +20,7 @@ class Url(Generic[ProtocolT]): if protocol is not None and protocol not in allowed_proto: raise ValueError(f"Unknown protocol: `{protocol}`, allowed: {allowed_proto}") + target_protocol: str = protocol or self._default_protocol() if isinstance(url, Url): self.__protocol: str = url.__protocol self.__address: str = url.__address @@ -78,6 +81,14 @@ class Url(Generic[ProtocolT]): """ return [""] + @classmethod + def factory(cls, *, port: int = 0, address: str = "127.0.0.1") -> Self: + return cls((f"{cls._default_protocol()}://" if cls._default_protocol() else "") + f"{address}:{port}") + + @classmethod + def _default_protocol(cls) -> str: + return cls._allowed_protocols()[0] + class HttpUrl(Url[HttpProtocolT]): @classmethod -- GitLab From 5b38d8c92a12690512527971ba30c2fc42fc11a0 Mon Sep 17 00:00:00 2001 From: kmochocki <kmochocki@syncad.com> Date: Sat, 22 Mar 2025 20:07:37 +0000 Subject: [PATCH 06/23] Move abstract classess to abc dir in executable --- beekeepy/beekeepy/_executable/abc/__init__.py | 16 ++ .../beekeepy/_executable/abc/arguments.py | 144 ++++++++++++++++++ .../{_interface => _executable/abc}/config.py | 50 ++++-- .../_executable/{ => abc}/executable.py | 109 +++++++++---- beekeepy/beekeepy/_executable/abc/streams.py | 87 +++++++++++ .../_executable/arguments/arguments.py | 64 -------- 6 files changed, 366 insertions(+), 104 deletions(-) create mode 100644 beekeepy/beekeepy/_executable/abc/__init__.py create mode 100644 beekeepy/beekeepy/_executable/abc/arguments.py rename beekeepy/beekeepy/{_interface => _executable/abc}/config.py (59%) rename beekeepy/beekeepy/_executable/{ => abc}/executable.py (63%) create mode 100644 beekeepy/beekeepy/_executable/abc/streams.py delete mode 100644 beekeepy/beekeepy/_executable/arguments/arguments.py diff --git a/beekeepy/beekeepy/_executable/abc/__init__.py b/beekeepy/beekeepy/_executable/abc/__init__.py new file mode 100644 index 00000000..57c9222f --- /dev/null +++ b/beekeepy/beekeepy/_executable/abc/__init__.py @@ -0,0 +1,16 @@ +from __future__ import annotations + +from beekeepy._executable.abc.arguments import Arguments +from beekeepy._executable.abc.config import Config +from beekeepy._executable.abc.executable import ArgumentT, ConfigT, Executable +from beekeepy._executable.abc.streams import StreamRepresentation, StreamsHolder + +__all__ = [ + "Arguments", + "Executable", + "StreamsHolder", + "StreamRepresentation", + "Config", + "ArgumentT", + "ConfigT", +] diff --git a/beekeepy/beekeepy/_executable/abc/arguments.py b/beekeepy/beekeepy/_executable/abc/arguments.py new file mode 100644 index 00000000..2fe6085a --- /dev/null +++ b/beekeepy/beekeepy/_executable/abc/arguments.py @@ -0,0 +1,144 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod +from pathlib import Path +from typing import TYPE_CHECKING, Any + +from pydantic import Field + +from schemas._preconfigured_base_model import PreconfiguredBaseModel + +if TYPE_CHECKING: + from typing_extensions import Self + + +__all__ = ["Arguments"] + + +class CliParser: + @classmethod + def parse_cli_input(cls, cli: list[str]) -> dict[str, str | list[str] | bool]: + ordered_cli: dict[str, str | set[str] | None] = {} + previous_key: str | None = None + for item in cli: + key, value = cls._preprocess_option(item) + if key.startswith("__"): + previous_key = key[2:] + ordered_cli[previous_key] = value + continue + if key.startswith("_"): + previous_key = key[1:] + ordered_cli[previous_key] = value + continue + + if key in ordered_cli: + if isinstance((dict_value := ordered_cli[key]), set): + dict_value.add(item) + elif ordered_cli[key] is None: + assert isinstance(item, str), "parsing failed, item is not string" # mypy check + ordered_cli[key] = item + else: # if ordered_cli[key] is not None and is not set + assert isinstance(item, str), "parsing failed, item is not string" # mypy check + dict_value = ordered_cli[key] + assert isinstance(dict_value, str), "parsing failed, dict_value is not string" # mypy check + ordered_cli[key] = {dict_value, item} + continue + + assert ( + previous_key is not None + ), "parsing failed, previous_key was not set and following argument is not prefixed" + ordered_cli[previous_key] = item + previous_key = None + + return cls._convert_sets_to_lists_and_none_to_boolean(ordered_cli) + + @classmethod + def _convert_sets_to_lists_and_none_to_boolean( + cls, ordered_cli: dict[str, str | set[str] | None] + ) -> dict[str, str | list[str] | bool]: + result: dict[str, str | list[str] | bool] = {} + for key, value in ordered_cli.items(): + if isinstance(value, set): + result[key] = list(value) + elif value is None: + result[key] = True + else: + result[key] = value + return result + + @classmethod + def _preprocess_option(cls, item: str) -> tuple[str, str | None]: + key = item + value = None + if "=" in item: + key, value = item.split("=") + + key = key.replace("-", "_") + return key, value + + +class Arguments(PreconfiguredBaseModel, ABC): + help_: bool = Field(alias="help", default=False) + version: bool = False + dump_config: bool = False + + class Config: + arbitrary_types_allowed = True + + def __convert_member_name_to_cli_value(self, member_name: str) -> str: + return member_name.replace("_", "-") + + def __convert_member_value_to_string(self, member_value: int | str | Path | Any) -> str: + if isinstance(member_value, bool): + return "" + if isinstance(member_value, str): + return member_value + if isinstance(member_value, int): + return str(member_value) + if isinstance(member_value, Path): + return member_value.as_posix() + if isinstance(result := self._convert_member_value_to_string_default(member_value=member_value), str): + return result + raise TypeError("Invalid type") + + @abstractmethod + def _convert_member_value_to_string_default(self, member_value: Any) -> str | Any: ... + + def __prepare_arguments(self, pattern: str) -> list[str]: + data = self.dict(by_alias=True, exclude_none=True, exclude_unset=True, exclude_defaults=True) + cli_arguments: list[str] = [] + for k, v in data.items(): + cli_arguments.append(pattern.format(self.__convert_member_name_to_cli_value(k))) + cli_arguments.append(self.__convert_member_value_to_string(v)) + return cli_arguments + + def process(self, *, with_prefix: bool = True) -> list[str]: + pattern = self._generate_argument_prefix(with_prefix=with_prefix) + return self.__prepare_arguments(pattern) + + def _generate_argument_prefix(self, *, with_prefix: bool) -> str: + return "--{0}" if with_prefix else "{0}" + + def update_with(self, other: Self | None) -> None: + if other is None: + return + + for other_name, other_value in other.dict(exclude_unset=True, exclude_defaults=True, exclude_none=True).items(): + assert isinstance(other_name, str), "Member name has to be string" + setattr(self, other_name, other_value) + + @classmethod + def parse_cli_input(cls, cli: list[str]) -> Self: + return cls(**CliParser.parse_cli_input(cli)) + + @classmethod + def just_get_help(cls) -> Self: + return cls(help_=True) + + @classmethod + def just_get_version(cls) -> Self: + return cls(version=True) + + @classmethod + def just_dump_config(cls) -> Self: + return cls(dump_config=True) diff --git a/beekeepy/beekeepy/_interface/config.py b/beekeepy/beekeepy/_executable/abc/config.py similarity index 59% rename from beekeepy/beekeepy/_interface/config.py rename to beekeepy/beekeepy/_executable/abc/config.py index 0504073d..413a43c2 100644 --- a/beekeepy/beekeepy/_interface/config.py +++ b/beekeepy/beekeepy/_executable/abc/config.py @@ -4,9 +4,10 @@ from pathlib import Path from types import UnionType from typing import TYPE_CHECKING, Any, ClassVar, get_args +from loguru import logger from pydantic import BaseModel -from beekeepy._interface.url import Url +from beekeepy._communication import Url from beekeepy.exceptions import InvalidOptionError if TYPE_CHECKING: @@ -25,26 +26,30 @@ class Config(BaseModel): out_file.write("# config automatically generated by helpy\n") for member_name, member_value in self.__dict__.items(): if member_value is not None: - out_file.write( - f"{self._convert_member_name_to_config_name(member_name)}={self._convert_member_value_to_config_value(member_value)}\n" - ) + if isinstance(member_value, list) and len(member_value) == 0: + continue + + entry_name = self._convert_member_name_to_config_name(member_name) + entry_value = self._convert_member_value_to_config_value(member_name, member_value) + for value in [entry_value] if not isinstance(entry_value, list) else entry_value: + out_file.write(f"{entry_name} = {value}\n") @classmethod def load(cls, source: Path) -> Self: source = source / Config.DEFAULT_FILE_NAME if source.is_dir() else source assert source.exists(), "Given file does not exists." fields = cls.__fields__ - values_to_write = {} + values_to_write: dict[str, Any] = {} with source.open("rt", encoding="utf-8") as in_file: for line in in_file: if (line := line.strip("\n")) and not line.startswith("#"): config_name, config_value = line.split("=") member_name = cls._convert_config_name_to_member_name(config_name) member_type = fields[member_name].annotation - if isinstance(member_type, UnionType) and get_args(member_type)[-1] is type(None): + if isinstance(member_type, UnionType) and (type(None) in get_args(member_type)): member_type = get_args(member_type)[0] values_to_write[member_name] = cls._convert_config_value_to_member_value( - config_value, expected=member_type + config_value, expected=member_type, current_value=values_to_write.get(member_name) ) return cls(**values_to_write) @@ -57,9 +62,9 @@ class Config(BaseModel): return config_name.strip().replace("-", "_") @classmethod - def _convert_member_value_to_config_value(cls, member_value: Any) -> str: + def _convert_member_value_to_config_value(cls, member_name: str, member_value: Any) -> str | list[str]: # noqa: ARG003 if isinstance(member_value, list): - return " ".join(member_value) + return member_value if isinstance(member_value, bool): return "yes" if member_value else "no" @@ -73,8 +78,8 @@ class Config(BaseModel): return str(member_value) @classmethod - def _convert_config_value_to_member_value( # noqa: PLR0911 - cls, config_value: str, *, expected: type[Any] + def _convert_config_value_to_member_value( # noqa: PLR0911, C901 + cls, config_value: str, *, expected: type[Any], current_value: Any | None ) -> Any | None: config_value = config_value.strip() if config_value is None: @@ -83,8 +88,21 @@ class Config(BaseModel): if expected == Path: return Path(config_value.replace('"', "")) - if expected == list[str]: - return config_value.split() + if issubclass(expected, list) or "list" in str(expected): + list_arg_t = get_args(expected)[0] + if len(get_args(list_arg_t)): # in case of unions + list_arg_t = get_args(list_arg_t)[0] + logger.info(f"{list_arg_t=}") + values = [ + cls._convert_config_value_to_member_value(value, expected=list_arg_t, current_value=None) + for value in config_value.split() + ] + if current_value is not None: + if isinstance(current_value, list): + current_value.extend(values) + return current_value + return [*values, current_value] + return values if expected == Url: return Url(config_value) @@ -99,4 +117,10 @@ class Config(BaseModel): raise InvalidOptionError(f"Expected `yes` or `no`, got: `{config_value}`") + if "str" in str(expected): + return config_value.strip('"') + + if isinstance(expected, type) and issubclass(expected, int | str) and hasattr(expected, "validate"): + return expected.validate(config_value) + return expected(config_value) if expected is not None else None diff --git a/beekeepy/beekeepy/_executable/executable.py b/beekeepy/beekeepy/_executable/abc/executable.py similarity index 63% rename from beekeepy/beekeepy/_executable/executable.py rename to beekeepy/beekeepy/_executable/abc/executable.py index 4f171554..4ba4628d 100644 --- a/beekeepy/beekeepy/_executable/executable.py +++ b/beekeepy/beekeepy/_executable/abc/executable.py @@ -3,17 +3,21 @@ from __future__ import annotations import os import signal import subprocess -import warnings +import time from abc import ABC, abstractmethod +from contextlib import contextmanager from typing import TYPE_CHECKING, Any, Generic, TypeVar -from beekeepy._executable.arguments.arguments import Arguments -from beekeepy._executable.streams import StreamsHolder -from beekeepy._interface.config import Config -from beekeepy._interface.context import ContextSync -from beekeepy.exceptions import BeekeeperIsNotRunningError, TimeoutReachWhileCloseError +import psutil + +from beekeepy._executable.abc.arguments import Arguments +from beekeepy._executable.abc.config import Config +from beekeepy._executable.abc.streams import StreamsHolder +from beekeepy._utilities.context import ContextSync +from beekeepy.exceptions import ExecutableIsNotRunningError, TimeoutReachWhileCloseError if TYPE_CHECKING: + from collections.abc import Iterator from pathlib import Path from loguru import Logger @@ -21,7 +25,7 @@ if TYPE_CHECKING: class Closeable(ABC): @abstractmethod - def close(self) -> None: ... + def close(self, timeout_secs: float = 10.0) -> None: ... class AutoCloser(ContextSync[None]): @@ -70,15 +74,41 @@ class Executable(Closeable, Generic[ConfigT, ArgumentT]): def config(self) -> ConfigT: return self.__config - def run( + @property + def arguments(self) -> ArgumentT: + return self.__arguments + + @property + def executable_path(self) -> Path: + return self.__executable_path + + def _run( self, *, blocking: bool, - arguments: ArgumentT | None = None, environ: dict[str, str] | None = None, propagate_sigint: bool = True, + save_config: bool = True, ) -> AutoCloser: - command, environment_variables = self.__prepare(arguments=arguments, environ=environ) + return self.__run( + blocking=blocking, + arguments=self.arguments, + environ=environ, + propagate_sigint=propagate_sigint, + save_config=save_config, + ) + + def __run( + self, + *, + blocking: bool, + arguments: ArgumentT, + environ: dict[str, str] | None = None, + propagate_sigint: bool = True, + save_config: bool = True, + ) -> AutoCloser: + command, environment_variables = self.__prepare(arguments=arguments, environ=environ, save_config=save_config) + self._logger.info(f"starting `{self.__executable_path.stem}` as: `{command}`") if blocking: with self.__files.stdout as stdout, self.__files.stderr as stderr: @@ -114,9 +144,11 @@ class Executable(Closeable, Generic[ConfigT, ArgumentT]): return result.decode().strip() def __prepare( - self, arguments: ArgumentT | None, environ: dict[str, str] | None + self, + arguments: ArgumentT, + environ: dict[str, str] | None, + save_config: bool = True, # noqa: FBT001, FBT002 ) -> tuple[list[str], dict[str, str]]: - arguments = arguments or self.__arguments environ = environ or {} self.__working_directory.mkdir(exist_ok=True) @@ -127,7 +159,8 @@ class Executable(Closeable, Generic[ConfigT, ArgumentT]): environment_variables = dict(os.environ) environment_variables.update(environ) - self.config.save(self.working_directory) + if save_config: + self.config.save(self.working_directory) return command, environment_variables @@ -136,7 +169,7 @@ class Executable(Closeable, Generic[ConfigT, ArgumentT]): def detach(self) -> int: if self.__process is None: - raise BeekeeperIsNotRunningError + raise ExecutableIsNotRunningError pid = self.pid self.__process = None self.__files.close() @@ -158,16 +191,6 @@ class Executable(Closeable, Generic[ConfigT, ArgumentT]): self.__process = None self.__files.close() - def __warn_if_pid_files_exists(self) -> None: - if self.__pid_files_exists(): - warnings.warn( - f"PID file has not been removed, malfunction may occur. Working directory: {self.working_directory}", - stacklevel=2, - ) - - def __pid_files_exists(self) -> bool: - return len(list(self.working_directory.glob("*.pid"))) > 0 - def is_running(self) -> bool: if not self.__process: return False @@ -177,6 +200,17 @@ class Executable(Closeable, Generic[ConfigT, ArgumentT]): def log_has_phrase(self, text: str) -> bool: return text in self.__files + @contextmanager + def restore_arguments(self, new_arguments: ArgumentT | None) -> Iterator[None]: + __backup = self.__arguments + self.__arguments = new_arguments or self.__arguments + try: + yield + except: # noqa: TRY302 # https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager + raise + finally: + self.__arguments = __backup + @abstractmethod def _construct_config(self) -> ConfigT: ... @@ -184,9 +218,20 @@ class Executable(Closeable, Generic[ConfigT, ArgumentT]): def _construct_arguments(self) -> ArgumentT: ... def generate_default_config(self) -> ConfigT: - path_to_config = self.working_directory / (Config.DEFAULT_FILE_NAME) - self.run(blocking=True, arguments=self.__arguments.just_dump_config()) - temp_path_to_file = path_to_config.rename(Config.DEFAULT_FILE_NAME + ".tmp") + if not self.working_directory.exists(): + self.working_directory.mkdir(parents=True) + orig_path_to_config: Path | None = None + path_to_config = self.working_directory / Config.DEFAULT_FILE_NAME + if path_to_config.exists(): + orig_path_to_config = path_to_config.rename( + path_to_config.with_suffix(".ini.orig") + ) # temporary move it to not interfere with config generation + arguments = self._construct_arguments() + arguments.dump_config = True + self.__run(blocking=True, arguments=arguments, save_config=False) + temp_path_to_file = path_to_config.rename(path_to_config.with_suffix(".ini.tmp")) + if orig_path_to_config is not None: + orig_path_to_config.rename(path_to_config) return self.config.load(temp_path_to_file) def get_help_text(self) -> str: @@ -194,3 +239,13 @@ class Executable(Closeable, Generic[ConfigT, ArgumentT]): def version(self) -> str: return self.run_and_get_output(arguments=self.__arguments.just_get_version()) + + def reserved_ports(self, *, timeout_seconds: int = 10) -> list[int]: + assert self.is_running(), "Cannot obtain reserved ports for not started executable" + start = time.perf_counter() + while start + timeout_seconds >= time.perf_counter(): + connections = psutil.net_connections("inet4") + reserved_ports = [connection.laddr[1] for connection in connections if connection.pid == self.pid] # type: ignore[misc] + if reserved_ports: + return reserved_ports + raise TimeoutError diff --git a/beekeepy/beekeepy/_executable/abc/streams.py b/beekeepy/beekeepy/_executable/abc/streams.py new file mode 100644 index 00000000..1e615284 --- /dev/null +++ b/beekeepy/beekeepy/_executable/abc/streams.py @@ -0,0 +1,87 @@ +from __future__ import annotations + +from dataclasses import dataclass, field +from typing import TYPE_CHECKING, TextIO, cast + +from beekeepy._utilities.context import ContextSync + +if TYPE_CHECKING: + from pathlib import Path + + +@dataclass +class StreamRepresentation(ContextSync[TextIO]): + filename: str + dirpath: Path | None = None + stream: TextIO | None = None + _backup_count: int = 0 + _current_filename: str | None = None + + def __get_path(self) -> Path: + assert self.dirpath is not None, "Path is not specified" + if self._current_filename is None: + self._current_filename = self.__next_filename() + return self.dirpath / self._current_filename + + def __get_stream(self) -> TextIO: + assert self.stream is not None, "Unable to get stream, as it is not opened" + return self.stream + + def open_stream(self, mode: str = "wt") -> TextIO: + assert self.stream is None, "Stream is already opened" + self.__next_filename() + self.__create_user_friendly_link() + self.stream = cast(TextIO, self.__get_path().open(mode)) + assert not self.stream.closed, f"Failed to open stream: `{self.stream.errors}`" + return self.stream + + def close_stream(self) -> None: + self.__get_stream().close() + self.stream = None + + def set_path_for_dir(self, dir_path: Path) -> None: + self.dirpath = dir_path + + def _enter(self) -> TextIO: + return self.open_stream() + + def _finally(self) -> None: + self.close_stream() + + def __create_user_friendly_link(self) -> None: + assert self.dirpath is not None, "dirpath is not set" + user_friendly_link_dst = self.dirpath / f"{self.filename}.log" + user_friendly_link_dst.unlink(missing_ok=True) + user_friendly_link_dst.symlink_to(self.__get_path()) + + def __next_filename(self) -> str: + self._current_filename = f"{self.filename}_{self._backup_count}.log" + self._backup_count += 1 + return self._current_filename + + def __contains__(self, text: str) -> bool: + if not self.__get_path().exists(): + return False + + with self.open_stream("rt") as file: + for line in file: + if text in line: + return True + return False + + +@dataclass +class StreamsHolder: + stdout: StreamRepresentation = field(default_factory=lambda: StreamRepresentation("stdout")) + stderr: StreamRepresentation = field(default_factory=lambda: StreamRepresentation("stderr")) + + def set_paths_for_dir(self, dir_path: Path) -> None: + self.stdout.set_path_for_dir(dir_path) + self.stderr.set_path_for_dir(dir_path) + + def close(self) -> None: + self.stdout.close_stream() + self.stderr.close_stream() + + def __contains__(self, text: str) -> bool: + return (text in self.stderr) or (text in self.stdout) diff --git a/beekeepy/beekeepy/_executable/arguments/arguments.py b/beekeepy/beekeepy/_executable/arguments/arguments.py deleted file mode 100644 index 4a248218..00000000 --- a/beekeepy/beekeepy/_executable/arguments/arguments.py +++ /dev/null @@ -1,64 +0,0 @@ -from __future__ import annotations - -from abc import ABC, abstractmethod -from pathlib import Path -from typing import TYPE_CHECKING, Any - -from pydantic import Field - -from schemas._preconfigured_base_model import PreconfiguredBaseModel - -if TYPE_CHECKING: - from typing_extensions import Self - - -class Arguments(PreconfiguredBaseModel, ABC): - help_: bool = Field(alias="help", default=False) - version: bool = False - dump_config: bool = False - - class Config: - arbitrary_types_allowed = True - - def __convert_member_name_to_cli_value(self, member_name: str) -> str: - return member_name.replace("_", "-") - - def __convert_member_value_to_string(self, member_value: int | str | Path | Any) -> str: - if isinstance(member_value, bool): - return "" - if isinstance(member_value, str): - return member_value - if isinstance(member_value, int): - return str(member_value) - if isinstance(member_value, Path): - return member_value.as_posix() - if isinstance(result := self._convert_member_value_to_string_default(member_value=member_value), str): - return result - raise TypeError("Invalid type") - - @abstractmethod - def _convert_member_value_to_string_default(self, member_value: Any) -> str | Any: ... - - def __prepare_arguments(self, pattern: str) -> list[str]: - data = self.dict(by_alias=True, exclude_none=True, exclude_unset=True, exclude_defaults=True) - cli_arguments: list[str] = [] - for k, v in data.items(): - cli_arguments.append(pattern.format(self.__convert_member_name_to_cli_value(k))) - cli_arguments.append(self.__convert_member_value_to_string(v)) - return cli_arguments - - def process(self, *, with_prefix: bool = True) -> list[str]: - pattern = "--{0}" if with_prefix else "{0}" - return self.__prepare_arguments(pattern) - - @classmethod - def just_get_help(cls) -> Self: - return cls(help_=True) - - @classmethod - def just_get_version(cls) -> Self: - return cls(version=True) - - @classmethod - def just_dump_config(cls) -> Self: - return cls(dump_config=True) -- GitLab From 1ade7449bd9ac0ffa4d4bde0db082396f75e1455 Mon Sep 17 00:00:00 2001 From: kmochocki <kmochocki@syncad.com> Date: Sat, 22 Mar 2025 20:10:06 +0000 Subject: [PATCH 07/23] Move batch_handle to abc dir in _remote_handle --- beekeepy/beekeepy/_remote_handle/{ => abc}/batch_handle.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename beekeepy/beekeepy/_remote_handle/{ => abc}/batch_handle.py (100%) diff --git a/beekeepy/beekeepy/_remote_handle/batch_handle.py b/beekeepy/beekeepy/_remote_handle/abc/batch_handle.py similarity index 100% rename from beekeepy/beekeepy/_remote_handle/batch_handle.py rename to beekeepy/beekeepy/_remote_handle/abc/batch_handle.py -- GitLab From bc2199ab55a1ac905499c6e31bcb21d2e2d2b463 Mon Sep 17 00:00:00 2001 From: kmochocki <kmochocki@syncad.com> Date: Sat, 22 Mar 2025 20:19:11 +0000 Subject: [PATCH 08/23] Adjust imports to moved classes and functions --- beekeepy/beekeepy/__init__.py | 25 +++++----- .../_communication/abc/communicator.py | 8 ++-- .../beekeepy/_communication/abc/overseer.py | 11 ++--- beekeepy/beekeepy/_communication/abc/rules.py | 2 +- .../_communication/aiohttp_communicator.py | 4 +- .../_communication/httpx_communicator.py | 4 +- .../_communication/request_communicator.py | 4 +- beekeepy/beekeepy/_communication/settings.py | 2 +- .../_executable/arguments/__init__.py | 0 .../arguments/beekeeper_arguments.py | 46 ------------------- .../beekeepy/_executable/beekeeper_config.py | 4 +- .../_executable/beekeeper_executable.py | 38 +++++++++------ beekeepy/beekeepy/_executable/defaults.py | 2 +- beekeepy/beekeepy/_interface/__init__.py | 22 +++++++++ beekeepy/beekeepy/_interface/abc/__init__.py | 23 ++++++++++ .../_interface/abc/asynchronous/beekeeper.py | 8 ++-- .../_interface/abc/asynchronous/session.py | 2 +- .../_interface/abc/asynchronous/wallet.py | 2 +- .../beekeepy/_interface/abc/packed_object.py | 19 +++----- .../_interface/abc/synchronous/beekeeper.py | 8 ++-- .../_interface/abc/synchronous/session.py | 2 +- .../_interface/abc/synchronous/wallet.py | 2 +- .../_interface/asynchronous/beekeeper.py | 7 ++- .../_interface/asynchronous/session.py | 3 +- .../_interface/asynchronous/wallet.py | 2 + .../_interface/synchronous/beekeeper.py | 5 +- .../_interface/synchronous/session.py | 3 +- .../beekeepy/_interface/synchronous/wallet.py | 2 + .../beekeepy/_interface/wallets_common.py | 21 +++++---- beekeepy/beekeepy/_remote_handle/__init__.py | 30 ++++++++++++ .../_remote_handle/abc/batch_handle.py | 12 ++--- .../beekeepy/_remote_handle/abc/handle.py | 44 ++++++++++-------- beekeepy/beekeepy/_remote_handle/beekeeper.py | 35 +++++++++----- .../beekeepy/_runnable_handle/__init__.py | 21 +++++++-- .../beekeepy/_runnable_handle/settings.py | 5 +- beekeepy/beekeepy/exceptions/base.py | 6 +-- beekeepy/beekeepy/exceptions/common.py | 10 +--- beekeepy/beekeepy/exceptions/overseer.py | 4 +- beekeepy/beekeepy/handle/remote.py | 26 +++++++---- beekeepy/beekeepy/handle/runnable.py | 23 ++-------- beekeepy/beekeepy/interfaces.py | 30 ++++++------ .../patterns/generate_help_pattern.py | 7 ++- .../application_options/test_backtrace.py | 9 ++-- .../test_export_keys_wallet.py | 2 +- .../application_options/test_log_json_rpc.py | 2 +- .../test_unlock_timeout.py | 2 +- .../application_options/test_wallet_dir.py | 2 +- .../test_webserver_http_endpoint.py | 4 +- .../test_webserver_thread_pool_size.py | 2 +- .../handle/commandline/conftest.py | 8 +++- .../handle/storage/test_storage.py | 9 ++-- .../handle/various/test_blocking_unlock.py | 4 +- tests/beekeepy_test/interface/test_setup.py | 16 ++++--- .../interface/test_standalone_beekeeper.py | 6 +-- tests/conftest.py | 2 +- .../beekeepy/account_credentials.py | 2 +- .../local_tools/beekeepy/network.py | 11 ++--- 57 files changed, 342 insertions(+), 273 deletions(-) delete mode 100644 beekeepy/beekeepy/_executable/arguments/__init__.py delete mode 100644 beekeepy/beekeepy/_executable/arguments/beekeeper_arguments.py diff --git a/beekeepy/beekeepy/__init__.py b/beekeepy/beekeepy/__init__.py index e416f596..b5402dc7 100644 --- a/beekeepy/beekeepy/__init__.py +++ b/beekeepy/beekeepy/__init__.py @@ -1,16 +1,19 @@ from __future__ import annotations -from beekeepy._interface.abc.asynchronous.beekeeper import Beekeeper as AsyncBeekeeper -from beekeepy._interface.abc.asynchronous.session import Session as AsyncSession -from beekeepy._interface.abc.asynchronous.wallet import UnlockedWallet as AsyncUnlockedWallet -from beekeepy._interface.abc.asynchronous.wallet import Wallet as AsyncWallet -from beekeepy._interface.abc.packed_object import PackedAsyncBeekeeper, PackedSyncBeekeeper -from beekeepy._interface.abc.synchronous.beekeeper import Beekeeper -from beekeepy._interface.abc.synchronous.session import Session -from beekeepy._interface.abc.synchronous.wallet import UnlockedWallet, Wallet -from beekeepy._remote_handle.settings import Settings as RemoteHandleSettings -from beekeepy._runnable_handle.close_already_running_beekeeper import close_already_running_beekeeper -from beekeepy._runnable_handle.settings import Settings +from beekeepy._interface import InterfaceSettings as Settings +from beekeepy._interface.abc import ( + AsyncBeekeeper, + AsyncSession, + AsyncUnlockedWallet, + AsyncWallet, + Beekeeper, + PackedAsyncBeekeeper, + PackedSyncBeekeeper, + Session, + UnlockedWallet, + Wallet, +) +from beekeepy._runnable_handle import close_already_running_beekeeper __all__ = [ "AsyncBeekeeper", diff --git a/beekeepy/beekeepy/_communication/abc/communicator.py b/beekeepy/beekeepy/_communication/abc/communicator.py index 2dae1791..d72446d9 100644 --- a/beekeepy/beekeepy/_communication/abc/communicator.py +++ b/beekeepy/beekeepy/_communication/abc/communicator.py @@ -7,13 +7,13 @@ from threading import Thread from typing import TYPE_CHECKING, Any, Awaitable from beekeepy._communication.settings import CommunicationSettings -from beekeepy._interface.settings_holder import SharedSettingsHolder -from beekeepy._interface.stopwatch import Stopwatch +from beekeepy._utilities.settings_holder import SharedSettingsHolder +from beekeepy._utilities.stopwatch import Stopwatch from beekeepy.exceptions import CommunicationError, TimeoutExceededError, UnknownDecisionPathError if TYPE_CHECKING: - from beekeepy._interface.stopwatch import StopwatchResult - from beekeepy._interface.url import HttpUrl + from beekeepy._communication.url import HttpUrl + from beekeepy._utilities.stopwatch import StopwatchResult class AbstractCommunicator(SharedSettingsHolder[CommunicationSettings], ABC): diff --git a/beekeepy/beekeepy/_communication/abc/overseer.py b/beekeepy/beekeepy/_communication/abc/overseer.py index 30d75296..756eae86 100644 --- a/beekeepy/beekeepy/_communication/abc/overseer.py +++ b/beekeepy/beekeepy/_communication/abc/overseer.py @@ -5,19 +5,16 @@ from abc import ABC, abstractmethod from enum import IntEnum from typing import TYPE_CHECKING, Any, Callable, ClassVar, Sequence -from beekeepy._interface.context import SelfContextSync -from beekeepy.exceptions import ( - GroupedErrorsError, - UnknownDecisionPathError, -) +from beekeepy._utilities.context import SelfContextSync +from beekeepy.exceptions import GroupedErrorsError, Json, UnknownDecisionPathError if TYPE_CHECKING: from types import TracebackType from beekeepy._communication.abc.communicator import AbstractCommunicator from beekeepy._communication.abc.rules import Rules, RulesClassifier - from beekeepy._interface.url import HttpUrl - from beekeepy.exceptions import Json, OverseerError + from beekeepy._communication.url import HttpUrl + from beekeepy.exceptions import OverseerError __all__ = ["AbstractOverseer"] diff --git a/beekeepy/beekeepy/_communication/abc/rules.py b/beekeepy/beekeepy/_communication/abc/rules.py index a87c0c5e..2234ae29 100644 --- a/beekeepy/beekeepy/_communication/abc/rules.py +++ b/beekeepy/beekeepy/_communication/abc/rules.py @@ -5,7 +5,7 @@ from dataclasses import dataclass from typing import TYPE_CHECKING, Sequence if TYPE_CHECKING: - from beekeepy._interface.url import HttpUrl + from beekeepy._communication.url import HttpUrl from beekeepy.exceptions import Json, OverseerError diff --git a/beekeepy/beekeepy/_communication/aiohttp_communicator.py b/beekeepy/beekeepy/_communication/aiohttp_communicator.py index f20e9442..07095537 100644 --- a/beekeepy/beekeepy/_communication/aiohttp_communicator.py +++ b/beekeepy/beekeepy/_communication/aiohttp_communicator.py @@ -13,8 +13,8 @@ from beekeepy.exceptions import CommunicationError, UnknownDecisionPathError if TYPE_CHECKING: from beekeepy._communication.settings import CommunicationSettings - from beekeepy._interface.stopwatch import StopwatchResult - from beekeepy._interface.url import HttpUrl + from beekeepy._communication.url import HttpUrl + from beekeepy._utilities.stopwatch import StopwatchResult class AioHttpCommunicator(AbstractCommunicator): diff --git a/beekeepy/beekeepy/_communication/httpx_communicator.py b/beekeepy/beekeepy/_communication/httpx_communicator.py index 30aaf85b..2162cc4b 100644 --- a/beekeepy/beekeepy/_communication/httpx_communicator.py +++ b/beekeepy/beekeepy/_communication/httpx_communicator.py @@ -11,8 +11,8 @@ from beekeepy.exceptions import CommunicationError if TYPE_CHECKING: from beekeepy._communication.settings import CommunicationSettings - from beekeepy._interface.stopwatch import StopwatchResult - from beekeepy._interface.url import HttpUrl + from beekeepy._communication.url import HttpUrl + from beekeepy._utilities.stopwatch import StopwatchResult ClientTypes = httpx.AsyncClient | httpx.Client diff --git a/beekeepy/beekeepy/_communication/request_communicator.py b/beekeepy/beekeepy/_communication/request_communicator.py index 258b494b..71b73d06 100644 --- a/beekeepy/beekeepy/_communication/request_communicator.py +++ b/beekeepy/beekeepy/_communication/request_communicator.py @@ -11,8 +11,8 @@ from beekeepy.exceptions import CommunicationError if TYPE_CHECKING: from beekeepy._communication.settings import CommunicationSettings - from beekeepy._interface.stopwatch import StopwatchResult - from beekeepy._interface.url import HttpUrl + from beekeepy._communication.url import HttpUrl + from beekeepy._utilities.stopwatch import StopwatchResult class RequestCommunicator(AbstractCommunicator): diff --git a/beekeepy/beekeepy/_communication/settings.py b/beekeepy/beekeepy/_communication/settings.py index ee6feaef..3a0d3a7c 100644 --- a/beekeepy/beekeepy/_communication/settings.py +++ b/beekeepy/beekeepy/_communication/settings.py @@ -6,7 +6,7 @@ from typing import TYPE_CHECKING, Any, ClassVar, cast from pydantic import BaseModel, Field -from beekeepy._interface.url import Url +from beekeepy._communication.url import Url if TYPE_CHECKING: from collections.abc import Callable diff --git a/beekeepy/beekeepy/_executable/arguments/__init__.py b/beekeepy/beekeepy/_executable/arguments/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/beekeepy/beekeepy/_executable/arguments/beekeeper_arguments.py b/beekeepy/beekeepy/_executable/arguments/beekeeper_arguments.py deleted file mode 100644 index 525fde4f..00000000 --- a/beekeepy/beekeepy/_executable/arguments/beekeeper_arguments.py +++ /dev/null @@ -1,46 +0,0 @@ -from __future__ import annotations - -from dataclasses import dataclass -from pathlib import Path -from typing import Any, ClassVar, Literal - -from beekeepy._executable.arguments.arguments import Arguments -from beekeepy._interface.url import HttpUrl - - -class BeekeeperArgumentsDefaults: - DEFAULT_BACKTRACE: ClassVar[Literal["yes", "no"]] = "yes" - DEFAULT_DATA_DIR: ClassVar[Path] = Path.cwd() - DEFAULT_EXPORT_KEYS_WALLET: ClassVar[ExportKeysWalletParams | None] = None - DEFAULT_LOG_JSON_RPC: ClassVar[Path | None] = None - DEFAULT_NOTIFICATIONS_ENDPOINT: ClassVar[HttpUrl | None] = None - DEFAULT_UNLOCK_TIMEOUT: ClassVar[int] = 900 - DEFAULT_UNLOCK_INTERVAL: ClassVar[int] = 500 - DEFAULT_WALLET_DIR: ClassVar[Path] = Path.cwd() - DEFAULT_WEBSERVER_THREAD_POOL_SIZE: ClassVar[int] = 32 - DEFAULT_WEBSERVER_HTTP_ENDPOINT: ClassVar[HttpUrl | None] = None - - -@dataclass -class ExportKeysWalletParams: - wallet_name: str - wallet_password: str - - -class BeekeeperArguments(Arguments): - backtrace: Literal["yes", "no"] | None = BeekeeperArgumentsDefaults.DEFAULT_BACKTRACE - data_dir: Path = BeekeeperArgumentsDefaults.DEFAULT_DATA_DIR - export_keys_wallet: ExportKeysWalletParams | None = BeekeeperArgumentsDefaults.DEFAULT_EXPORT_KEYS_WALLET - log_json_rpc: Path | None = BeekeeperArgumentsDefaults.DEFAULT_LOG_JSON_RPC - notifications_endpoint: HttpUrl | None = BeekeeperArgumentsDefaults.DEFAULT_NOTIFICATIONS_ENDPOINT - unlock_timeout: int | None = BeekeeperArgumentsDefaults.DEFAULT_UNLOCK_TIMEOUT - wallet_dir: Path | None = BeekeeperArgumentsDefaults.DEFAULT_WALLET_DIR - webserver_thread_pool_size: int | None = BeekeeperArgumentsDefaults.DEFAULT_WEBSERVER_THREAD_POOL_SIZE - webserver_http_endpoint: HttpUrl | None = BeekeeperArgumentsDefaults.DEFAULT_WEBSERVER_HTTP_ENDPOINT - - def _convert_member_value_to_string_default(self, member_value: Any) -> str | Any: - if isinstance(member_value, HttpUrl): - return member_value.as_string(with_protocol=False) - if isinstance(member_value, ExportKeysWalletParams): - return f'["{member_value.wallet_name}","{member_value.wallet_password}"]' - return member_value diff --git a/beekeepy/beekeepy/_executable/beekeeper_config.py b/beekeepy/beekeepy/_executable/beekeeper_config.py index 3aea8726..8dd7fdac 100644 --- a/beekeepy/beekeepy/_executable/beekeeper_config.py +++ b/beekeepy/beekeepy/_executable/beekeeper_config.py @@ -4,9 +4,9 @@ from pathlib import Path # noqa: TCH003 from pydantic import Field +from beekeepy._communication import HttpUrl, WsUrl +from beekeepy._executable.abc.config import Config from beekeepy._executable.defaults import BeekeeperDefaults, ExportKeysWalletParams -from beekeepy._interface.config import Config -from beekeepy._interface.url import HttpUrl, WsUrl def http_webserver_default() -> HttpUrl: diff --git a/beekeepy/beekeepy/_executable/beekeeper_executable.py b/beekeepy/beekeepy/_executable/beekeeper_executable.py index 39e94765..e710c947 100644 --- a/beekeepy/beekeepy/_executable/beekeeper_executable.py +++ b/beekeepy/beekeepy/_executable/beekeeper_executable.py @@ -6,23 +6,19 @@ import tempfile from pathlib import Path from typing import TYPE_CHECKING -from beekeepy._executable.arguments.beekeeper_arguments import BeekeeperArguments, ExportKeysWalletParams +from beekeepy._executable.abc.executable import AutoCloser, Executable +from beekeepy._executable.beekeeper_arguments import BeekeeperArguments, ExportKeysWalletParams from beekeepy._executable.beekeeper_config import BeekeeperConfig from beekeepy._executable.beekeeper_executable_discovery import get_beekeeper_binary_path -from beekeepy._executable.executable import Executable -from beekeepy._interface.key_pair import KeyPair -from beekeepy._interface.url import HttpUrl -from beekeepy._runnable_handle.settings import Settings +from beekeepy._utilities.key_pair import KeyPair if TYPE_CHECKING: from loguru import Logger class BeekeeperExecutable(Executable[BeekeeperConfig, BeekeeperArguments]): - def __init__(self, settings: Settings, logger: Logger) -> None: - super().__init__( - settings.binary_path or get_beekeeper_binary_path(), settings.ensured_working_directory, logger - ) + def __init__(self, executable_path: Path | None, working_directory: Path, logger: Logger) -> None: + super().__init__(executable_path or get_beekeeper_binary_path(), working_directory, logger) def _construct_config(self) -> BeekeeperConfig: config = BeekeeperConfig(wallet_dir=self.working_directory) @@ -40,16 +36,19 @@ class BeekeeperExecutable(Executable[BeekeeperConfig, BeekeeperArguments]): wallet_file_name = f"{wallet_name}.wallet" shutil.copyfile(self.working_directory / wallet_file_name, tempdir_path / wallet_file_name) bk = BeekeeperExecutable( - settings=Settings(binary_path=get_beekeeper_binary_path(), working_directory=self.working_directory), + executable_path=self.executable_path, + working_directory=self.working_directory, logger=self._logger, ) - bk.run( - blocking=True, - arguments=BeekeeperArguments( + with bk.restore_arguments( + BeekeeperArguments( data_dir=tempdir_path, export_keys_wallet=ExportKeysWalletParams(wallet_name=wallet_name, wallet_password=wallet_password), - ), - ) + ) + ): + bk.run( + blocking=True, + ) keys_path = bk.working_directory / f"{wallet_name}.keys" if extract_to is not None: @@ -58,3 +57,12 @@ class BeekeeperExecutable(Executable[BeekeeperConfig, BeekeeperArguments]): with keys_path.open("r") as file: return [KeyPair(**obj) for obj in json.load(file)] + + def run( + self, + *, + blocking: bool, + environ: dict[str, str] | None = None, + propagate_sigint: bool = True, + ) -> AutoCloser: + return self._run(blocking=blocking, environ=environ, propagate_sigint=propagate_sigint) diff --git a/beekeepy/beekeepy/_executable/defaults.py b/beekeepy/beekeepy/_executable/defaults.py index 955fcf0a..894b2a37 100644 --- a/beekeepy/beekeepy/_executable/defaults.py +++ b/beekeepy/beekeepy/_executable/defaults.py @@ -6,7 +6,7 @@ from typing import ClassVar from pydantic import BaseModel -from beekeepy._interface.url import HttpUrl # noqa: TCH001 +from beekeepy._communication import HttpUrl # noqa: TCH001 @dataclass diff --git a/beekeepy/beekeepy/_interface/__init__.py b/beekeepy/beekeepy/_interface/__init__.py index e69de29b..9802b2ef 100644 --- a/beekeepy/beekeepy/_interface/__init__.py +++ b/beekeepy/beekeepy/_interface/__init__.py @@ -0,0 +1,22 @@ +from __future__ import annotations + +from beekeepy._interface import abc +from beekeepy._interface.asynchronous.beekeeper import Beekeeper as AsyncBeekeper +from beekeepy._interface.asynchronous.session import Session as AsyncSession +from beekeepy._interface.asynchronous.wallet import UnlockedWallet as AsyncUnlockedWallet +from beekeepy._interface.asynchronous.wallet import Wallet as AsyncWallet +from beekeepy._interface.synchronous.beekeeper import Beekeeper +from beekeepy._interface.synchronous.session import Session +from beekeepy._interface.synchronous.wallet import UnlockedWallet, Wallet + +__all__ = [ + "abc", + "AsyncBeekeper", + "AsyncSession", + "AsyncUnlockedWallet", + "AsyncWallet", + "Beekeeper", + "Session", + "UnlockedWallet", + "Wallet", +] diff --git a/beekeepy/beekeepy/_interface/abc/__init__.py b/beekeepy/beekeepy/_interface/abc/__init__.py index e69de29b..b73bcd06 100644 --- a/beekeepy/beekeepy/_interface/abc/__init__.py +++ b/beekeepy/beekeepy/_interface/abc/__init__.py @@ -0,0 +1,23 @@ +from __future__ import annotations + +from beekeepy._interface.abc.asynchronous.beekeeper import Beekeeper as AsyncBeekeeper +from beekeepy._interface.abc.asynchronous.session import Session as AsyncSession +from beekeepy._interface.abc.asynchronous.wallet import UnlockedWallet as AsyncUnlockedWallet +from beekeepy._interface.abc.asynchronous.wallet import Wallet as AsyncWallet +from beekeepy._interface.abc.packed_object import PackedAsyncBeekeeper, PackedSyncBeekeeper +from beekeepy._interface.abc.synchronous.beekeeper import Beekeeper +from beekeepy._interface.abc.synchronous.session import Session +from beekeepy._interface.abc.synchronous.wallet import UnlockedWallet, Wallet + +__all__ = [ + "AsyncBeekeeper", + "AsyncSession", + "AsyncUnlockedWallet", + "AsyncWallet", + "Beekeeper", + "PackedAsyncBeekeeper", + "PackedSyncBeekeeper", + "Session", + "UnlockedWallet", + "Wallet", +] diff --git a/beekeepy/beekeepy/_interface/abc/asynchronous/beekeeper.py b/beekeepy/beekeepy/_interface/abc/asynchronous/beekeeper.py index f1509340..ddda2365 100644 --- a/beekeepy/beekeepy/_interface/abc/asynchronous/beekeeper.py +++ b/beekeepy/beekeepy/_interface/abc/asynchronous/beekeeper.py @@ -3,15 +3,15 @@ from __future__ import annotations from abc import ABC, abstractmethod from typing import TYPE_CHECKING, cast -from beekeepy._communication.settings import CommunicationSettings +from beekeepy._communication import CommunicationSettings from beekeepy._interface.context import ContextAsync -from beekeepy._interface.context_settings_updater import ContextSettingsUpdater -from beekeepy._runnable_handle.settings import Settings +from beekeepy._utilities.context import ContextAsync +from beekeepy._utilities.context_settings_updater import ContextSettingsUpdater if TYPE_CHECKING: + from beekeepy._communication import HttpUrl from beekeepy._interface.abc.asynchronous.session import Session from beekeepy._interface.abc.packed_object import PackedAsyncBeekeeper - from beekeepy._interface.url import HttpUrl class Beekeeper(ContextAsync["Beekeeper"], ContextSettingsUpdater[CommunicationSettings], ABC): diff --git a/beekeepy/beekeepy/_interface/abc/asynchronous/session.py b/beekeepy/beekeepy/_interface/abc/asynchronous/session.py index befad86f..8d2c2e93 100644 --- a/beekeepy/beekeepy/_interface/abc/asynchronous/session.py +++ b/beekeepy/beekeepy/_interface/abc/asynchronous/session.py @@ -3,7 +3,7 @@ from __future__ import annotations from abc import ABC, abstractmethod from typing import TYPE_CHECKING, TypeAlias, overload -from beekeepy._interface.context import ContextAsync +from beekeepy._utilities.context import ContextAsync if TYPE_CHECKING: from beekeepy._interface.abc.asynchronous.wallet import UnlockedWallet, Wallet diff --git a/beekeepy/beekeepy/_interface/abc/asynchronous/wallet.py b/beekeepy/beekeepy/_interface/abc/asynchronous/wallet.py index f251972e..27726dde 100644 --- a/beekeepy/beekeepy/_interface/abc/asynchronous/wallet.py +++ b/beekeepy/beekeepy/_interface/abc/asynchronous/wallet.py @@ -3,8 +3,8 @@ from __future__ import annotations from abc import ABC, abstractmethod from typing import TYPE_CHECKING -from beekeepy._interface.context import ContextAsync from beekeepy._interface.wallets_common import ContainsWalletName +from beekeepy._utilities.context import ContextAsync if TYPE_CHECKING: from datetime import datetime diff --git a/beekeepy/beekeepy/_interface/abc/packed_object.py b/beekeepy/beekeepy/_interface/abc/packed_object.py index c92f7ddb..5ba364b8 100644 --- a/beekeepy/beekeepy/_interface/abc/packed_object.py +++ b/beekeepy/beekeepy/_interface/abc/packed_object.py @@ -2,17 +2,17 @@ from __future__ import annotations from typing import TYPE_CHECKING, Generic, Protocol, TypeVar -from beekeepy._interface.settings_holder import UniqueSettingsHolder -from beekeepy._runnable_handle.settings import Settings +from beekeepy._interface.settings import InterfaceSettings +from beekeepy._utilities.settings_holder import UniqueSettingsHolder if TYPE_CHECKING: + from beekeepy._communication import HttpUrl from beekeepy._interface.abc.asynchronous.beekeeper import ( Beekeeper as AsynchronousBeekeeperInterface, ) from beekeepy._interface.abc.synchronous.beekeeper import ( Beekeeper as SynchronousBeekeeperInterface, ) - from beekeepy._interface.url import HttpUrl __all__ = [ "PackedSyncBeekeeper", @@ -21,27 +21,22 @@ __all__ = [ class _SyncRemoteFactoryCallable(Protocol): - def __call__(self, *, url_or_settings: HttpUrl | Settings) -> SynchronousBeekeeperInterface: ... + def __call__(self, *, url_or_settings: HttpUrl | InterfaceSettings) -> SynchronousBeekeeperInterface: ... class _AsyncRemoteFactoryCallable(Protocol): - async def __call__(self, *, url_or_settings: HttpUrl | Settings) -> AsynchronousBeekeeperInterface: ... + async def __call__(self, *, url_or_settings: HttpUrl | InterfaceSettings) -> AsynchronousBeekeeperInterface: ... FactoryT = TypeVar("FactoryT", bound=_SyncRemoteFactoryCallable | _AsyncRemoteFactoryCallable) -class Packed(UniqueSettingsHolder[Settings], Generic[FactoryT]): +class Packed(UniqueSettingsHolder[InterfaceSettings], Generic[FactoryT]): """Allows to transfer beekeeper handle to other process.""" - def __init__(self, settings: Settings, unpack_factory: FactoryT) -> None: + def __init__(self, settings: InterfaceSettings, unpack_factory: FactoryT) -> None: super().__init__(settings=settings) self._unpack_factory = unpack_factory - self._prepare_settings_for_packing() - - def _prepare_settings_for_packing(self) -> None: - with self.update_settings() as settings: - settings.notification_endpoint = None class PackedSyncBeekeeper(Packed[_SyncRemoteFactoryCallable]): diff --git a/beekeepy/beekeepy/_interface/abc/synchronous/beekeeper.py b/beekeepy/beekeepy/_interface/abc/synchronous/beekeeper.py index 22febafd..ac6dc8ed 100644 --- a/beekeepy/beekeepy/_interface/abc/synchronous/beekeeper.py +++ b/beekeepy/beekeepy/_interface/abc/synchronous/beekeeper.py @@ -3,15 +3,15 @@ from __future__ import annotations from abc import ABC, abstractmethod from typing import TYPE_CHECKING, cast -from beekeepy._communication.settings import CommunicationSettings +from beekeepy._communication import CommunicationSettings from beekeepy._interface.context import ContextSync -from beekeepy._interface.context_settings_updater import ContextSettingsUpdater -from beekeepy._runnable_handle.settings import Settings +from beekeepy._utilities.context import ContextSync +from beekeepy._utilities.context_settings_updater import ContextSettingsUpdater if TYPE_CHECKING: + from beekeepy._communication import HttpUrl from beekeepy._interface.abc.packed_object import PackedSyncBeekeeper from beekeepy._interface.abc.synchronous.session import Session - from beekeepy._interface.url import HttpUrl class Beekeeper(ContextSync["Beekeeper"], ContextSettingsUpdater[CommunicationSettings], ABC): diff --git a/beekeepy/beekeepy/_interface/abc/synchronous/session.py b/beekeepy/beekeepy/_interface/abc/synchronous/session.py index 458b9640..2292259b 100644 --- a/beekeepy/beekeepy/_interface/abc/synchronous/session.py +++ b/beekeepy/beekeepy/_interface/abc/synchronous/session.py @@ -3,7 +3,7 @@ from __future__ import annotations from abc import ABC, abstractmethod from typing import TYPE_CHECKING, TypeAlias, overload -from beekeepy._interface.context import ContextSync +from beekeepy._utilities.context import ContextSync if TYPE_CHECKING: from beekeepy._interface.abc.synchronous.wallet import UnlockedWallet, Wallet diff --git a/beekeepy/beekeepy/_interface/abc/synchronous/wallet.py b/beekeepy/beekeepy/_interface/abc/synchronous/wallet.py index 31f07bec..51895086 100644 --- a/beekeepy/beekeepy/_interface/abc/synchronous/wallet.py +++ b/beekeepy/beekeepy/_interface/abc/synchronous/wallet.py @@ -3,8 +3,8 @@ from __future__ import annotations from abc import ABC, abstractmethod from typing import TYPE_CHECKING -from beekeepy._interface.context import ContextSync from beekeepy._interface.wallets_common import ContainsWalletName +from beekeepy._utilities.context import ContextSync from schemas.fields.basic import PublicKey if TYPE_CHECKING: diff --git a/beekeepy/beekeepy/_interface/asynchronous/beekeeper.py b/beekeepy/beekeepy/_interface/asynchronous/beekeeper.py index 799922a0..b54837bb 100644 --- a/beekeepy/beekeepy/_interface/asynchronous/beekeeper.py +++ b/beekeepy/beekeepy/_interface/asynchronous/beekeeper.py @@ -10,8 +10,8 @@ from beekeepy._interface.asynchronous.session import Session from beekeepy._interface.delay_guard import AsyncDelayGuard from beekeepy._interface.state_invalidator import StateInvalidator from beekeepy._remote_handle.beekeeper import AsyncBeekeeper as AsynchronousRemoteBeekeeperHandle -from beekeepy._runnable_handle.beekeeper import AsyncBeekeeper as AsynchronousBeekeeperHandle -from beekeepy._runnable_handle.settings import Settings +from beekeepy._utilities.delay_guard import AsyncDelayGuard +from beekeepy._utilities.state_invalidator import StateInvalidator from beekeepy.exceptions import ( DetachRemoteBeekeeperError, InvalidatedStateByClosingBeekeeperError, @@ -19,11 +19,10 @@ from beekeepy.exceptions import ( ) if TYPE_CHECKING: - from beekeepy._communication.settings import CommunicationSettings + from beekeepy._communication import CommunicationSettings, HttpUrl from beekeepy._interface.abc.asynchronous.session import ( Session as SessionInterface, ) - from beekeepy._interface.url import HttpUrl class Beekeeper(BeekeeperInterface, StateInvalidator): diff --git a/beekeepy/beekeepy/_interface/asynchronous/session.py b/beekeepy/beekeepy/_interface/asynchronous/session.py index 6e808935..35e3704e 100644 --- a/beekeepy/beekeepy/_interface/asynchronous/session.py +++ b/beekeepy/beekeepy/_interface/asynchronous/session.py @@ -9,8 +9,8 @@ from beekeepy._interface.asynchronous.wallet import ( UnlockedWallet, Wallet, ) -from beekeepy._interface.state_invalidator import StateInvalidator from beekeepy._interface.validators import validate_digest, validate_public_keys, validate_timeout +from beekeepy._utilities.state_invalidator import StateInvalidator from beekeepy.exceptions import ( InvalidatedStateByClosingSessionError, InvalidWalletError, @@ -29,6 +29,7 @@ if TYPE_CHECKING: ) from beekeepy._interface.delay_guard import AsyncDelayGuard from beekeepy._remote_handle.beekeeper import AsyncBeekeeper as AsynchronousRemoteBeekeeperHandle + from beekeepy._utilities.delay_guard import AsyncDelayGuard from schemas.apis.beekeeper_api import GetInfo from schemas.fields.basic import PublicKey from schemas.fields.hex import Signature diff --git a/beekeepy/beekeepy/_interface/asynchronous/wallet.py b/beekeepy/beekeepy/_interface/asynchronous/wallet.py index 761cb085..5cfac6dc 100644 --- a/beekeepy/beekeepy/_interface/asynchronous/wallet.py +++ b/beekeepy/beekeepy/_interface/asynchronous/wallet.py @@ -13,6 +13,8 @@ from beekeepy._interface.validators import validate_digest, validate_private_key from beekeepy._interface.wallets_common import WalletCommons from beekeepy._remote_handle.beekeeper import AsyncBeekeeper as AsyncRemoteBeekeeper from beekeepy._runnable_handle.callbacks_protocol import AsyncWalletLocked +from beekeepy._runnable_handle import AsyncWalletLocked +from beekeepy._utilities.delay_guard import AsyncDelayGuard from beekeepy.exceptions import ( InvalidPasswordError, InvalidPrivateKeyError, diff --git a/beekeepy/beekeepy/_interface/synchronous/beekeeper.py b/beekeepy/beekeepy/_interface/synchronous/beekeeper.py index 3761fe38..d6042276 100644 --- a/beekeepy/beekeepy/_interface/synchronous/beekeeper.py +++ b/beekeepy/beekeepy/_interface/synchronous/beekeeper.py @@ -12,6 +12,8 @@ from beekeepy._interface.synchronous.session import Session from beekeepy._remote_handle.beekeeper import Beekeeper as SynchronousRemoteBeekeeperHandle from beekeepy._runnable_handle.beekeeper import Beekeeper as SynchronousBeekeeperHandle from beekeepy._runnable_handle.settings import Settings +from beekeepy._utilities.delay_guard import SyncDelayGuard +from beekeepy._utilities.state_invalidator import StateInvalidator from beekeepy.exceptions import ( DetachRemoteBeekeeperError, InvalidatedStateByClosingBeekeeperError, @@ -19,11 +21,10 @@ from beekeepy.exceptions import ( ) if TYPE_CHECKING: - from beekeepy._communication.settings import CommunicationSettings + from beekeepy._communication import CommunicationSettings, HttpUrl from beekeepy._interface.abc.synchronous.session import ( Session as SessionInterface, ) - from beekeepy._interface.url import HttpUrl class Beekeeper(BeekeeperInterface, StateInvalidator): diff --git a/beekeepy/beekeepy/_interface/synchronous/session.py b/beekeepy/beekeepy/_interface/synchronous/session.py index 3e616a05..9fab6de8 100644 --- a/beekeepy/beekeepy/_interface/synchronous/session.py +++ b/beekeepy/beekeepy/_interface/synchronous/session.py @@ -4,12 +4,12 @@ from typing import TYPE_CHECKING, Any, Callable from beekeepy._interface.abc.synchronous.session import Password from beekeepy._interface.abc.synchronous.session import Session as SessionInterface -from beekeepy._interface.state_invalidator import StateInvalidator from beekeepy._interface.synchronous.wallet import ( UnlockedWallet, Wallet, ) from beekeepy._interface.validators import validate_digest, validate_public_keys, validate_timeout +from beekeepy._utilities.state_invalidator import StateInvalidator from beekeepy.exceptions import ( InvalidatedStateByClosingSessionError, InvalidWalletError, @@ -28,6 +28,7 @@ if TYPE_CHECKING: ) from beekeepy._interface.delay_guard import SyncDelayGuard from beekeepy._remote_handle.beekeeper import Beekeeper as SyncRemoteBeekeeper + from beekeepy._utilities.delay_guard import SyncDelayGuard from schemas.apis.beekeeper_api import GetInfo from schemas.fields.basic import PublicKey from schemas.fields.hex import Signature diff --git a/beekeepy/beekeepy/_interface/synchronous/wallet.py b/beekeepy/beekeepy/_interface/synchronous/wallet.py index d23ae316..5fbbb202 100644 --- a/beekeepy/beekeepy/_interface/synchronous/wallet.py +++ b/beekeepy/beekeepy/_interface/synchronous/wallet.py @@ -13,6 +13,8 @@ from beekeepy._interface.validators import validate_private_keys, validate_publi from beekeepy._interface.wallets_common import WalletCommons from beekeepy._remote_handle.beekeeper import Beekeeper as SyncRemoteBeekeeper from beekeepy._runnable_handle.callbacks_protocol import SyncWalletLocked +from beekeepy._runnable_handle import SyncWalletLocked +from beekeepy._utilities.delay_guard import SyncDelayGuard from beekeepy.exceptions import ( InvalidPasswordError, InvalidPrivateKeyError, diff --git a/beekeepy/beekeepy/_interface/wallets_common.py b/beekeepy/beekeepy/_interface/wallets_common.py index 2750323c..328655f4 100644 --- a/beekeepy/beekeepy/_interface/wallets_common.py +++ b/beekeepy/beekeepy/_interface/wallets_common.py @@ -6,11 +6,12 @@ from asyncio import iscoroutinefunction from functools import wraps from typing import TYPE_CHECKING, Any, Generic, NoReturn, ParamSpec, TypeVar, overload -from beekeepy._interface.delay_guard import AsyncDelayGuard, SyncDelayGuard -from beekeepy._interface.state_invalidator import StateInvalidator -from beekeepy._remote_handle.beekeeper import AsyncBeekeeper as AsyncRemoteBeekeeper -from beekeepy._remote_handle.beekeeper import Beekeeper as SyncRemoteBeekeeper -from beekeepy._runnable_handle.callbacks_protocol import AsyncWalletLocked, SyncWalletLocked +from beekeepy._interface.settings import InterfaceSettings +from beekeepy._remote_handle import AsyncBeekeeperTemplate as AsyncRemoteBeekeeper +from beekeepy._remote_handle import BeekeeperTemplate as SyncRemoteBeekeeper +from beekeepy._runnable_handle import AsyncWalletLocked, SyncWalletLocked +from beekeepy._utilities.delay_guard import AsyncDelayGuard, SyncDelayGuard +from beekeepy._utilities.state_invalidator import StateInvalidator from beekeepy.exceptions import WalletIsLockedError if TYPE_CHECKING: @@ -20,7 +21,9 @@ if TYPE_CHECKING: P = ParamSpec("P") ResultT = TypeVar("ResultT") -BeekeeperT = TypeVar("BeekeeperT", bound=SyncRemoteBeekeeper | AsyncRemoteBeekeeper) +BeekeeperT = TypeVar( + "BeekeeperT", bound=SyncRemoteBeekeeper[InterfaceSettings] | AsyncRemoteBeekeeper[InterfaceSettings] +) CallbackT = TypeVar("CallbackT", bound=AsyncWalletLocked | SyncWalletLocked) GuardT = TypeVar("GuardT", bound=SyncDelayGuard | AsyncDelayGuard) @@ -132,7 +135,9 @@ class WalletCommons(ContainsWalletName, StateInvalidator, Generic[BeekeeperT, Ca @wraps(wrapped_function) async def async_impl(*args: P.args, **kwrags: P.kwargs) -> ResultT: - this: WalletCommons[AsyncRemoteBeekeeper, AsyncWalletLocked, AsyncDelayGuard] = args[0] # type: ignore[assignment] + this: WalletCommons[AsyncRemoteBeekeeper[InterfaceSettings], AsyncWalletLocked, AsyncDelayGuard] = args[ + 0 + ] # type: ignore[assignment] await this._async_call_callback_if_locked(wallet_name=this.name, token=this.session_token) return await wrapped_function(*args, **kwrags) # type: ignore[no-any-return] @@ -140,7 +145,7 @@ class WalletCommons(ContainsWalletName, StateInvalidator, Generic[BeekeeperT, Ca @wraps(wrapped_function) def sync_impl(*args: P.args, **kwrags: P.kwargs) -> ResultT: - this: WalletCommons[SyncRemoteBeekeeper, SyncWalletLocked, SyncDelayGuard] = args[0] # type: ignore[assignment] + this: WalletCommons[SyncRemoteBeekeeper[InterfaceSettings], SyncWalletLocked, SyncDelayGuard] = args[0] # type: ignore[assignment] this._sync_call_callback_if_locked(wallet_name=this.name, token=this.session_token) return wrapped_function(*args, **kwrags) # type: ignore[return-value] diff --git a/beekeepy/beekeepy/_remote_handle/__init__.py b/beekeepy/beekeepy/_remote_handle/__init__.py index e69de29b..e8d89f4d 100644 --- a/beekeepy/beekeepy/_remote_handle/__init__.py +++ b/beekeepy/beekeepy/_remote_handle/__init__.py @@ -0,0 +1,30 @@ +from __future__ import annotations + +from beekeepy._remote_handle.abc.batch_handle import ApiFactory, AsyncBatchHandle, SyncBatchHandle +from beekeepy._remote_handle.abc.handle import AbstractAsyncHandle, AbstractSyncHandle +from beekeepy._remote_handle.app_status_probe import AppStatusProbe +from beekeepy._remote_handle.beekeeper import AsyncBeekeeper as AsyncBeekeeperTemplate +from beekeepy._remote_handle.beekeeper import Beekeeper as BeekeeperTemplate +from beekeepy._remote_handle.beekeeper import _AsyncSessionBatchHandle as AsyncBeekeeprBatchHandle +from beekeepy._remote_handle.beekeeper import _SyncSessionBatchHandle as SyncBeekeeprBatchHandle +from beekeepy._remote_handle.settings import RemoteHandleSettings + +AsyncBeekeeper = AsyncBeekeeperTemplate[RemoteHandleSettings] +Beekeeper = BeekeeperTemplate[RemoteHandleSettings] + +__all__ = [ + "AbstractAsyncHandle", + "AbstractSyncHandle", + "ApiFactory", + "AppStatusProbe", + "AsyncBatchHandle", + "AsyncBeekeeper", + "AsyncBeekeeperTemplate", + "AsyncBeekeeprBatchHandle", + "Beekeeper", + "BeekeeperTemplate", + "RemoteHandleSettings", + "SyncBatchHandle", + "SyncBatchHandle", + "SyncBeekeeprBatchHandle", +] diff --git a/beekeepy/beekeepy/_remote_handle/abc/batch_handle.py b/beekeepy/beekeepy/_remote_handle/abc/batch_handle.py index a8e364f4..e0ad80a1 100644 --- a/beekeepy/beekeepy/_remote_handle/abc/batch_handle.py +++ b/beekeepy/beekeepy/_remote_handle/abc/batch_handle.py @@ -6,8 +6,9 @@ from dataclasses import dataclass from typing import TYPE_CHECKING, Any, Generic, TypeVar from beekeepy import exceptions -from beekeepy._interface.context import ContextAsync, ContextSync, EnterReturnT -from beekeepy._remote_handle.build_json_rpc_call import build_json_rpc_call +from beekeepy._apis.abc import AsyncSendable, SyncSendable +from beekeepy._utilities.build_json_rpc_call import build_json_rpc_call +from beekeepy._utilities.context import ContextAsync, ContextSync, EnterReturnT from schemas.jsonrpc import ExpectResultT, JSONRPCResult, get_response_model if TYPE_CHECKING: @@ -15,8 +16,7 @@ if TYPE_CHECKING: from typing_extensions import Self - from beekeepy._communication.abc.overseer import AbstractOverseer - from beekeepy._interface.url import HttpUrl + from beekeepy._communication import AbstractOverseer, HttpUrl class _DelayedResponseWrapper: @@ -207,7 +207,7 @@ OwnerT = TypeVar("OwnerT") ApiFactory = Callable[[OwnerT], ApiT] -class SyncBatchHandle(_BatchHandle["SyncBatchHandle"], Generic[ApiT]): # type: ignore[type-arg] +class SyncBatchHandle(_BatchHandle["SyncBatchHandle"], SyncSendable, Generic[ApiT]): # type: ignore[type-arg] def __init__( self, url: HttpUrl, @@ -224,7 +224,7 @@ class SyncBatchHandle(_BatchHandle["SyncBatchHandle"], Generic[ApiT]): # type: return self._impl_handle_request(endpoint, params, expect_type=expected_type) # type: ignore[arg-type] -class AsyncBatchHandle(_BatchHandle["AsyncBatchHandle"], Generic[ApiT]): # type: ignore[type-arg] +class AsyncBatchHandle(_BatchHandle["AsyncBatchHandle"], AsyncSendable, Generic[ApiT]): # type: ignore[type-arg] def __init__( self, url: HttpUrl, diff --git a/beekeepy/beekeepy/_remote_handle/abc/handle.py b/beekeepy/beekeepy/_remote_handle/abc/handle.py index dc77ea88..3005e4f1 100644 --- a/beekeepy/beekeepy/_remote_handle/abc/handle.py +++ b/beekeepy/beekeepy/_remote_handle/abc/handle.py @@ -5,36 +5,40 @@ from typing import TYPE_CHECKING, Any, Generic, TypeVar from loguru import logger as loguru_logger -from beekeepy._communication.aiohttp_communicator import AioHttpCommunicator -from beekeepy._communication.request_communicator import RequestCommunicator -from beekeepy._interface.context import SelfContextAsync, SelfContextSync -from beekeepy._interface.settings_holder import UniqueSettingsHolder -from beekeepy._interface.stopwatch import Stopwatch -from beekeepy._remote_handle.build_json_rpc_call import build_json_rpc_call -from beekeepy._remote_handle.settings import Settings +from beekeepy._apis.abc import ( + AbstractAsyncApiCollection, + AbstractSyncApiCollection, +) +from beekeepy._apis.abc.sendable import AsyncSendable, SyncSendable +from beekeepy._communication import AioHttpCommunicator, RequestCommunicator +from beekeepy._remote_handle.settings import RemoteHandleSettings +from beekeepy._utilities.build_json_rpc_call import build_json_rpc_call +from beekeepy._utilities.context import SelfContextAsync, SelfContextSync +from beekeepy._utilities.settings_holder import UniqueSettingsHolder +from beekeepy._utilities.stopwatch import Stopwatch from beekeepy.exceptions import CommunicationError from schemas.jsonrpc import ExpectResultT, JSONRPCResult, get_response_model if TYPE_CHECKING: from loguru import Logger - from beekeepy._communication.abc.communicator import AbstractCommunicator - from beekeepy._communication.abc.overseer import AbstractOverseer - from beekeepy._interface.url import HttpUrl - from beekeepy._remote_handle.batch_handle import AsyncBatchHandle, SyncBatchHandle + from beekeepy._communication import AbstractCommunicator, AbstractOverseer, HttpUrl + from beekeepy._remote_handle.abc.batch_handle import AsyncBatchHandle, SyncBatchHandle from beekeepy.exceptions import Json +RemoteSettingsT = TypeVar("RemoteSettingsT", bound=RemoteHandleSettings) -ApiT = TypeVar("ApiT") +ApiT = TypeVar("ApiT", bound=AbstractAsyncApiCollection | AbstractSyncApiCollection) -class AbstractHandle(UniqueSettingsHolder[Settings], ABC, Generic[ApiT]): + +class AbstractHandle(UniqueSettingsHolder[RemoteSettingsT], ABC, Generic[RemoteSettingsT, ApiT]): """Provides basic interface for all network handles.""" def __init__( self, *args: Any, - settings: Settings, + settings: RemoteSettingsT, logger: Logger | None = None, **kwargs: Any, ) -> None: @@ -63,6 +67,10 @@ class AbstractHandle(UniqueSettingsHolder[Settings], ABC, Generic[ApiT]): with self.update_settings() as settings: settings.http_endpoint = value + @property + def apis(self) -> ApiT: + return self.__api + @property def api(self) -> ApiT: return self.__api @@ -138,14 +146,14 @@ class AbstractHandle(UniqueSettingsHolder[Settings], ABC, Generic[ApiT]): ) -class AbstractAsyncHandle(AbstractHandle[ApiT], SelfContextAsync, ABC): +class AbstractAsyncHandle(AbstractHandle[RemoteSettingsT, ApiT], SelfContextAsync, AsyncSendable, ABC): """Base class for service handlers that uses asynchronous communication.""" async def _async_send( self, *, endpoint: str, params: str, expected_type: type[ExpectResultT] ) -> JSONRPCResult[ExpectResultT]: """Sends data asynchronously to handled service basing on jsonrpc.""" - from beekeepy._interface.error_logger import ErrorLogger + from beekeepy._utilities.error_logger import ErrorLogger request = build_json_rpc_call(method=endpoint, params=params) self._log_request(request) @@ -168,12 +176,12 @@ class AbstractAsyncHandle(AbstractHandle[ApiT], SelfContextAsync, ABC): self.teardown() -class AbstractSyncHandle(AbstractHandle[ApiT], SelfContextSync, ABC): +class AbstractSyncHandle(AbstractHandle[RemoteSettingsT, ApiT], SelfContextSync, SyncSendable, ABC): """Base class for service handlers that uses synchronous communication.""" def _send(self, *, endpoint: str, params: str, expected_type: type[ExpectResultT]) -> JSONRPCResult[ExpectResultT]: """Sends data synchronously to handled service basing on jsonrpc.""" - from beekeepy._interface.error_logger import ErrorLogger + from beekeepy._utilities.error_logger import ErrorLogger request = build_json_rpc_call(method=endpoint, params=params) self._log_request(request) diff --git a/beekeepy/beekeepy/_remote_handle/beekeeper.py b/beekeepy/beekeepy/_remote_handle/beekeeper.py index cd21bb10..eac3915d 100644 --- a/beekeepy/beekeepy/_remote_handle/beekeeper.py +++ b/beekeepy/beekeepy/_remote_handle/beekeeper.py @@ -3,21 +3,24 @@ from __future__ import annotations import uuid from typing import TYPE_CHECKING, Any, NoReturn -from beekeepy._interface._sanitize import sanitize -from beekeepy._remote_handle.abc.handle import AbstractAsyncHandle, AbstractSyncHandle -from beekeepy._remote_handle.api.api_collection import ( +from beekeepy._apis import ( + AsyncBeekeeperApi, BeekeeperAsyncApiCollection, BeekeeperSyncApiCollection, + SyncBeekeeperApi, ) -from beekeepy._remote_handle.api.session_holder import AsyncSessionHolder, SyncSessionHolder -from beekeepy._remote_handle.batch_handle import ApiFactory, AsyncBatchHandle, SyncBatchHandle +from beekeepy._apis.abc import ( + AsyncSessionHolder, + SyncSessionHolder, +) +from beekeepy._remote_handle.abc.batch_handle import ApiFactory, AsyncBatchHandle, SyncBatchHandle +from beekeepy._remote_handle.abc.handle import AbstractAsyncHandle, AbstractSyncHandle, RemoteSettingsT +from beekeepy._utilities.sanitize import sanitize if TYPE_CHECKING: from typing_extensions import Self - from beekeepy._communication.abc.overseer import AbstractOverseer - from beekeepy._interface.url import HttpUrl - from beekeepy._remote_handle.api import AsyncBeekeeperApi, SyncBeekeeperApi + from beekeepy._communication import AbstractOverseer, HttpUrl from beekeepy.exceptions import Json _handle_target_service_name = "beekeeper" @@ -67,15 +70,19 @@ class _AsyncSessionBatchHandle(AsyncBatchHandle[BeekeeperAsyncApiCollection], As _raise_acquire_not_possible() -class Beekeeper(AbstractSyncHandle[BeekeeperSyncApiCollection], SyncSessionHolder): +class Beekeeper(AbstractSyncHandle[RemoteSettingsT, BeekeeperSyncApiCollection], SyncSessionHolder): """Synchronous handle for beekeeper service communication.""" def _construct_api(self) -> BeekeeperSyncApiCollection: return BeekeeperSyncApiCollection(owner=self) + @property + def apis(self) -> BeekeeperSyncApiCollection: + return super().api + @property def api(self) -> SyncBeekeeperApi: # type: ignore[override] - return super().api.beekeeper + return self.apis.beekeeper def _target_service(self) -> str: return _handle_target_service_name @@ -99,15 +106,19 @@ class Beekeeper(AbstractSyncHandle[BeekeeperSyncApiCollection], SyncSessionHolde return sanitize(data) -class AsyncBeekeeper(AbstractAsyncHandle[BeekeeperAsyncApiCollection], AsyncSessionHolder): +class AsyncBeekeeper(AbstractAsyncHandle[RemoteSettingsT, BeekeeperAsyncApiCollection], AsyncSessionHolder): """Asynchronous handle for beekeeper service communication.""" def _construct_api(self) -> BeekeeperAsyncApiCollection: return BeekeeperAsyncApiCollection(owner=self) + @property + def apis(self) -> BeekeeperAsyncApiCollection: + return super().api + @property def api(self) -> AsyncBeekeeperApi: # type: ignore[override] - return super().api.beekeeper + return self.apis.beekeeper def _target_service(self) -> str: return _handle_target_service_name diff --git a/beekeepy/beekeepy/_runnable_handle/__init__.py b/beekeepy/beekeepy/_runnable_handle/__init__.py index 3e4206d5..65e1c9a6 100644 --- a/beekeepy/beekeepy/_runnable_handle/__init__.py +++ b/beekeepy/beekeepy/_runnable_handle/__init__.py @@ -1,6 +1,21 @@ from __future__ import annotations -from beekeepy._runnable_handle.beekeeper import AsyncBeekeeper, Beekeeper -from beekeepy._runnable_handle.notification_handler_base import BeekeeperNotificationHandler +from beekeepy._runnable_handle.beekeeper import AsyncBeekeeper as AsyncBeekeeperTemplate +from beekeepy._runnable_handle.beekeeper import Beekeeper as BeekeeperTemplate +from beekeepy._runnable_handle.callbacks_protocol import AsyncWalletLocked, SyncWalletLocked +from beekeepy._runnable_handle.close_already_running_beekeeper import close_already_running_beekeeper +from beekeepy._runnable_handle.settings import Settings as RunnableHandleSettings -__all__ = ["AsyncBeekeeper", "Beekeeper", "BeekeeperNotificationHandler"] +AsyncBeekeeper = AsyncBeekeeperTemplate[RunnableHandleSettings] +Beekeeper = BeekeeperTemplate[RunnableHandleSettings] + +__all__ = [ + "AsyncBeekeeper", + "AsyncBeekeeperTemplate", + "AsyncWalletLocked", + "Beekeeper", + "BeekeeperTemplate", + "close_already_running_beekeeper", + "RunnableHandleSettings", + "SyncWalletLocked", +] diff --git a/beekeepy/beekeepy/_runnable_handle/settings.py b/beekeepy/beekeepy/_runnable_handle/settings.py index 7adf312c..1ee8b0c2 100644 --- a/beekeepy/beekeepy/_runnable_handle/settings.py +++ b/beekeepy/beekeepy/_runnable_handle/settings.py @@ -5,9 +5,8 @@ from distutils.util import strtobool from pathlib import Path from typing import ClassVar -from beekeepy._communication.abc.communicator import AbstractCommunicator # noqa: TCH001 -from beekeepy._interface.url import HttpUrl # noqa: TCH001 -from beekeepy._remote_handle.settings import Settings as RemoteHandleSettings +from beekeepy._communication import HttpUrl # noqa: TCH001 +from beekeepy._remote_handle import RemoteHandleSettings class Settings(RemoteHandleSettings): diff --git a/beekeepy/beekeepy/exceptions/base.py b/beekeepy/beekeepy/exceptions/base.py index d59b0d84..ba088f05 100644 --- a/beekeepy/beekeepy/exceptions/base.py +++ b/beekeepy/beekeepy/exceptions/base.py @@ -5,18 +5,18 @@ from typing import TYPE_CHECKING, Any from pydantic import StrRegexError -from beekeepy._interface.context import ContextSync +from beekeepy._utilities.context import ContextSync if TYPE_CHECKING: from types import TracebackType - from beekeepy._interface.url import Url + from beekeepy._communication import Url Json = dict[str, Any] CommunicationResponseT = str | Json | list[Json] -class BeekeepyError(Exception): +class BeekeepyError(BaseException, ABC): """Base class for all exception raised by beekeepy.""" @property diff --git a/beekeepy/beekeepy/exceptions/common.py b/beekeepy/beekeepy/exceptions/common.py index 29b3efd0..93bc622e 100644 --- a/beekeepy/beekeepy/exceptions/common.py +++ b/beekeepy/beekeepy/exceptions/common.py @@ -13,7 +13,7 @@ from beekeepy.exceptions.base import ( ) if TYPE_CHECKING: - from beekeepy._interface.url import Url + from beekeepy._communication import Url class BatchRequestError(BeekeepyError): @@ -59,14 +59,6 @@ class WalletIsLockedError(BeekeepyError): super().__init__(f"Wallet `{wallet_name}` is locked.") -class TimeoutReachWhileCloseError(BeekeepyError): - """Raises when beekeeper did not closed during specified timeout.""" - - def __init__(self) -> None: - """Constructor.""" - super().__init__("Process was force-closed with SIGKILL, because didn't close before timeout") - - class NotPositiveTimeError(BeekeepyError): """Raises when given time value was 0 or negative.""" diff --git a/beekeepy/beekeepy/exceptions/overseer.py b/beekeepy/beekeepy/exceptions/overseer.py index 1fdc616d..4007e825 100644 --- a/beekeepy/beekeepy/exceptions/overseer.py +++ b/beekeepy/beekeepy/exceptions/overseer.py @@ -5,7 +5,7 @@ from typing import TYPE_CHECKING, Any, Sequence from beekeepy.exceptions.base import BeekeepyError, CommunicationResponseT, Json, OverseerError if TYPE_CHECKING: - from beekeepy._interface.url import Url + from beekeepy._communication import Url class UnableToAcquireDatabaseLockError(OverseerError): @@ -110,7 +110,7 @@ class ErrorInResponseError(OverseerError): class GroupedErrorsError(BeekeepyError): - def __init__(self, exceptions: Sequence[Exception]) -> None: + def __init__(self, exceptions: Sequence[BaseException]) -> None: self.exceptions = list(exceptions) def get_exception_for(self, *, request_id: int) -> OverseerError | None: diff --git a/beekeepy/beekeepy/handle/remote.py b/beekeepy/beekeepy/handle/remote.py index e0a6bea4..e5330f99 100644 --- a/beekeepy/beekeepy/handle/remote.py +++ b/beekeepy/beekeepy/handle/remote.py @@ -1,11 +1,21 @@ from __future__ import annotations -from beekeepy._remote_handle.abc.api import AbstractAsyncApi, AbstractSyncApi, AsyncHandleT -from beekeepy._remote_handle.abc.api_collection import AbstractAsyncApiCollection, AbstractSyncApiCollection -from beekeepy._remote_handle.abc.handle import AbstractAsyncHandle, AbstractSyncHandle -from beekeepy._remote_handle.batch_handle import AsyncBatchHandle, SyncBatchHandle -from beekeepy._remote_handle.beekeeper import AsyncBeekeeper, Beekeeper -from beekeepy._remote_handle.settings import Settings as RemoteSettings +from beekeepy._apis.abc import ( + AbstractAsyncApi, + AbstractAsyncApiCollection, + AbstractSyncApi, + AbstractSyncApiCollection, + RegisteredApisT, +) +from beekeepy._remote_handle import ( + AbstractAsyncHandle, + AbstractSyncHandle, + AsyncBatchHandle, + AsyncBeekeeper, + Beekeeper, + RemoteHandleSettings, + SyncBatchHandle, +) __all__ = [ "AbstractAsyncApi", @@ -16,8 +26,8 @@ __all__ = [ "AbstractSyncHandle", "AsyncBatchHandle", "AsyncBeekeeper", - "AsyncHandleT", "Beekeeper", - "RemoteSettings", + "RegisteredApisT", + "RemoteHandleSettings", "SyncBatchHandle", ] diff --git a/beekeepy/beekeepy/handle/runnable.py b/beekeepy/beekeepy/handle/runnable.py index 0b2b567c..247ed9e6 100644 --- a/beekeepy/beekeepy/handle/runnable.py +++ b/beekeepy/beekeepy/handle/runnable.py @@ -1,27 +1,14 @@ from __future__ import annotations -from beekeepy._communication.appbase_notification_handler import AppbaseNotificationHandler -from beekeepy._communication.async_server import AsyncHttpServer -from beekeepy._communication.notification_decorator import notification -from beekeepy._communication.universal_notification_server import UniversalNotificationHandler -from beekeepy._executable.arguments.beekeeper_arguments import BeekeeperArguments, BeekeeperArgumentsDefaults -from beekeepy._executable.beekeeper_config import BeekeeperConfig -from beekeepy._runnable_handle.beekeeper import AsyncBeekeeper, Beekeeper -from beekeepy._runnable_handle.close_already_running_beekeeper import close_already_running_beekeeper -from beekeepy._runnable_handle.notification_handler_base import BeekeeperNotificationHandler -from beekeepy._runnable_handle.settings import Settings as RunnableSettings +from beekeepy._executable import BeekeeperArguments, BeekeeperConfig, BeekeeperExecutable +from beekeepy._runnable_handle import AsyncBeekeeper, Beekeeper, RunnableHandleSettings, close_already_running_beekeeper __all__ = [ - "AppbaseNotificationHandler", "AsyncBeekeeper", - "AsyncHttpServer", "Beekeeper", - "BeekeeperArguments", - "BeekeeperArgumentsDefaults", "BeekeeperConfig", - "BeekeeperNotificationHandler", + "BeekeeperArguments", + "BeekeeperExecutable", "close_already_running_beekeeper", - "notification", - "RunnableSettings", - "UniversalNotificationHandler", + "RunnableHandleSettings", ] diff --git a/beekeepy/beekeepy/interfaces.py b/beekeepy/beekeepy/interfaces.py index 65742fb8..4b137461 100644 --- a/beekeepy/beekeepy/interfaces.py +++ b/beekeepy/beekeepy/interfaces.py @@ -1,27 +1,22 @@ from __future__ import annotations -from beekeepy._interface._sanitize import mask, sanitize -from beekeepy._interface._suppress_api_not_found import SuppressApiNotFound -from beekeepy._interface.context import ( - ContextAsync, - ContextSync, - SelfContextAsync, - SelfContextSync, -) -from beekeepy._interface.context_settings_updater import ContextSettingsUpdater -from beekeepy._interface.error_logger import ErrorLogger -from beekeepy._interface.key_pair import KeyPair -from beekeepy._interface.settings_holder import ( - SharedSettingsHolder, - UniqueSettingsHolder, -) -from beekeepy._interface.stopwatch import Stopwatch, StopwatchResult -from beekeepy._interface.url import HttpUrl, P2PUrl, Url, WsUrl +from beekeepy._communication import HttpUrl, P2PUrl, Url, WsUrl +from beekeepy._utilities.context import ContextAsync, ContextSync, SelfContextAsync, SelfContextSync +from beekeepy._utilities.context_settings_updater import ContextSettingsUpdater +from beekeepy._utilities.delay_guard import AsyncDelayGuard, DelayGuardBase, SyncDelayGuard +from beekeepy._utilities.error_logger import ErrorLogger +from beekeepy._utilities.key_pair import KeyPair +from beekeepy._utilities.sanitize import mask, sanitize +from beekeepy._utilities.settings_holder import SharedSettingsHolder, UniqueSettingsHolder +from beekeepy._utilities.stopwatch import Stopwatch, StopwatchResult +from beekeepy._utilities.suppress_api_not_found import SuppressApiNotFound __all__ = [ + "AsyncDelayGuard", "ContextAsync", "ContextSettingsUpdater", "ContextSync", + "DelayGuardBase", "ErrorLogger", "HttpUrl", "KeyPair", @@ -34,6 +29,7 @@ __all__ = [ "Stopwatch", "StopwatchResult", "SuppressApiNotFound", + "SyncDelayGuard", "UniqueSettingsHolder", "Url", "WsUrl", diff --git a/tests/beekeepy_test/handle/commandline/application_command_line_options/patterns/generate_help_pattern.py b/tests/beekeepy_test/handle/commandline/application_command_line_options/patterns/generate_help_pattern.py index 67224e33..c59a3fa3 100644 --- a/tests/beekeepy_test/handle/commandline/application_command_line_options/patterns/generate_help_pattern.py +++ b/tests/beekeepy_test/handle/commandline/application_command_line_options/patterns/generate_help_pattern.py @@ -5,9 +5,12 @@ from pathlib import Path import loguru from beekeepy import Settings -from beekeepy._executable.beekeeper_executable import BeekeeperExecutable +from beekeepy.handle.runnable import BeekeeperExecutable if __name__ == "__main__": - help_text = BeekeeperExecutable(settings=Settings(), logger=loguru.logger).get_help_text() + settings = Settings() + help_text = BeekeeperExecutable( + executable_path=settings.binary_path, working_directory=settings.ensured_working_directory, logger=loguru.logger + ).get_help_text() help_pattern_file = Path.cwd() / "help_pattern.txt" help_pattern_file.write_text(help_text.strip()) diff --git a/tests/beekeepy_test/handle/commandline/application_options/test_backtrace.py b/tests/beekeepy_test/handle/commandline/application_options/test_backtrace.py index b843da39..8310070f 100644 --- a/tests/beekeepy_test/handle/commandline/application_options/test_backtrace.py +++ b/tests/beekeepy_test/handle/commandline/application_options/test_backtrace.py @@ -6,10 +6,10 @@ from typing import TYPE_CHECKING, Literal import pytest from local_tools.beekeepy import checkers -from beekeepy._executable.arguments.beekeeper_arguments import BeekeeperArguments +from beekeepy.handle.runnable import BeekeeperArguments if TYPE_CHECKING: - from beekeepy._executable.beekeeper_executable import ( + from beekeepy.handle.runnable import ( BeekeeperExecutable, ) @@ -19,9 +19,10 @@ def test_backtrace(backtrace: Literal["yes", "no"], beekeeper_exe: BeekeeperExec """Test will check command line flag --backtrace.""" # ARRAGNE & ACT - with beekeeper_exe.run( + with beekeeper_exe.restore_arguments( + BeekeeperArguments(data_dir=beekeeper_exe.working_directory, backtrace=backtrace) + ), beekeeper_exe.run( blocking=False, - arguments=BeekeeperArguments(data_dir=beekeeper_exe.working_directory, backtrace=backtrace), ): time.sleep(0.1) # ASSERT diff --git a/tests/beekeepy_test/handle/commandline/application_options/test_export_keys_wallet.py b/tests/beekeepy_test/handle/commandline/application_options/test_export_keys_wallet.py index b2cb2f02..5f5a1104 100644 --- a/tests/beekeepy_test/handle/commandline/application_options/test_export_keys_wallet.py +++ b/tests/beekeepy_test/handle/commandline/application_options/test_export_keys_wallet.py @@ -3,8 +3,8 @@ from __future__ import annotations import json from typing import TYPE_CHECKING, Final -from beekeepy._interface.key_pair import KeyPair from beekeepy.handle.runnable import Beekeeper +from beekeepy.interfaces import KeyPair if TYPE_CHECKING: from pathlib import Path diff --git a/tests/beekeepy_test/handle/commandline/application_options/test_log_json_rpc.py b/tests/beekeepy_test/handle/commandline/application_options/test_log_json_rpc.py index af22146a..1e48dbd8 100644 --- a/tests/beekeepy_test/handle/commandline/application_options/test_log_json_rpc.py +++ b/tests/beekeepy_test/handle/commandline/application_options/test_log_json_rpc.py @@ -2,7 +2,7 @@ from __future__ import annotations from typing import TYPE_CHECKING -from beekeepy._executable.arguments.beekeeper_arguments import BeekeeperArguments +from beekeepy.handle.runnable import BeekeeperArguments if TYPE_CHECKING: from pathlib import Path diff --git a/tests/beekeepy_test/handle/commandline/application_options/test_unlock_timeout.py b/tests/beekeepy_test/handle/commandline/application_options/test_unlock_timeout.py index b04bb005..a04ff79b 100644 --- a/tests/beekeepy_test/handle/commandline/application_options/test_unlock_timeout.py +++ b/tests/beekeepy_test/handle/commandline/application_options/test_unlock_timeout.py @@ -5,7 +5,7 @@ from typing import TYPE_CHECKING import pytest -from beekeepy._executable.arguments.beekeeper_arguments import BeekeeperArguments +from beekeepy.handle.runnable import BeekeeperArguments if TYPE_CHECKING: from beekeepy.handle.runnable import Beekeeper diff --git a/tests/beekeepy_test/handle/commandline/application_options/test_wallet_dir.py b/tests/beekeepy_test/handle/commandline/application_options/test_wallet_dir.py index ec879fec..0a967be3 100644 --- a/tests/beekeepy_test/handle/commandline/application_options/test_wallet_dir.py +++ b/tests/beekeepy_test/handle/commandline/application_options/test_wallet_dir.py @@ -5,7 +5,7 @@ from typing import TYPE_CHECKING import pytest if TYPE_CHECKING: - from beekeepy._remote_handle.beekeeper import Beekeeper + from beekeepy.handle.remote import Beekeeper def check_wallets_size(beekeeper: Beekeeper, required_size: int) -> None: diff --git a/tests/beekeepy_test/handle/commandline/application_options/test_webserver_http_endpoint.py b/tests/beekeepy_test/handle/commandline/application_options/test_webserver_http_endpoint.py index 97793b53..ae56ed72 100644 --- a/tests/beekeepy_test/handle/commandline/application_options/test_webserver_http_endpoint.py +++ b/tests/beekeepy_test/handle/commandline/application_options/test_webserver_http_endpoint.py @@ -7,8 +7,8 @@ import pytest import requests from local_tools.beekeepy.network import get_port -from beekeepy._executable.arguments.beekeeper_arguments import BeekeeperArguments -from beekeepy._interface.url import HttpUrl +from beekeepy.handle.runnable import BeekeeperArguments +from beekeepy.interfaces import HttpUrl from schemas.apis import beekeeper_api from schemas.jsonrpc import get_response_model diff --git a/tests/beekeepy_test/handle/commandline/application_options/test_webserver_thread_pool_size.py b/tests/beekeepy_test/handle/commandline/application_options/test_webserver_thread_pool_size.py index 38dbfb82..0f042945 100644 --- a/tests/beekeepy_test/handle/commandline/application_options/test_webserver_thread_pool_size.py +++ b/tests/beekeepy_test/handle/commandline/application_options/test_webserver_thread_pool_size.py @@ -5,7 +5,7 @@ from typing import TYPE_CHECKING import pytest from local_tools.beekeepy import checkers -from beekeepy._executable.arguments.beekeeper_arguments import BeekeeperArguments +from beekeepy.handle.runnable import BeekeeperArguments if TYPE_CHECKING: from beekeepy.handle.runnable import Beekeeper diff --git a/tests/beekeepy_test/handle/commandline/conftest.py b/tests/beekeepy_test/handle/commandline/conftest.py index 2b34069b..bea91cb9 100644 --- a/tests/beekeepy_test/handle/commandline/conftest.py +++ b/tests/beekeepy_test/handle/commandline/conftest.py @@ -4,7 +4,7 @@ from typing import TYPE_CHECKING import pytest -from beekeepy._executable.beekeeper_executable import BeekeeperExecutable +from beekeepy.handle.runnable import BeekeeperExecutable if TYPE_CHECKING: from local_tools.beekeepy.models import SettingsLoggerFactory @@ -13,4 +13,8 @@ if TYPE_CHECKING: @pytest.fixture def beekeeper_exe(settings_with_logger: SettingsLoggerFactory) -> BeekeeperExecutable: incoming_settings, logger = settings_with_logger() - return BeekeeperExecutable(settings=incoming_settings, logger=logger) + return BeekeeperExecutable( + executable_path=incoming_settings.binary_path, + working_directory=incoming_settings.ensured_working_directory, + logger=logger, + ) diff --git a/tests/beekeepy_test/handle/storage/test_storage.py b/tests/beekeepy_test/handle/storage/test_storage.py index e98e83a7..7fd9420f 100644 --- a/tests/beekeepy_test/handle/storage/test_storage.py +++ b/tests/beekeepy_test/handle/storage/test_storage.py @@ -1,17 +1,16 @@ from __future__ import annotations -import json import shutil -from pathlib import Path +from typing import TYPE_CHECKING -import pytest from local_tools.beekeepy import checkers from loguru import logger from beekeepy import Settings -from beekeepy.exceptions import BeekeeperFailedToStartError from beekeepy.handle.runnable import Beekeeper -from beekeepy.interfaces import HttpUrl + +if TYPE_CHECKING: + from pathlib import Path def prepare_directory(path: Path) -> None: diff --git a/tests/beekeepy_test/handle/various/test_blocking_unlock.py b/tests/beekeepy_test/handle/various/test_blocking_unlock.py index 821630bb..70310523 100644 --- a/tests/beekeepy_test/handle/various/test_blocking_unlock.py +++ b/tests/beekeepy_test/handle/various/test_blocking_unlock.py @@ -8,12 +8,12 @@ from local_tools.beekeepy.generators import default_wallet_credentials from local_tools.beekeepy.models import SettingsLoggerFactory, WalletInfo from local_tools.beekeepy.network import async_raw_http_call -from beekeepy._interface.delay_guard import DelayGuardBase from beekeepy.handle.runnable import AsyncBeekeeper +from beekeepy.interfaces import DelayGuardBase from schemas.jsonrpc import JSONRPCRequest if TYPE_CHECKING: - from beekeepy._interface.url import HttpUrl as Url + from beekeepy.interfaces import HttpUrl as Url # We have 500ms time period protection on ulocking wallet. diff --git a/tests/beekeepy_test/interface/test_setup.py b/tests/beekeepy_test/interface/test_setup.py index 9680c4cb..a8607533 100644 --- a/tests/beekeepy_test/interface/test_setup.py +++ b/tests/beekeepy_test/interface/test_setup.py @@ -2,7 +2,10 @@ from __future__ import annotations from typing import TYPE_CHECKING +import pytest + from beekeepy import Beekeeper +from beekeepy.exceptions import InvalidatedStateByClosingBeekeeperError, InvalidatedStateByClosingSessionError if TYPE_CHECKING: from local_tools.beekeepy.models import SettingsFactory @@ -10,19 +13,20 @@ if TYPE_CHECKING: def test_closing_with_delete(settings: SettingsFactory) -> None: # ARRANGE - sets = settings() - bk = Beekeeper.factory(settings=sets) + bk = Beekeeper.factory(settings=settings()) # ACT & ASSERT (no throw) bk.teardown() - assert not (sets.ensured_working_directory / "beekeeper.pid").exists() + with pytest.raises(InvalidatedStateByClosingBeekeeperError): + bk.create_session() def test_closing_with_with(settings: SettingsFactory) -> None: # ARRANGE, ACT & ASSERT (no throw) - sets = settings() - with Beekeeper.factory(settings=sets): - assert (sets.ensured_working_directory / "beekeeper.pid").exists() + with Beekeeper.factory(settings=settings()) as bk, bk.create_session() as session: + pass + with pytest.raises(InvalidatedStateByClosingSessionError): + session.wallets # noqa: B018 # part of test def test_session_tokens(settings: SettingsFactory) -> None: diff --git a/tests/beekeepy_test/interface/test_standalone_beekeeper.py b/tests/beekeepy_test/interface/test_standalone_beekeeper.py index 47f5833d..265cc1eb 100644 --- a/tests/beekeepy_test/interface/test_standalone_beekeeper.py +++ b/tests/beekeepy_test/interface/test_standalone_beekeeper.py @@ -1,6 +1,5 @@ from __future__ import annotations -import json import os import sys import time @@ -30,8 +29,7 @@ def verify_beekeeper_status(path_or_pid: Path | int, alive: bool) -> int: pid: int | None = None if isinstance(path_or_pid, Path): assert path_or_pid.exists(), f"Beekeeper started too slow, missing file: {path_or_pid.as_posix()}" - with path_or_pid.open("r") as rfile: - pid = int(json.load(rfile).get("pid", -1)) + pid = int(path_or_pid.read_text().strip()) else: pid = path_or_pid @@ -45,7 +43,7 @@ def test_standalone_beekeeper(working_directory: Path) -> None: path_to_resource_directory = Path(__file__).resolve().parent / "resources" path_to_script = path_to_resource_directory / "standalone_beekeeper_by_args.py" path_to_working_directory = working_directory / "wdir" - path_to_pid_file = path_to_working_directory / "beekeeper.pid" + path_to_pid_file = path_to_working_directory / "pid.txt" # ACT & ASSERT run_python_script( diff --git a/tests/conftest.py b/tests/conftest.py index 8e438830..d2441bd5 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -6,7 +6,7 @@ from pathlib import Path import pytest -from beekeepy._remote_handle.abc.api import AbstractApi, RegisteredApisT +from beekeepy.handle.remote import AbstractSyncApi, RegisteredApisT from beekeepy.interfaces import HttpUrl diff --git a/tests/local-tools/local_tools/beekeepy/account_credentials.py b/tests/local-tools/local_tools/beekeepy/account_credentials.py index a30be4bf..c52da96a 100644 --- a/tests/local-tools/local_tools/beekeepy/account_credentials.py +++ b/tests/local-tools/local_tools/beekeepy/account_credentials.py @@ -3,7 +3,7 @@ from __future__ import annotations import random from typing import Final -from beekeepy._interface.key_pair import KeyPair +from beekeepy.interfaces import KeyPair ACCOUNTS_DATA: Final[list[dict[str, str]]] = [ { diff --git a/tests/local-tools/local_tools/beekeepy/network.py b/tests/local-tools/local_tools/beekeepy/network.py index 2daa290b..40ff64ce 100644 --- a/tests/local-tools/local_tools/beekeepy/network.py +++ b/tests/local-tools/local_tools/beekeepy/network.py @@ -4,18 +4,17 @@ import socket from json import loads from typing import TYPE_CHECKING, Any -from beekeepy import Settings -from beekeepy._communication.aiohttp_communicator import AioHttpCommunicator -from beekeepy._communication.request_communicator import RequestCommunicator +from beekeepy._communication import AioHttpCommunicator, RequestCommunicator +from beekeepy.handle.remote import RemoteHandleSettings if TYPE_CHECKING: - from beekeepy._interface.url import HttpUrl + from beekeepy.interfaces import HttpUrl from schemas.jsonrpc import JSONRPCRequest async def async_raw_http_call(*, http_endpoint: HttpUrl, data: JSONRPCRequest) -> dict[str, Any]: """Make raw call with given data to given http_endpoint.""" - communicator = AioHttpCommunicator(settings=Settings(http_endpoint=http_endpoint)) + communicator = AioHttpCommunicator(settings=RemoteHandleSettings(http_endpoint=http_endpoint)) response = await communicator.async_send(url=http_endpoint, data=data.json(by_alias=True)) parsed = loads(response) assert isinstance(parsed, dict), "expected json object" @@ -24,7 +23,7 @@ async def async_raw_http_call(*, http_endpoint: HttpUrl, data: JSONRPCRequest) - def raw_http_call(*, http_endpoint: HttpUrl, data: JSONRPCRequest) -> dict[str, Any]: """Make raw call with given data to given http_endpoint.""" - communicator = RequestCommunicator(settings=Settings(http_endpoint=http_endpoint)) + communicator = RequestCommunicator(settings=RemoteHandleSettings(http_endpoint=http_endpoint)) response = communicator.send(url=http_endpoint, data=data.json(by_alias=True)) parsed = loads(response) assert isinstance(parsed, dict), "expected json object" -- GitLab From 3366efd820003dfbeb616221826f6b080a82def4 Mon Sep 17 00:00:00 2001 From: kmochocki <kmochocki@syncad.com> Date: Sat, 22 Mar 2025 20:20:27 +0000 Subject: [PATCH 09/23] Add beekeeper_arguments --- .../_executable/beekeeper_arguments.py | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 beekeepy/beekeepy/_executable/beekeeper_arguments.py diff --git a/beekeepy/beekeepy/_executable/beekeeper_arguments.py b/beekeepy/beekeepy/_executable/beekeeper_arguments.py new file mode 100644 index 00000000..5af2501b --- /dev/null +++ b/beekeepy/beekeepy/_executable/beekeeper_arguments.py @@ -0,0 +1,43 @@ +from __future__ import annotations + +from dataclasses import dataclass +from pathlib import Path +from typing import Any, ClassVar, Literal + +from beekeepy._communication import HttpUrl +from beekeepy._executable.abc.arguments import Arguments + + +class BeekeeperArgumentsDefaults: + DEFAULT_BACKTRACE: ClassVar[Literal["yes", "no"]] = "yes" + DEFAULT_EXPORT_KEYS_WALLET: ClassVar[ExportKeysWalletParams | None] = None + DEFAULT_LOG_JSON_RPC: ClassVar[Path | None] = None + DEFAULT_UNLOCK_TIMEOUT: ClassVar[int] = 900 + DEFAULT_UNLOCK_INTERVAL: ClassVar[int] = 500 + DEFAULT_WALLET_DIR: ClassVar[Path] = Path.cwd() + DEFAULT_WEBSERVER_THREAD_POOL_SIZE: ClassVar[int] = 32 + DEFAULT_WEBSERVER_HTTP_ENDPOINT: ClassVar[HttpUrl | None] = None + + +@dataclass +class ExportKeysWalletParams: + wallet_name: str + wallet_password: str + + +class BeekeeperArguments(Arguments): + backtrace: Literal["yes", "no"] | None = BeekeeperArgumentsDefaults.DEFAULT_BACKTRACE + data_dir: Path | None = None + export_keys_wallet: ExportKeysWalletParams | None = BeekeeperArgumentsDefaults.DEFAULT_EXPORT_KEYS_WALLET + log_json_rpc: Path | None = BeekeeperArgumentsDefaults.DEFAULT_LOG_JSON_RPC + unlock_timeout: int | None = BeekeeperArgumentsDefaults.DEFAULT_UNLOCK_TIMEOUT + wallet_dir: Path | None = BeekeeperArgumentsDefaults.DEFAULT_WALLET_DIR + webserver_thread_pool_size: int | None = BeekeeperArgumentsDefaults.DEFAULT_WEBSERVER_THREAD_POOL_SIZE + webserver_http_endpoint: HttpUrl | None = BeekeeperArgumentsDefaults.DEFAULT_WEBSERVER_HTTP_ENDPOINT + + def _convert_member_value_to_string_default(self, member_value: Any) -> str | Any: + if isinstance(member_value, HttpUrl): + return member_value.as_string(with_protocol=False) + if isinstance(member_value, ExportKeysWalletParams): + return f'["{member_value.wallet_name}","{member_value.wallet_password}"]' + return member_value -- GitLab From 4a0d85a3b37f78042f14beabc20b5643176d3e2c Mon Sep 17 00:00:00 2001 From: kmochocki <kmochocki@syncad.com> Date: Sat, 22 Mar 2025 20:23:26 +0000 Subject: [PATCH 10/23] Add InterfaceSettings to easly distinguish Settings types --- beekeepy/beekeepy/_interface/__init__.py | 2 ++ .../_interface/abc/asynchronous/beekeeper.py | 10 +++--- .../_interface/abc/synchronous/beekeeper.py | 10 +++--- .../_interface/asynchronous/beekeeper.py | 34 +++++++++++------- .../_interface/asynchronous/session.py | 6 ++-- .../_interface/asynchronous/wallet.py | 9 ++--- beekeepy/beekeepy/_interface/settings.py | 7 ++++ .../_interface/synchronous/beekeeper.py | 36 +++++++++++-------- .../_interface/synchronous/session.py | 6 ++-- .../beekeepy/_interface/synchronous/wallet.py | 7 ++-- 10 files changed, 77 insertions(+), 50 deletions(-) create mode 100644 beekeepy/beekeepy/_interface/settings.py diff --git a/beekeepy/beekeepy/_interface/__init__.py b/beekeepy/beekeepy/_interface/__init__.py index 9802b2ef..1a826ee5 100644 --- a/beekeepy/beekeepy/_interface/__init__.py +++ b/beekeepy/beekeepy/_interface/__init__.py @@ -5,6 +5,7 @@ from beekeepy._interface.asynchronous.beekeeper import Beekeeper as AsyncBeekepe from beekeepy._interface.asynchronous.session import Session as AsyncSession from beekeepy._interface.asynchronous.wallet import UnlockedWallet as AsyncUnlockedWallet from beekeepy._interface.asynchronous.wallet import Wallet as AsyncWallet +from beekeepy._interface.settings import InterfaceSettings from beekeepy._interface.synchronous.beekeeper import Beekeeper from beekeepy._interface.synchronous.session import Session from beekeepy._interface.synchronous.wallet import UnlockedWallet, Wallet @@ -16,6 +17,7 @@ __all__ = [ "AsyncUnlockedWallet", "AsyncWallet", "Beekeeper", + "InterfaceSettings", "Session", "UnlockedWallet", "Wallet", diff --git a/beekeepy/beekeepy/_interface/abc/asynchronous/beekeeper.py b/beekeepy/beekeepy/_interface/abc/asynchronous/beekeeper.py index ddda2365..0604b7bc 100644 --- a/beekeepy/beekeepy/_interface/abc/asynchronous/beekeeper.py +++ b/beekeepy/beekeepy/_interface/abc/asynchronous/beekeeper.py @@ -4,7 +4,7 @@ from abc import ABC, abstractmethod from typing import TYPE_CHECKING, cast from beekeepy._communication import CommunicationSettings -from beekeepy._interface.context import ContextAsync +from beekeepy._interface.settings import InterfaceSettings from beekeepy._utilities.context import ContextAsync from beekeepy._utilities.context_settings_updater import ContextSettingsUpdater @@ -19,9 +19,9 @@ class Beekeeper(ContextAsync["Beekeeper"], ContextSettingsUpdater[CommunicationS async def create_session(self, *, salt: str | None = None) -> Session: ... @property - def settings(self) -> Settings: + def settings(self) -> InterfaceSettings: """Returns read-only settings.""" - return cast(Settings, self._get_copy_of_settings()) + return cast(InterfaceSettings, self._get_copy_of_settings()) @property def http_endpoint(self) -> HttpUrl: @@ -42,13 +42,13 @@ class Beekeeper(ContextAsync["Beekeeper"], ContextSettingsUpdater[CommunicationS """Detaches process and returns PID.""" @classmethod - async def factory(cls, *, settings: Settings | None = None) -> Beekeeper: + async def factory(cls, *, settings: InterfaceSettings | None = None) -> Beekeeper: from beekeepy._interface.asynchronous.beekeeper import Beekeeper as BeekeeperImplementation return await BeekeeperImplementation._factory(settings=settings) @classmethod - async def remote_factory(cls, *, url_or_settings: Settings | HttpUrl) -> Beekeeper: + async def remote_factory(cls, *, url_or_settings: InterfaceSettings | HttpUrl) -> Beekeeper: from beekeepy._interface.asynchronous.beekeeper import Beekeeper as BeekeeperImplementation return await BeekeeperImplementation._remote_factory(url_or_settings=url_or_settings) diff --git a/beekeepy/beekeepy/_interface/abc/synchronous/beekeeper.py b/beekeepy/beekeepy/_interface/abc/synchronous/beekeeper.py index ac6dc8ed..a8068fb2 100644 --- a/beekeepy/beekeepy/_interface/abc/synchronous/beekeeper.py +++ b/beekeepy/beekeepy/_interface/abc/synchronous/beekeeper.py @@ -4,7 +4,7 @@ from abc import ABC, abstractmethod from typing import TYPE_CHECKING, cast from beekeepy._communication import CommunicationSettings -from beekeepy._interface.context import ContextSync +from beekeepy._interface.settings import InterfaceSettings from beekeepy._utilities.context import ContextSync from beekeepy._utilities.context_settings_updater import ContextSettingsUpdater @@ -19,9 +19,9 @@ class Beekeeper(ContextSync["Beekeeper"], ContextSettingsUpdater[CommunicationSe def create_session(self, *, salt: str | None = None) -> Session: ... @property - def settings(self) -> Settings: + def settings(self) -> InterfaceSettings: """Returns read-only settings.""" - return cast(Settings, self._get_copy_of_settings()) + return cast(InterfaceSettings, self._get_copy_of_settings()) @property def http_endpoint(self) -> HttpUrl: @@ -42,13 +42,13 @@ class Beekeeper(ContextSync["Beekeeper"], ContextSettingsUpdater[CommunicationSe """Detaches process and returns PID.""" @classmethod - def factory(cls, *, settings: Settings | None = None) -> Beekeeper: + def factory(cls, *, settings: InterfaceSettings | None = None) -> Beekeeper: from beekeepy._interface.synchronous.beekeeper import Beekeeper as BeekeeperImplementation return BeekeeperImplementation._factory(settings=settings) @classmethod - def remote_factory(cls, *, url_or_settings: Settings | HttpUrl) -> Beekeeper: + def remote_factory(cls, *, url_or_settings: InterfaceSettings | HttpUrl) -> Beekeeper: from beekeepy._interface.synchronous.beekeeper import Beekeeper as BeekeeperImplementation return BeekeeperImplementation._remote_factory(url_or_settings=url_or_settings) diff --git a/beekeepy/beekeepy/_interface/asynchronous/beekeeper.py b/beekeepy/beekeepy/_interface/asynchronous/beekeeper.py index b54837bb..d195568d 100644 --- a/beekeepy/beekeepy/_interface/asynchronous/beekeeper.py +++ b/beekeepy/beekeepy/_interface/asynchronous/beekeeper.py @@ -7,9 +7,9 @@ from loguru import logger from beekeepy._interface.abc.asynchronous.beekeeper import Beekeeper as BeekeeperInterface from beekeepy._interface.abc.packed_object import PackedAsyncBeekeeper from beekeepy._interface.asynchronous.session import Session -from beekeepy._interface.delay_guard import AsyncDelayGuard -from beekeepy._interface.state_invalidator import StateInvalidator -from beekeepy._remote_handle.beekeeper import AsyncBeekeeper as AsynchronousRemoteBeekeeperHandle +from beekeepy._interface.settings import InterfaceSettings +from beekeepy._remote_handle import AsyncBeekeeperTemplate as AsynchronousRemoteBeekeeperHandle +from beekeepy._runnable_handle import AsyncBeekeeperTemplate as AsynchronousBeekeeperHandle from beekeepy._utilities.delay_guard import AsyncDelayGuard from beekeepy._utilities.state_invalidator import StateInvalidator from beekeepy.exceptions import ( @@ -26,7 +26,7 @@ if TYPE_CHECKING: class Beekeeper(BeekeeperInterface, StateInvalidator): - def __init__(self, *args: Any, handle: AsynchronousRemoteBeekeeperHandle, **kwargs: Any) -> None: + def __init__(self, *args: Any, handle: AsynchronousRemoteBeekeeperHandle[InterfaceSettings], **kwargs: Any) -> None: super().__init__(*args, **kwargs) self.__instance = handle self.__guard = AsyncDelayGuard() @@ -47,7 +47,7 @@ class Beekeeper(BeekeeperInterface, StateInvalidator): self.__default_session = self.__create_session((await self._get_instance().session).token) return self.__default_session - def _get_instance(self) -> AsynchronousRemoteBeekeeperHandle: + def _get_instance(self) -> AsynchronousRemoteBeekeeperHandle[InterfaceSettings]: return self.__instance @StateInvalidator.empty_call_after_invalidation(None) @@ -79,28 +79,38 @@ class Beekeeper(BeekeeperInterface, StateInvalidator): return PackedAsyncBeekeeper(settings=self.settings, unpack_factory=Beekeeper._remote_factory) @classmethod - async def _factory(cls, *, settings: Settings | None = None) -> BeekeeperInterface: - settings = settings or Settings() - handle = AsynchronousBeekeeperHandle(settings=settings, logger=logger) + async def _factory(cls, *, settings: InterfaceSettings | None = None) -> BeekeeperInterface: + settings = settings or InterfaceSettings() + handle = cls.__create_local_handle(settings=settings) handle.run() return cls(handle=handle) @classmethod - async def _remote_factory(cls, *, url_or_settings: Settings | HttpUrl) -> BeekeeperInterface: - if isinstance(url_or_settings, Settings): + async def _remote_factory(cls, *, url_or_settings: InterfaceSettings | HttpUrl) -> BeekeeperInterface: + if isinstance(url_or_settings, InterfaceSettings): assert ( url_or_settings.http_endpoint is not None ), "Settings.http_endpoint has to be set when passing to remote_factory" - settings = url_or_settings if isinstance(url_or_settings, Settings) else Settings(http_endpoint=url_or_settings) + settings = ( + url_or_settings + if isinstance(url_or_settings, InterfaceSettings) + else InterfaceSettings(http_endpoint=url_or_settings) + ) handle = AsynchronousRemoteBeekeeperHandle(settings=settings) cls.__apply_existing_session_token(settings=settings, handle=handle) return cls(handle=handle) @classmethod - def __apply_existing_session_token(cls, settings: Settings, handle: AsynchronousRemoteBeekeeperHandle) -> None: + def __apply_existing_session_token( + cls, settings: InterfaceSettings, handle: AsynchronousRemoteBeekeeperHandle[InterfaceSettings] + ) -> None: if settings.use_existing_session: handle.set_session_token(settings.use_existing_session) + @classmethod + def __create_local_handle(cls, settings: InterfaceSettings) -> AsynchronousBeekeeperHandle[InterfaceSettings]: + return AsynchronousBeekeeperHandle(settings=settings, logger=logger) + async def _aenter(self) -> BeekeeperInterface: return self diff --git a/beekeepy/beekeepy/_interface/asynchronous/session.py b/beekeepy/beekeepy/_interface/asynchronous/session.py index 35e3704e..470f58d0 100644 --- a/beekeepy/beekeepy/_interface/asynchronous/session.py +++ b/beekeepy/beekeepy/_interface/asynchronous/session.py @@ -27,8 +27,8 @@ if TYPE_CHECKING: from beekeepy._interface.abc.asynchronous.wallet import ( Wallet as WalletInterface, ) - from beekeepy._interface.delay_guard import AsyncDelayGuard - from beekeepy._remote_handle.beekeeper import AsyncBeekeeper as AsynchronousRemoteBeekeeperHandle + from beekeepy._interface.settings import InterfaceSettings + from beekeepy._remote_handle import AsyncBeekeeperTemplate as AsynchronousRemoteBeekeeperHandle from beekeepy._utilities.delay_guard import AsyncDelayGuard from schemas.apis.beekeeper_api import GetInfo from schemas.fields.basic import PublicKey @@ -39,7 +39,7 @@ class Session(SessionInterface, StateInvalidator): def __init__( self, *args: Any, - beekeeper: AsynchronousRemoteBeekeeperHandle, + beekeeper: AsynchronousRemoteBeekeeperHandle[InterfaceSettings], guard: AsyncDelayGuard, use_session_token: str | None = None, default_session_close_callback: Callable[[], None] | None = None, diff --git a/beekeepy/beekeepy/_interface/asynchronous/wallet.py b/beekeepy/beekeepy/_interface/asynchronous/wallet.py index 5cfac6dc..20173c12 100644 --- a/beekeepy/beekeepy/_interface/asynchronous/wallet.py +++ b/beekeepy/beekeepy/_interface/asynchronous/wallet.py @@ -8,11 +8,10 @@ from beekeepy._interface.abc.asynchronous.wallet import ( from beekeepy._interface.abc.asynchronous.wallet import ( Wallet as WalletInterface, ) -from beekeepy._interface.delay_guard import AsyncDelayGuard +from beekeepy._interface.settings import InterfaceSettings from beekeepy._interface.validators import validate_digest, validate_private_keys, validate_public_keys from beekeepy._interface.wallets_common import WalletCommons -from beekeepy._remote_handle.beekeeper import AsyncBeekeeper as AsyncRemoteBeekeeper -from beekeepy._runnable_handle.callbacks_protocol import AsyncWalletLocked +from beekeepy._remote_handle import AsyncBeekeeperTemplate as AsyncRemoteBeekeeper from beekeepy._runnable_handle import AsyncWalletLocked from beekeepy._utilities.delay_guard import AsyncDelayGuard from beekeepy.exceptions import ( @@ -31,7 +30,9 @@ if TYPE_CHECKING: from schemas.fields.hex import Signature -class Wallet(WalletCommons[AsyncRemoteBeekeeper, AsyncWalletLocked, AsyncDelayGuard], WalletInterface): +class Wallet( + WalletCommons[AsyncRemoteBeekeeper[InterfaceSettings], AsyncWalletLocked, AsyncDelayGuard], WalletInterface +): @property async def public_keys(self) -> list[PublicKey]: return [ diff --git a/beekeepy/beekeepy/_interface/settings.py b/beekeepy/beekeepy/_interface/settings.py new file mode 100644 index 00000000..701919db --- /dev/null +++ b/beekeepy/beekeepy/_interface/settings.py @@ -0,0 +1,7 @@ +from __future__ import annotations + +from beekeepy._runnable_handle import RunnableHandleSettings + + +class InterfaceSettings(RunnableHandleSettings): + """Settings for beekeeper interface.""" diff --git a/beekeepy/beekeepy/_interface/synchronous/beekeeper.py b/beekeepy/beekeepy/_interface/synchronous/beekeeper.py index d6042276..e287b00b 100644 --- a/beekeepy/beekeepy/_interface/synchronous/beekeeper.py +++ b/beekeepy/beekeepy/_interface/synchronous/beekeeper.py @@ -6,12 +6,10 @@ from loguru import logger from beekeepy._interface.abc.packed_object import PackedSyncBeekeeper from beekeepy._interface.abc.synchronous.beekeeper import Beekeeper as BeekeeperInterface -from beekeepy._interface.delay_guard import SyncDelayGuard -from beekeepy._interface.state_invalidator import StateInvalidator +from beekeepy._interface.settings import InterfaceSettings from beekeepy._interface.synchronous.session import Session -from beekeepy._remote_handle.beekeeper import Beekeeper as SynchronousRemoteBeekeeperHandle -from beekeepy._runnable_handle.beekeeper import Beekeeper as SynchronousBeekeeperHandle -from beekeepy._runnable_handle.settings import Settings +from beekeepy._remote_handle import BeekeeperTemplate as SynchronousRemoteBeekeeperHandle +from beekeepy._runnable_handle import BeekeeperTemplate as SynchronousBeekeeperHandle from beekeepy._utilities.delay_guard import SyncDelayGuard from beekeepy._utilities.state_invalidator import StateInvalidator from beekeepy.exceptions import ( @@ -28,7 +26,7 @@ if TYPE_CHECKING: class Beekeeper(BeekeeperInterface, StateInvalidator): - def __init__(self, *args: Any, handle: SynchronousRemoteBeekeeperHandle, **kwargs: Any) -> None: + def __init__(self, *args: Any, handle: SynchronousRemoteBeekeeperHandle[InterfaceSettings], **kwargs: Any) -> None: super().__init__(*args, **kwargs) self.__instance = handle self.__guard = SyncDelayGuard() @@ -49,7 +47,7 @@ class Beekeeper(BeekeeperInterface, StateInvalidator): self.__default_session = self.__create_session(self._get_instance().session.token, default_session=True) return self.__default_session - def _get_instance(self) -> SynchronousRemoteBeekeeperHandle: + def _get_instance(self) -> SynchronousRemoteBeekeeperHandle[InterfaceSettings]: return self.__instance @StateInvalidator.empty_call_after_invalidation(None) @@ -81,28 +79,38 @@ class Beekeeper(BeekeeperInterface, StateInvalidator): return PackedSyncBeekeeper(settings=self.settings, unpack_factory=Beekeeper._remote_factory) @classmethod - def _factory(cls, *, settings: Settings | None = None) -> BeekeeperInterface: - settings = settings or Settings() - handle = SynchronousBeekeeperHandle(settings=settings, logger=logger) + def _factory(cls, *, settings: InterfaceSettings | None = None) -> BeekeeperInterface: + settings = settings or InterfaceSettings() + handle = cls.__create_local_handle(settings=settings) handle.run() return cls(handle=handle) @classmethod - def _remote_factory(cls, *, url_or_settings: Settings | HttpUrl) -> BeekeeperInterface: - if isinstance(url_or_settings, Settings): + def _remote_factory(cls, *, url_or_settings: InterfaceSettings | HttpUrl) -> BeekeeperInterface: + if isinstance(url_or_settings, InterfaceSettings): assert ( url_or_settings.http_endpoint is not None ), "Settings.http_endpoint has to be set when passing to remote_factory" - settings = url_or_settings if isinstance(url_or_settings, Settings) else Settings(http_endpoint=url_or_settings) + settings = ( + url_or_settings + if isinstance(url_or_settings, InterfaceSettings) + else InterfaceSettings(http_endpoint=url_or_settings) + ) handle = SynchronousRemoteBeekeeperHandle(settings=settings) cls.__apply_existing_session_token(settings=settings, handle=handle) return cls(handle=handle) @classmethod - def __apply_existing_session_token(cls, settings: Settings, handle: SynchronousRemoteBeekeeperHandle) -> None: + def __apply_existing_session_token( + cls, settings: InterfaceSettings, handle: SynchronousRemoteBeekeeperHandle[InterfaceSettings] + ) -> None: if settings.use_existing_session: handle.set_session_token(settings.use_existing_session) + @classmethod + def __create_local_handle(cls, settings: InterfaceSettings) -> SynchronousBeekeeperHandle[InterfaceSettings]: + return SynchronousBeekeeperHandle(settings=settings, logger=logger) + def _enter(self) -> BeekeeperInterface: return self diff --git a/beekeepy/beekeepy/_interface/synchronous/session.py b/beekeepy/beekeepy/_interface/synchronous/session.py index 9fab6de8..5b9e559d 100644 --- a/beekeepy/beekeepy/_interface/synchronous/session.py +++ b/beekeepy/beekeepy/_interface/synchronous/session.py @@ -26,8 +26,8 @@ if TYPE_CHECKING: from beekeepy._interface.abc.synchronous.wallet import ( Wallet as WalletInterface, ) - from beekeepy._interface.delay_guard import SyncDelayGuard - from beekeepy._remote_handle.beekeeper import Beekeeper as SyncRemoteBeekeeper + from beekeepy._interface.settings import InterfaceSettings + from beekeepy._remote_handle import BeekeeperTemplate as SyncRemoteBeekeeper from beekeepy._utilities.delay_guard import SyncDelayGuard from schemas.apis.beekeeper_api import GetInfo from schemas.fields.basic import PublicKey @@ -38,7 +38,7 @@ class Session(SessionInterface, StateInvalidator): def __init__( self, *args: Any, - beekeeper: SyncRemoteBeekeeper, + beekeeper: SyncRemoteBeekeeper[InterfaceSettings], guard: SyncDelayGuard, use_session_token: str | None = None, default_session_close_callback: Callable[[], None] | None = None, diff --git a/beekeepy/beekeepy/_interface/synchronous/wallet.py b/beekeepy/beekeepy/_interface/synchronous/wallet.py index 5fbbb202..81ffec89 100644 --- a/beekeepy/beekeepy/_interface/synchronous/wallet.py +++ b/beekeepy/beekeepy/_interface/synchronous/wallet.py @@ -8,11 +8,10 @@ from beekeepy._interface.abc.synchronous.wallet import ( from beekeepy._interface.abc.synchronous.wallet import ( Wallet as WalletInterface, ) -from beekeepy._interface.delay_guard import SyncDelayGuard +from beekeepy._interface.settings import InterfaceSettings from beekeepy._interface.validators import validate_private_keys, validate_public_keys from beekeepy._interface.wallets_common import WalletCommons -from beekeepy._remote_handle.beekeeper import Beekeeper as SyncRemoteBeekeeper -from beekeepy._runnable_handle.callbacks_protocol import SyncWalletLocked +from beekeepy._remote_handle import BeekeeperTemplate as SyncRemoteBeekeeper from beekeepy._runnable_handle import SyncWalletLocked from beekeepy._utilities.delay_guard import SyncDelayGuard from beekeepy.exceptions import ( @@ -31,7 +30,7 @@ if TYPE_CHECKING: from schemas.fields.hex import Signature -class Wallet(WalletCommons[SyncRemoteBeekeeper, SyncWalletLocked, SyncDelayGuard], WalletInterface): +class Wallet(WalletCommons[SyncRemoteBeekeeper[InterfaceSettings], SyncWalletLocked, SyncDelayGuard], WalletInterface): @property def public_keys(self) -> list[PublicKey]: return [ -- GitLab From 4ceeab8c477149737c34add729b7b30e7f783e12 Mon Sep 17 00:00:00 2001 From: kmochocki <kmochocki@syncad.com> Date: Sat, 22 Mar 2025 20:23:58 +0000 Subject: [PATCH 11/23] Fill missing imports in _executable sub-package interface --- beekeepy/beekeepy/_executable/__init__.py | 24 +++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/beekeepy/beekeepy/_executable/__init__.py b/beekeepy/beekeepy/_executable/__init__.py index a734003e..6b444e5d 100644 --- a/beekeepy/beekeepy/_executable/__init__.py +++ b/beekeepy/beekeepy/_executable/__init__.py @@ -1,13 +1,29 @@ from __future__ import annotations -from beekeepy._executable.arguments.arguments import Arguments -from beekeepy._executable.arguments.beekeeper_arguments import BeekeeperArguments +from beekeepy._executable.abc import ( + Arguments, + ArgumentT, + Config, + ConfigT, + Executable, + StreamRepresentation, + StreamsHolder, +) +from beekeepy._executable.beekeeper_arguments import BeekeeperArguments +from beekeepy._executable.beekeeper_config import BeekeeperConfig from beekeepy._executable.beekeeper_executable import BeekeeperExecutable -from beekeepy._executable.executable import Executable +from beekeepy._utilities.key_pair import KeyPair __all__ = [ - "Arguments", + "BeekeeperConfig", "BeekeeperArguments", "BeekeeperExecutable", + "Arguments", "Executable", + "Config", + "StreamRepresentation", + "StreamsHolder", + "ArgumentT", + "ConfigT", + "KeyPair", ] -- GitLab From b0445fa2f4f318335e150d509699cee41b12c40e Mon Sep 17 00:00:00 2001 From: kmochocki <kmochocki@syncad.com> Date: Sat, 22 Mar 2025 20:35:57 +0000 Subject: [PATCH 12/23] Update way of generating generated_ test directories --- .../patterns/config.ini | 2 +- tests/conftest.py | 16 +++++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/tests/beekeepy_test/handle/commandline/application_command_line_options/patterns/config.ini b/tests/beekeepy_test/handle/commandline/application_command_line_options/patterns/config.ini index 1b428e92..f55ef484 100644 --- a/tests/beekeepy_test/handle/commandline/application_command_line_options/patterns/config.ini +++ b/tests/beekeepy_test/handle/commandline/application_command_line_options/patterns/config.ini @@ -1,5 +1,5 @@ # config automatically generated by helpy -wallet-dir=/workspace/hive/tests/python/functional/beekeepy/handle/commandline/application_command_line_options/patterns +wallet-dir=/workspace/helpy/tests/beekeepy_test/handle/commandline/application_command_line_options/patterns unlock-timeout=900 unlock-interval=500 webserver-http-endpoint=0.0.0.0:0 diff --git a/tests/conftest.py b/tests/conftest.py index d2441bd5..b505bdcb 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -30,12 +30,14 @@ def _convert_test_name_to_directory_name(test_name: str) -> str: @pytest.fixture(autouse=True) def working_directory(request: pytest.FixtureRequest) -> Path: name_of_directory = _convert_test_name_to_directory_name(request.node.name) - path_to_generated = request.node.path.parent / name_of_directory - if path_to_generated.exists(): - shutil.rmtree(path_to_generated) - path_to_generated.mkdir() - assert isinstance(path_to_generated, Path), "given object is not Path" - return path_to_generated + path_to_module_generated = request.node.path.parent / f"generated_{request.node.path.stem}" + path_to_module_generated.mkdir(exist_ok=True) + path_to_test_artifacts = path_to_module_generated / name_of_directory + if path_to_test_artifacts.exists(): + shutil.rmtree(path_to_test_artifacts) + path_to_test_artifacts.mkdir() + assert isinstance(path_to_test_artifacts, Path), "given object is not Path" + return path_to_test_artifacts def pytest_addoption(parser: pytest.Parser) -> None: @@ -47,7 +49,7 @@ def pytest_addoption(parser: pytest.Parser) -> None: @pytest.fixture def registered_apis() -> RegisteredApisT: """Return registered methods.""" - return AbstractApi._get_registered_methods() + return AbstractSyncApi._get_registered_methods() @pytest.fixture -- GitLab From 1986fb73a755eee6552e452d4ac21db00c99f56b Mon Sep 17 00:00:00 2001 From: kmochocki <kmochocki@syncad.com> Date: Sat, 22 Mar 2025 20:37:22 +0000 Subject: [PATCH 13/23] Fix in test due to change of is_running from property to method --- .../application_options/test_export_keys_wallet.py | 2 +- tests/beekeepy_test/handle/conftest.py | 2 +- tests/beekeepy_test/handle/storage/test_storage.py | 6 +++--- tests/beekeepy_test/handle/various/test_blocking_unlock.py | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/beekeepy_test/handle/commandline/application_options/test_export_keys_wallet.py b/tests/beekeepy_test/handle/commandline/application_options/test_export_keys_wallet.py index 5f5a1104..b609ec5c 100644 --- a/tests/beekeepy_test/handle/commandline/application_options/test_export_keys_wallet.py +++ b/tests/beekeepy_test/handle/commandline/application_options/test_export_keys_wallet.py @@ -91,4 +91,4 @@ def test_export_keys(beekeeper: Beekeeper) -> None: # ASSERT # Check default path of wallet_name.keys check_dumped_keys(extract_path / wallet_name_keys, keys1) - assert bk.is_running is False + assert bk.is_running() is False diff --git a/tests/beekeepy_test/handle/conftest.py b/tests/beekeepy_test/handle/conftest.py index d13ae688..d5512de6 100644 --- a/tests/beekeepy_test/handle/conftest.py +++ b/tests/beekeepy_test/handle/conftest.py @@ -27,7 +27,7 @@ def beekeeper_not_started(settings_with_logger: SettingsLoggerFactory) -> Iterat yield bk - if bk.is_running: + if bk.is_running(): bk.teardown() diff --git a/tests/beekeepy_test/handle/storage/test_storage.py b/tests/beekeepy_test/handle/storage/test_storage.py index 7fd9420f..bdf54514 100644 --- a/tests/beekeepy_test/handle/storage/test_storage.py +++ b/tests/beekeepy_test/handle/storage/test_storage.py @@ -31,7 +31,7 @@ def test_multiply_beekeepeer_same_storage(working_directory: Path) -> None: # ACT & ASSERT 1 with Beekeeper(settings=settings, logger=logger) as bk1: - assert bk1.is_running is True, "First instance of beekeeper should launch without any problems." + assert bk1.is_running() is True, "First instance of beekeeper should launch without any problems." # ACT & ASSERT 2 bk2 = Beekeeper(settings=settings, logger=logger) @@ -62,8 +62,8 @@ def test_multiply_beekeepeer_different_storage(working_directory: Path) -> None: settings=Settings(working_directory=bk2_path), logger=logger ) as bk2: # ASSERT - assert bk1.is_running, "First instance of beekeeper should be working." - assert bk2.is_running, "Second instance of beekeeper should be working." + assert bk1.is_running(), "First instance of beekeeper should be working." + assert bk2.is_running(), "Second instance of beekeeper should be working." bks.extend((bk1, bk2)) for bk in bks: diff --git a/tests/beekeepy_test/handle/various/test_blocking_unlock.py b/tests/beekeepy_test/handle/various/test_blocking_unlock.py index 70310523..485d6bcd 100644 --- a/tests/beekeepy_test/handle/various/test_blocking_unlock.py +++ b/tests/beekeepy_test/handle/various/test_blocking_unlock.py @@ -29,7 +29,7 @@ def beekeeper_not_started(settings_with_logger: SettingsLoggerFactory) -> Iterat yield bk - if bk.is_running: + if bk.is_running(): bk.teardown() -- GitLab From e4561c4dfe82e49a68155152c3b07eeddba3fd77 Mon Sep 17 00:00:00 2001 From: kmochocki <kmochocki@syncad.com> Date: Sat, 22 Mar 2025 20:39:25 +0000 Subject: [PATCH 14/23] Fix tests because of changes in beekeeper behaviour list_wallets -> list_created_wallets change: list_wallets does not refresh timeout and tests aware on this will fail --- .../handle/api_tests/test_api_close.py | 8 +++- .../handle/api_tests/test_api_set_timeout.py | 4 +- .../beekeepy_test/handle/basic/test_wallet.py | 4 +- .../patterns/help_pattern.txt | 6 +-- .../test_unlock_timeout.py | 2 +- .../handle/storage/test_storage.py | 44 ++----------------- 6 files changed, 16 insertions(+), 52 deletions(-) diff --git a/tests/beekeepy_test/handle/api_tests/test_api_close.py b/tests/beekeepy_test/handle/api_tests/test_api_close.py index e4c56b92..dc521f72 100644 --- a/tests/beekeepy_test/handle/api_tests/test_api_close.py +++ b/tests/beekeepy_test/handle/api_tests/test_api_close.py @@ -14,6 +14,10 @@ if TYPE_CHECKING: from beekeepy.handle.runnable import Beekeeper +# NOTE 1: Beekeeper should not raise exception while calling close on already closed wallet or not existing wallet. +# It should be treated as a success for UX purposes. + + def test_api_close(beekeeper: Beekeeper, wallet: WalletInfo) -> None: """Test test_api_close will test beekeeper_api.close api call.""" # ARRANGE @@ -51,7 +55,7 @@ def test_api_close_double_close( beekeeper.api.close(wallet_name=wallet.name) # ASSERT - # According to behavior change of Beekeeper, it should not throw + # SHOULD NOT RAISE (NOTE 1) beekeeper.api.close(wallet_name=wallet.name) @@ -61,5 +65,5 @@ def test_api_close_not_existing_wallet(beekeeper: Beekeeper) -> None: wallet = WalletInfo(password=generate_wallet_password(), name=generate_wallet_name()) # ACT & ASSERT - # According to behavior change of Beekeeper, it should not throw + # SHOULD NOT RAISE (NOTE 1) beekeeper.api.close(wallet_name=wallet.name) diff --git a/tests/beekeepy_test/handle/api_tests/test_api_set_timeout.py b/tests/beekeepy_test/handle/api_tests/test_api_set_timeout.py index aba784b3..65f678b8 100644 --- a/tests/beekeepy_test/handle/api_tests/test_api_set_timeout.py +++ b/tests/beekeepy_test/handle/api_tests/test_api_set_timeout.py @@ -12,7 +12,7 @@ if TYPE_CHECKING: def test_api_set_timeout(beekeeper: Beekeeper, wallet: WalletInfo) -> None: # noqa: ARG001 """Test test_api_set_timeout will test beekeeper_api.set_timeout api call.""" # ARRANGE - bk_wallet = (beekeeper.api.list_wallets()).wallets[0] + bk_wallet = (beekeeper.api.list_created_wallets()).wallets[0] assert bk_wallet.unlocked is True, "Wallet should be unlocked." # ACT @@ -20,5 +20,5 @@ def test_api_set_timeout(beekeeper: Beekeeper, wallet: WalletInfo) -> None: # n time.sleep(1.5) # ASSERT - bk_wallet = (beekeeper.api.list_wallets()).wallets[0] + bk_wallet = (beekeeper.api.list_created_wallets()).wallets[0] assert bk_wallet.unlocked is False, "Wallet after timeout should be locked." diff --git a/tests/beekeepy_test/handle/basic/test_wallet.py b/tests/beekeepy_test/handle/basic/test_wallet.py index 56253a75..ef1eac19 100644 --- a/tests/beekeepy_test/handle/basic/test_wallet.py +++ b/tests/beekeepy_test/handle/basic/test_wallet.py @@ -73,13 +73,13 @@ def test_timeout(beekeeper: Beekeeper, wallet: WalletInfo) -> None: # ASSERT info = beekeeper.api.get_info() assert timeout - (info.timeout_time - info.now).total_seconds() <= comparison_error_max_delta - check_wallets(beekeeper.api.list_wallets(), [wallet.name]) + check_wallets(beekeeper.api.list_created_wallets(), [wallet.name]) # ACT time.sleep(timeout + 1) # ASSERT - check_wallets(beekeeper.api.list_wallets(), [wallet.name], unlocked=False) + check_wallets(beekeeper.api.list_created_wallets(), [wallet.name], unlocked=False) @pytest.mark.parametrize("wallet_name", ["test", "123"]) diff --git a/tests/beekeepy_test/handle/commandline/application_command_line_options/patterns/help_pattern.txt b/tests/beekeepy_test/handle/commandline/application_command_line_options/patterns/help_pattern.txt index 22331b24..b08615a8 100644 --- a/tests/beekeepy_test/handle/commandline/application_command_line_options/patterns/help_pattern.txt +++ b/tests/beekeepy_test/handle/commandline/application_command_line_options/patterns/help_pattern.txt @@ -27,9 +27,6 @@ Application Options: --export-keys-wallet "["green-wallet", "PW5KYF9Rt4ETnuP4uheHSCm9kLbCuunf6RqeKgQ8QRoxZmGeZUhhk"]" - --notifications-endpoint arg - list of addresses, that will receive notification about in-chain - events --unlock-interval arg (=500) Protection against unlocking by bots. Every wrong `unlock` enables a delay. By default 500[ms]. @@ -62,5 +59,4 @@ Application Command Line Options: -d [ --data-dir ] dir Directory containing configuration file config.ini. Default location: $HOME/.beekeeper or CWD/. beekeeper -c [ --config ] filename (="config.ini") - Configuration file name relative to data-dir - + Configuration file name relative to data-dir \ No newline at end of file diff --git a/tests/beekeepy_test/handle/commandline/application_options/test_unlock_timeout.py b/tests/beekeepy_test/handle/commandline/application_options/test_unlock_timeout.py index a04ff79b..0ac7518a 100644 --- a/tests/beekeepy_test/handle/commandline/application_options/test_unlock_timeout.py +++ b/tests/beekeepy_test/handle/commandline/application_options/test_unlock_timeout.py @@ -13,7 +13,7 @@ if TYPE_CHECKING: def check_wallet_lock(beekeeper: Beekeeper, required_status: bool) -> None: """Check if wallets are have required unlock status.""" - response_list_wallets = beekeeper.api.list_wallets() + response_list_wallets = beekeeper.api.list_created_wallets() for wallet in response_list_wallets.wallets: assert wallet.unlocked == required_status diff --git a/tests/beekeepy_test/handle/storage/test_storage.py b/tests/beekeepy_test/handle/storage/test_storage.py index bdf54514..b5712ba6 100644 --- a/tests/beekeepy_test/handle/storage/test_storage.py +++ b/tests/beekeepy_test/handle/storage/test_storage.py @@ -34,14 +34,10 @@ def test_multiply_beekeepeer_same_storage(working_directory: Path) -> None: assert bk1.is_running() is True, "First instance of beekeeper should launch without any problems." # ACT & ASSERT 2 - bk2 = Beekeeper(settings=settings, logger=logger) - with pytest.raises(BeekeeperFailedToStartError): - bk2.run() - - assert checkers.check_for_pattern_in_file( - bk2.settings.ensured_working_directory / "stderr.log", - "Failed to lock access to wallet directory; is another `beekeeper` running?", - ), "There should be an info about another instance of beekeeper locking wallet directory." + with Beekeeper(settings=settings, logger=logger) as bk2: + assert ( + "opening beekeeper failed" in bk2.apis.app_status.get_app_status().statuses + ), "Second instance of beekeeper should fail to start." def test_multiply_beekeepeer_different_storage(working_directory: Path) -> None: @@ -76,44 +72,12 @@ def test_multiply_beekeepeer_different_storage(working_directory: Path) -> None: ), "There should be an no info about another instance of beekeeper locking wallet directory." -def get_remote_address_from_connection_file(working_dir: Path) -> HttpUrl: - connection: dict[str, str | int] = {} - with (working_dir / "beekeeper.connection").open() as file: - connection = json.load(file) - return HttpUrl( - f"{connection['address']}:{connection['port']}", - protocol=str(connection["type"]).lower(), # type: ignore[arg-type] - ) - - def test_beekeepers_files_generation(beekeeper: Beekeeper) -> None: """Test test_beekeepers_files_generation will check if beekeeper files are generated and have same content.""" # ARRANGE & ACT wallet_dir = beekeeper.settings.ensured_working_directory - beekeeper_connection_file = wallet_dir / "beekeeper.connection" - beekeeper_pid_file = wallet_dir / "beekeeper.pid" beekeeper_wallet_lock_file = wallet_dir / "beekeeper.wallet.lock" # ASSERT - assert beekeeper_connection_file.exists() is True, "File 'beekeeper.connection' should exists" - assert beekeeper_pid_file.exists() is True, "File 'beekeeper.pid' should exists" # File beekeeper.wallet.lock holds no value inside, so we need only to check is its exists. assert beekeeper_wallet_lock_file.exists() is True, "File 'beekeeper.wallet.lock' should exists" - - connection_url = get_remote_address_from_connection_file(wallet_dir) - assert connection_url is not None, "There should be connection details." - - if beekeeper.http_endpoint.address == "127.0.0.1": - assert connection_url.address in [ - "0.0.0.0", # noqa: S104 - "127.0.0.1", - ], "Address should point to localhost or all interfaces." - else: - assert connection_url.address == beekeeper.http_endpoint.address, "Host should be the same." - assert connection_url.port == beekeeper.http_endpoint.port, "Port should be the same." - assert connection_url.protocol == beekeeper.http_endpoint.protocol, "Protocol should be the same." - - with Path.open(beekeeper_pid_file) as pid: - content = json.load(pid) - - assert content["pid"] == str(beekeeper.pid), "Pid should be the same" -- GitLab From 24ad4488da66f67abca54eef91dde282efaebf8c Mon Sep 17 00:00:00 2001 From: kmochocki <kmochocki@syncad.com> Date: Wed, 9 Apr 2025 11:28:24 +0000 Subject: [PATCH 15/23] wip --- .gitlab-ci.yml | 4 ++-- hive | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 666fc2b8..5b86d727 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -34,7 +34,7 @@ variables: include: - project: 'hive/hive' # This has to be the same as the commit checked out in the submodule - ref: 40e6bc384b63fe116f370153a20c15a46888011f + ref: 89df1f6296f0dba171c96855bca30c5c708acd57 file: '/scripts/ci-helpers/prepare_data_image_job.yml' # DO NOT include ccc here. It will be indirectly included by above yaml file. #- project: 'hive/common-ci-configuration' @@ -101,7 +101,7 @@ build_beekeepy_wheel: extends: .build_wheel_template needs: - job: pre_commit_checks - - job: run_beekeepy_tests + # - job: run_beekeepy_tests - job: prepare_hived_image artifacts: true variables: diff --git a/hive b/hive index 40e6bc38..89df1f62 160000 --- a/hive +++ b/hive @@ -1 +1 @@ -Subproject commit 40e6bc384b63fe116f370153a20c15a46888011f +Subproject commit 89df1f6296f0dba171c96855bca30c5c708acd57 -- GitLab From ae21d226628e993a42de8f56b8934c6a70cd9b57 Mon Sep 17 00:00:00 2001 From: kmochocki <kmochocki@syncad.com> Date: Sat, 12 Apr 2025 19:49:52 +0000 Subject: [PATCH 16/23] Fill missing imports in __init__.py --- beekeepy/beekeepy/_executable/__init__.py | 12 ++-- beekeepy/beekeepy/_executable/abc/__init__.py | 11 ++-- beekeepy/beekeepy/_executable/abc/config.py | 58 ++++++++++++------- beekeepy/beekeepy/communication.py | 2 + beekeepy/beekeepy/handle/remote.py | 26 +++++++++ beekeepy/beekeepy/handle/runnable.py | 14 ++++- beekeepy/beekeepy/interfaces.py | 3 +- 7 files changed, 93 insertions(+), 33 deletions(-) diff --git a/beekeepy/beekeepy/_executable/__init__.py b/beekeepy/beekeepy/_executable/__init__.py index 6b444e5d..1f8cfa1f 100644 --- a/beekeepy/beekeepy/_executable/__init__.py +++ b/beekeepy/beekeepy/_executable/__init__.py @@ -15,15 +15,15 @@ from beekeepy._executable.beekeeper_executable import BeekeeperExecutable from beekeepy._utilities.key_pair import KeyPair __all__ = [ - "BeekeeperConfig", + "Arguments", + "ArgumentT", "BeekeeperArguments", + "BeekeeperConfig", "BeekeeperExecutable", - "Arguments", - "Executable", "Config", - "StreamRepresentation", - "StreamsHolder", - "ArgumentT", "ConfigT", + "Executable", "KeyPair", + "StreamRepresentation", + "StreamsHolder", ] diff --git a/beekeepy/beekeepy/_executable/abc/__init__.py b/beekeepy/beekeepy/_executable/abc/__init__.py index 57c9222f..ad16be2e 100644 --- a/beekeepy/beekeepy/_executable/abc/__init__.py +++ b/beekeepy/beekeepy/_executable/abc/__init__.py @@ -2,15 +2,16 @@ from __future__ import annotations from beekeepy._executable.abc.arguments import Arguments from beekeepy._executable.abc.config import Config -from beekeepy._executable.abc.executable import ArgumentT, ConfigT, Executable +from beekeepy._executable.abc.executable import ArgumentT, AutoCloser, ConfigT, Executable from beekeepy._executable.abc.streams import StreamRepresentation, StreamsHolder __all__ = [ "Arguments", - "Executable", - "StreamsHolder", - "StreamRepresentation", - "Config", "ArgumentT", + "AutoCloser", + "Config", "ConfigT", + "Executable", + "StreamRepresentation", + "StreamsHolder", ] diff --git a/beekeepy/beekeepy/_executable/abc/config.py b/beekeepy/beekeepy/_executable/abc/config.py index 413a43c2..ae4a402f 100644 --- a/beekeepy/beekeepy/_executable/abc/config.py +++ b/beekeepy/beekeepy/_executable/abc/config.py @@ -24,33 +24,42 @@ class Config(BaseModel): destination = destination / Config.DEFAULT_FILE_NAME if destination.is_dir() else destination with destination.open("wt", encoding="utf-8") as out_file: out_file.write("# config automatically generated by helpy\n") - for member_name, member_value in self.__dict__.items(): - if member_value is not None: - if isinstance(member_value, list) and len(member_value) == 0: - continue - - entry_name = self._convert_member_name_to_config_name(member_name) - entry_value = self._convert_member_value_to_config_value(member_name, member_value) - for value in [entry_value] if not isinstance(entry_value, list) else entry_value: - out_file.write(f"{entry_name} = {value}\n") + for line in self.write_to_lines(): + out_file.write(line + "\n") + + def write_to_lines(self) -> list[str]: + result = [] + for member_name, member_value in self.__dict__.items(): + if member_value is not None: + if isinstance(member_value, list) and len(member_value) == 0: + continue + + entry_name = self._convert_member_name_to_config_name(member_name) + entry_value = self._convert_member_value_to_config_value(member_name, member_value) + for value in [entry_value] if not isinstance(entry_value, list) else entry_value: + result.append(f"{entry_name} = {value}") # noqa: PERF401 # would be unreadable + return result @classmethod def load(cls, source: Path) -> Self: source = source / Config.DEFAULT_FILE_NAME if source.is_dir() else source assert source.exists(), "Given file does not exists." + return cls.load_from_lines(source.read_text().strip().splitlines()) + + @classmethod + def load_from_lines(cls, lines: list[str]) -> Self: fields = cls.__fields__ values_to_write: dict[str, Any] = {} - with source.open("rt", encoding="utf-8") as in_file: - for line in in_file: - if (line := line.strip("\n")) and not line.startswith("#"): - config_name, config_value = line.split("=") - member_name = cls._convert_config_name_to_member_name(config_name) - member_type = fields[member_name].annotation - if isinstance(member_type, UnionType) and (type(None) in get_args(member_type)): - member_type = get_args(member_type)[0] - values_to_write[member_name] = cls._convert_config_value_to_member_value( - config_value, expected=member_type, current_value=values_to_write.get(member_name) - ) + for line in lines: + if (line := line.strip("\n")) and not line.startswith("#"): + config_name, config_value = line.split("=") + member_name = cls._convert_config_name_to_member_name(config_name) + member_type = fields[member_name].annotation + if isinstance(member_type, UnionType) and (type(None) in get_args(member_type)): + member_type = get_args(member_type)[0] + values_to_write[member_name] = cls._convert_config_value_to_member_value( + config_value, expected=member_type, current_value=values_to_write.get(member_name) + ) return cls(**values_to_write) @classmethod @@ -124,3 +133,12 @@ class Config(BaseModel): return expected.validate(config_value) return expected(config_value) if expected is not None else None + + def get_differences_between(self, other: Self) -> dict[str, tuple[Any, Any]]: + differences = {} + for member_name in self.__dict__: + self_value = getattr(self, member_name) + other_value = getattr(other, member_name) + if self_value != other_value: + differences[member_name] = (self_value, other_value) + return differences diff --git a/beekeepy/beekeepy/communication.py b/beekeepy/beekeepy/communication.py index 7d2a6fdd..146c92b7 100644 --- a/beekeepy/beekeepy/communication.py +++ b/beekeepy/beekeepy/communication.py @@ -1,5 +1,6 @@ from __future__ import annotations +from beekeepy._communication import rules from beekeepy._communication.abc.communicator import AbstractCommunicator from beekeepy._communication.abc.overseer import AbstractOverseer from beekeepy._communication.abc.rules import OverseerRule, RulesClassifier @@ -18,6 +19,7 @@ __all__ = [ "HttpxCommunicator", "OverseerRule", "RequestCommunicator", + "rules", "RulesClassifier", "StrictOverseer", ] diff --git a/beekeepy/beekeepy/handle/remote.py b/beekeepy/beekeepy/handle/remote.py index e5330f99..eeb3e652 100644 --- a/beekeepy/beekeepy/handle/remote.py +++ b/beekeepy/beekeepy/handle/remote.py @@ -1,11 +1,24 @@ from __future__ import annotations +from beekeepy._apis import ( + AppStatusProbeAsyncApiCollection, + AppStatusProbeSyncApiCollection, + AsyncAppStatusApi, + AsyncBeekeeperApi, + BeekeeperAsyncApiCollection, + BeekeeperSyncApiCollection, + SyncAppStatusApi, + SyncBeekeeperApi, +) from beekeepy._apis.abc import ( AbstractAsyncApi, AbstractAsyncApiCollection, AbstractSyncApi, AbstractSyncApiCollection, + ApiArgumentSerialization, + AsyncSendable, RegisteredApisT, + SyncSendable, ) from beekeepy._remote_handle import ( AbstractAsyncHandle, @@ -16,6 +29,7 @@ from beekeepy._remote_handle import ( RemoteHandleSettings, SyncBatchHandle, ) +from beekeepy._remote_handle.abc.handle import RemoteSettingsT __all__ = [ "AbstractAsyncApi", @@ -24,10 +38,22 @@ __all__ = [ "AbstractSyncApi", "AbstractSyncApiCollection", "AbstractSyncHandle", + "ApiArgumentSerialization", + "AppStatusProbeAsyncApiCollection", + "AppStatusProbeSyncApiCollection", + "AsyncAppStatusApi", "AsyncBatchHandle", "AsyncBeekeeper", + "AsyncBeekeeperApi", + "AsyncSendable", "Beekeeper", + "BeekeeperAsyncApiCollection", + "BeekeeperSyncApiCollection", "RegisteredApisT", "RemoteHandleSettings", + "RemoteSettingsT", + "SyncAppStatusApi", "SyncBatchHandle", + "SyncBeekeeperApi", + "SyncSendable", ] diff --git a/beekeepy/beekeepy/handle/runnable.py b/beekeepy/beekeepy/handle/runnable.py index 247ed9e6..a9db11d2 100644 --- a/beekeepy/beekeepy/handle/runnable.py +++ b/beekeepy/beekeepy/handle/runnable.py @@ -1,14 +1,26 @@ from __future__ import annotations from beekeepy._executable import BeekeeperArguments, BeekeeperConfig, BeekeeperExecutable +from beekeepy._executable.abc import Arguments, ArgumentT, AutoCloser, Config, ConfigT, Executable from beekeepy._runnable_handle import AsyncBeekeeper, Beekeeper, RunnableHandleSettings, close_already_running_beekeeper +from beekeepy._runnable_handle.match_ports import PortMatchingResult, match_ports +from beekeepy._runnable_handle.runnable_handle import RunnableHandle __all__ = [ + "Arguments", + "ArgumentT", "AsyncBeekeeper", + "AutoCloser", "Beekeeper", - "BeekeeperConfig", "BeekeeperArguments", + "BeekeeperConfig", "BeekeeperExecutable", "close_already_running_beekeeper", + "Config", + "ConfigT", + "Executable", + "match_ports", + "PortMatchingResult", + "RunnableHandle", "RunnableHandleSettings", ] diff --git a/beekeepy/beekeepy/interfaces.py b/beekeepy/beekeepy/interfaces.py index 4b137461..41a299c7 100644 --- a/beekeepy/beekeepy/interfaces.py +++ b/beekeepy/beekeepy/interfaces.py @@ -1,6 +1,6 @@ from __future__ import annotations -from beekeepy._communication import HttpUrl, P2PUrl, Url, WsUrl +from beekeepy._communication import AnyUrl, HttpUrl, P2PUrl, Url, WsUrl from beekeepy._utilities.context import ContextAsync, ContextSync, SelfContextAsync, SelfContextSync from beekeepy._utilities.context_settings_updater import ContextSettingsUpdater from beekeepy._utilities.delay_guard import AsyncDelayGuard, DelayGuardBase, SyncDelayGuard @@ -12,6 +12,7 @@ from beekeepy._utilities.stopwatch import Stopwatch, StopwatchResult from beekeepy._utilities.suppress_api_not_found import SuppressApiNotFound __all__ = [ + "AnyUrl", "AsyncDelayGuard", "ContextAsync", "ContextSettingsUpdater", -- GitLab From bd0e522351b68a0b87e7ce515b4dbe3139818019 Mon Sep 17 00:00:00 2001 From: kmochocki <kmochocki@syncad.com> Date: Mon, 14 Apr 2025 02:09:54 +0000 Subject: [PATCH 17/23] wip --- beekeepy/beekeepy/_executable/abc/config.py | 22 +++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/beekeepy/beekeepy/_executable/abc/config.py b/beekeepy/beekeepy/_executable/abc/config.py index ae4a402f..5793fb71 100644 --- a/beekeepy/beekeepy/_executable/abc/config.py +++ b/beekeepy/beekeepy/_executable/abc/config.py @@ -1,8 +1,10 @@ from __future__ import annotations +import re +from inspect import isclass from pathlib import Path from types import UnionType -from typing import TYPE_CHECKING, Any, ClassVar, get_args +from typing import TYPE_CHECKING, Any, ClassVar, Final, get_args from loguru import logger from pydantic import BaseModel @@ -13,6 +15,8 @@ from beekeepy.exceptions import InvalidOptionError if TYPE_CHECKING: from typing_extensions import Self +CONFIG_MEMBER_REGEX: Final[re.Pattern[str]] = re.compile(r"^([a-zA-Z]+)(\-([a-zA-Z0-9]+))*$") + class Config(BaseModel): DEFAULT_FILE_NAME: ClassVar[str] = "config.ini" @@ -49,7 +53,7 @@ class Config(BaseModel): @classmethod def load_from_lines(cls, lines: list[str]) -> Self: fields = cls.__fields__ - values_to_write: dict[str, Any] = {} + values_to_write: dict[str, Any] = cls().dict() for line in lines: if (line := line.strip("\n")) and not line.startswith("#"): config_name, config_value = line.split("=") @@ -68,6 +72,9 @@ class Config(BaseModel): @classmethod def _convert_config_name_to_member_name(cls, config_name: str) -> str: + config_name = config_name.strip("""" """) + if CONFIG_MEMBER_REGEX.match(config_name) is None: + raise KeyError(f"""Unknown config entry name: `{config_name}`.""") return config_name.strip().replace("-", "_") @classmethod @@ -87,7 +94,7 @@ class Config(BaseModel): return str(member_value) @classmethod - def _convert_config_value_to_member_value( # noqa: PLR0911, C901 + def _convert_config_value_to_member_value( # noqa: PLR0911, PLR0912, C901 cls, config_value: str, *, expected: type[Any], current_value: Any | None ) -> Any | None: config_value = config_value.strip() @@ -97,7 +104,11 @@ class Config(BaseModel): if expected == Path: return Path(config_value.replace('"', "")) - if issubclass(expected, list) or "list" in str(expected): + if ( + (isclass(expected) and issubclass(expected, list)) + or isinstance(current_value, list) + or "list" in str(expected) + ): list_arg_t = get_args(expected)[0] if len(get_args(list_arg_t)): # in case of unions list_arg_t = get_args(list_arg_t)[0] @@ -132,6 +143,9 @@ class Config(BaseModel): if isinstance(expected, type) and issubclass(expected, int | str) and hasattr(expected, "validate"): return expected.validate(config_value) + if str(expected).startswith("typing.Union["): + expected = get_args(expected)[0] + return expected(config_value) if expected is not None else None def get_differences_between(self, other: Self) -> dict[str, tuple[Any, Any]]: -- GitLab From 68b2b51f94ab7d1b543b6d9531352af733b629f8 Mon Sep 17 00:00:00 2001 From: kmochocki <kmochocki@syncad.com> Date: Wed, 16 Apr 2025 10:59:32 +0000 Subject: [PATCH 18/23] wip --- .pre-commit-config.yaml | 4 +- beekeepy/poetry.lock | 71 ++++++++++++++++++------ tests/local-tools/poetry.lock | 100 +++++++++++++++++++++++++++------- 3 files changed, 135 insertions(+), 40 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d8dea697..aca1dd4d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -14,14 +14,14 @@ repos: - id: end-of-file-fixer exclude: ".*_pattern.txt|.*wallet" - repo: https://github.com/python-poetry/poetry - rev: 1.7.1 + rev: 2.1.2 hooks: - id: poetry-lock name: checking if beekeepy/poetry.lock is consistent with pyproject.toml args: [ "-C", "./beekeepy", "--no-update" ] language_version: python3.12 - repo: https://github.com/python-poetry/poetry - rev: 1.7.1 + rev: 2.1.2 hooks: - id: poetry-lock name: checking if tests/local-tools/poetry.lock is consistent with pyproject.toml diff --git a/beekeepy/poetry.lock b/beekeepy/poetry.lock index 21c71f37..ae4da323 100644 --- a/beekeepy/poetry.lock +++ b/beekeepy/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.2 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" @@ -6,6 +6,7 @@ version = "2.6.1" description = "Happy Eyeballs for asyncio" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8"}, {file = "aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558"}, @@ -17,6 +18,7 @@ version = "3.11.16" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "aiohttp-3.11.16-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fb46bb0f24813e6cede6cc07b1961d4b04f331f7112a23b5e21f567da4ee50aa"}, {file = "aiohttp-3.11.16-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:54eb3aead72a5c19fad07219acd882c1643a1027fbcdefac9b502c267242f955"}, @@ -111,7 +113,7 @@ propcache = ">=0.2.0" yarl = ">=1.17.0,<2.0" [package.extras] -speedups = ["Brotli", "aiodns (>=3.2.0)", "brotlicffi"] +speedups = ["Brotli ; platform_python_implementation == \"CPython\"", "aiodns (>=3.2.0) ; sys_platform == \"linux\" or sys_platform == \"darwin\"", "brotlicffi ; platform_python_implementation != \"CPython\""] [[package]] name = "aiosignal" @@ -119,6 +121,7 @@ version = "1.3.1" description = "aiosignal: a list of registered asynchronous callbacks" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"}, {file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"}, @@ -133,6 +136,7 @@ version = "4.7.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "anyio-4.7.0-py3-none-any.whl", hash = "sha256:ea60c3723ab42ba6fff7e8ccb0488c898ec538ff4df1f1d5e642c3601d07e352"}, {file = "anyio-4.7.0.tar.gz", hash = "sha256:2f834749c602966b7d456a7567cafcb309f96482b5081d14ac93ccd457f9dd48"}, @@ -145,7 +149,7 @@ typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} [package.extras] doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx_rtd_theme"] -test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21)"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1) ; python_version >= \"3.10\"", "uvloop (>=0.21) ; platform_python_implementation == \"CPython\" and platform_system != \"Windows\""] trio = ["trio (>=0.26.1)"] [[package]] @@ -154,18 +158,19 @@ version = "24.2.0" description = "Classes Without Boilerplate" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"}, {file = "attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346"}, ] [package.extras] -benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +benchmark = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\"", "pytest-xdist[psutil]"] +cov = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\"", "pytest-xdist[psutil]"] +dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\"", "pytest-xdist[psutil]"] docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] -tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] +tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\"", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\""] [[package]] name = "certifi" @@ -173,6 +178,7 @@ version = "2024.8.30" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, @@ -184,6 +190,7 @@ version = "2.0.12" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.5.0" +groups = ["main"] files = [ {file = "charset-normalizer-2.0.12.tar.gz", hash = "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597"}, {file = "charset_normalizer-2.0.12-py3-none-any.whl", hash = "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"}, @@ -198,6 +205,8 @@ version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["main"] +markers = "sys_platform == \"win32\"" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, @@ -209,6 +218,7 @@ version = "1.5.0" description = "A list-like structure which implements collections.abc.MutableSequence" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "frozenlist-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5b6a66c18b5b9dd261ca98dffcb826a525334b2f29e7caa54e182255c5f6a65a"}, {file = "frozenlist-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d1b3eb7b05ea246510b43a7e53ed1653e55c2121019a97e60cad7efb881a97bb"}, @@ -310,6 +320,7 @@ version = "0.14.0" description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, @@ -321,6 +332,7 @@ version = "4.1.0" description = "HTTP/2 State-Machine based protocol implementation" optional = false python-versions = ">=3.6.1" +groups = ["main"] files = [ {file = "h2-4.1.0-py3-none-any.whl", hash = "sha256:03a46bcf682256c95b5fd9e9a99c1323584c3eec6440d379b9903d709476bc6d"}, {file = "h2-4.1.0.tar.gz", hash = "sha256:a83aca08fbe7aacb79fec788c9c0bac936343560ed9ec18b82a13a12c28d2abb"}, @@ -336,6 +348,7 @@ version = "4.0.0" description = "Pure-Python HPACK header compression" optional = false python-versions = ">=3.6.1" +groups = ["main"] files = [ {file = "hpack-4.0.0-py3-none-any.whl", hash = "sha256:84a076fad3dc9a9f8063ccb8041ef100867b1878b25ef0ee63847a5d53818a6c"}, {file = "hpack-4.0.0.tar.gz", hash = "sha256:fc41de0c63e687ebffde81187a948221294896f6bdc0ae2312708df339430095"}, @@ -347,6 +360,7 @@ version = "0.16.3" description = "A minimal low-level HTTP client." optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "httpcore-0.16.3-py3-none-any.whl", hash = "sha256:da1fb708784a938aa084bde4feb8317056c55037247c787bd7e19eb2c2949dc0"}, {file = "httpcore-0.16.3.tar.gz", hash = "sha256:c5d6f04e2fc530f39e0c077e6a30caa53f1451096120f1f38b954afd0b17c0cb"}, @@ -368,6 +382,7 @@ version = "0.23.3" description = "The next generation HTTP client." optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "httpx-0.23.3-py3-none-any.whl", hash = "sha256:a211fcce9b1254ea24f0cd6af9869b3d29aba40154e947d2a07bb499b3e310d6"}, {file = "httpx-0.23.3.tar.gz", hash = "sha256:9818458eb565bb54898ccb9b8b251a28785dd4a55afbc23d0eb410754fe7d0f9"}, @@ -381,7 +396,7 @@ rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]} sniffio = "*" [package.extras] -brotli = ["brotli", "brotlicffi"] +brotli = ["brotli ; platform_python_implementation == \"CPython\"", "brotlicffi ; platform_python_implementation != \"CPython\""] cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<13)"] http2 = ["h2 (>=3,<5)"] socks = ["socksio (==1.*)"] @@ -392,6 +407,7 @@ version = "6.0.1" description = "HTTP/2 framing layer for Python" optional = false python-versions = ">=3.6.1" +groups = ["main"] files = [ {file = "hyperframe-6.0.1-py3-none-any.whl", hash = "sha256:0ec6bafd80d8ad2195c4f03aacba3a8265e57bc4cff261e802bf39970ed02a15"}, {file = "hyperframe-6.0.1.tar.gz", hash = "sha256:ae510046231dc8e9ecb1a6586f63d2347bf4c8905914aa84ba585ae85f28a914"}, @@ -403,6 +419,7 @@ version = "3.10" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, @@ -417,6 +434,7 @@ version = "0.7.2" description = "Python logging made (stupidly) simple" optional = false python-versions = ">=3.5" +groups = ["main"] files = [ {file = "loguru-0.7.2-py3-none-any.whl", hash = "sha256:003d71e3d3ed35f0f8984898359d65b79e5b21943f78af86aa5491210429b8eb"}, {file = "loguru-0.7.2.tar.gz", hash = "sha256:e671a53522515f34fd406340ee968cb9ecafbc4b36c679da03c18fd8d0bd51ac"}, @@ -427,7 +445,7 @@ colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""} win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""} [package.extras] -dev = ["Sphinx (==7.2.5)", "colorama (==0.4.5)", "colorama (==0.4.6)", "exceptiongroup (==1.1.3)", "freezegun (==1.1.0)", "freezegun (==1.2.2)", "mypy (==v0.910)", "mypy (==v0.971)", "mypy (==v1.4.1)", "mypy (==v1.5.1)", "pre-commit (==3.4.0)", "pytest (==6.1.2)", "pytest (==7.4.0)", "pytest-cov (==2.12.1)", "pytest-cov (==4.1.0)", "pytest-mypy-plugins (==1.9.3)", "pytest-mypy-plugins (==3.0.0)", "sphinx-autobuild (==2021.3.14)", "sphinx-rtd-theme (==1.3.0)", "tox (==3.27.1)", "tox (==4.11.0)"] +dev = ["Sphinx (==7.2.5) ; python_version >= \"3.9\"", "colorama (==0.4.5) ; python_version < \"3.8\"", "colorama (==0.4.6) ; python_version >= \"3.8\"", "exceptiongroup (==1.1.3) ; python_version >= \"3.7\" and python_version < \"3.11\"", "freezegun (==1.1.0) ; python_version < \"3.8\"", "freezegun (==1.2.2) ; python_version >= \"3.8\"", "mypy (==v0.910) ; python_version < \"3.6\"", "mypy (==v0.971) ; python_version == \"3.6\"", "mypy (==v1.4.1) ; python_version == \"3.7\"", "mypy (==v1.5.1) ; python_version >= \"3.8\"", "pre-commit (==3.4.0) ; python_version >= \"3.8\"", "pytest (==6.1.2) ; python_version < \"3.8\"", "pytest (==7.4.0) ; python_version >= \"3.8\"", "pytest-cov (==2.12.1) ; python_version < \"3.8\"", "pytest-cov (==4.1.0) ; python_version >= \"3.8\"", "pytest-mypy-plugins (==1.9.3) ; python_version >= \"3.6\" and python_version < \"3.8\"", "pytest-mypy-plugins (==3.0.0) ; python_version >= \"3.8\"", "sphinx-autobuild (==2021.3.14) ; python_version >= \"3.9\"", "sphinx-rtd-theme (==1.3.0) ; python_version >= \"3.9\"", "tox (==3.27.1) ; python_version < \"3.8\"", "tox (==4.11.0) ; python_version >= \"3.8\""] [[package]] name = "multidict" @@ -435,6 +453,7 @@ version = "6.1.0" description = "multidict implementation" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3380252550e372e8511d49481bd836264c009adb826b23fefcc5dd3c69692f60"}, {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99f826cbf970077383d7de805c0681799491cb939c25450b9b5b3ced03ca99f1"}, @@ -536,6 +555,7 @@ version = "0.2.1" description = "Accelerated property cache" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "propcache-0.2.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6b3f39a85d671436ee3d12c017f8fdea38509e4f25b28eb25877293c98c243f6"}, {file = "propcache-0.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d51fbe4285d5db5d92a929e3e21536ea3dd43732c5b177c7ef03f918dff9f2"}, @@ -627,6 +647,7 @@ version = "7.0.0" description = "Cross-platform lib for process and system monitoring in Python. NOTE: the syntax of this script MUST be kept compatible with Python 2.7." optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25"}, {file = "psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da"}, @@ -650,6 +671,7 @@ version = "1.10.18" description = "Data validation and settings management using python type hints" optional = false python-versions = ">=3.7" +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"}, @@ -709,6 +731,7 @@ version = "2.8.2" description = "Extensions to the standard Python datetime module" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main"] files = [ {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, @@ -723,6 +746,7 @@ version = "2.32.3" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, @@ -744,6 +768,7 @@ version = "1.5.0" description = "Validating URI References per RFC 3986" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"}, {file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"}, @@ -761,6 +786,7 @@ version = "0.0.1.dev336+c6f2974" description = "Tools for checking if message fits expected format" optional = false python-versions = ">=3.12,<4.0" +groups = ["main"] files = [ {file = "schemas-0.0.1.dev336+c6f2974-py3-none-any.whl", hash = "sha256:9a2148f014bdf82e05d138629df11cb20f53799eb32dcfff14942b4b20a76cc6"}, ] @@ -779,19 +805,20 @@ version = "77.0.3" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.9" +groups = ["main"] 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)", "ruff (>=0.8.0)"] -core = ["importlib_metadata (>=6)", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\"", "ruff (>=0.8.0) ; sys_platform != \"cygwin\""] +core = ["importlib_metadata (>=6) ; python_version < \"3.10\"", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1) ; python_version < \"3.11\"", "wheel (>=0.43.0)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] enabler = ["pytest-enabler (>=2.2)"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] -type = ["importlib_metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.14.*)", "pytest-mypy"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21) ; python_version >= \"3.9\" and sys_platform != \"cygwin\"", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf ; sys_platform != \"cygwin\"", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] +type = ["importlib_metadata (>=7.0.2) ; python_version < \"3.10\"", "jaraco.develop (>=7.21) ; sys_platform != \"cygwin\"", "mypy (==1.14.*)", "pytest-mypy"] [[package]] name = "six" @@ -799,6 +826,7 @@ version = "1.17.0" description = "Python 2 and 3 compatibility utilities" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main"] files = [ {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, @@ -810,6 +838,7 @@ version = "1.3.1" description = "Sniff out which async library your code is running under" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, @@ -821,6 +850,7 @@ version = "76.0.0.20250313" description = "Typing stubs for setuptools" optional = false python-versions = ">=3.9" +groups = ["main"] 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"}, @@ -835,6 +865,7 @@ version = "4.12.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, @@ -846,14 +877,15 @@ version = "1.26.20" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +groups = ["main"] files = [ {file = "urllib3-1.26.20-py2.py3-none-any.whl", hash = "sha256:0ed14ccfbf1c30a9072c7ca157e4319b70d65f623e91e7b32fadb2853431016e"}, {file = "urllib3-1.26.20.tar.gz", hash = "sha256:40c2dc0c681e47eb8f90e7e27bf6ff7df2e677421fd46756da1161c39ca70d32"}, ] [package.extras] -brotli = ["brotli (==1.0.9)", "brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] -secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] +brotli = ["brotli (==1.0.9) ; os_name != \"nt\" and python_version < \"3\" and platform_python_implementation == \"CPython\"", "brotli (>=1.0.9) ; python_version >= \"3\" and platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; (os_name != \"nt\" or python_version >= \"3\") and platform_python_implementation != \"CPython\"", "brotlipy (>=0.6.0) ; os_name == \"nt\" and python_version < \"3\""] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress ; python_version == \"2.7\"", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] @@ -862,13 +894,15 @@ version = "1.2.0" description = "A small Python utility to set file creation time on Windows" optional = false python-versions = ">=3.5" +groups = ["main"] +markers = "sys_platform == \"win32\"" files = [ {file = "win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390"}, {file = "win32_setctime-1.2.0.tar.gz", hash = "sha256:ae1fdf948f5640aae05c511ade119313fb6a30d7eabe25fef9764dca5873c4c0"}, ] [package.extras] -dev = ["black (>=19.3b0)", "pytest (>=4.6.2)"] +dev = ["black (>=19.3b0) ; python_version >= \"3.6\"", "pytest (>=4.6.2)"] [[package]] name = "yarl" @@ -876,6 +910,7 @@ version = "1.18.3" description = "Yet another URL library" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7df647e8edd71f000a5208fe6ff8c382a1de8edfbccdbbfe649d263de07d8c34"}, {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c69697d3adff5aa4f874b19c0e4ed65180ceed6318ec856ebc423aa5850d84f7"}, @@ -967,6 +1002,6 @@ multidict = ">=4.0" propcache = ">=0.2.0" [metadata] -lock-version = "2.0" +lock-version = "2.1" python-versions = "^3.12" content-hash = "82e955944afb54dfa4727975524023ccae164af25866f36871b7ae34129ccdd8" diff --git a/tests/local-tools/poetry.lock b/tests/local-tools/poetry.lock index 9db12e17..106acc48 100644 --- a/tests/local-tools/poetry.lock +++ b/tests/local-tools/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.2 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" @@ -6,6 +6,7 @@ version = "2.6.1" description = "Happy Eyeballs for asyncio" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8"}, {file = "aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558"}, @@ -17,6 +18,7 @@ version = "3.11.16" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "aiohttp-3.11.16-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fb46bb0f24813e6cede6cc07b1961d4b04f331f7112a23b5e21f567da4ee50aa"}, {file = "aiohttp-3.11.16-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:54eb3aead72a5c19fad07219acd882c1643a1027fbcdefac9b502c267242f955"}, @@ -111,7 +113,7 @@ propcache = ">=0.2.0" yarl = ">=1.17.0,<2.0" [package.extras] -speedups = ["Brotli", "aiodns (>=3.2.0)", "brotlicffi"] +speedups = ["Brotli ; platform_python_implementation == \"CPython\"", "aiodns (>=3.2.0) ; sys_platform == \"linux\" or sys_platform == \"darwin\"", "brotlicffi ; platform_python_implementation != \"CPython\""] [[package]] name = "aiosignal" @@ -119,6 +121,7 @@ version = "1.3.1" description = "aiosignal: a list of registered asynchronous callbacks" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"}, {file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"}, @@ -133,6 +136,7 @@ version = "4.4.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "anyio-4.4.0-py3-none-any.whl", hash = "sha256:c1b2d8f46a8a812513012e1107cb0e68c17159a7a594208005a57dc776e1bdc7"}, {file = "anyio-4.4.0.tar.gz", hash = "sha256:5aadc6a1bbb7cdb0bede386cac5e2940f5e2ff3aa20277e991cf028e0585ce94"}, @@ -144,7 +148,7 @@ sniffio = ">=1.1" [package.extras] doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] -test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17) ; platform_python_implementation == \"CPython\" and platform_system != \"Windows\""] trio = ["trio (>=0.23)"] [[package]] @@ -153,18 +157,19 @@ version = "24.2.0" description = "Classes Without Boilerplate" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"}, {file = "attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346"}, ] [package.extras] -benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +benchmark = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\"", "pytest-xdist[psutil]"] +cov = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\"", "pytest-xdist[psutil]"] +dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\"", "pytest-xdist[psutil]"] docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] -tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] +tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\"", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\""] [[package]] name = "beekeepy" @@ -172,6 +177,7 @@ version = "0.0.0" description = "All in one package for beekeeper interaction via Python interface." optional = false python-versions = "^3.12" +groups = ["main"] files = [] develop = true @@ -196,6 +202,7 @@ version = "2024.8.30" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, @@ -207,6 +214,7 @@ version = "3.4.0" description = "Validate configuration and produce human readable error messages." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, @@ -218,6 +226,7 @@ version = "2.0.12" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.5.0" +groups = ["main"] files = [ {file = "charset-normalizer-2.0.12.tar.gz", hash = "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597"}, {file = "charset_normalizer-2.0.12-py3-none-any.whl", hash = "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"}, @@ -232,6 +241,8 @@ version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["main", "dev"] +markers = "sys_platform == \"win32\"" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, @@ -243,6 +254,7 @@ version = "0.3.8" description = "Distribution utilities" optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"}, {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, @@ -254,6 +266,7 @@ version = "2.1.1" description = "execnet: rapid multi-Python deployment" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc"}, {file = "execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3"}, @@ -268,6 +281,7 @@ version = "3.16.1" description = "A platform independent file lock." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"}, {file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"}, @@ -276,7 +290,7 @@ files = [ [package.extras] docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4.1)"] testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"] -typing = ["typing-extensions (>=4.12.2)"] +typing = ["typing-extensions (>=4.12.2) ; python_version < \"3.11\""] [[package]] name = "frozenlist" @@ -284,6 +298,7 @@ version = "1.4.1" description = "A list-like structure which implements collections.abc.MutableSequence" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f9aa1878d1083b276b0196f2dfbe00c9b7e752475ed3b682025ff20c1c1f51ac"}, {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29acab3f66f0f24674b7dc4736477bcd4bc3ad4b896f5f45379a67bce8b96868"}, @@ -370,6 +385,7 @@ version = "0.14.0" description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, @@ -381,6 +397,7 @@ version = "4.1.0" description = "HTTP/2 State-Machine based protocol implementation" optional = false python-versions = ">=3.6.1" +groups = ["main"] files = [ {file = "h2-4.1.0-py3-none-any.whl", hash = "sha256:03a46bcf682256c95b5fd9e9a99c1323584c3eec6440d379b9903d709476bc6d"}, {file = "h2-4.1.0.tar.gz", hash = "sha256:a83aca08fbe7aacb79fec788c9c0bac936343560ed9ec18b82a13a12c28d2abb"}, @@ -396,6 +413,7 @@ version = "4.0.0" description = "Pure-Python HPACK header compression" optional = false python-versions = ">=3.6.1" +groups = ["main"] files = [ {file = "hpack-4.0.0-py3-none-any.whl", hash = "sha256:84a076fad3dc9a9f8063ccb8041ef100867b1878b25ef0ee63847a5d53818a6c"}, {file = "hpack-4.0.0.tar.gz", hash = "sha256:fc41de0c63e687ebffde81187a948221294896f6bdc0ae2312708df339430095"}, @@ -407,6 +425,7 @@ version = "0.16.3" description = "A minimal low-level HTTP client." optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "httpcore-0.16.3-py3-none-any.whl", hash = "sha256:da1fb708784a938aa084bde4feb8317056c55037247c787bd7e19eb2c2949dc0"}, {file = "httpcore-0.16.3.tar.gz", hash = "sha256:c5d6f04e2fc530f39e0c077e6a30caa53f1451096120f1f38b954afd0b17c0cb"}, @@ -428,6 +447,7 @@ version = "0.23.3" description = "The next generation HTTP client." optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "httpx-0.23.3-py3-none-any.whl", hash = "sha256:a211fcce9b1254ea24f0cd6af9869b3d29aba40154e947d2a07bb499b3e310d6"}, {file = "httpx-0.23.3.tar.gz", hash = "sha256:9818458eb565bb54898ccb9b8b251a28785dd4a55afbc23d0eb410754fe7d0f9"}, @@ -441,7 +461,7 @@ rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]} sniffio = "*" [package.extras] -brotli = ["brotli", "brotlicffi"] +brotli = ["brotli ; platform_python_implementation == \"CPython\"", "brotlicffi ; platform_python_implementation != \"CPython\""] cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<13)"] http2 = ["h2 (>=3,<5)"] socks = ["socksio (==1.*)"] @@ -452,6 +472,7 @@ version = "6.0.1" description = "HTTP/2 framing layer for Python" optional = false python-versions = ">=3.6.1" +groups = ["main"] files = [ {file = "hyperframe-6.0.1-py3-none-any.whl", hash = "sha256:0ec6bafd80d8ad2195c4f03aacba3a8265e57bc4cff261e802bf39970ed02a15"}, {file = "hyperframe-6.0.1.tar.gz", hash = "sha256:ae510046231dc8e9ecb1a6586f63d2347bf4c8905914aa84ba585ae85f28a914"}, @@ -463,6 +484,7 @@ version = "2.6.1" description = "File identification library for Python" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "identify-2.6.1-py2.py3-none-any.whl", hash = "sha256:53863bcac7caf8d2ed85bd20312ea5dcfc22226800f6d6881f232d861db5a8f0"}, {file = "identify-2.6.1.tar.gz", hash = "sha256:91478c5fb7c3aac5ff7bf9b4344f803843dc586832d5f110d672b19aa1984c98"}, @@ -477,6 +499,7 @@ version = "3.10" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, @@ -491,6 +514,7 @@ version = "2.0.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, @@ -502,6 +526,7 @@ version = "0.7.2" description = "Python logging made (stupidly) simple" optional = false python-versions = ">=3.5" +groups = ["main"] files = [ {file = "loguru-0.7.2-py3-none-any.whl", hash = "sha256:003d71e3d3ed35f0f8984898359d65b79e5b21943f78af86aa5491210429b8eb"}, {file = "loguru-0.7.2.tar.gz", hash = "sha256:e671a53522515f34fd406340ee968cb9ecafbc4b36c679da03c18fd8d0bd51ac"}, @@ -512,7 +537,7 @@ colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""} win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""} [package.extras] -dev = ["Sphinx (==7.2.5)", "colorama (==0.4.5)", "colorama (==0.4.6)", "exceptiongroup (==1.1.3)", "freezegun (==1.1.0)", "freezegun (==1.2.2)", "mypy (==v0.910)", "mypy (==v0.971)", "mypy (==v1.4.1)", "mypy (==v1.5.1)", "pre-commit (==3.4.0)", "pytest (==6.1.2)", "pytest (==7.4.0)", "pytest-cov (==2.12.1)", "pytest-cov (==4.1.0)", "pytest-mypy-plugins (==1.9.3)", "pytest-mypy-plugins (==3.0.0)", "sphinx-autobuild (==2021.3.14)", "sphinx-rtd-theme (==1.3.0)", "tox (==3.27.1)", "tox (==4.11.0)"] +dev = ["Sphinx (==7.2.5) ; python_version >= \"3.9\"", "colorama (==0.4.5) ; python_version < \"3.8\"", "colorama (==0.4.6) ; python_version >= \"3.8\"", "exceptiongroup (==1.1.3) ; python_version >= \"3.7\" and python_version < \"3.11\"", "freezegun (==1.1.0) ; python_version < \"3.8\"", "freezegun (==1.2.2) ; python_version >= \"3.8\"", "mypy (==v0.910) ; python_version < \"3.6\"", "mypy (==v0.971) ; python_version == \"3.6\"", "mypy (==v1.4.1) ; python_version == \"3.7\"", "mypy (==v1.5.1) ; python_version >= \"3.8\"", "pre-commit (==3.4.0) ; python_version >= \"3.8\"", "pytest (==6.1.2) ; python_version < \"3.8\"", "pytest (==7.4.0) ; python_version >= \"3.8\"", "pytest-cov (==2.12.1) ; python_version < \"3.8\"", "pytest-cov (==4.1.0) ; python_version >= \"3.8\"", "pytest-mypy-plugins (==1.9.3) ; python_version >= \"3.6\" and python_version < \"3.8\"", "pytest-mypy-plugins (==3.0.0) ; python_version >= \"3.8\"", "sphinx-autobuild (==2021.3.14) ; python_version >= \"3.9\"", "sphinx-rtd-theme (==1.3.0) ; python_version >= \"3.9\"", "tox (==3.27.1) ; python_version < \"3.8\"", "tox (==4.11.0) ; python_version >= \"3.8\""] [[package]] name = "multidict" @@ -520,6 +545,7 @@ version = "6.1.0" description = "multidict implementation" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3380252550e372e8511d49481bd836264c009adb826b23fefcc5dd3c69692f60"}, {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99f826cbf970077383d7de805c0681799491cb939c25450b9b5b3ced03ca99f1"}, @@ -621,6 +647,7 @@ version = "1.11.2" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "mypy-1.11.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d42a6dd818ffce7be66cce644f1dff482f1d97c53ca70908dff0b9ddc120b77a"}, {file = "mypy-1.11.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:801780c56d1cdb896eacd5619a83e427ce436d86a3bdf9112527f24a66618fef"}, @@ -667,6 +694,7 @@ version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." optional = false python-versions = ">=3.5" +groups = ["dev"] files = [ {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, @@ -678,6 +706,7 @@ version = "1.9.1" description = "Node.js virtual environment builder" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["dev"] files = [ {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, @@ -689,6 +718,7 @@ version = "24.1" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, @@ -700,6 +730,7 @@ version = "4.3.6" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, @@ -716,6 +747,7 @@ version = "1.5.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, @@ -731,6 +763,7 @@ version = "2.21.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "pre_commit-2.21.0-py2.py3-none-any.whl", hash = "sha256:e2f91727039fc39a92f58a588a25b87f936de6567eed4f0e673e0507edc75bad"}, {file = "pre_commit-2.21.0.tar.gz", hash = "sha256:31ef31af7e474a8d8995027fefdfcf509b5c913ff31f2015b4ec4beb26a6f658"}, @@ -749,6 +782,7 @@ version = "0.3.0" description = "Accelerated property cache" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "propcache-0.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:efa44f64c37cc30c9f05932c740a8b40ce359f51882c70883cc95feac842da4d"}, {file = "propcache-0.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2383a17385d9800b6eb5855c2f05ee550f803878f344f58b6e194de08b96352c"}, @@ -856,6 +890,7 @@ version = "7.0.0" description = "Cross-platform lib for process and system monitoring in Python. NOTE: the syntax of this script MUST be kept compatible with Python 2.7." optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25"}, {file = "psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da"}, @@ -879,6 +914,7 @@ version = "1.10.18" description = "Data validation and settings management using python type hints" optional = false python-versions = ">=3.7" +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"}, @@ -938,6 +974,7 @@ version = "8.3.5" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820"}, {file = "pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845"}, @@ -958,6 +995,7 @@ version = "0.25.3" description = "Pytest support for asyncio" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "pytest_asyncio-0.25.3-py3-none-any.whl", hash = "sha256:9e89518e0f9bd08928f97a3482fdc4e244df17529460bc038291ccaf8f85c7c3"}, {file = "pytest_asyncio-0.25.3.tar.gz", hash = "sha256:fc1da2cf9f125ada7e710b4ddad05518d4cee187ae9412e9ac9271003497f07a"}, @@ -976,6 +1014,7 @@ version = "3.6.1" description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pytest_xdist-3.6.1-py3-none-any.whl", hash = "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7"}, {file = "pytest_xdist-3.6.1.tar.gz", hash = "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d"}, @@ -996,6 +1035,7 @@ version = "2.8.2" description = "Extensions to the standard Python datetime module" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main"] files = [ {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, @@ -1010,6 +1050,7 @@ version = "6.0.2" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, @@ -1072,6 +1113,7 @@ version = "2.32.3" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, @@ -1093,6 +1135,7 @@ version = "1.5.0" description = "Validating URI References per RFC 3986" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"}, {file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"}, @@ -1110,6 +1153,7 @@ version = "0.6.5" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "ruff-0.6.5-py3-none-linux_armv6l.whl", hash = "sha256:7e4e308f16e07c95fc7753fc1aaac690a323b2bb9f4ec5e844a97bb7fbebd748"}, {file = "ruff-0.6.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:932cd69eefe4daf8c7d92bd6689f7e8182571cb934ea720af218929da7bd7d69"}, @@ -1137,6 +1181,7 @@ version = "0.0.1.dev336+c6f2974" description = "Tools for checking if message fits expected format" optional = false python-versions = ">=3.12,<4.0" +groups = ["main"] files = [ {file = "schemas-0.0.1.dev336+c6f2974-py3-none-any.whl", hash = "sha256:9a2148f014bdf82e05d138629df11cb20f53799eb32dcfff14942b4b20a76cc6"}, ] @@ -1155,19 +1200,20 @@ version = "77.0.3" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.9" +groups = ["main"] 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)", "ruff (>=0.8.0)"] -core = ["importlib_metadata (>=6)", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\"", "ruff (>=0.8.0) ; sys_platform != \"cygwin\""] +core = ["importlib_metadata (>=6) ; python_version < \"3.10\"", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1) ; python_version < \"3.11\"", "wheel (>=0.43.0)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] enabler = ["pytest-enabler (>=2.2)"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] -type = ["importlib_metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.14.*)", "pytest-mypy"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21) ; python_version >= \"3.9\" and sys_platform != \"cygwin\"", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf ; sys_platform != \"cygwin\"", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] +type = ["importlib_metadata (>=7.0.2) ; python_version < \"3.10\"", "jaraco.develop (>=7.21) ; sys_platform != \"cygwin\"", "mypy (==1.14.*)", "pytest-mypy"] [[package]] name = "six" @@ -1175,6 +1221,7 @@ version = "1.16.0" description = "Python 2 and 3 compatibility utilities" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +groups = ["main"] files = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, @@ -1186,6 +1233,7 @@ version = "1.3.1" description = "Sniff out which async library your code is running under" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, @@ -1197,6 +1245,7 @@ version = "6.0.0.20240901" description = "Typing stubs for psutil" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "types-psutil-6.0.0.20240901.tar.gz", hash = "sha256:437affa76670363db9ffecfa4f153cc6900bf8a7072b3420f3bc07a593f92226"}, {file = "types_psutil-6.0.0.20240901-py3-none-any.whl", hash = "sha256:20af311bfb0386a018a27ae47dc952119d7c0e849ff72b6aa24fc0433afb92a6"}, @@ -1208,6 +1257,7 @@ version = "2.8.19.14" description = "Typing stubs for python-dateutil" optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "types-python-dateutil-2.8.19.14.tar.gz", hash = "sha256:1f4f10ac98bb8b16ade9dbee3518d9ace017821d94b057a425b069f834737f4b"}, {file = "types_python_dateutil-2.8.19.14-py3-none-any.whl", hash = "sha256:f977b8de27787639986b4e28963263fd0e5158942b3ecef91b9335c130cb1ce9"}, @@ -1219,6 +1269,7 @@ version = "6.0.12.4" description = "Typing stubs for PyYAML" optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "types-PyYAML-6.0.12.4.tar.gz", hash = "sha256:ade6e328a5a3df816c47c912c2e1e946ae2bace90744aa73111ee6834b03a314"}, {file = "types_PyYAML-6.0.12.4-py3-none-any.whl", hash = "sha256:de3bacfc4e0772d9b1baf007c37354f3c34c8952e90307d5155b6de0fc183a67"}, @@ -1230,6 +1281,7 @@ version = "2.31.0.2" description = "Typing stubs for requests" optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "types-requests-2.31.0.2.tar.gz", hash = "sha256:6aa3f7faf0ea52d728bb18c0a0d1522d9bfd8c72d26ff6f61bfc3d06a411cf40"}, {file = "types_requests-2.31.0.2-py3-none-any.whl", hash = "sha256:56d181c85b5925cbc59f4489a57e72a8b2166f18273fd8ba7b6fe0c0b986f12a"}, @@ -1244,6 +1296,7 @@ version = "76.0.0.20250313" description = "Typing stubs for setuptools" optional = false python-versions = ">=3.9" +groups = ["main"] 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"}, @@ -1258,6 +1311,7 @@ version = "1.26.25.14" description = "Typing stubs for urllib3" optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "types-urllib3-1.26.25.14.tar.gz", hash = "sha256:229b7f577c951b8c1b92c1bc2b2fdb0b49847bd2af6d1cc2a2e3dd340f3bda8f"}, {file = "types_urllib3-1.26.25.14-py3-none-any.whl", hash = "sha256:9683bbb7fb72e32bfe9d2be6e04875fbe1b3eeec3cbb4ea231435aa7fd6b4f0e"}, @@ -1269,6 +1323,7 @@ version = "4.12.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, @@ -1280,14 +1335,15 @@ version = "1.26.20" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +groups = ["main"] files = [ {file = "urllib3-1.26.20-py2.py3-none-any.whl", hash = "sha256:0ed14ccfbf1c30a9072c7ca157e4319b70d65f623e91e7b32fadb2853431016e"}, {file = "urllib3-1.26.20.tar.gz", hash = "sha256:40c2dc0c681e47eb8f90e7e27bf6ff7df2e677421fd46756da1161c39ca70d32"}, ] [package.extras] -brotli = ["brotli (==1.0.9)", "brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] -secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] +brotli = ["brotli (==1.0.9) ; os_name != \"nt\" and python_version < \"3\" and platform_python_implementation == \"CPython\"", "brotli (>=1.0.9) ; python_version >= \"3\" and platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; (os_name != \"nt\" or python_version >= \"3\") and platform_python_implementation != \"CPython\"", "brotlipy (>=0.6.0) ; os_name == \"nt\" and python_version < \"3\""] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress ; python_version == \"2.7\"", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] @@ -1296,6 +1352,7 @@ version = "20.26.5" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "virtualenv-20.26.5-py3-none-any.whl", hash = "sha256:4f3ac17b81fba3ce3bd6f4ead2749a72da5929c01774948e243db9ba41df4ff6"}, {file = "virtualenv-20.26.5.tar.gz", hash = "sha256:ce489cac131aa58f4b25e321d6d186171f78e6cb13fafbf32a840cee67733ff4"}, @@ -1308,7 +1365,7 @@ platformdirs = ">=3.9.1,<5" [package.extras] docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] -test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8) ; platform_python_implementation == \"PyPy\" or platform_python_implementation == \"CPython\" and sys_platform == \"win32\" and python_version >= \"3.13\"", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10) ; platform_python_implementation == \"CPython\""] [[package]] name = "win32-setctime" @@ -1316,13 +1373,15 @@ version = "1.1.0" description = "A small Python utility to set file creation time on Windows" optional = false python-versions = ">=3.5" +groups = ["main"] +markers = "sys_platform == \"win32\"" files = [ {file = "win32_setctime-1.1.0-py3-none-any.whl", hash = "sha256:231db239e959c2fe7eb1d7dc129f11172354f98361c4fa2d6d2d7e278baa8aad"}, {file = "win32_setctime-1.1.0.tar.gz", hash = "sha256:15cf5750465118d6929ae4de4eb46e8edae9a5634350c01ba582df868e932cb2"}, ] [package.extras] -dev = ["black (>=19.3b0)", "pytest (>=4.6.2)"] +dev = ["black (>=19.3b0) ; python_version >= \"3.6\"", "pytest (>=4.6.2)"] [[package]] name = "yarl" @@ -1330,6 +1389,7 @@ version = "1.18.3" description = "Yet another URL library" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7df647e8edd71f000a5208fe6ff8c382a1de8edfbccdbbfe649d263de07d8c34"}, {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c69697d3adff5aa4f874b19c0e4ed65180ceed6318ec856ebc423aa5850d84f7"}, @@ -1421,6 +1481,6 @@ multidict = ">=4.0" propcache = ">=0.2.0" [metadata] -lock-version = "2.0" +lock-version = "2.1" python-versions = "^3.12" content-hash = "a7125ab9d77d3431d5076460d8ebb6d574bc96408318c0f6fa02df550b633eab" -- GitLab From c23bd7002059f444dc79458e372903a2135a3e5e Mon Sep 17 00:00:00 2001 From: kmochocki <kmochocki@syncad.com> Date: Wed, 16 Apr 2025 11:05:06 +0000 Subject: [PATCH 19/23] wip --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index aca1dd4d..b4de8c5e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -18,14 +18,14 @@ repos: hooks: - id: poetry-lock name: checking if beekeepy/poetry.lock is consistent with pyproject.toml - args: [ "-C", "./beekeepy", "--no-update" ] + args: [ "-C", "./beekeepy" ] language_version: python3.12 - repo: https://github.com/python-poetry/poetry rev: 2.1.2 hooks: - id: poetry-lock name: checking if tests/local-tools/poetry.lock is consistent with pyproject.toml - args: [ "-C", "./tests/local-tools", "--no-update" ] + args: [ "-C", "./tests/local-tools" ] language_version: python3.12 - repo: https://github.com/charliermarsh/ruff-pre-commit rev: 'v0.6.5' -- GitLab From a9ee4c1bb0664045ca733bcfbf53c1d7666d3fdd Mon Sep 17 00:00:00 2001 From: kmochocki <kmochocki@syncad.com> Date: Wed, 16 Apr 2025 11:51:01 +0000 Subject: [PATCH 20/23] wp --- hive | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hive b/hive index 89df1f62..bcf663ac 160000 --- a/hive +++ b/hive @@ -1 +1 @@ -Subproject commit 89df1f6296f0dba171c96855bca30c5c708acd57 +Subproject commit bcf663ac828958d77f4510dcefb885dc371575bb -- GitLab From 01bcfa6205e9a62edf56299468791a405ea8cd52 Mon Sep 17 00:00:00 2001 From: kmochocki <kmochocki@syncad.com> Date: Thu, 17 Apr 2025 11:59:55 +0000 Subject: [PATCH 21/23] wip --- .../_runnable_handle/runnable_handle.py | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/beekeepy/beekeepy/_runnable_handle/runnable_handle.py b/beekeepy/beekeepy/_runnable_handle/runnable_handle.py index 5efd171f..11d05f50 100644 --- a/beekeepy/beekeepy/_runnable_handle/runnable_handle.py +++ b/beekeepy/beekeepy/_runnable_handle/runnable_handle.py @@ -20,6 +20,7 @@ from beekeepy.exceptions import ( FailedToDetectReservedPortsError, FailedToStartExecutableError, ) +from beekeepy.interfaces import Stopwatch if TYPE_CHECKING: from loguru import Logger @@ -120,10 +121,10 @@ class RunnableHandle(ABC, Generic[ExecutableT, ConfigT, ArgumentT, SettingsT]): except SubprocessError as e: raise FailedToStartExecutableError from e try: - self._wait_for_app_to_start() + ports = self._wait_for_app_to_start() except TimeoutError as e: raise FailedToDetectReservedPortsError from e - self._setup_ports(self.__discover_ports()) + self._setup_ports(ports) @abstractmethod def _construct_executable(self) -> ExecutableT: @@ -177,12 +178,19 @@ class RunnableHandle(ABC, Generic[ExecutableT, ConfigT, ArgumentT, SettingsT]): ports -- list of ports reserved by started application. """ - def _wait_for_app_to_start(self) -> None: + def _wait_for_app_to_start(self) -> PortMatchingResult: """Waits for application to start.""" - while not self._exec.reserved_ports(): - if not self._exec.is_running(): - raise FailedToStartExecutableError - time.sleep(0.1) + with Stopwatch() as stopwatch: + while stopwatch.lap <= self._get_settings().initialization_timeout.total_seconds() and not bool( + discovered_ports := self.__discover_ports() + ): + if not self._exec.is_running(): + raise FailedToStartExecutableError + time.sleep(0.1) + + if bool(discovered_ports): + return discovered_ports + raise TimeoutError("Timeout while waiting for application to start") def __choose_working_directory(self, settings: Settings) -> Path: return self.__choose_value( -- GitLab From 6116038346997a727a7577462b15a42832885d29 Mon Sep 17 00:00:00 2001 From: kmochocki <kmochocki@syncad.com> Date: Fri, 18 Apr 2025 10:04:29 +0000 Subject: [PATCH 22/23] wip --- beekeepy/beekeepy/_runnable_handle/match_ports.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/beekeepy/beekeepy/_runnable_handle/match_ports.py b/beekeepy/beekeepy/_runnable_handle/match_ports.py index 09f46fb6..95d37854 100644 --- a/beekeepy/beekeepy/_runnable_handle/match_ports.py +++ b/beekeepy/beekeepy/_runnable_handle/match_ports.py @@ -20,6 +20,9 @@ class PortMatchingResult: websocket: WsUrl | None = None p2p: list[P2PUrl] = field(default_factory=list) + def __bool__(self) -> bool: + return self.http is not None + def test_http(address: HttpUrl) -> bool: assert address.port is not None, "HTTP CHECK: Port has to be set" -- GitLab From 28ac56d44fa1ead12f69c6a7184e35165696acb2 Mon Sep 17 00:00:00 2001 From: kmochocki <kmochocki@syncad.com> Date: Fri, 18 Apr 2025 12:48:20 +0000 Subject: [PATCH 23/23] wip --- .../beekeepy/_runnable_handle/runnable_handle.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/beekeepy/beekeepy/_runnable_handle/runnable_handle.py b/beekeepy/beekeepy/_runnable_handle/runnable_handle.py index 11d05f50..4238ae20 100644 --- a/beekeepy/beekeepy/_runnable_handle/runnable_handle.py +++ b/beekeepy/beekeepy/_runnable_handle/runnable_handle.py @@ -1,5 +1,6 @@ from __future__ import annotations +import contextlib import time import warnings from abc import ABC, abstractmethod @@ -180,17 +181,23 @@ class RunnableHandle(ABC, Generic[ExecutableT, ConfigT, ArgumentT, SettingsT]): def _wait_for_app_to_start(self) -> PortMatchingResult: """Waits for application to start.""" + + def ignore_timeout_error() -> PortMatchingResult | None: + with contextlib.suppress(TimeoutError): + return self.__discover_ports() + return None + with Stopwatch() as stopwatch: while stopwatch.lap <= self._get_settings().initialization_timeout.total_seconds() and not bool( - discovered_ports := self.__discover_ports() + discovered_ports := ignore_timeout_error() ): if not self._exec.is_running(): raise FailedToStartExecutableError time.sleep(0.1) - if bool(discovered_ports): + if discovered_ports is not None and bool(discovered_ports): return discovered_ports - raise TimeoutError("Timeout while waiting for application to start") + raise TimeoutError(f"Timeout after {stopwatch.seconds_delta :2f} waiting for application to start") def __choose_working_directory(self, settings: Settings) -> Path: return self.__choose_value( -- GitLab