From 8dfcdb44292ca2c17d5c4e0fc0924dd2fb46cd07 Mon Sep 17 00:00:00 2001 From: Vaughn Kottler Date: Fri, 23 Feb 2024 00:25:54 -0600 Subject: [PATCH 1/3] Initial idea for global channel environment --- .github/workflows/python-package.yml | 2 +- README.md | 4 +- local/variables/package.yaml | 6 +-- pyproject.toml | 2 +- runtimepy/__init__.py | 4 +- runtimepy/channel/registry.py | 55 ++++++++++++++++++++++++++++ 6 files changed, 64 insertions(+), 9 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 2d86f0b1..0d3cba7b 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -68,7 +68,7 @@ jobs: - run: | mk python-release owner=vkottler \ - repo=runtimepy version=3.7.6 + repo=runtimepy version=4.0.0 if: | matrix.python-version == '3.11' && matrix.system == 'ubuntu-latest' diff --git a/README.md b/README.md index 7a8dbaeb..2e553963 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,11 @@ ===================================== generator=datazen version=3.1.4 - hash=5e658f86474b5fde932a9ad02e57bb7b + hash=0eae1fb0651f9608411daf093deabfbc ===================================== --> -# runtimepy ([3.7.6](https://pypi.org/project/runtimepy/)) +# runtimepy ([4.0.0](https://pypi.org/project/runtimepy/)) [![python](https://img.shields.io/pypi/pyversions/runtimepy.svg)](https://pypi.org/project/runtimepy/) ![Build Status](https://github.com/vkottler/runtimepy/workflows/Python%20Package/badge.svg) diff --git a/local/variables/package.yaml b/local/variables/package.yaml index 9202727b..e79e830c 100644 --- a/local/variables/package.yaml +++ b/local/variables/package.yaml @@ -1,5 +1,5 @@ --- -major: 3 -minor: 7 -patch: 6 +major: 4 +minor: 0 +patch: 0 entry: runtimepy diff --git a/pyproject.toml b/pyproject.toml index 88029f75..a949cd55 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta:__legacy__" [project] name = "runtimepy" -version = "3.7.6" +version = "4.0.0" description = "A framework for implementing Python services." readme = "README.md" requires-python = ">=3.11" diff --git a/runtimepy/__init__.py b/runtimepy/__init__.py index 1cf1e77d..ebdb3e60 100644 --- a/runtimepy/__init__.py +++ b/runtimepy/__init__.py @@ -1,7 +1,7 @@ # ===================================== # generator=datazen # version=3.1.4 -# hash=f807afa762f8662fce8e0c03e04b39d8 +# hash=372c7409fed9799f8307142ddbc1c739 # ===================================== """ @@ -10,7 +10,7 @@ DESCRIPTION = "A framework for implementing Python services." PKG_NAME = "runtimepy" -VERSION = "3.7.6" +VERSION = "4.0.0" # runtimepy-specific content. METRICS_NAME = "metrics" diff --git a/runtimepy/channel/registry.py b/runtimepy/channel/registry.py index a5ae30f2..f59e3abc 100644 --- a/runtimepy/channel/registry.py +++ b/runtimepy/channel/registry.py @@ -4,6 +4,7 @@ # built-in from typing import Any as _Any +from typing import NamedTuple from typing import Optional as _Optional from typing import Type as _Type from typing import Union @@ -29,6 +30,46 @@ class ChannelNameRegistry(_NameRegistry): name_regex = _CHANNEL_PATTERN +class ChannelCreation(NamedTuple): + """A container for channel-creation parameters.""" + + name: str + kind: Union[Primitive[_Any], _Primitivelike] + commandable: bool = False + enum: _Optional[_RegistryKey] = None + scaling: _Optional[ChannelScaling] = None + + +class GlobalEnvironment: + """A global environment management interface.""" + + registry: "ChannelRegistry" + + def __init__(self) -> None: + """Initialize this instance.""" + + self.duplicates: list[tuple[_AnyChannel, ChannelCreation]] = [] + + # We should add a 'num_duplicates' channel, maybe 'num_channels' as + # well? + + def handle(self, channel: _AnyChannel, meta: ChannelCreation) -> None: + """Handle global channel registration (used for instrumentation).""" + + if not self.registry.channel( + meta.name, + meta.kind, + commandable=meta.commandable, + enum=meta.enum, + scaling=meta.scaling, + ): + self.duplicates.append((channel, meta)) + + +# Keep track of global channels. +GLOBAL: GlobalEnvironment = GlobalEnvironment() + + class ChannelRegistry(_Registry[_Channel[_Any]]): """A runtime enumeration registry.""" @@ -77,4 +118,18 @@ def channel( if result is not None: result.raw = primitive + GLOBAL.handle( + result, + ChannelCreation( + name, + kind, + commandable=commandable, + enum=enum, + scaling=scaling, + ), + ) + return result + + +GLOBAL.registry = ChannelRegistry.create() From 4650f1a77dbb189d5ad00848fdf8997b4bda4bcb Mon Sep 17 00:00:00 2001 From: Vaughn Kottler Date: Fri, 23 Feb 2024 18:26:43 -0600 Subject: [PATCH 2/3] Add shutdown logging to connection manager --- runtimepy/net/manager.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/runtimepy/net/manager.py b/runtimepy/net/manager.py index 7eecab1d..2ed7d620 100644 --- a/runtimepy/net/manager.py +++ b/runtimepy/net/manager.py @@ -14,6 +14,7 @@ # third-party from vcorelib.asyncio import log_exceptions as _log_exceptions +from vcorelib.logging import LoggerMixin from vcorelib.math import default_time_ns as _default_time_ns # internal @@ -22,11 +23,13 @@ T = _TypeVar("T", bound=_Connection) -class ConnectionManager: +class ConnectionManager(LoggerMixin): """A class for managing connection processing at runtime.""" def __init__(self) -> None: """Initialize this connection manager.""" + + super().__init__() self.queue: _asyncio.Queue[_Connection] = _asyncio.Queue() self._running = False self._conns: _List[_Connection] = [] @@ -113,10 +116,11 @@ async def manage(self, stop_sig: _asyncio.Event) -> None: tasks = next_tasks - # Allow existing tasks to clean up. - if new_conn_task is not None: - new_conn_task.cancel() - for task in tasks: - await task + with self.log_time("Shutting down", reminder=True): + # Allow existing tasks to clean up. + if new_conn_task is not None: + new_conn_task.cancel() + for task in tasks: + await task self._running = False From 7b1401d74b3890c80e6f115370f7bda0c828b92b Mon Sep 17 00:00:00 2001 From: Vaughn Kottler Date: Sat, 24 Feb 2024 10:47:13 -0600 Subject: [PATCH 3/3] Final changes, going to abandon global channels --- runtimepy/channel/environment/__init__.py | 12 ++++++++---- runtimepy/channel/environment/names.py | 20 -------------------- runtimepy/net/arbiter/base.py | 3 +-- 3 files changed, 9 insertions(+), 26 deletions(-) delete mode 100644 runtimepy/channel/environment/names.py diff --git a/runtimepy/channel/environment/__init__.py b/runtimepy/channel/environment/__init__.py index 91a7caea..d18354a0 100644 --- a/runtimepy/channel/environment/__init__.py +++ b/runtimepy/channel/environment/__init__.py @@ -2,6 +2,9 @@ A module implementing a channel environment. """ +# built-in +from typing import Iterator as _Iterator + # internal from runtimepy.channel.environment.array import ( ArrayChannelEnvironment as _ArrayChannelEnvironment, @@ -12,15 +15,16 @@ from runtimepy.channel.environment.file import ( FileChannelEnvironment as _FileChannelEnvironment, ) -from runtimepy.channel.environment.names import ( - ChannelNameEnvironment as _ChannelNameEnvironment, -) class ChannelEnvironment( _ArrayChannelEnvironment, _FileChannelEnvironment, _CreateChannelEnvironment, - _ChannelNameEnvironment, ): """A class integrating channel and enumeration registries.""" + + @property + def names(self) -> _Iterator[str]: + """Iterate over registered names in the environment.""" + yield from self.channels.names.names diff --git a/runtimepy/channel/environment/names.py b/runtimepy/channel/environment/names.py deleted file mode 100644 index e3dcb190..00000000 --- a/runtimepy/channel/environment/names.py +++ /dev/null @@ -1,20 +0,0 @@ -""" -A channel-environment extension for working with channel names. -""" - -# built-in -from typing import Iterator as _Iterator - -# internal -from runtimepy.channel.environment.base import ( - BaseChannelEnvironment as _BaseChannelEnvironment, -) - - -class ChannelNameEnvironment(_BaseChannelEnvironment): - """An environment extension for working with channel names.""" - - @property - def names(self) -> _Iterator[str]: - """Iterate over registered names in the environment.""" - yield from self.channels.names.names diff --git a/runtimepy/net/arbiter/base.py b/runtimepy/net/arbiter/base.py index f522203f..2658bece 100644 --- a/runtimepy/net/arbiter/base.py +++ b/runtimepy/net/arbiter/base.py @@ -182,6 +182,7 @@ async def _entry( """ result = -1 + info: Optional[AppInfo] = None try: # Wait for servers to start. @@ -211,8 +212,6 @@ async def _entry( for name, conn in self._connections.items(): register_env(name, conn.command) - info: Optional[AppInfo] = None - # Run application, but only if all the registered connections are # still alive after initialization. if not check_connections or not any(