Coverage for netbox/configuration.py: 84%
50 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-10 18:35 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-10 18:35 +0000
1## Generic Parts
2# These functions are providing the functionality to load
3# arbitrary configuration files.
4#
5# They can be imported by other code (see `ldap_config.py` for an example).
7import importlib.util
8import sys
9from os import scandir
10from os.path import abspath, isfile
13def _filename(f):
14 return f.name
17def _import(module_name, path, loaded_configurations):
18 spec = importlib.util.spec_from_file_location('', path)
19 module = importlib.util.module_from_spec(spec)
20 spec.loader.exec_module(module)
21 sys.modules[module_name] = module
23 loaded_configurations.insert(0, module)
25 print(f"🧬 loaded config '{path}'")
28def read_configurations(config_module, config_dir, main_config):
29 loaded_configurations = []
31 main_config_path = abspath(f'{config_dir}/{main_config}.py')
32 if isfile(main_config_path): 32 ↛ 35line 32 didn't jump to line 35 because the condition on line 32 was always true
33 _import(f'{config_module}.{main_config}', main_config_path, loaded_configurations)
34 else:
35 print(f"⚠️ Main configuration '{main_config_path}' not found.")
37 with scandir(config_dir) as it:
38 for f in sorted(it, key=_filename):
39 if not f.is_file():
40 continue
42 if f.name.startswith('__'): 42 ↛ 43line 42 didn't jump to line 43 because the condition on line 42 was never true
43 continue
45 if not f.name.endswith('.py'): 45 ↛ 46line 45 didn't jump to line 46 because the condition on line 45 was never true
46 continue
48 if f.name == f'{main_config}.py':
49 continue
51 if f.name == f'{config_dir}.py': 51 ↛ 52line 51 didn't jump to line 52 because the condition on line 51 was never true
52 continue
54 module_name = f'{config_module}.{f.name[: -len(".py")]}'.replace('.', '_')
55 _import(module_name, f.path, loaded_configurations)
57 if len(loaded_configurations) == 0: 57 ↛ 58line 57 didn't jump to line 58 because the condition on line 57 was never true
58 print(f"‼️ No configuration files found in '{config_dir}'.")
59 raise ImportError(f"No configuration files found in '{config_dir}'.")
61 return loaded_configurations
64## Specific Parts
65# This section's code actually loads the various configuration files
66# into the module with the given name.
67# It contains the logic to resolve arbitrary configuration options by
68# levaraging dynamic programming using `__getattr__`.
71_loaded_configurations = read_configurations(
72 config_dir='/etc/netbox/config/',
73 config_module='netbox.configuration',
74 main_config='configuration',
75)
78def __getattr__(name):
79 for config in _loaded_configurations:
80 try:
81 return getattr(config, name)
82 except:
83 pass
84 raise AttributeError
87def __dir__():
88 names = []
89 for config in _loaded_configurations:
90 names.extend(config.__dir__())
91 return names