Skip to content

Commit 76b584e

Browse files
committed
Clean up the application registry
1 parent 001ef31 commit 76b584e

File tree

6 files changed

+53
-65
lines changed

6 files changed

+53
-65
lines changed

netbox/extras/constants.py

-12
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,2 @@
11
# Webhook content types
22
HTTP_CONTENT_TYPE_JSON = 'application/json'
3-
4-
# Registerable extras features
5-
EXTRAS_FEATURES = [
6-
'custom_fields',
7-
'custom_links',
8-
'export_templates',
9-
'job_results',
10-
'journaling',
11-
'synced_data',
12-
'tags',
13-
'webhooks'
14-
]

netbox/extras/plugins/__init__.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@
1414
from .templates import *
1515

1616
# Initialize plugin registry
17-
registry['plugins'] = {
17+
registry['plugins'].update({
1818
'graphql_schemas': [],
1919
'menus': [],
2020
'menu_items': {},
2121
'preferences': {},
2222
'template_extensions': collections.defaultdict(list),
23-
}
23+
})
2424

2525
DEFAULT_RESOURCE_PATHS = {
2626
'search_indexes': 'search.indexes',

netbox/extras/utils.py

+11-6
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
from django.utils.deconstruct import deconstructible
33
from taggit.managers import _TaggableManager
44

5-
from extras.constants import EXTRAS_FEATURES
65
from netbox.registry import registry
76

87

@@ -18,7 +17,7 @@ def is_taggable(obj):
1817

1918
def image_upload(instance, filename):
2019
"""
21-
Return a path for uploading image attchments.
20+
Return a path for uploading image attachments.
2221
"""
2322
path = 'image-attachments/'
2423

@@ -56,8 +55,14 @@ def get_query(self):
5655

5756

5857
def register_features(model, features):
58+
"""
59+
Register model features in the application registry.
60+
"""
61+
app_label, model_name = model._meta.label_lower.split('.')
5962
for feature in features:
60-
if feature not in EXTRAS_FEATURES:
61-
raise ValueError(f"{feature} is not a valid extras feature!")
62-
app_label, model_name = model._meta.label_lower.split('.')
63-
registry['model_features'][feature][app_label].add(model_name)
63+
try:
64+
registry['model_features'][feature][app_label].add(model_name)
65+
except KeyError:
66+
raise KeyError(
67+
f"{feature} is not a valid model feature! Valid keys are: {registry['model_features'].keys()}"
68+
)

netbox/netbox/models/features.py

+16-11
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
from extras.choices import CustomFieldVisibilityChoices, ObjectChangeActionChoices
1414
from extras.utils import is_taggable, register_features
15+
from netbox.registry import registry
1516
from netbox.signals import post_clean
1617
from utilities.json import CustomFieldJSONEncoder
1718
from utilities.utils import serialize_object
@@ -388,22 +389,26 @@ def sync_data(self):
388389
raise NotImplementedError(f"{self.__class__} must implement a sync_data() method.")
389390

390391

391-
FEATURES_MAP = (
392-
('custom_fields', CustomFieldsMixin),
393-
('custom_links', CustomLinksMixin),
394-
('export_templates', ExportTemplatesMixin),
395-
('job_results', JobResultsMixin),
396-
('journaling', JournalingMixin),
397-
('synced_data', SyncedDataMixin),
398-
('tags', TagsMixin),
399-
('webhooks', WebhooksMixin),
400-
)
392+
FEATURES_MAP = {
393+
'custom_fields': CustomFieldsMixin,
394+
'custom_links': CustomLinksMixin,
395+
'export_templates': ExportTemplatesMixin,
396+
'job_results': JobResultsMixin,
397+
'journaling': JournalingMixin,
398+
'synced_data': SyncedDataMixin,
399+
'tags': TagsMixin,
400+
'webhooks': WebhooksMixin,
401+
}
402+
403+
registry['model_features'].update({
404+
feature: defaultdict(set) for feature in FEATURES_MAP.keys()
405+
})
401406

402407

403408
@receiver(class_prepared)
404409
def _register_features(sender, **kwargs):
405410
features = {
406-
feature for feature, cls in FEATURES_MAP if issubclass(sender, cls)
411+
feature for feature, cls in FEATURES_MAP.items() if issubclass(sender, cls)
407412
}
408413
register_features(sender, features)
409414

netbox/netbox/registry.py

+11-15
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
import collections
22

3-
from extras.constants import EXTRAS_FEATURES
4-
53

64
class Registry(dict):
75
"""
8-
Central registry for registration of functionality. Once a store (key) is defined, it cannot be overwritten or
9-
deleted (although its value may be manipulated).
6+
Central registry for registration of functionality. Once a Registry is initialized, keys cannot be added or
7+
removed (though the value of each key is mutable).
108
"""
119
def __getitem__(self, key):
1210
try:
@@ -15,20 +13,18 @@ def __getitem__(self, key):
1513
raise KeyError(f"Invalid store: {key}")
1614

1715
def __setitem__(self, key, value):
18-
if key in self:
19-
raise KeyError(f"Store already set: {key}")
20-
super().__setitem__(key, value)
16+
raise TypeError("Cannot add stores to registry after initialization")
2117

2218
def __delitem__(self, key):
2319
raise TypeError("Cannot delete stores from registry")
2420

2521

2622
# Initialize the global registry
27-
registry = Registry()
28-
registry['data_backends'] = dict()
29-
registry['denormalized_fields'] = collections.defaultdict(list)
30-
registry['model_features'] = {
31-
feature: collections.defaultdict(set) for feature in EXTRAS_FEATURES
32-
}
33-
registry['search'] = dict()
34-
registry['views'] = collections.defaultdict(dict)
23+
registry = Registry({
24+
'data_backends': dict(),
25+
'denormalized_fields': collections.defaultdict(list),
26+
'model_features': dict(),
27+
'plugins': dict(),
28+
'search': dict(),
29+
'views': collections.defaultdict(dict),
30+
})

netbox/netbox/tests/test_registry.py

+13-19
Original file line numberDiff line numberDiff line change
@@ -5,29 +5,23 @@
55

66
class RegistryTest(TestCase):
77

8-
def test_add_store(self):
9-
reg = Registry()
10-
reg['foo'] = 123
11-
12-
self.assertEqual(reg['foo'], 123)
8+
def test_set_store(self):
9+
reg = Registry({
10+
'foo': 123,
11+
})
12+
with self.assertRaises(TypeError):
13+
reg['bar'] = 456
1314

14-
def test_manipulate_store(self):
15-
reg = Registry()
16-
reg['foo'] = [1, 2]
15+
def test_mutate_store(self):
16+
reg = Registry({
17+
'foo': [1, 2],
18+
})
1719
reg['foo'].append(3)
18-
1920
self.assertListEqual(reg['foo'], [1, 2, 3])
2021

21-
def test_overwrite_store(self):
22-
reg = Registry()
23-
reg['foo'] = 123
24-
25-
with self.assertRaises(KeyError):
26-
reg['foo'] = 456
27-
2822
def test_delete_store(self):
29-
reg = Registry()
30-
reg['foo'] = 123
31-
23+
reg = Registry({
24+
'foo': 123,
25+
})
3226
with self.assertRaises(TypeError):
3327
del reg['foo']

0 commit comments

Comments
 (0)