Skip to content

Commit fdfbaa1

Browse files
Merge pull request #1132 from Backblaze/scrap-phx-registry
Replace the `phx-class-registry` dependency with a simple in-house implementation.
2 parents 0e361ca + d350501 commit fdfbaa1

9 files changed

Lines changed: 295 additions & 91 deletions

File tree

.github/workflows/cd.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ jobs:
3636
with:
3737
python-version: ${{ env.PYTHON_DEFAULT_VERSION }}
3838
- name: Install dependencies
39-
run: python -m pip install --upgrade nox pdm==2.26.2
39+
run: python -m pip install --upgrade nox pdm==2.26.4
4040
- name: Build the distribution
4141
id: build
4242
run: nox -vs build
@@ -79,7 +79,7 @@ jobs:
7979
run: |
8080
sudo apt-get -y update
8181
sudo apt-get -y install patchelf scons
82-
python -m pip install --upgrade nox pdm==2.26.2
82+
python -m pip install --upgrade nox pdm==2.26.4
8383
git config --global --add safe.directory '*'
8484
- name: Bundle the distribution
8585
id: bundle
@@ -114,7 +114,7 @@ jobs:
114114
with:
115115
python-version: ${{ env.PYTHON_DEFAULT_VERSION }}
116116
- name: Install dependencies
117-
run: python -m pip install --upgrade nox pdm==2.26.2
117+
run: python -m pip install --upgrade nox pdm==2.26.4
118118
- name: Bundle the distribution
119119
id: bundle
120120
shell: bash
@@ -159,7 +159,7 @@ jobs:
159159
with:
160160
python-version: ${{ env.PYTHON_DEFAULT_VERSION }}
161161
- name: Install dependencies
162-
run: python -m pip install --upgrade nox pdm==2.26.2
162+
run: python -m pip install --upgrade nox pdm==2.26.4
163163
- name: Build Dockerfile
164164
run: nox -vs generate_dockerfile
165165
- name: Set up QEMU

.github/workflows/ci.yml

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ jobs:
2525
with:
2626
python-version: ${{ env.PYTHON_DEFAULT_VERSION }}
2727
- name: Install dependencies
28-
run: python -m pip install --upgrade nox pdm==2.26.2
28+
run: python -m pip install --upgrade nox pdm==2.26.4
2929
- name: Run linters
3030
run: nox -vs lint
3131
- name: Validate new changelog entries
@@ -47,7 +47,7 @@ jobs:
4747
with:
4848
python-version: ${{ env.PYTHON_DEFAULT_VERSION }}
4949
- name: Install dependencies
50-
run: python -m pip install --upgrade nox pdm==2.26.2
50+
run: python -m pip install --upgrade nox pdm==2.26.4
5151
- name: Build the distribution
5252
run: nox -vs build
5353
cleanup_buckets:
@@ -70,7 +70,7 @@ jobs:
7070
cache: "pip"
7171
- name: Install dependencies
7272
if: ${{ env.B2_TEST_APPLICATION_KEY != '' && env.B2_TEST_APPLICATION_KEY_ID != '' }} # TODO: skip this whole job instead
73-
run: python -m pip install --upgrade nox pdm==2.26.2
73+
run: python -m pip install --upgrade nox pdm==2.26.4
7474
- name: Find and remove old buckets
7575
if: ${{ env.B2_TEST_APPLICATION_KEY != '' && env.B2_TEST_APPLICATION_KEY_ID != '' }} # TODO: skip this whole job instead
7676
run: nox -vs cleanup_buckets
@@ -111,7 +111,7 @@ jobs:
111111
run: |
112112
brew install fish
113113
- name: Install dependencies
114-
run: python -m pip install --upgrade nox pdm==2.26.2
114+
run: python -m pip install --upgrade nox pdm==2.26.4
115115
- name: Run unit tests
116116
run: nox -vs unit -p ${{ matrix.python-version }}
117117
- name: Run integration tests (without secrets)
@@ -137,7 +137,7 @@ jobs:
137137
with:
138138
python-version: ${{ env.PYTHON_DEFAULT_VERSION }}
139139
- name: Install dependencies
140-
run: python -m pip install --upgrade nox pdm==2.26.2
140+
run: python -m pip install --upgrade nox pdm==2.26.4
141141
- name: Generate Dockerfile
142142
run: nox -vs generate_dockerfile
143143
- name: Set up QEMU
@@ -178,7 +178,7 @@ jobs:
178178
run: |
179179
sudo apt-get -y update
180180
sudo apt-get -y install patchelf scons
181-
python -m pip install --upgrade nox pdm==2.26.2
181+
python -m pip install --upgrade nox pdm==2.26.4
182182
git config --global --add safe.directory '*'
183183
- name: Bundle the distribution
184184
id: bundle
@@ -217,7 +217,7 @@ jobs:
217217
with:
218218
python-version: ${{ env.PYTHON_DEFAULT_VERSION }}
219219
- name: Install dependencies
220-
run: python -m pip install --upgrade nox pdm==2.26.2
220+
run: python -m pip install --upgrade nox pdm==2.26.4
221221
- name: Bundle the distribution
222222
id: bundle
223223
shell: bash
@@ -256,6 +256,6 @@ jobs:
256256
run: |
257257
sudo apt-get update -y
258258
sudo apt-get install -y graphviz plantuml
259-
python -m pip install --upgrade nox pdm==2.26.2
259+
python -m pip install --upgrade nox pdm==2.26.4
260260
- name: Build the docs
261261
run: nox --non-interactive -vs doc

b2/_internal/_cli/autocomplete_install.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@
2525
from shlex import quote
2626

2727
import argcomplete
28-
from class_registry import ClassRegistry, RegistryKeyError
28+
29+
from b2._internal.class_registry import ClassRegistry, RegistryKeyError
2930

3031
logger = logging.getLogger(__name__)
3132

b2/_internal/class_registry.py

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
######################################################################
2+
#
3+
# File: b2/_internal/class_registry.py
4+
#
5+
# Copyright 2026 Backblaze Inc. All Rights Reserved.
6+
#
7+
# License https://www.backblaze.com/using_b2_code.html
8+
#
9+
######################################################################
10+
from __future__ import annotations
11+
12+
from collections import OrderedDict
13+
from collections.abc import Callable, Hashable, Iterable, Iterator
14+
from typing import Any
15+
16+
17+
class RegistryKeyError(KeyError):
18+
"""Raised when a registry lookup fails."""
19+
20+
21+
class ClassRegistry:
22+
"""Registry with decorator-based class registration and instantiation."""
23+
24+
def __init__(self, attr_name: str | None = None, unique: bool = False) -> None:
25+
self.attr_name = attr_name
26+
self.unique = unique
27+
self._registry: OrderedDict[Hashable, type] = OrderedDict()
28+
29+
def __contains__(self, key: Hashable) -> bool:
30+
try:
31+
self.get_class(key)
32+
except RegistryKeyError:
33+
return False
34+
return True
35+
36+
def __getitem__(self, key: Hashable) -> object:
37+
return self.get(key)
38+
39+
def __iter__(self) -> Iterator[Hashable]:
40+
return self.keys()
41+
42+
def __len__(self) -> int:
43+
return len(self._registry)
44+
45+
def __repr__(self) -> str:
46+
return f'{type(self).__name__}(attr_name={self.attr_name!r}, unique={self.unique!r})'
47+
48+
def __setitem__(self, key: Hashable, class_: type) -> None:
49+
self._register(key, class_)
50+
51+
def __delitem__(self, key: Hashable) -> None:
52+
self._unregister(key)
53+
54+
def __missing__(self, key: Hashable) -> object:
55+
raise RegistryKeyError(key)
56+
57+
@staticmethod
58+
def create_instance(class_: type, *args: Any, **kwargs: Any) -> object:
59+
return class_(*args, **kwargs)
60+
61+
def get_class(self, key: Hashable) -> type:
62+
try:
63+
return self._registry[key]
64+
except KeyError:
65+
return self.__missing__(key)
66+
67+
def get(self, key: Hashable, *args: Any, **kwargs: Any) -> object:
68+
return self.create_instance(self.get_class(key), *args, **kwargs)
69+
70+
def items(self) -> Iterable[tuple[Hashable, type]]:
71+
return self._registry.items()
72+
73+
def keys(self) -> Iterable[Hashable]:
74+
return self._registry.keys()
75+
76+
def values(self) -> Iterable[type]:
77+
return self._registry.values()
78+
79+
def register(self, key: Hashable | type) -> Callable[[type], type] | type:
80+
if isinstance(key, type):
81+
if not self.attr_name:
82+
raise ValueError(
83+
f'Attempting to register {key.__name__} via decorator, but attr_name is not set.'
84+
)
85+
attr_key = getattr(key, self.attr_name)
86+
self._register(attr_key, key)
87+
return key
88+
89+
def _decorator(cls: type) -> type:
90+
self._register(key, cls)
91+
return cls
92+
93+
return _decorator
94+
95+
def unregister(self, key: Hashable) -> type:
96+
return self._unregister(key)
97+
98+
def _register(self, key: Hashable, class_: type) -> None:
99+
if key in ['', None]:
100+
raise ValueError(
101+
f'Attempting to register class {class_.__name__} with empty registry key {key!r}.'
102+
)
103+
if self.unique and key in self._registry:
104+
raise RegistryKeyError(f'{class_.__name__} with key {key!r} is already registered.')
105+
self._registry[key] = class_
106+
107+
def _unregister(self, key: Hashable) -> type:
108+
return self._registry.pop(key) if key in self._registry else self.__missing__(key)

b2/_internal/console_tool.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,6 @@
118118
UnableToCreateDirectory,
119119
)
120120
from b2sdk.version import VERSION as b2sdk_version
121-
from class_registry import ClassRegistry
122121
from tabulate import tabulate
123122

124123
from b2._internal._cli.arg_parser_types import (
@@ -163,6 +162,7 @@
163162
from b2._internal._cli.shell import detect_shell, resolve_short_call_name
164163
from b2._internal._utils.uri import B2URI, B2FileIdURI, B2URIAdapter, B2URIBase
165164
from b2._internal.arg_parser import B2ArgumentParser, add_normalized_argument
165+
from b2._internal.class_registry import ClassRegistry
166166
from b2._internal.json_encoder import B2CliJsonEncoder
167167
from b2._internal.version import VERSION
168168

changelog.d/1081.changed.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Replace the `phx-class-registry` dependency with a simple in-house implementation.

0 commit comments

Comments
 (0)