lionweb 0.2.0__py3-none-any.whl

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.
Files changed (106) hide show
  1. lionweb/__init__.py +2 -0
  2. lionweb/api/__init__.py +0 -0
  3. lionweb/api/classifier_instance_resolver.py +30 -0
  4. lionweb/api/composite_classifier_instance_resolver.py +29 -0
  5. lionweb/api/local_classifier_instance_resolver.py +24 -0
  6. lionweb/api/unresolved_classifier_instance_exception.py +8 -0
  7. lionweb/generation/__init__.py +0 -0
  8. lionweb/generation/deserializer_generation.py +165 -0
  9. lionweb/generation/generator.py +54 -0
  10. lionweb/generation/language_generation.py +332 -0
  11. lionweb/generation/node_classes_generation.py +519 -0
  12. lionweb/generation/utils.py +21 -0
  13. lionweb/language/__init__.py +22 -0
  14. lionweb/language/annotation.py +110 -0
  15. lionweb/language/classifier.py +184 -0
  16. lionweb/language/concept.py +127 -0
  17. lionweb/language/containment.py +87 -0
  18. lionweb/language/data_type.py +23 -0
  19. lionweb/language/debug_utils.py +20 -0
  20. lionweb/language/enumeration.py +40 -0
  21. lionweb/language/enumeration_literal.py +93 -0
  22. lionweb/language/feature.py +93 -0
  23. lionweb/language/field.py +80 -0
  24. lionweb/language/ikeyed.py +14 -0
  25. lionweb/language/inamed.py +9 -0
  26. lionweb/language/interface.py +80 -0
  27. lionweb/language/language.py +187 -0
  28. lionweb/language/language_entity.py +82 -0
  29. lionweb/language/link.py +66 -0
  30. lionweb/language/lioncore_builtins.py +164 -0
  31. lionweb/language/namespace_provider.py +13 -0
  32. lionweb/language/namespaced_entity.py +31 -0
  33. lionweb/language/primitive_type.py +28 -0
  34. lionweb/language/property.py +132 -0
  35. lionweb/language/reference.py +113 -0
  36. lionweb/language/structured_data_type.py +57 -0
  37. lionweb/lionweb_version.py +20 -0
  38. lionweb/model/__init__.py +2 -0
  39. lionweb/model/annotation_instance.py +20 -0
  40. lionweb/model/classifier_instance.py +73 -0
  41. lionweb/model/classifier_instance_utils.py +176 -0
  42. lionweb/model/has_feature_values.py +50 -0
  43. lionweb/model/has_settable_parent.py +10 -0
  44. lionweb/model/impl/__init__.py +0 -0
  45. lionweb/model/impl/abstract_classifier_instance.py +109 -0
  46. lionweb/model/impl/dynamic_annotation_instance.py +74 -0
  47. lionweb/model/impl/dynamic_classifier_instance.py +246 -0
  48. lionweb/model/impl/dynamic_node.py +117 -0
  49. lionweb/model/impl/dynamic_structured_datype_instance.py +64 -0
  50. lionweb/model/impl/enumeration_value.py +15 -0
  51. lionweb/model/impl/enumeration_value_impl.py +35 -0
  52. lionweb/model/impl/m3node.py +216 -0
  53. lionweb/model/impl/proxy_node.py +90 -0
  54. lionweb/model/node.py +189 -0
  55. lionweb/model/reference_value.py +39 -0
  56. lionweb/model/structured_data_type_instance.py +28 -0
  57. lionweb/model/structured_data_type_instance_utils.py +70 -0
  58. lionweb/presentation/__init__.py +0 -0
  59. lionweb/presentation/show_nodes.py +228 -0
  60. lionweb/repoclient/__init__.py +1 -0
  61. lionweb/repoclient/repo_client.py +303 -0
  62. lionweb/self/__init__.py +0 -0
  63. lionweb/self/lioncore.py +447 -0
  64. lionweb/serialization/__init__.py +7 -0
  65. lionweb/serialization/abstract_serialization.py +528 -0
  66. lionweb/serialization/classifier_resolver.py +57 -0
  67. lionweb/serialization/data/__init__.py +0 -0
  68. lionweb/serialization/data/metapointer.py +63 -0
  69. lionweb/serialization/data/raw_reference_value.py +7 -0
  70. lionweb/serialization/data/serialized_chunk.py +70 -0
  71. lionweb/serialization/data/serialized_classifier_instance.py +168 -0
  72. lionweb/serialization/data/serialized_containment_value.py +32 -0
  73. lionweb/serialization/data/serialized_property_value.py +32 -0
  74. lionweb/serialization/data/serialized_reference_value.py +67 -0
  75. lionweb/serialization/data/used_language.py +77 -0
  76. lionweb/serialization/deserialization_exception.py +11 -0
  77. lionweb/serialization/deserialization_status.py +82 -0
  78. lionweb/serialization/instantiator.py +131 -0
  79. lionweb/serialization/json_serialization.py +114 -0
  80. lionweb/serialization/json_utils.py +6 -0
  81. lionweb/serialization/low_level_json_serialization.py +325 -0
  82. lionweb/serialization/map_based_resolver.py +23 -0
  83. lionweb/serialization/node_populator.py +149 -0
  84. lionweb/serialization/primitives_values_serialization.py +204 -0
  85. lionweb/serialization/serialization_provider.py +50 -0
  86. lionweb/serialization/serialization_utils.py +103 -0
  87. lionweb/serialization/serialized_json_comparison_utils.py +185 -0
  88. lionweb/serialization/unavailable_node_policy.py +13 -0
  89. lionweb/utils/__init__.py +0 -0
  90. lionweb/utils/autoresolve.py +3 -0
  91. lionweb/utils/common_checks.py +14 -0
  92. lionweb/utils/id_utils.py +5 -0
  93. lionweb/utils/invalid_name.py +6 -0
  94. lionweb/utils/issue.py +43 -0
  95. lionweb/utils/issue_severity.py +6 -0
  96. lionweb/utils/language_validator.py +246 -0
  97. lionweb/utils/model_comparator.py +306 -0
  98. lionweb/utils/naming.py +21 -0
  99. lionweb/utils/node_tree_validator.py +60 -0
  100. lionweb/utils/validation_result.py +33 -0
  101. lionweb/utils/validator.py +12 -0
  102. lionweb-0.2.0.dist-info/METADATA +262 -0
  103. lionweb-0.2.0.dist-info/RECORD +106 -0
  104. lionweb-0.2.0.dist-info/WHEEL +5 -0
  105. lionweb-0.2.0.dist-info/licenses/LICENSE.txt +201 -0
  106. lionweb-0.2.0.dist-info/top_level.txt +1 -0
lionweb/__init__.py ADDED
@@ -0,0 +1,2 @@
1
+ __version__ = "0.2.0"
2
+ from .lionweb_version import LionWebVersion as LionWebVersion
File without changes
@@ -0,0 +1,30 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import Any, Optional
3
+
4
+ from lionweb.api.unresolved_classifier_instance_exception import \
5
+ UnresolvedClassifierInstanceException
6
+ from lionweb.model.impl.proxy_node import ProxyNode
7
+
8
+
9
+ class ClassifierInstanceResolver(ABC):
10
+
11
+ @abstractmethod
12
+ def resolve(self, instance_id: str) -> Optional[Any]:
13
+ """Return the classifier instance or None if not found."""
14
+ pass
15
+
16
+ def can_resolve(self, instance_id: str) -> bool:
17
+ """Return True if the instance can be resolved, False otherwise."""
18
+ return self.resolve(instance_id) is not None
19
+
20
+ def strictly_resolve(self, instance_id: str) -> Any:
21
+ """Return the classifier instance or raise an exception if not found."""
22
+ instance = self.resolve(instance_id)
23
+ if instance is None:
24
+ raise UnresolvedClassifierInstanceException(instance_id)
25
+ return instance
26
+
27
+ def resolve_or_proxy(self, instance_id: str) -> Any:
28
+ """Return the classifier instance or a ProxyNode if not found."""
29
+ instance = self.resolve(instance_id)
30
+ return instance if instance is not None else ProxyNode(instance_id)
@@ -0,0 +1,29 @@
1
+ from typing import List, Optional
2
+
3
+ from lionweb.api.classifier_instance_resolver import ClassifierInstanceResolver
4
+ from lionweb.model import ClassifierInstance
5
+
6
+
7
+ class CompositeClassifierInstanceResolver(ClassifierInstanceResolver):
8
+ def __init__(self, *classifier_instance_resolvers: ClassifierInstanceResolver):
9
+ self.classifier_instance_resolvers: List[ClassifierInstanceResolver] = list(
10
+ classifier_instance_resolvers
11
+ )
12
+
13
+ def add(
14
+ self, classifier_instance_resolver: ClassifierInstanceResolver
15
+ ) -> "CompositeClassifierInstanceResolver":
16
+ self.classifier_instance_resolvers.append(classifier_instance_resolver)
17
+ return self
18
+
19
+ def resolve(self, instance_id: str) -> Optional[ClassifierInstance]:
20
+ for resolver in self.classifier_instance_resolvers:
21
+ instance = resolver.resolve(instance_id)
22
+ if instance is not None:
23
+ return instance
24
+ return None
25
+
26
+ def __str__(self) -> str:
27
+ return (
28
+ f"CompositeClassifierInstanceResolver({self.classifier_instance_resolvers})"
29
+ )
@@ -0,0 +1,24 @@
1
+ from lionweb.api.classifier_instance_resolver import ClassifierInstanceResolver
2
+
3
+
4
+ class LocalClassifierInstanceResolver(ClassifierInstanceResolver):
5
+ def __init__(self, *instances):
6
+ self.instances = {instance.id: instance for instance in instances}
7
+
8
+ def add(self, instance):
9
+ self.instances[instance.id] = instance
10
+
11
+ def resolve(self, instance_id):
12
+ return self.instances.get(instance_id)
13
+
14
+ def add_all(self, instances):
15
+ for instance in instances:
16
+ self.add(instance)
17
+
18
+ def add_tree(self, root):
19
+ self.add(root)
20
+ for child in root.get_children():
21
+ self.add_tree(child)
22
+
23
+ def __str__(self):
24
+ return f"LocalClassifierInstanceResolver({list(self.instances.keys())})"
@@ -0,0 +1,8 @@
1
+ class UnresolvedClassifierInstanceException(RuntimeError):
2
+
3
+ def __init__(self, instance_id: str):
4
+ super().__init__("Unable to resolve classifier instance with ID=" + instance_id)
5
+ self._instance_id = instance_id
6
+
7
+ def get_instance_id(self) -> str:
8
+ return self._instance_id
File without changes
@@ -0,0 +1,165 @@
1
+ import ast
2
+ from _ast import stmt
3
+ from pathlib import Path
4
+ from typing import List, cast
5
+
6
+ import astor # type: ignore
7
+
8
+ from lionweb.language import Concept, Language
9
+
10
+
11
+ def deserializer_generation(click, language: Language, output):
12
+ module_body = []
13
+
14
+ # Import statements
15
+ module_body.append(
16
+ ast.ImportFrom(
17
+ module="gen.language",
18
+ names=[
19
+ ast.alias(name=f"get_{cast(str, c.get_name()).lower()}", asname=None)
20
+ for c in language.get_elements()
21
+ if isinstance(c, Concept)
22
+ ],
23
+ level=0,
24
+ )
25
+ )
26
+ module_body.append(
27
+ ast.ImportFrom(
28
+ module="gen.node_classes",
29
+ names=[
30
+ ast.alias(name=cast(str, c.get_name()), asname=None)
31
+ for c in language.get_elements()
32
+ if isinstance(c, Concept)
33
+ ],
34
+ level=0,
35
+ )
36
+ )
37
+ module_body.append(
38
+ ast.ImportFrom(
39
+ module="lionweb.serialization",
40
+ names=[ast.alias(name="AbstractSerialization", asname=None)],
41
+ level=0,
42
+ )
43
+ )
44
+ module_body.append(
45
+ ast.ImportFrom(
46
+ module="lionweb.serialization.data.serialized_classifier_instance",
47
+ names=[ast.alias(name="SerializedClassifierInstance", asname=None)],
48
+ level=0,
49
+ )
50
+ )
51
+
52
+ register_func_body: List[stmt] = []
53
+ for language_element in language.get_elements():
54
+ if isinstance(language_element, Concept):
55
+ concept_name = cast(str, language_element.get_name())
56
+ # deserializer() inner function
57
+ register_func_body.append(
58
+ ast.FunctionDef(
59
+ name=f"deserializer_{concept_name.lower()}",
60
+ args=ast.arguments(
61
+ posonlyargs=[],
62
+ args=[
63
+ ast.arg(arg="classifier"),
64
+ ast.arg(
65
+ arg="serialized_instance",
66
+ annotation=ast.Name(
67
+ id="SerializedClassifierInstance", ctx=ast.Load()
68
+ ),
69
+ ),
70
+ ast.arg(arg="deserialized_instances_by_id"),
71
+ ast.arg(arg="properties_values"),
72
+ ],
73
+ kwonlyargs=[],
74
+ kw_defaults=[],
75
+ defaults=[],
76
+ ),
77
+ body=[
78
+ ast.Return(
79
+ value=ast.Call(
80
+ func=ast.Name(id=concept_name, ctx=ast.Load()),
81
+ args=[
82
+ ast.Attribute(
83
+ value=ast.Name(
84
+ id="serialized_instance", ctx=ast.Load()
85
+ ),
86
+ attr="id",
87
+ ctx=ast.Load(),
88
+ )
89
+ ],
90
+ keywords=[],
91
+ )
92
+ )
93
+ ],
94
+ decorator_list=[],
95
+ returns=None,
96
+ )
97
+ )
98
+
99
+ # register_deserializers() function
100
+ register_func_body.append(
101
+ ast.Expr(
102
+ value=ast.Call(
103
+ func=ast.Attribute(
104
+ value=ast.Attribute(
105
+ value=ast.Name(id="serialization", ctx=ast.Load()),
106
+ attr="instantiator",
107
+ ctx=ast.Load(),
108
+ ),
109
+ attr="register_custom_deserializer",
110
+ ctx=ast.Load(),
111
+ ),
112
+ args=[
113
+ ast.Attribute(
114
+ value=ast.Call(
115
+ func=ast.Name(
116
+ id=f"get_{concept_name.lower()}", ctx=ast.Load()
117
+ ),
118
+ args=[],
119
+ keywords=[],
120
+ ),
121
+ attr="id",
122
+ ctx=ast.Load(),
123
+ )
124
+ ],
125
+ keywords=[
126
+ ast.keyword(
127
+ arg="deserializer",
128
+ value=ast.Name(
129
+ id=f"deserializer_{concept_name.lower()}",
130
+ ctx=ast.Load(),
131
+ ),
132
+ )
133
+ ],
134
+ )
135
+ )
136
+ )
137
+
138
+ register_func = ast.FunctionDef(
139
+ name="register_deserializers",
140
+ args=ast.arguments(
141
+ posonlyargs=[],
142
+ args=[
143
+ ast.arg(
144
+ arg="serialization",
145
+ annotation=ast.Name(id="AbstractSerialization", ctx=ast.Load()),
146
+ )
147
+ ],
148
+ kwonlyargs=[],
149
+ kw_defaults=[],
150
+ defaults=[],
151
+ ),
152
+ body=register_func_body,
153
+ decorator_list=[],
154
+ returns=None,
155
+ )
156
+
157
+ # Final module
158
+ module = ast.Module(body=module_body + [register_func], type_ignores=[])
159
+
160
+ generated_code = astor.to_source(module)
161
+ output_path = Path(output)
162
+ output_path.mkdir(parents=True, exist_ok=True)
163
+ click.echo(f"📂 Saving deserializer to: {output}")
164
+ with Path(f"{output}/deserializer.py").open("w", encoding="utf-8") as f:
165
+ f.write(generated_code)
@@ -0,0 +1,54 @@
1
+ from typing import cast
2
+
3
+ import click
4
+
5
+ from lionweb.generation.language_generation import language_generation
6
+ from lionweb.language import Language
7
+ from lionweb.lionweb_version import LionWebVersion
8
+ from lionweb.serialization.serialization_provider import SerializationProvider
9
+
10
+
11
+ @click.command()
12
+ @click.option(
13
+ "-d",
14
+ "--dependencies",
15
+ type=click.Path(exists=True, dir_okay=False, readable=True),
16
+ multiple=True,
17
+ )
18
+ @click.argument(
19
+ "lionweb-language", type=click.Path(exists=True, dir_okay=False, readable=True)
20
+ )
21
+ @click.argument("output", type=click.Path(exists=False, file_okay=False, writable=True))
22
+ def main(dependencies, lionweb_language, output):
23
+ from lionweb.generation.deserializer_generation import \
24
+ deserializer_generation
25
+ from lionweb.generation.node_classes_generation import \
26
+ node_classes_generation
27
+
28
+ """Simple CLI that processes a file and writes results to a directory."""
29
+ serialization = SerializationProvider.get_standard_json_serialization(
30
+ LionWebVersion.V2023_1
31
+ )
32
+
33
+ for dep in dependencies:
34
+ click.echo(f"Processing dependency {dep}")
35
+ with open(dep, "r", encoding="utf-8") as f:
36
+ content = f.read()
37
+ language = cast(
38
+ Language, serialization.deserialize_string_to_nodes(content)[0]
39
+ )
40
+ serialization.register_language(language=language)
41
+ serialization.classifier_resolver.register_language(language)
42
+ serialization.instance_resolver.add_tree(language)
43
+
44
+ click.echo(f"📄 Processing file: {lionweb_language}")
45
+ with open(lionweb_language, "r", encoding="utf-8") as f:
46
+ content = f.read()
47
+ language = cast(Language, serialization.deserialize_string_to_nodes(content)[0])
48
+ language_generation(click, language, output)
49
+ node_classes_generation(click, language, output)
50
+ deserializer_generation(click, language, output)
51
+
52
+
53
+ if __name__ == "__main__":
54
+ main()
@@ -0,0 +1,332 @@
1
+ import ast
2
+ from _ast import expr, stmt
3
+ from pathlib import Path
4
+ from typing import List, cast
5
+
6
+ import astor # type: ignore
7
+
8
+ from lionweb.language import (Concept, Containment, DataType, Language,
9
+ LionCoreBuiltins, Property)
10
+ from lionweb.language.reference import Reference
11
+
12
+
13
+ def _set_lw_version(language: Language):
14
+ return ast.keyword(
15
+ arg="lion_web_version",
16
+ value=ast.Attribute(
17
+ value=ast.Name(id="LionWebVersion", ctx=ast.Load()),
18
+ attr=language.get_lionweb_version().name,
19
+ ctx=ast.Load(),
20
+ ),
21
+ )
22
+
23
+
24
+ def _generate_language(language: Language) -> ast.Assign:
25
+ return ast.Assign(
26
+ targets=[ast.Name(id="language", ctx=ast.Store())],
27
+ value=ast.Call(
28
+ func=ast.Name(id="Language", ctx=ast.Load()),
29
+ args=[],
30
+ keywords=[
31
+ _set_lw_version(language),
32
+ ast.keyword(arg="id", value=ast.Constant(value=language.id)),
33
+ ast.keyword(arg="name", value=ast.Constant(value=language.get_name())),
34
+ ast.keyword(arg="key", value=ast.Constant(value=language.key)),
35
+ ast.keyword(
36
+ arg="version", value=ast.Constant(value=language.get_version())
37
+ ),
38
+ ],
39
+ ),
40
+ )
41
+
42
+
43
+ def language_generation(click, language: Language, output):
44
+ body: List[stmt] = []
45
+ body.append(
46
+ ast.ImportFrom(
47
+ module="lionweb.language",
48
+ names=[
49
+ ast.alias(name="Language", asname=None),
50
+ ast.alias(name="Concept", asname=None),
51
+ ast.alias(name="Property", asname=None),
52
+ ast.alias(name="Containment", asname=None),
53
+ ast.alias(name="Reference", asname=None),
54
+ ast.alias(name="LionCoreBuiltins", asname=None),
55
+ ],
56
+ level=0,
57
+ )
58
+ )
59
+ body.append(
60
+ ast.ImportFrom(
61
+ module="lionweb.lionweb_version",
62
+ names=[ast.alias(name="LionWebVersion", asname=None)],
63
+ level=0,
64
+ )
65
+ )
66
+ body.append(
67
+ ast.ImportFrom(
68
+ module="functools",
69
+ names=[ast.alias(name="lru_cache", asname=None)],
70
+ level=0,
71
+ )
72
+ )
73
+ # Decorator: @lru_cache(maxsize=1)
74
+ decorator = ast.Call(
75
+ func=ast.Name(id="lru_cache", ctx=ast.Load()),
76
+ args=[],
77
+ keywords=[ast.keyword(arg="maxsize", value=ast.Constant(value=1))],
78
+ )
79
+
80
+ # Function body for get_language()
81
+ function_body: List[stmt] = []
82
+ function_body.append(_generate_language(language))
83
+
84
+ for language_element in language.get_elements():
85
+ if isinstance(language_element, Concept):
86
+ concept_name = cast(str, language_element.get_name())
87
+ function_body.append(
88
+ ast.Assign(
89
+ targets=[ast.Name(id=concept_name, ctx=ast.Store())],
90
+ value=ast.Call(
91
+ func=ast.Name(id="Concept", ctx=ast.Load()),
92
+ args=[],
93
+ keywords=[
94
+ _set_lw_version(language),
95
+ ast.keyword(
96
+ arg="id", value=ast.Constant(value=language_element.id)
97
+ ),
98
+ ast.keyword(
99
+ arg="name", value=ast.Constant(value=concept_name)
100
+ ),
101
+ ast.keyword(
102
+ arg="key",
103
+ value=ast.Constant(value=language_element.key),
104
+ ),
105
+ ],
106
+ ),
107
+ )
108
+ )
109
+
110
+ if language_element.get_extended_concept():
111
+ ec = cast(Concept, language_element.get_extended_concept())
112
+ ec_name = cast(str, ec.get_name())
113
+ function_body.append(
114
+ ast.Expr(
115
+ ast.Call(
116
+ func=ast.Attribute(
117
+ value=ast.Name(id=concept_name, ctx=ast.Load()),
118
+ attr="set_extended_concept",
119
+ ctx=ast.Load(),
120
+ ),
121
+ args=[ast.Name(id=ec_name, ctx=ast.Load())],
122
+ keywords=[],
123
+ )
124
+ )
125
+ )
126
+
127
+ for interf in language_element.get_implemented():
128
+ function_body.append(
129
+ ast.Expr(
130
+ ast.Call(
131
+ func=ast.Attribute(
132
+ value=ast.Name(id=concept_name, ctx=ast.Load()),
133
+ attr="add_implemented",
134
+ ctx=ast.Load(),
135
+ ),
136
+ args=[],
137
+ keywords=[],
138
+ )
139
+ )
140
+ )
141
+
142
+ # language.add_element(concept1)
143
+ function_body.append(
144
+ ast.Expr(
145
+ value=ast.Call(
146
+ func=ast.Attribute(
147
+ value=ast.Name(id="language", ctx=ast.Load()),
148
+ attr="add_element",
149
+ ctx=ast.Load(),
150
+ ),
151
+ args=[ast.Name(id=concept_name, ctx=ast.Load())],
152
+ keywords=[],
153
+ )
154
+ )
155
+ )
156
+
157
+ for feature in language_element.get_features():
158
+ if isinstance(feature, Reference):
159
+ feature_creation = ast.Call(
160
+ func=ast.Name(id="Reference", ctx=ast.Load()),
161
+ args=[],
162
+ keywords=[
163
+ _set_lw_version(language),
164
+ ast.keyword(arg="id", value=ast.Constant(value=feature.id)),
165
+ ast.keyword(
166
+ arg="name", value=ast.Constant(value=feature.get_name())
167
+ ),
168
+ ast.keyword(
169
+ arg="key", value=ast.Constant(value=feature.key)
170
+ ),
171
+ ],
172
+ )
173
+ function_body.append(
174
+ ast.Expr(
175
+ value=ast.Call(
176
+ func=ast.Attribute(
177
+ value=ast.Name(id=concept_name, ctx=ast.Load()),
178
+ attr="add_feature",
179
+ ctx=ast.Load(),
180
+ ),
181
+ args=[feature_creation],
182
+ keywords=[],
183
+ )
184
+ )
185
+ )
186
+ elif isinstance(feature, Property):
187
+ pt = cast(DataType, feature.type)
188
+ property_type: expr
189
+ if pt == LionCoreBuiltins.get_string(feature.lion_web_version):
190
+ property_type = ast.Call(
191
+ func=ast.Attribute(
192
+ value=ast.Name(id="LionCoreBuiltins", ctx=ast.Load()),
193
+ attr="get_string",
194
+ ctx=ast.Load(),
195
+ ),
196
+ args=[],
197
+ keywords=[_set_lw_version(language)],
198
+ )
199
+ elif pt == LionCoreBuiltins.get_integer(feature.lion_web_version):
200
+ property_type = ast.Call(
201
+ func=ast.Attribute(
202
+ value=ast.Name(id="LionCoreBuiltins", ctx=ast.Load()),
203
+ attr="get_integer",
204
+ ctx=ast.Load(),
205
+ ),
206
+ args=[],
207
+ keywords=[_set_lw_version(language)],
208
+ )
209
+ else:
210
+ raise ValueError(cast(str, pt.get_name()))
211
+ feature_creation = ast.Call(
212
+ func=ast.Name(id="Property", ctx=ast.Load()),
213
+ args=[],
214
+ keywords=[
215
+ _set_lw_version(language),
216
+ ast.keyword(arg="id", value=ast.Constant(value=feature.id)),
217
+ ast.keyword(
218
+ arg="name", value=ast.Constant(value=feature.get_name())
219
+ ),
220
+ ast.keyword(
221
+ arg="key", value=ast.Constant(value=feature.key)
222
+ ),
223
+ ast.keyword(arg="type", value=property_type),
224
+ ],
225
+ )
226
+ function_body.append(
227
+ ast.Expr(
228
+ value=ast.Call(
229
+ func=ast.Attribute(
230
+ value=ast.Name(id=concept_name, ctx=ast.Load()),
231
+ attr="add_feature",
232
+ ctx=ast.Load(),
233
+ ),
234
+ args=[feature_creation],
235
+ keywords=[],
236
+ )
237
+ )
238
+ )
239
+ elif isinstance(feature, Containment):
240
+ feature_creation = ast.Call(
241
+ func=ast.Name(id="Containment", ctx=ast.Load()),
242
+ args=[],
243
+ keywords=[
244
+ _set_lw_version(language),
245
+ ast.keyword(arg="id", value=ast.Constant(value=feature.id)),
246
+ ast.keyword(
247
+ arg="name", value=ast.Constant(value=feature.get_name())
248
+ ),
249
+ ast.keyword(
250
+ arg="key", value=ast.Constant(value=feature.key)
251
+ ),
252
+ ],
253
+ )
254
+ function_body.append(
255
+ ast.Expr(
256
+ value=ast.Call(
257
+ func=ast.Attribute(
258
+ value=ast.Name(id=concept_name, ctx=ast.Load()),
259
+ attr="add_feature",
260
+ ctx=ast.Load(),
261
+ ),
262
+ args=[feature_creation],
263
+ keywords=[],
264
+ )
265
+ )
266
+ )
267
+
268
+ # return language
269
+ function_body.append(ast.Return(value=ast.Name(id="language", ctx=ast.Load())))
270
+
271
+ # Define get_language function
272
+ get_language_def = ast.FunctionDef(
273
+ name="get_language",
274
+ args=ast.arguments(
275
+ posonlyargs=[], args=[], kwonlyargs=[], kw_defaults=[], defaults=[]
276
+ ),
277
+ body=function_body,
278
+ decorator_list=[decorator],
279
+ returns=ast.Name(id="Language", ctx=ast.Load()),
280
+ type_comment=None,
281
+ )
282
+
283
+ # Wrap function in module
284
+ body.append(get_language_def)
285
+
286
+ for language_element in language.get_elements():
287
+ if isinstance(language_element, Concept):
288
+ concept_name = cast(str, language_element.get_name())
289
+ body.append(
290
+ ast.FunctionDef(
291
+ name=f"get_{concept_name.lower()}",
292
+ args=ast.arguments(
293
+ posonlyargs=[],
294
+ args=[],
295
+ kwonlyargs=[],
296
+ kw_defaults=[],
297
+ defaults=[],
298
+ ),
299
+ body=[
300
+ ast.Return(
301
+ value=ast.Call(
302
+ func=ast.Attribute(
303
+ value=ast.Call(
304
+ func=ast.Name(
305
+ id="get_language", ctx=ast.Load()
306
+ ),
307
+ args=[],
308
+ keywords=[],
309
+ ),
310
+ attr="get_concept_by_name",
311
+ ctx=ast.Load(),
312
+ ),
313
+ args=[ast.Constant(value=language_element.get_name())],
314
+ keywords=[],
315
+ )
316
+ )
317
+ ],
318
+ decorator_list=[],
319
+ returns=ast.Name(id="Concept", ctx=ast.Load()),
320
+ type_comment=None,
321
+ )
322
+ )
323
+
324
+ module = ast.Module(body=body, type_ignores=[])
325
+
326
+ click.echo(f"📂 Saving language to: {output}")
327
+ generated_code = astor.to_source(module)
328
+ output_path = Path(output)
329
+ output_path.mkdir(parents=True, exist_ok=True)
330
+
331
+ with Path(f"{output}/language.py").open("w", encoding="utf-8") as f:
332
+ f.write(generated_code)