confybara 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,9 @@
1
+ Metadata-Version: 2.3
2
+ Name: confybara
3
+ Version: 0.0.1a1
4
+ Summary: Add your description here
5
+ Requires-Python: >=3.14
6
+ Description-Content-Type: text/markdown
7
+
8
+ # confibara
9
+
@@ -0,0 +1,2 @@
1
+ # confibara
2
+
@@ -0,0 +1,14 @@
1
+ [project]
2
+ name = "confybara"
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 = "confybara"
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,2 @@
1
+ def main() -> None:
2
+ print("Hello from confibara!")
@@ -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