From c9ffb973ba25681dfb48dce82bae35610a50d144 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 9 Sep 2026 17:01:17 +0000 Subject: [PATCH 1/2] Libraries: add a library's requirements to the `Application` automatically Before this change, `Application` used a library's `requirements()` only to refuse to start when one was missing, so using the OAuth token manager meant listing `oauth_library()`, `ciphertext_library()`, and `ordered_map_library()`, and using a queue meant listing the sorted-map library too: what each library was built on was the application's problem to know. Now `Library` records every subclass by `name` as it is defined, and `Application` walks each listed library's requirements, constructing any it can with defaults and adding their requirements in turn, so listing a library is enough to run it. A library the application lists itself is kept, which is how an application still customizes a dependency, e.g. with an authorizer. A requirement Reboot cannot construct, because nothing registered that name or its constructor needs arguments, still fails at startup, now naming the library that needed it. - The `store_tokens=True` check asks for `oauth_library()` alone. - Tests cover the added, transitive, listed-instance, unknown, and needs-arguments cases. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01Votcp4rQnqt25q6XJGNavW --- reboot/aio/applications.py | 103 +++++++++++++++++-------- tests/reboot/aio/libraries_test.py | 118 +++++++++++++++++++++++++++-- 2 files changed, 184 insertions(+), 37 deletions(-) diff --git a/reboot/aio/applications.py b/reboot/aio/applications.py index f00c6e19a..4403a76ba 100644 --- a/reboot/aio/applications.py +++ b/reboot/aio/applications.py @@ -65,7 +65,15 @@ from reboot.version import REBOOT_VERSION from reboot.versioning import version_less_than from starlette.staticfiles import StaticFiles -from typing import Any, Awaitable, Callable, Mapping, NoReturn, Optional +from typing import ( + Any, + Awaitable, + Callable, + ClassVar, + Mapping, + NoReturn, + Optional, +) logger = get_logger(__name__) @@ -184,6 +192,12 @@ class Library(AbstractLibrary): any checks on subclasses. """ + # Every library class by its `name`, so that a library's + # `requirements()` can be constructed without the application + # listing them. A class defined later under the same name replaces + # the earlier one, which keeps reloading a module harmless. + _registry: ClassVar[dict[str, type['Library']]] = {} + def __init_subclass__(cls, **kwargs): # `name` checked here because this class expects its subclasses # to define it. @@ -193,6 +207,7 @@ def __init_subclass__(cls, **kwargs): "Please set `name` as a class variable. For best practices, " "please define the name as a constant in your library module." ) + Library._registry[cls.name] = cls async def pre_run(self, application: Application) -> None: """ @@ -231,6 +246,25 @@ async def initialize(self, context: InitializeContext) -> None: await self._initialize(context) +def _construct_library(name: str, required_by: str) -> Library: + """Constructs, with its defaults, the library registered as `name`.""" + library_type = Library._registry.get(name) + if library_type is None: + raise ValueError( + f"Library `{required_by}` requires library `{name}`, which is " + "not one Reboot can construct itself. Please construct it and " + "pass it in the `libraries` parameter." + ) + try: + return library_type() + except TypeError as error: + raise ValueError( + f"Library `{required_by}` requires library `{name}`, which can " + "not be constructed without arguments. Please construct it and " + "pass it in the `libraries` parameter." + ) from error + + class Application: """Entry point for all Reboot applications.""" @@ -258,7 +292,10 @@ def __init__( :param legacy_grpc_servicers: the types of legacy gRPC servicers (not using Reboot libraries) that this Application will serve. - :param libraries: the libraries this Application will use. + :param libraries: the libraries this Application will use. A + library's own requirements are added automatically; list a + required library yourself only to customize it, e.g. with + an authorizer. :param initialize: will be called after the Application's servicers have started for the first time, so that it can perform initialization logic (e.g., creating some well-known @@ -321,23 +358,30 @@ def __init__( "library once in `libraries`." ) - # Verify we have all the requirements for libraries. - requirements_names = set( - requirement for library in libraries - for requirement in library.requirements() - ) - needed_requirements = requirements_names - library_names - - if len(needed_requirements) > 0: - raise ValueError( - "Missing required libraries: " - f"{', '.join(needed_requirements)}. " - "Please add these libraries and pass them to the " - "`libraries` parameter." - ) + # Add each library's requirements, and theirs in turn, so + # that listing a library is enough to run it. A library the + # application lists itself takes precedence over one + # constructed here, which is how an application customizes + # a dependency, e.g. with an authorizer. + libraries_by_name = { + library.name: library for library in libraries + } + pending = list(libraries) + while len(pending) > 0: + library = pending.pop() + for requirement in library.requirements(): + if requirement in libraries_by_name: + continue + required = _construct_library( + requirement, required_by=library.name + ) + libraries_by_name[requirement] = required + pending.append(required) # Sort libraries by name for guaranteed ordering. - libraries = sorted(libraries, key=lambda library: library.name) + libraries = sorted( + libraries_by_name.values(), key=lambda library: library.name + ) # Add the library servicers to the list of servicers. library_servicers = [ @@ -958,27 +1002,26 @@ def _frontend_project_root(self) -> Path: def _require_oauth_libraries(self) -> None: """Fail fast if an OAuth provider with `store_tokens=True` is used - without the `oauth` (and its `ciphertext`) library mounted — they - encrypt and persist the identity provider's tokens. + without the `oauth` library mounted — it (with the `ciphertext` + library it builds on) encrypts and persists the identity + provider's tokens. """ - # Imported lazily: both libraries import `reboot.aio.applications`, + # Imported lazily: the library imports `reboot.aio.applications`, # so a module-level import would be circular. - from reboot.std.ciphertext.v1.ciphertext import CIPHERTEXT_LIBRARY_NAME from reboot.std.oauth.v1.oauth import OAUTH_LIBRARY_NAME names = {library.name for library in (self._libraries or [])} - if OAUTH_LIBRARY_NAME in names and CIPHERTEXT_LIBRARY_NAME in names: + if OAUTH_LIBRARY_NAME in names: return raise InputError( reason=( "An OAuth provider with `store_tokens=True` needs the " - "`oauth` and `ciphertext` libraries to encrypt and persist " - "the identity provider's tokens, but they aren't all " - "mounted. Add them to your `Application`, e.g. " - "`Application(..., libraries=[oauth_library(), " - "ciphertext_library(), ordered_map_library()])` (import " - "`oauth_library` from `reboot.std.oauth.v1.oauth` and " - "`ciphertext_library` from " - "`reboot.std.ciphertext.v1.ciphertext`)." + "`oauth` library to encrypt and persist the identity " + "provider's tokens, but it isn't mounted. Add it to your " + "`Application`, e.g. `Application(..., " + "libraries=[oauth_library()])` (import `oauth_library` " + "from `reboot.std.oauth.v1.oauth`); the `ciphertext` and " + "`ordered_map` libraries it builds on come along " + "automatically." ), ) diff --git a/tests/reboot/aio/libraries_test.py b/tests/reboot/aio/libraries_test.py index 444550947..89cd51946 100644 --- a/tests/reboot/aio/libraries_test.py +++ b/tests/reboot/aio/libraries_test.py @@ -57,6 +57,22 @@ def requirements(self): return [GREETER_LIBRARY_NAME] +GREETER_4_LIBRARY_NAME = "tests.reboot.aio.libraries_test.Greeter4Library" + + +class Greeter4Library(Library): + """A library with no servicers, requiring a library that itself + requires another.""" + + name = GREETER_4_LIBRARY_NAME + + def servicers(self): + return [] + + def requirements(self): + return [GREETER_2_LIBRARY_NAME] + + def greeter_library(): return GreeterLibrary() @@ -69,6 +85,10 @@ def greeter3_library(): return Greeter3Library() +def greeter4_library(): + return Greeter4Library() + + class TestCase(unittest.IsolatedAsyncioTestCase): async def test_adds_servicers(self) -> None: @@ -125,15 +145,99 @@ async def test_adds_dependent_library_servicers(self) -> None: self.assertIn(Greeter2Servicer, application.servicers) self.assertIn(Greeter3Servicer, application.servicers) - async def test_throws_if_not_all_requirements_present(self) -> None: + async def test_adds_required_libraries(self) -> None: + application = Application(libraries=[greeter2_library()]) + + # The required library is added, with its servicers. + self.assertEqual( + {GreeterLibrary, Greeter2Library}, + set(type(library) for library in application.libraries), + ) + self.assertIn(MyGreeterServicer, application.servicers) + self.assertIn(Greeter2Servicer, application.servicers) + + async def test_adds_required_libraries_transitively(self) -> None: + application = Application(libraries=[greeter4_library()]) + + self.assertEqual( + {GreeterLibrary, Greeter2Library, Greeter4Library}, + set(type(library) for library in application.libraries), + ) + + async def test_keeps_listed_instance_of_required_library(self) -> None: + greeter = greeter_library() + application = Application( + libraries=[greeter2_library(), greeter], + ) + + # The listed instance is used rather than a fresh one. + self.assertEqual(2, len(application.libraries)) + self.assertTrue( + any(library is greeter for library in application.libraries) + ) + + async def test_throws_if_requirement_unknown(self) -> None: + + class NeedsUnknownLibrary(Library): + + name = "tests.reboot.aio.libraries_test.NeedsUnknownLibrary" + + def servicers(self): + return [] + + def requirements(self): + return ["tests.reboot.aio.libraries_test.Unknown"] + with self.assertRaises(ValueError) as error: - Application(libraries=[greeter2_library()]) - self.assertEqual(type(error.exception), ValueError) + Application(libraries=[NeedsUnknownLibrary()]) self.assertIn( - f"Missing required libraries: {GREETER_LIBRARY_NAME}. Please add these libraries and pass them to the `libraries` parameter.", - str(error.exception) + "requires library `tests.reboot.aio.libraries_test.Unknown`, " + "which is not one Reboot can construct itself", + str(error.exception), + ) + + async def test_throws_if_requirement_needs_arguments(self) -> None: + + class NeedsArgumentLibrary(Library): + + name = "tests.reboot.aio.libraries_test.NeedsArgumentLibrary" + + def __init__(self, argument: str): + self.argument = argument + + def servicers(self): + return [MyGreeterServicer] + + class RequiresNeedsArgumentLibrary(Library): + + name = ( + "tests.reboot.aio.libraries_test." + "RequiresNeedsArgumentLibrary" + ) + + def servicers(self): + return [] + + def requirements(self): + return [NeedsArgumentLibrary.name] + + with self.assertRaises(ValueError) as error: + Application(libraries=[RequiresNeedsArgumentLibrary()]) + + self.assertIn( + "can not be constructed without arguments", + str(error.exception), + ) + + # Listing a constructed instance satisfies the requirement. + application = Application( + libraries=[ + RequiresNeedsArgumentLibrary(), + NeedsArgumentLibrary("argument"), + ] ) + self.assertEqual(2, len(application.libraries)) async def test_require_class_name(self) -> None: with self.assertRaises(NotImplementedError) as error: @@ -155,7 +259,7 @@ async def test_initialize(self) -> None: class Library1WithInitialize(Library): """Library with an initialize function.""" - name = "tests.reboot.aio.libraries_test.GreeterLibrary" + name = "tests.reboot.aio.libraries_test.Library1WithInitialize" def servicers(self): return [MyGreeterServicer] @@ -167,7 +271,7 @@ async def initialize(self, context: InitializeContext) -> None: class Library2WithInitialize(Library): """Library with an initialize function.""" - name = "tests.reboot.aio.libraries_test.Greeter2Library" + name = "tests.reboot.aio.libraries_test.Library2WithInitialize" def servicers(self): return [MyGreeterServicer] From f70ec460b976268f59cc1ae9af8e41d6ef5873e9 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 9 Sep 2026 17:01:17 +0000 Subject: [PATCH 2/2] Docs: list only the library an application uses Before this change, the `oauth`, `ciphertext`, `queue`, and `pubsub` pages, and the plugin skills for those libraries, told readers to register each library's dependencies by hand and explained what each was built on, because `Application` required it. Now that a library's requirements come along automatically in Python, the Python examples list the one library the application uses, the overview says so, and the skills stop instructing agents to add the dependencies. The TypeScript examples still list every library, since the TypeScript `Application` never resolved requirements. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01Votcp4rQnqt25q6XJGNavW --- .../docs/library_services/ciphertext.mdx | 10 ++++---- .../library_services/oauth_token_manager.mdx | 16 ++++--------- .../docs/library_services/overview.md | 5 ++++ .../docs/library_services/pubsub.mdx | 16 +++++-------- documentation/docs/library_services/queue.mdx | 10 ++++---- .../mcp-ui/references/auth-store-tokens.md | 9 ++++---- .../references/auth-external-api-calls.md | 9 +++----- .../references/lifecycle-application-entry.md | 23 +++++++++---------- .../python/references/stdlib-ciphertext.md | 12 ++++------ .../python/references/stdlib-oauth-tokens.md | 10 +++----- .../skills/python/references/stdlib-pubsub.md | 18 +++++++-------- .../skills/python/references/stdlib-queue.md | 20 ++++++++-------- 12 files changed, 67 insertions(+), 91 deletions(-) diff --git a/documentation/docs/library_services/ciphertext.mdx b/documentation/docs/library_services/ciphertext.mdx index c229bdfd6..42452dab7 100644 --- a/documentation/docs/library_services/ciphertext.mdx +++ b/documentation/docs/library_services/ciphertext.mdx @@ -54,8 +54,9 @@ application — you do not need to set it yourself. ## Imports and servicers -The `ciphertext` library depends on [`OrderedMap`](./ordered_map), so include -both when starting your `Application`. +The `ciphertext` library depends on [`OrderedMap`](./ordered_map). In +Python, listing `ciphertext_library()` brings it along; in TypeScript, +list both. @@ -63,14 +64,11 @@ both when starting your `Application`. ```py from reboot.std.ciphertext.v1.ciphertext import ciphertext_library -from reboot.std.collections.ordered_map.v1.ordered_map import ( - ordered_map_library, -) async def main(): application = Application( servicers=[MyServicer], - libraries=[ciphertext_library(), ordered_map_library()], + libraries=[ciphertext_library()], ) await application.run() ``` diff --git a/documentation/docs/library_services/oauth_token_manager.mdx b/documentation/docs/library_services/oauth_token_manager.mdx index 11182e4a8..3af204802 100644 --- a/documentation/docs/library_services/oauth_token_manager.mdx +++ b/documentation/docs/library_services/oauth_token_manager.mdx @@ -38,28 +38,20 @@ service without one (e.g. `"slack.com"`). ## Imports and servicers -The `oauth` library builds on [`Ciphertext`](./ciphertext) (which builds -on [`OrderedMap`](./ordered_map)), so include all three when starting -your `Application`; omitting any of them fails fast at startup. +The `oauth` library builds on [`Ciphertext`](./ciphertext), which builds +on [`OrderedMap`](./ordered_map). In Python, listing `oauth_library()` +brings both along; in TypeScript, list all three. ```py from reboot.std.oauth.v1.oauth import oauth_library -from reboot.std.ciphertext.v1.ciphertext import ciphertext_library -from reboot.std.collections.ordered_map.v1.ordered_map import ( - ordered_map_library, -) async def main(): application = Application( servicers=[MyServicer], - libraries=[ - oauth_library(), - ciphertext_library(), - ordered_map_library(), - ], + libraries=[oauth_library()], ) await application.run() ``` diff --git a/documentation/docs/library_services/overview.md b/documentation/docs/library_services/overview.md index 368d55f74..2f6cd3517 100644 --- a/documentation/docs/library_services/overview.md +++ b/documentation/docs/library_services/overview.md @@ -57,6 +57,11 @@ new Application({ +In Python, a library's own requirements come along automatically: +listing `queue_library()` also mounts the `SortedMap` library it builds +on. List a required library yourself only to customize it, for example +with an authorizer. + ## Future libraries and integrations [Reach out to us](https://discord.gg/cRbdcS94Nr) if there are any integrations or standard library features you want us to prioritize. Or we can help you build them, if that's more your speed! diff --git a/documentation/docs/library_services/pubsub.mdx b/documentation/docs/library_services/pubsub.mdx index a3e13cce9..a26f4e40f 100644 --- a/documentation/docs/library_services/pubsub.mdx +++ b/documentation/docs/library_services/pubsub.mdx @@ -55,25 +55,21 @@ import { Topic } from "@reboot-dev/reboot-std/pubsub/v1"; -Also make sure to include the pub/sub library and the dependent -[`Queue`](./queue) and [`SortedMap`](./sorted_map) libraries when starting up -your `Application`. (Note: this import is different from above.) +Also make sure to include the pub/sub library when starting up your +`Application`. In Python, listing `pubsub_library()` brings along the +[`Queue`](./queue) and [`SortedMap`](./sorted_map) libraries it depends +on; in TypeScript, list all three. (Note: this import is different from +above.) ```py -from reboot.std.collections.queue.v1.queue import queue_library -from reboot.std.collections.v1.sorted_map import sorted_map_library from reboot.std.pubsub.v1.pubsub import pubsub_library async def main(): application = Application( servicers=[MyServicer], - libraries=[ - pubsub_library(), - queue_library(), - sorted_map_library(), - ], + libraries=[pubsub_library()], ) await application.run() ``` diff --git a/documentation/docs/library_services/queue.mdx b/documentation/docs/library_services/queue.mdx index 673f198a6..823ec2533 100644 --- a/documentation/docs/library_services/queue.mdx +++ b/documentation/docs/library_services/queue.mdx @@ -46,20 +46,20 @@ import { Queue } from "@reboot-dev/reboot-std/collections/queue/v1"; -Also make sure to include the `Queue` library and the dependent [`SortedMap`](./sorted_map) -library when starting up your `Application`. (Note: this import is different -from above.) +Also make sure to include the `Queue` library when starting up your +`Application`. In Python, listing `queue_library()` brings along the +[`SortedMap`](./sorted_map) library it depends on; in TypeScript, list +both. (Note: this import is different from above.) ```py from reboot.std.collections.queue.v1.queue import queue_library -from reboot.std.collections.v1.sorted_map import sorted_map_library async def main(): application = Application( servicers=[MyServicer], - libraries=[queue_library(), sorted_map_library()], + libraries=[queue_library()], ) await application.run() ``` diff --git a/reboot/plugin/skills/mcp-ui/references/auth-store-tokens.md b/reboot/plugin/skills/mcp-ui/references/auth-store-tokens.md index 3dc46d6bb..c6b0b0c6a 100644 --- a/reboot/plugin/skills/mcp-ui/references/auth-store-tokens.md +++ b/reboot/plugin/skills/mcp-ui/references/auth-store-tokens.md @@ -69,11 +69,10 @@ def _google() -> Google: async def main() -> None: application = Application( servicers=[UserServicer], - # `store_tokens=True` persists tokens via the `oauth` library, - # which encrypts them via `ciphertext`, which in turn needs - # `ordered_map`. Without all three the app fails fast at startup. - libraries=[oauth_library(), ciphertext_library(), - ordered_map_library()], + # `store_tokens=True` persists tokens via the `oauth` library; + # the `ciphertext` and `ordered_map` libraries it builds on come + # along automatically. + libraries=[oauth_library()], oauth=OAuth( provider=OAuthProviderByEnvironment( # The calendar needs a real provider token even in diff --git a/reboot/plugin/skills/python/references/auth-external-api-calls.md b/reboot/plugin/skills/python/references/auth-external-api-calls.md index 2b756da95..c16a746eb 100644 --- a/reboot/plugin/skills/python/references/auth-external-api-calls.md +++ b/reboot/plugin/skills/python/references/auth-external-api-calls.md @@ -34,12 +34,9 @@ fails fast at startup: ```python from reboot.std.oauth.v1.oauth import oauth_library -from reboot.std.ciphertext.v1.ciphertext import ciphertext_library -from reboot.std.collections.ordered_map.v1.ordered_map import ( - ordered_map_library, -) -# libraries=[oauth_library(), ciphertext_library(), ordered_map_library()] +# libraries=[oauth_library()]; the `ciphertext` and `ordered_map` +# libraries it builds on come along automatically. ``` `REBOOT_CRYPTO_ROOT_KEYS` backs the encryption; it is auto-provisioned @@ -310,7 +307,7 @@ await KeyManager.ref(_key_manager_id(GOOGLE)).shred(context, scope=user_id) ## Checklist -- [ ] `libraries=[oauth_library(), ciphertext_library(), ordered_map_library()]` on the `Application` (Path C needs only the latter two). +- [ ] `libraries=[oauth_library()]` on the `Application` (Path C needs only `ciphertext_library()`). - [ ] **Capture** — Path A (identity provider's own API): `scopes=[...]` (least privilege) + `store_tokens=True`. Path B (any other service): your own authorize + callback routes, callback registered `app_internal=True`, the diff --git a/reboot/plugin/skills/python/references/lifecycle-application-entry.md b/reboot/plugin/skills/python/references/lifecycle-application-entry.md index 2b333a272..1515f437a 100644 --- a/reboot/plugin/skills/python/references/lifecycle-application-entry.md +++ b/reboot/plugin/skills/python/references/lifecycle-application-entry.md @@ -99,18 +99,17 @@ async def main(): ## See Also -If you're using any stdlib state type, the wiring lives in **two** -places: its `servicers()` list goes into `servicers=[...]`, and its -`_library()` factory goes into `libraries=[...]`. Forgetting -either gives a runtime "unknown actor type" error. The references for -each type call out exactly what to register: - -- `stdlib-ordered-map.md` — `ordered_map.servicers()` + `ordered_map_library()` -- `stdlib-queue.md` — `queue.servicers()` + the stdlib map library - (`Queue` uses a stdlib sorted-map actor under the hood — see the - reference for the exact import) -- `stdlib-pubsub.md` — `pubsub.servicers()` (transitively pulls - `queue.servicers()`) + the stdlib map library +If you're using any stdlib state type, its `_library()` factory +goes into `libraries=[...]`; that registers its servicers and mounts +whatever the library itself depends on. Forgetting it gives a runtime +"unknown actor type" error. The references for each type call out +exactly what to register: + +- `stdlib-ordered-map.md` — `ordered_map_library()` +- `stdlib-queue.md` — `queue_library()` (brings along the stdlib + sorted-map library `Queue` uses under the hood) +- `stdlib-pubsub.md` — `pubsub_library()` (brings along `Queue` and + the sorted-map library) - `stdlib-presence.md` — `presence.servicers()` (returns three Servicers; no library factory) diff --git a/reboot/plugin/skills/python/references/stdlib-ciphertext.md b/reboot/plugin/skills/python/references/stdlib-ciphertext.md index 10d37b84b..13ef1e036 100644 --- a/reboot/plugin/skills/python/references/stdlib-ciphertext.md +++ b/reboot/plugin/skills/python/references/stdlib-ciphertext.md @@ -7,9 +7,10 @@ tags: stdlib, ciphertext, encryption, envelope, crypto-shred, gdpr, right-to-era ## Use `Ciphertext` for Envelope Encryption and Crypto-Shredding -> **Critical:** register **both** `ciphertext_library()` **and** > `ordered_map_library()` in `Application(libraries=[...])` — -> `Ciphertext` depends on `OrderedMap`; forgetting either fails at boot -> with "unknown actor type." `associated_data` must be supplied +> **Critical:** register `ciphertext_library()` in +> `Application(libraries=[...])`; it brings along the `OrderedMap` +> library it depends on. Forgetting it fails at boot with "unknown +> actor type." `associated_data` must be supplied > **byte-for-byte identical** at decrypt as at encrypt — build it with > `make_associated_data`, never an ad-hoc string. These methods are > **app-internal by default** (no authorizer); call them from within @@ -47,15 +48,12 @@ root KEK — derived from REBOOT_CRYPTO_ROOT_KEYS (auto-provisioned); never st ```python from reboot.std.ciphertext.v1.ciphertext import ciphertext_library -from reboot.std.collections.ordered_map.v1.ordered_map import ( - ordered_map_library, -) async def main(): await Application( servicers=[VaultServicer], - libraries=[ciphertext_library(), ordered_map_library()], + libraries=[ciphertext_library()], ).run() ``` diff --git a/reboot/plugin/skills/python/references/stdlib-oauth-tokens.md b/reboot/plugin/skills/python/references/stdlib-oauth-tokens.md index cf3db2a01..963f9bfeb 100644 --- a/reboot/plugin/skills/python/references/stdlib-oauth-tokens.md +++ b/reboot/plugin/skills/python/references/stdlib-oauth-tokens.md @@ -37,18 +37,14 @@ makes the app fail fast at boot: ```python from reboot.std.oauth.v1.oauth import oauth_library -from reboot.std.ciphertext.v1.ciphertext import ciphertext_library -from reboot.std.collections.ordered_map.v1.ordered_map import ( - ordered_map_library, -) async def main(): await Application( servicers=[...], - libraries=[ - oauth_library(), ciphertext_library(), ordered_map_library(), - ], + # The `ciphertext` and `ordered_map` libraries `oauth` builds on + # come along automatically. + libraries=[oauth_library()], ).run() ``` diff --git a/reboot/plugin/skills/python/references/stdlib-pubsub.md b/reboot/plugin/skills/python/references/stdlib-pubsub.md index 5bdb6a52f..cc9369a1c 100644 --- a/reboot/plugin/skills/python/references/stdlib-pubsub.md +++ b/reboot/plugin/skills/python/references/stdlib-pubsub.md @@ -47,25 +47,23 @@ topic's buffer into each subscriber's queue. ### Register the Library `Topic` is built on `Queue`, which is itself backed by an internal -stdlib sorted-map actor: +stdlib sorted-map actor. Registering `pubsub_library()` mounts all +three: ```python -from reboot.std.pubsub.v1 import pubsub -from reboot.std.collections.v1.sorted_map import sorted_map_library +from reboot.std.pubsub.v1.pubsub import pubsub_library async def main(): await Application( - servicers=[MyServicer] + pubsub.servicers(), - libraries=[sorted_map_library()], + servicers=[MyServicer], + libraries=[pubsub_library()], ).run() ``` -`pubsub.servicers()` returns `[TopicServicer] + queue.servicers()`, so -you don't need to add `queue.servicers()` separately. The -`sorted_map_library()` registration is the only place you mention -the backing sorted-map actor — for a user-facing sorted key/value -collection, use `OrderedMap` (see `stdlib-ordered-map.md`). +You should not reach for the backing queue or sorted-map actors' +types directly in application code. For a user-facing sorted +key/value collection, use `OrderedMap` (see `stdlib-ordered-map.md`). ### Subscribe a Queue to a Topic diff --git a/reboot/plugin/skills/python/references/stdlib-queue.md b/reboot/plugin/skills/python/references/stdlib-queue.md index f81b7747e..693e9e8d4 100644 --- a/reboot/plugin/skills/python/references/stdlib-queue.md +++ b/reboot/plugin/skills/python/references/stdlib-queue.md @@ -41,26 +41,24 @@ available). A `try_dequeue` exists for one-shot non-blocking pulls from ### Register the Library -`Queue` is backed by an internal stdlib sorted-map actor — its -servicers list pulls those in, and the matching library factory -must be registered. Use the `queue.servicers()` helper: +`Queue` is backed by an internal stdlib sorted-map actor. Registering +`queue_library()` mounts the queue's servicers and the sorted-map +library it depends on: ```python -from reboot.std.collections.queue.v1 import queue -from reboot.std.collections.v1.sorted_map import sorted_map_library +from reboot.std.collections.queue.v1.queue import queue_library async def main(): await Application( - servicers=[MyServicer] + queue.servicers(), - libraries=[sorted_map_library()], + servicers=[MyServicer], + libraries=[queue_library()], ).run() ``` -The `sorted_map_library()` registration is the only place you -mention the backing sorted-map actor — you should not reach for -its types directly in application code. For a user-facing sorted -key/value collection, use `OrderedMap` (see `stdlib-ordered-map.md`). +You should not reach for the backing sorted-map actor's types +directly in application code. For a user-facing sorted key/value +collection, use `OrderedMap` (see `stdlib-ordered-map.md`). ### Producer Pattern