confibara 0.0.1a1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "confibara"
|
|
3
|
+
version = "0.0.1a1"
|
|
4
|
+
description = "Add your description here"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.14"
|
|
7
|
+
dependencies = []
|
|
8
|
+
|
|
9
|
+
[project.scripts]
|
|
10
|
+
confibara = "confibara:main"
|
|
11
|
+
|
|
12
|
+
[build-system]
|
|
13
|
+
requires = ["uv_build>=0.13.5,<0.14.0"]
|
|
14
|
+
build-backend = "uv_build"
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "confibara"
|
|
3
|
+
version = "0.0.1a1"
|
|
4
|
+
description = "Add your description here"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.14"
|
|
7
|
+
dependencies = []
|
|
8
|
+
|
|
9
|
+
[project.scripts]
|
|
10
|
+
confibara = "confibara:main"
|
|
11
|
+
|
|
12
|
+
[build-system]
|
|
13
|
+
requires = ["uv_build>=0.13.5,<0.14.0"]
|
|
14
|
+
build-backend = "uv_build"
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import importlib.util
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
from copy import copy
|
|
5
|
+
from dataclasses import is_dataclass, fields
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import get_type_hints, get_origin, get_args
|
|
8
|
+
import yaml
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
MODULE_NAME = 'models'
|
|
12
|
+
MODELS_PATH = os.environ["CONFIBARA_MODELS_PATH"]
|
|
13
|
+
CONFIG_PATH = os.environ["CONFIBARA_CONFIG_PATH"]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _load_models_module():
|
|
17
|
+
spec = importlib.util.spec_from_file_location(MODULE_NAME, MODELS_PATH)
|
|
18
|
+
module = importlib.util.module_from_spec(spec)
|
|
19
|
+
sys.modules[MODULE_NAME] = module
|
|
20
|
+
spec.loader.exec_module(module)
|
|
21
|
+
|
|
22
|
+
return module
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _get_dataclasses(module):
|
|
26
|
+
names = [
|
|
27
|
+
name for name, obj in vars(module).items()
|
|
28
|
+
if isinstance(obj, type)
|
|
29
|
+
and is_dataclass(obj)
|
|
30
|
+
and obj.__module__ == module.__name__
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
dataclasses = [getattr(module, name) for name in names]
|
|
34
|
+
return dataclasses
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _get_config_paths(dataclasses):
|
|
38
|
+
config_paths = {
|
|
39
|
+
cls.__name__: Path(CONFIG_PATH) / (cls.__name__.lower() + "s.yaml")
|
|
40
|
+
for cls in dataclasses
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return config_paths
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _get_load_order(dataclasses):
|
|
47
|
+
# Get dependencies for every dataclass
|
|
48
|
+
dependencies = {}
|
|
49
|
+
for cls in dataclasses:
|
|
50
|
+
type_hints = get_type_hints(cls)
|
|
51
|
+
nested = []
|
|
52
|
+
for field in fields(cls):
|
|
53
|
+
resolved = type_hints[field.name]
|
|
54
|
+
if is_dataclass(resolved):
|
|
55
|
+
nested.append(resolved)
|
|
56
|
+
# Handle generics: List[D], Optional[E], etc.
|
|
57
|
+
origin = get_origin(resolved)
|
|
58
|
+
if origin is not None:
|
|
59
|
+
for arg in get_args(resolved):
|
|
60
|
+
if is_dataclass(arg):
|
|
61
|
+
nested.append(arg)
|
|
62
|
+
dependencies[cls] = set(nested)
|
|
63
|
+
|
|
64
|
+
# Get all classes that are dependencies
|
|
65
|
+
hit = set()
|
|
66
|
+
for dep in dependencies.values():
|
|
67
|
+
for cls in dep:
|
|
68
|
+
hit.add(cls)
|
|
69
|
+
|
|
70
|
+
nodes = set(dataclasses)
|
|
71
|
+
edges = set()
|
|
72
|
+
for cls, deps in dependencies.items():
|
|
73
|
+
for dep in deps:
|
|
74
|
+
edges.add((cls, dep))
|
|
75
|
+
|
|
76
|
+
# Run Kahn's algorithm to sort dataclasses topologically
|
|
77
|
+
S = nodes - hit
|
|
78
|
+
L = []
|
|
79
|
+
|
|
80
|
+
while S:
|
|
81
|
+
n = S.pop()
|
|
82
|
+
L.append(n)
|
|
83
|
+
for dep in dependencies[n]:
|
|
84
|
+
edges.remove((n, dep))
|
|
85
|
+
if not dep in set(x for _, x in edges):
|
|
86
|
+
S.add(dep)
|
|
87
|
+
|
|
88
|
+
return L[::-1]
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _get_components_by_name(name, cls, components):
|
|
92
|
+
if type(name) == str:
|
|
93
|
+
for comp in components[cls]:
|
|
94
|
+
if comp.name == name:
|
|
95
|
+
return comp
|
|
96
|
+
elif type(name) == list:
|
|
97
|
+
comps = []
|
|
98
|
+
for n in name:
|
|
99
|
+
comps.append(_get_components_by_name(n, cls, components))
|
|
100
|
+
return comps
|
|
101
|
+
|
|
102
|
+
raise ValueError("No component with that name: ", name)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _load_component(name, cls, config_paths, loaded_components):
|
|
106
|
+
try:
|
|
107
|
+
with open(config_paths[name], "r") as f:
|
|
108
|
+
data = yaml.safe_load(f)
|
|
109
|
+
except FileNotFoundError:
|
|
110
|
+
return loaded_components
|
|
111
|
+
|
|
112
|
+
args = [args for args in data[name]]
|
|
113
|
+
print("Args: ", args)
|
|
114
|
+
type_hints = get_type_hints(cls)
|
|
115
|
+
|
|
116
|
+
for f in fields(cls):
|
|
117
|
+
type_args = get_args(type_hints[f.name])
|
|
118
|
+
if type_args and type_args[0] in loaded_components.keys():
|
|
119
|
+
for argdict in args:
|
|
120
|
+
argdict[f.name] = _get_components_by_name(argdict[f.name], type_args[0], loaded_components)
|
|
121
|
+
elif type_hints[f.name] in loaded_components.keys():
|
|
122
|
+
for argdict in args:
|
|
123
|
+
argdict[f.name] = _get_components_by_name(argdict[f.name], type_hints[f.name], loaded_components)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
new_comps = copy(loaded_components)
|
|
127
|
+
new_comps[cls] = [cls(**argdict) for argdict in args]
|
|
128
|
+
|
|
129
|
+
return new_comps
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def load():
|
|
133
|
+
module = _load_models_module()
|
|
134
|
+
dataclasses = _get_dataclasses(module)
|
|
135
|
+
config_paths = _get_config_paths(dataclasses)
|
|
136
|
+
load_order = _get_load_order(dataclasses)
|
|
137
|
+
components = {}
|
|
138
|
+
for cls in load_order:
|
|
139
|
+
components = _load_component(cls.__name__, cls, config_paths, components)
|
|
140
|
+
|
|
141
|
+
return components
|