From 51f83dcaf05fd2a9a10b13a83a03a0ae7b3a26db Mon Sep 17 00:00:00 2001 From: jbranchaud Date: Tue, 21 Jul 2026 17:55:20 -0500 Subject: [PATCH] Add Select Implementation With Class Registry as a Python TIL --- README.md | 3 +- ...lect-implementation-with-class-registry.md | 48 +++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 python/select-implementation-with-class-registry.md diff --git a/README.md b/README.md index b470719..a545606 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ working across different projects via [VisualMode](https://www.visualmode.dev/). For a steady stream of TILs, [sign up for my newsletter](https://visualmode.kit.com/newsletter). -_1829 TILs and counting..._ +_1830 TILs and counting..._ See some of the other learning resources I work on: @@ -1099,6 +1099,7 @@ If you've learned something here, support my efforts writing daily TILs by - [Override The Boolean Context Of A Class](python/override-the-boolean-context-of-a-class.md) - [Parse Relative Time To datetime Object](python/parse-relative-time-to-datetime-object.md) - [Reclassify Certain Packages As Dev Dependencies](python/reclassify-certain-packages-as-dev-dependencies.md) +- [Select Implementation With Class Registry](python/select-implementation-with-class-registry.md) - [Set Up Pyright Type Checking In GitHub](python/set-up-pyright-type-checking-in-github.md) - [Skip Specific Pytest Test Cases](python/skip-specific-pytest-test-cases.md) - [Sort A List Of Dataclass Instances](python/sort-a-list-of-dataclass-instances.md) diff --git a/python/select-implementation-with-class-registry.md b/python/select-implementation-with-class-registry.md new file mode 100644 index 0000000..52753dd --- /dev/null +++ b/python/select-implementation-with-class-registry.md @@ -0,0 +1,48 @@ +# Select Implementation With Class Registry + +I am working on [supporting multiple storage formats (JSON and +SQLite)](https://github.com/jbranchaud/py-vmt/pull/1) for my [`py-vmt` +project](https://github.com/jbranchaud/py-vmt). While SQLite will be the default +storage format, the CLI can be configured via a `config.json` file to use +another supported `storage_format` (e.g. JSON). + +When the `CliContext` is initialized, I need to determine which will be used so +that I can use the correct _repository_ implementation for reads, writes, etc. I +decided to use a simplified _Class Registry Pattern_ to build the correct +implementation without a messy chain of conditionals. + +```python +class CliContext: + def __init__(self) -> None: + self.config = self.read_config() + self.repo = self._initialize_configured_repo() + # ... + + # ... + + _REPOS: dict[str, type[SessionRepository]] = { + "json": JsonRepository, + "sqlite": SqliteRepository, + } + + def _initialize_configured_repo(self) -> SessionRepository: + default_format = "sqlite" + format = self.config.get("storage_format", default_format) + try: + return self._REPOS[format]() + except KeyError: + raise ValueError(f"Unknown storage_format: {format!r}") +``` + +I define a `dict` called `_REPOS` that registers each of the repository +implementations that I support. Notice each value is a `SessionRepository` +class. Then the `_initialize_configured_repo` function (which is called in +`__init__`) initializes the correct repository implementation. It first tries to +grab the `storage_format` from the config, then pulls that out of `_REPOS`, and +then tags on `()` to initialize it. If I have a bad config, a `KeyError` will +be raised which I will re-raise as a `ValueError`. + +Some class registries support decorators or have a whole mechanism registering +and unregistering classes. I don't need anything quite that sophisticated, so I +stuck to a hard-coded dict. If I add support for more storage formats, I can add +their repository classes to the registry.