metaobjects 0.9.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.
- metaobjects/__init__.py +75 -0
- metaobjects/agent_context/__init__.py +55 -0
- metaobjects/agent_context/_content/README.md +14 -0
- metaobjects/agent_context/_content/servers/csharp.meta.json +5 -0
- metaobjects/agent_context/_content/servers/java.meta.json +5 -0
- metaobjects/agent_context/_content/servers/kotlin.meta.json +5 -0
- metaobjects/agent_context/_content/servers/python.meta.json +5 -0
- metaobjects/agent_context/_content/servers/typescript.meta.json +5 -0
- metaobjects/agent_context/_content/skills/metaobjects-authoring/SKILL.md +301 -0
- metaobjects/agent_context/_content/skills/metaobjects-codegen/SKILL.md +99 -0
- metaobjects/agent_context/_content/skills/metaobjects-codegen/references/csharp.md +87 -0
- metaobjects/agent_context/_content/skills/metaobjects-codegen/references/java.md +94 -0
- metaobjects/agent_context/_content/skills/metaobjects-codegen/references/kotlin.md +110 -0
- metaobjects/agent_context/_content/skills/metaobjects-codegen/references/typescript.md +135 -0
- metaobjects/agent_context/_content/skills/metaobjects-prompts/SKILL.md +148 -0
- metaobjects/agent_context/_content/skills/metaobjects-prompts/references/csharp.md +110 -0
- metaobjects/agent_context/_content/skills/metaobjects-prompts/references/java.md +108 -0
- metaobjects/agent_context/_content/skills/metaobjects-prompts/references/kotlin.md +130 -0
- metaobjects/agent_context/_content/skills/metaobjects-prompts/references/python.md +116 -0
- metaobjects/agent_context/_content/skills/metaobjects-prompts/references/typescript.md +150 -0
- metaobjects/agent_context/_content/skills/metaobjects-runtime-ui/SKILL.md +130 -0
- metaobjects/agent_context/_content/skills/metaobjects-runtime-ui/references/java.md +96 -0
- metaobjects/agent_context/_content/skills/metaobjects-runtime-ui/references/kotlin.md +99 -0
- metaobjects/agent_context/_content/skills/metaobjects-runtime-ui/references/react.md +86 -0
- metaobjects/agent_context/_content/skills/metaobjects-runtime-ui/references/tanstack.md +119 -0
- metaobjects/agent_context/_content/skills/metaobjects-runtime-ui/references/typescript.md +92 -0
- metaobjects/agent_context/_content/skills/metaobjects-verify/SKILL.md +107 -0
- metaobjects/agent_context/_content/skills/metaobjects-verify/references/migration.md +72 -0
- metaobjects/agent_context/_content/templates/always-on.md.mustache +27 -0
- metaobjects/agent_context/assemble.py +133 -0
- metaobjects/agent_context/content_root.py +54 -0
- metaobjects/agent_context/scaffold.py +191 -0
- metaobjects/agent_context/types.py +44 -0
- metaobjects/attr_class_map.py +23 -0
- metaobjects/cli.py +696 -0
- metaobjects/codegen/__init__.py +0 -0
- metaobjects/codegen/config.py +11 -0
- metaobjects/codegen/constants.py +13 -0
- metaobjects/codegen/extract_delegate_emitter.py +384 -0
- metaobjects/codegen/extract_schema_emitter.py +139 -0
- metaobjects/codegen/format.py +31 -0
- metaobjects/codegen/fr010_field_mapping.py +220 -0
- metaobjects/codegen/generator.py +62 -0
- metaobjects/codegen/generator_registry.py +163 -0
- metaobjects/codegen/generators/__init__.py +0 -0
- metaobjects/codegen/generators/entity_model.py +263 -0
- metaobjects/codegen/generators/extractor_generator.py +317 -0
- metaobjects/codegen/generators/filter_allowlist_generator.py +309 -0
- metaobjects/codegen/generators/m2m_codegen.py +192 -0
- metaobjects/codegen/generators/output_parser_generator.py +272 -0
- metaobjects/codegen/generators/output_prompt_generator.py +192 -0
- metaobjects/codegen/generators/payload_vo_generator.py +672 -0
- metaobjects/codegen/generators/render_helper_generator.py +451 -0
- metaobjects/codegen/generators/router_generator.py +635 -0
- metaobjects/codegen/generators/template_generator.py +70 -0
- metaobjects/codegen/generators/tph_plan.py +120 -0
- metaobjects/codegen/generators/trace_helper_generator.py +336 -0
- metaobjects/codegen/instance_artifacts.py +15 -0
- metaobjects/codegen/output_format_spec_emitter.py +79 -0
- metaobjects/codegen/overwrite_policy.py +27 -0
- metaobjects/codegen/runner.py +110 -0
- metaobjects/codegen/runtime/__init__.py +6 -0
- metaobjects/codegen/runtime/filter_parser.py +193 -0
- metaobjects/codegen/type_map.py +84 -0
- metaobjects/core_types.py +809 -0
- metaobjects/datatype.py +19 -0
- metaobjects/documentation/__init__.py +28 -0
- metaobjects/documentation/doc_constants.py +20 -0
- metaobjects/documentation/doc_provider.py +20 -0
- metaobjects/documentation/doc_schema.py +24 -0
- metaobjects/errors.py +124 -0
- metaobjects/loader/__init__.py +0 -0
- metaobjects/loader/merge.py +287 -0
- metaobjects/loader/meta_data_loader.py +245 -0
- metaobjects/loader/sources/__init__.py +24 -0
- metaobjects/loader/sources/directory_source.py +50 -0
- metaobjects/loader/sources/file_source.py +41 -0
- metaobjects/loader/sources/meta_data_source.py +67 -0
- metaobjects/loader/sources/uri_source.py +56 -0
- metaobjects/loader/validate_discriminator.py +181 -0
- metaobjects/loader/validate_field_readonly.py +146 -0
- metaobjects/loader/validate_source_parameter_ref.py +159 -0
- metaobjects/loader/validate_source_physical_names.py +140 -0
- metaobjects/loader/validation_passes.py +1513 -0
- metaobjects/meta/__init__.py +1 -0
- metaobjects/meta/core/__init__.py +0 -0
- metaobjects/meta/core/attr/__init__.py +0 -0
- metaobjects/meta/core/attr/attr_constants.py +31 -0
- metaobjects/meta/core/attr/meta_attr.py +136 -0
- metaobjects/meta/core/field/__init__.py +0 -0
- metaobjects/meta/core/field/field_constants.py +105 -0
- metaobjects/meta/core/field/meta_field.py +76 -0
- metaobjects/meta/core/identity/__init__.py +0 -0
- metaobjects/meta/core/identity/identity_constants.py +19 -0
- metaobjects/meta/core/identity/meta_identity.py +8 -0
- metaobjects/meta/core/object/__init__.py +0 -0
- metaobjects/meta/core/object/meta_object.py +65 -0
- metaobjects/meta/core/object/meta_object_aware.py +43 -0
- metaobjects/meta/core/object/object_class_registry.py +56 -0
- metaobjects/meta/core/object/object_constants.py +13 -0
- metaobjects/meta/core/object/object_extract.py +400 -0
- metaobjects/meta/core/object/value_object.py +70 -0
- metaobjects/meta/core/relationship/__init__.py +0 -0
- metaobjects/meta/core/relationship/derive_m2m_fields.py +180 -0
- metaobjects/meta/core/relationship/meta_relationship.py +54 -0
- metaobjects/meta/core/relationship/relationship_constants.py +51 -0
- metaobjects/meta/core/validator/__init__.py +0 -0
- metaobjects/meta/core/validator/validator_constants.py +18 -0
- metaobjects/meta/meta_data.py +206 -0
- metaobjects/meta/meta_root.py +8 -0
- metaobjects/meta/persistence/__init__.py +0 -0
- metaobjects/meta/persistence/db/__init__.py +1 -0
- metaobjects/meta/persistence/db/db_constants.py +41 -0
- metaobjects/meta/persistence/db/db_provider.py +60 -0
- metaobjects/meta/persistence/origin/__init__.py +0 -0
- metaobjects/meta/persistence/origin/meta_origin.py +8 -0
- metaobjects/meta/persistence/origin/origin_constants.py +20 -0
- metaobjects/meta/persistence/source/__init__.py +0 -0
- metaobjects/meta/persistence/source/meta_source.py +137 -0
- metaobjects/meta/persistence/source/source_constants.py +115 -0
- metaobjects/meta/presentation/__init__.py +0 -0
- metaobjects/meta/presentation/layout/__init__.py +0 -0
- metaobjects/meta/presentation/layout/layout_constants.py +13 -0
- metaobjects/meta/presentation/layout/meta_layout.py +8 -0
- metaobjects/meta/presentation/view/__init__.py +0 -0
- metaobjects/meta/presentation/view/meta_view.py +8 -0
- metaobjects/meta/presentation/view/view_constants.py +22 -0
- metaobjects/meta/template/__init__.py +0 -0
- metaobjects/meta/template/meta_template.py +46 -0
- metaobjects/meta/template/template_constants.py +112 -0
- metaobjects/meta/template/template_provider.py +43 -0
- metaobjects/parser.py +380 -0
- metaobjects/parser_yaml.py +82 -0
- metaobjects/provider.py +111 -0
- metaobjects/py.typed +0 -0
- metaobjects/registry.py +210 -0
- metaobjects/registry_manifest.py +223 -0
- metaobjects/render/__init__.py +74 -0
- metaobjects/render/email_document.py +14 -0
- metaobjects/render/escapers.py +109 -0
- metaobjects/render/extract/__init__.py +59 -0
- metaobjects/render/extract/coerce.py +279 -0
- metaobjects/render/extract/extract.py +211 -0
- metaobjects/render/extract/extract_map.py +61 -0
- metaobjects/render/extract/json_forgiving_reader.py +203 -0
- metaobjects/render/extract/locate.py +65 -0
- metaobjects/render/extract/normalize.py +96 -0
- metaobjects/render/extract/strip.py +20 -0
- metaobjects/render/extract/types.py +332 -0
- metaobjects/render/extract/xml_forgiving_reader.py +162 -0
- metaobjects/render/filesystem_provider.py +51 -0
- metaobjects/render/prompt/__init__.py +32 -0
- metaobjects/render/prompt/output_format_renderer.py +340 -0
- metaobjects/render/prompt/output_format_spec.py +28 -0
- metaobjects/render/prompt/prompt_field.py +29 -0
- metaobjects/render/prompt/prompt_overrides.py +29 -0
- metaobjects/render/prompt/prompt_style.py +38 -0
- metaobjects/render/renderer.py +358 -0
- metaobjects/render/verify.py +266 -0
- metaobjects/runtime/__init__.py +39 -0
- metaobjects/runtime/llm_recorder.py +210 -0
- metaobjects/runtime/n2m_resolver.py +155 -0
- metaobjects/runtime/object_manager.py +715 -0
- metaobjects/runtime/tph.py +50 -0
- metaobjects/serializer_json.py +172 -0
- metaobjects/shared/__init__.py +0 -0
- metaobjects/shared/base_types.py +16 -0
- metaobjects/shared/separators.py +4 -0
- metaobjects/shared/structural.py +9 -0
- metaobjects/source/__init__.py +79 -0
- metaobjects/source/error_source.py +266 -0
- metaobjects/source/json_path.py +106 -0
- metaobjects/source/semantic_diff.py +98 -0
- metaobjects/source/yaml_positions.py +174 -0
- metaobjects/super_resolve.py +128 -0
- metaobjects/yaml_desugar.py +481 -0
- metaobjects-0.9.0.dist-info/METADATA +97 -0
- metaobjects-0.9.0.dist-info/RECORD +181 -0
- metaobjects-0.9.0.dist-info/WHEEL +4 -0
- metaobjects-0.9.0.dist-info/entry_points.txt +2 -0
- metaobjects-0.9.0.dist-info/licenses/LICENSE +189 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""metaobjects.meta — node base and per-concern node modules."""
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""Attribute subtype vocabulary (colocated with the attr node — ADR-0003)."""
|
|
2
|
+
from ....shared.base_types import SUBTYPE_BASE # noqa: F401 (re-export convenience)
|
|
3
|
+
|
|
4
|
+
ATTR_SUBTYPE_STRING = "string"
|
|
5
|
+
ATTR_SUBTYPE_INT = "int"
|
|
6
|
+
ATTR_SUBTYPE_LONG = "long"
|
|
7
|
+
ATTR_SUBTYPE_DOUBLE = "double"
|
|
8
|
+
ATTR_SUBTYPE_BOOLEAN = "boolean"
|
|
9
|
+
ATTR_SUBTYPE_FILTER = "filter"
|
|
10
|
+
ATTR_SUBTYPE_PROPERTIES = "properties"
|
|
11
|
+
ATTR_SUBTYPE_CLASS = "class"
|
|
12
|
+
|
|
13
|
+
# The retired "stringarray" array attr subtype. It is NO LONGER a registered
|
|
14
|
+
# (attr, sub_type) (not in ATTR_SUBTYPES) — array-ness is modeled as a "string"
|
|
15
|
+
# attr with the orthogonal AttrSchema.is_array flag (matching Java's
|
|
16
|
+
# StringAttribute + @isArray), so "stringarray" never appears in the registry
|
|
17
|
+
# manifest. The constant survives only as the array-coercion class-map key
|
|
18
|
+
# (bare-string → one-element list).
|
|
19
|
+
ATTR_SUBTYPE_STRINGARRAY = "stringarray"
|
|
20
|
+
|
|
21
|
+
ATTR_SUBTYPES = (
|
|
22
|
+
SUBTYPE_BASE,
|
|
23
|
+
ATTR_SUBTYPE_STRING,
|
|
24
|
+
ATTR_SUBTYPE_INT,
|
|
25
|
+
ATTR_SUBTYPE_LONG,
|
|
26
|
+
ATTR_SUBTYPE_DOUBLE,
|
|
27
|
+
ATTR_SUBTYPE_BOOLEAN,
|
|
28
|
+
ATTR_SUBTYPE_FILTER,
|
|
29
|
+
ATTR_SUBTYPE_PROPERTIES,
|
|
30
|
+
ATTR_SUBTYPE_CLASS,
|
|
31
|
+
)
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"""MetaAttr base + value-shaped subclasses. Value behavior lives here (ADR-0002)."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from ....attr_class_map import register_attr_class, register_fallback_attr_class
|
|
5
|
+
from ....datatype import DataType
|
|
6
|
+
from ...meta_data import MetaData
|
|
7
|
+
from ....shared.base_types import SUBTYPE_BASE
|
|
8
|
+
from .attr_constants import (
|
|
9
|
+
ATTR_SUBTYPE_BOOLEAN,
|
|
10
|
+
ATTR_SUBTYPE_CLASS,
|
|
11
|
+
ATTR_SUBTYPE_DOUBLE,
|
|
12
|
+
ATTR_SUBTYPE_FILTER,
|
|
13
|
+
ATTR_SUBTYPE_INT,
|
|
14
|
+
ATTR_SUBTYPE_LONG,
|
|
15
|
+
ATTR_SUBTYPE_PROPERTIES,
|
|
16
|
+
ATTR_SUBTYPE_STRING,
|
|
17
|
+
ATTR_SUBTYPE_STRINGARRAY,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
_SCALAR_DATA_TYPE = {
|
|
21
|
+
ATTR_SUBTYPE_STRING: DataType.STRING,
|
|
22
|
+
ATTR_SUBTYPE_INT: DataType.INT,
|
|
23
|
+
ATTR_SUBTYPE_LONG: DataType.LONG,
|
|
24
|
+
ATTR_SUBTYPE_DOUBLE: DataType.DOUBLE,
|
|
25
|
+
ATTR_SUBTYPE_BOOLEAN: DataType.BOOLEAN,
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class MetaAttr(MetaData):
|
|
30
|
+
def __init__(self, type_: str, sub_type: str, name: str) -> None:
|
|
31
|
+
super().__init__(type_, sub_type, name)
|
|
32
|
+
self.value: object = None
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
def data_type(self) -> DataType:
|
|
36
|
+
return _SCALAR_DATA_TYPE.get(self.sub_type, DataType.STRING)
|
|
37
|
+
|
|
38
|
+
def coerce(self, raw: object) -> object:
|
|
39
|
+
"""Base coercion mirrors TS convertToDataType (data-converter.ts):
|
|
40
|
+
directional toward the declared DataType.
|
|
41
|
+
|
|
42
|
+
SUBTYPE_BASE is the polymorphic/unconstrained marker (an undeclared
|
|
43
|
+
@-attr or an attr.base child node): the value is preserved as-is and
|
|
44
|
+
never stringified. Only attrs whose owner declared `value_type=string`
|
|
45
|
+
(i.e. the registry mapped them to ATTR_SUBTYPE_STRING) are coerced.
|
|
46
|
+
This keeps cross-language parity with TS: YAML 1.2 scalar coercion is
|
|
47
|
+
reported once by the desugar's D2 guard (ERR_YAML_COERCION) and the
|
|
48
|
+
downstream attr-schema validator does NOT double-report the same
|
|
49
|
+
offence (since the stored value already matches the declared type).
|
|
50
|
+
"""
|
|
51
|
+
if self.sub_type == SUBTYPE_BASE:
|
|
52
|
+
return raw # polymorphic / unconstrained — type-preserve
|
|
53
|
+
if self.sub_type == ATTR_SUBTYPE_STRING:
|
|
54
|
+
if raw is None or isinstance(raw, str):
|
|
55
|
+
return raw
|
|
56
|
+
if isinstance(raw, bool):
|
|
57
|
+
# bool must NOT be Pythonically rendered as "True"/"False" —
|
|
58
|
+
# use the JSON lower-cased form for cross-language byte parity.
|
|
59
|
+
return "true" if raw else "false"
|
|
60
|
+
if isinstance(raw, (int, float)):
|
|
61
|
+
return str(raw)
|
|
62
|
+
return raw
|
|
63
|
+
return raw
|
|
64
|
+
|
|
65
|
+
def desugar(self, value: object) -> object:
|
|
66
|
+
return value
|
|
67
|
+
|
|
68
|
+
def set_value(self, raw: object) -> None:
|
|
69
|
+
self._require_mutable()
|
|
70
|
+
self.value = self.desugar(self.coerce(raw))
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class StringArrayAttr(MetaAttr):
|
|
74
|
+
@property
|
|
75
|
+
def data_type(self) -> DataType:
|
|
76
|
+
return DataType.STRING_ARRAY
|
|
77
|
+
|
|
78
|
+
def coerce(self, raw: object) -> object:
|
|
79
|
+
if isinstance(raw, list):
|
|
80
|
+
return [str(el) for el in raw]
|
|
81
|
+
if isinstance(raw, str):
|
|
82
|
+
return [raw] # degenerate scalar form -> single-element list
|
|
83
|
+
return raw
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
_FILTER_OPS = frozenset({"eq", "ne", "gt", "gte", "lt", "lte", "in", "like", "isNull"})
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _is_op_object(value: object) -> bool:
|
|
90
|
+
"""Return True if value is already a filter op-object (all keys are filter ops)."""
|
|
91
|
+
return (
|
|
92
|
+
isinstance(value, dict)
|
|
93
|
+
and len(value) > 0
|
|
94
|
+
and all(k in _FILTER_OPS for k in value)
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _desugar_filter_value(value: object) -> object:
|
|
99
|
+
"""Desugar a single field-level filter shorthand into an op-object."""
|
|
100
|
+
if value is None:
|
|
101
|
+
return {"isNull": True}
|
|
102
|
+
if isinstance(value, list):
|
|
103
|
+
return {"in": value}
|
|
104
|
+
if _is_op_object(value):
|
|
105
|
+
return value # already an op-object — pass through unchanged
|
|
106
|
+
return {"eq": value}
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class FilterAttr(MetaAttr):
|
|
110
|
+
@property
|
|
111
|
+
def data_type(self) -> DataType:
|
|
112
|
+
return DataType.OBJECT
|
|
113
|
+
|
|
114
|
+
def desugar(self, value: object) -> object:
|
|
115
|
+
if not isinstance(value, dict):
|
|
116
|
+
return value
|
|
117
|
+
return {field: _desugar_filter_value(v) for field, v in value.items()}
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class PropertiesAttr(MetaAttr):
|
|
121
|
+
@property
|
|
122
|
+
def data_type(self) -> DataType:
|
|
123
|
+
return DataType.OBJECT
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class ClassAttr(MetaAttr):
|
|
127
|
+
@property
|
|
128
|
+
def data_type(self) -> DataType:
|
|
129
|
+
return DataType.STRING
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
register_fallback_attr_class(MetaAttr)
|
|
133
|
+
register_attr_class(ATTR_SUBTYPE_STRINGARRAY, StringArrayAttr)
|
|
134
|
+
register_attr_class(ATTR_SUBTYPE_FILTER, FilterAttr)
|
|
135
|
+
register_attr_class(ATTR_SUBTYPE_PROPERTIES, PropertiesAttr)
|
|
136
|
+
register_attr_class(ATTR_SUBTYPE_CLASS, ClassAttr)
|
|
File without changes
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"""Field subtype vocabulary (colocated)."""
|
|
2
|
+
from ....shared.base_types import SUBTYPE_BASE
|
|
3
|
+
|
|
4
|
+
FIELD_SUBTYPE_STRING = "string"
|
|
5
|
+
FIELD_SUBTYPE_INT = "int"
|
|
6
|
+
FIELD_SUBTYPE_LONG = "long"
|
|
7
|
+
FIELD_SUBTYPE_DOUBLE = "double"
|
|
8
|
+
FIELD_SUBTYPE_FLOAT = "float"
|
|
9
|
+
FIELD_SUBTYPE_BOOLEAN = "boolean"
|
|
10
|
+
FIELD_SUBTYPE_DATE = "date"
|
|
11
|
+
FIELD_SUBTYPE_TIMESTAMP = "timestamp"
|
|
12
|
+
FIELD_SUBTYPE_TIME = "time"
|
|
13
|
+
FIELD_SUBTYPE_DECIMAL = "decimal"
|
|
14
|
+
FIELD_SUBTYPE_OBJECT = "object"
|
|
15
|
+
FIELD_SUBTYPE_CURRENCY = "currency"
|
|
16
|
+
FIELD_SUBTYPE_ENUM = "enum"
|
|
17
|
+
# R6 Plan 2a — field.uuid is a logical identity scalar (ADR-0013). String-backed
|
|
18
|
+
# on the wire (lowercase-canonical UUID string); the idiomatic native binding
|
|
19
|
+
# (Python uuid.UUID) is a build-time codegen concern. A bare scalar like
|
|
20
|
+
# field.long: no required attrs, no loader value-validation.
|
|
21
|
+
FIELD_SUBTYPE_UUID = "uuid"
|
|
22
|
+
|
|
23
|
+
FIELD_SUBTYPES = (
|
|
24
|
+
SUBTYPE_BASE,
|
|
25
|
+
FIELD_SUBTYPE_STRING,
|
|
26
|
+
FIELD_SUBTYPE_INT,
|
|
27
|
+
FIELD_SUBTYPE_LONG,
|
|
28
|
+
FIELD_SUBTYPE_DOUBLE,
|
|
29
|
+
FIELD_SUBTYPE_FLOAT,
|
|
30
|
+
FIELD_SUBTYPE_BOOLEAN,
|
|
31
|
+
FIELD_SUBTYPE_DATE,
|
|
32
|
+
FIELD_SUBTYPE_TIMESTAMP,
|
|
33
|
+
FIELD_SUBTYPE_TIME,
|
|
34
|
+
FIELD_SUBTYPE_DECIMAL,
|
|
35
|
+
FIELD_SUBTYPE_OBJECT,
|
|
36
|
+
FIELD_SUBTYPE_CURRENCY,
|
|
37
|
+
FIELD_SUBTYPE_UUID,
|
|
38
|
+
# Note: FIELD_SUBTYPE_ENUM is intentionally excluded here; it is registered
|
|
39
|
+
# separately in core_types.py with its dedicated @values AttrSchema.
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
# Reserved field attribute names (read by codegen; open attrs at load time).
|
|
43
|
+
# Mirrors server/typescript/packages/metadata/src/core/field/field-constants.ts.
|
|
44
|
+
FIELD_ATTR_REQUIRED = "required"
|
|
45
|
+
# @readOnly — boolean; the field is exposed read-only (omitted from create/update
|
|
46
|
+
# input DTOs). On every field subtype. Mirrors TS FIELD_ATTR_READ_ONLY.
|
|
47
|
+
FIELD_ATTR_READ_ONLY = "readOnly"
|
|
48
|
+
FIELD_ATTR_UNIQUE = "unique"
|
|
49
|
+
# @currency — ISO 4217 code on a field.currency. Storage is integer minor units;
|
|
50
|
+
# defaults to "USD" when omitted. Only on field.currency. Mirrors TS FIELD_ATTR_CURRENCY.
|
|
51
|
+
FIELD_ATTR_CURRENCY = "currency"
|
|
52
|
+
FIELD_ATTR_DEFAULT = "default"
|
|
53
|
+
FIELD_ATTR_MAX_LENGTH = "maxLength"
|
|
54
|
+
FIELD_ATTR_PRECISION = "precision"
|
|
55
|
+
FIELD_ATTR_SCALE = "scale"
|
|
56
|
+
FIELD_ATTR_FILTERABLE = "filterable"
|
|
57
|
+
FIELD_ATTR_SORTABLE = "sortable"
|
|
58
|
+
FIELD_ATTR_SORTABLE_DEFAULT_ORDER = "sortableDefaultOrder"
|
|
59
|
+
FIELD_ATTR_AUTO_SET = "autoSet"
|
|
60
|
+
FIELD_ATTR_OBJECT_REF = "objectRef"
|
|
61
|
+
FIELD_ATTR_VALUES = "values"
|
|
62
|
+
# FR-010 field-teaching attrs (any field; drive the output-format prompt fragment).
|
|
63
|
+
# Never carried in comments.
|
|
64
|
+
FIELD_ATTR_EXAMPLE = "example"
|
|
65
|
+
FIELD_ATTR_INSTRUCTION = "instruction"
|
|
66
|
+
# FR-010 enum attrs (field.enum only). `properties`-shaped maps:
|
|
67
|
+
# @enumAlias: off-vocabulary token -> canonical member (feeds the extract alias-fold).
|
|
68
|
+
# @enumDoc: member -> human-readable description (shown per-member in the 'guide' prompt).
|
|
69
|
+
FIELD_ATTR_ENUM_ALIAS = "enumAlias"
|
|
70
|
+
FIELD_ATTR_ENUM_DOC = "enumDoc"
|
|
71
|
+
# FR-011 enum extract-hardening attrs (field.enum only).
|
|
72
|
+
# @coerceDefault: present-but-uncoercible extract fallback member → DEFAULTED.
|
|
73
|
+
# Member-validated against the effective @values (own or inherited).
|
|
74
|
+
# @normalize: ASCII normalization mode applied during tolerant enum extract.
|
|
75
|
+
# Closed enum (none|collapse|strip, default strip). On field.enum (per-field)
|
|
76
|
+
# and object.value (object-level default for its enum fields). NOT on entity/base.
|
|
77
|
+
# @default (FIELD_ATTR_DEFAULT, declared above) doubles as the enum absent-fill member.
|
|
78
|
+
FIELD_ATTR_COERCE_DEFAULT = "coerceDefault"
|
|
79
|
+
FIELD_ATTR_NORMALIZE = "normalize"
|
|
80
|
+
# @normalize closed-enum modes (cross-port; ASCII-only normalization rule).
|
|
81
|
+
NORMALIZE_NONE = "none"
|
|
82
|
+
NORMALIZE_COLLAPSE = "collapse"
|
|
83
|
+
NORMALIZE_STRIP = "strip"
|
|
84
|
+
# Default @normalize mode when absent on both field and owning object.
|
|
85
|
+
NORMALIZE_DEFAULT = NORMALIZE_STRIP
|
|
86
|
+
NORMALIZE_MODES = (NORMALIZE_NONE, NORMALIZE_COLLAPSE, NORMALIZE_STRIP)
|
|
87
|
+
# Persistence-side storage shape for owned field.object data. Cross-port values.
|
|
88
|
+
FIELD_ATTR_STORAGE = "storage"
|
|
89
|
+
STORAGE_VALUES = ("flattened", "jsonb", "subdocument")
|
|
90
|
+
# Physical column name override (cross-port; renamed from @dbColumn).
|
|
91
|
+
FIELD_ATTR_COLUMN = "column"
|
|
92
|
+
|
|
93
|
+
# @autoSet allowed values (cross-port).
|
|
94
|
+
AUTO_SET_ON_CREATE = "onCreate"
|
|
95
|
+
AUTO_SET_ON_UPDATE = "onUpdate"
|
|
96
|
+
AUTO_SET_VALUES = (AUTO_SET_ON_CREATE, AUTO_SET_ON_UPDATE)
|
|
97
|
+
|
|
98
|
+
# @sortableDefaultOrder allowed values (cross-port).
|
|
99
|
+
SORT_ORDER_ASC = "asc"
|
|
100
|
+
SORT_ORDER_DESC = "desc"
|
|
101
|
+
SORT_ORDER_VALUES = (SORT_ORDER_ASC, SORT_ORDER_DESC)
|
|
102
|
+
|
|
103
|
+
# Regex pattern for enum member symbols — must be identifier-safe.
|
|
104
|
+
# Cross-language contract: every port enforces this pattern.
|
|
105
|
+
ENUM_MEMBER_PATTERN = r"^[A-Za-z_][A-Za-z0-9_]*$"
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""MetaField — data_type resolves by subtype (ADR-0002)."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from ....datatype import DataType
|
|
5
|
+
from ...meta_data import MetaData
|
|
6
|
+
from . import field_constants as fc
|
|
7
|
+
|
|
8
|
+
_FIELD_DATA_TYPE = {
|
|
9
|
+
fc.FIELD_SUBTYPE_STRING: DataType.STRING,
|
|
10
|
+
fc.FIELD_SUBTYPE_INT: DataType.INT,
|
|
11
|
+
fc.FIELD_SUBTYPE_LONG: DataType.LONG,
|
|
12
|
+
fc.FIELD_SUBTYPE_DOUBLE: DataType.DOUBLE,
|
|
13
|
+
fc.FIELD_SUBTYPE_FLOAT: DataType.DOUBLE,
|
|
14
|
+
fc.FIELD_SUBTYPE_BOOLEAN: DataType.BOOLEAN,
|
|
15
|
+
fc.FIELD_SUBTYPE_DATE: DataType.DATE,
|
|
16
|
+
fc.FIELD_SUBTYPE_TIMESTAMP: DataType.DATE,
|
|
17
|
+
fc.FIELD_SUBTYPE_TIME: DataType.DATE,
|
|
18
|
+
fc.FIELD_SUBTYPE_DECIMAL: DataType.DECIMAL,
|
|
19
|
+
fc.FIELD_SUBTYPE_OBJECT: DataType.OBJECT,
|
|
20
|
+
fc.FIELD_SUBTYPE_CURRENCY: DataType.LONG,
|
|
21
|
+
fc.FIELD_SUBTYPE_ENUM: DataType.STRING,
|
|
22
|
+
# R6 Plan 2a — field.uuid is string-backed on the wire; the native uuid.UUID
|
|
23
|
+
# binding is surfaced at codegen time (see codegen/type_map.py).
|
|
24
|
+
fc.FIELD_SUBTYPE_UUID: DataType.STRING,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class MetaField(MetaData):
|
|
29
|
+
@property
|
|
30
|
+
def data_type(self) -> DataType:
|
|
31
|
+
return _FIELD_DATA_TYPE.get(self.sub_type, DataType.STRING)
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def object_ref(self) -> str | None:
|
|
35
|
+
"""The ``@objectRef`` target FQN for ``field.object`` fields, else None.
|
|
36
|
+
|
|
37
|
+
The value is the package-folded form (e.g. ``com::example::om::Address``)
|
|
38
|
+
— matching a target object's ``resolution_key()``.
|
|
39
|
+
"""
|
|
40
|
+
v = self.attr(fc.FIELD_ATTR_OBJECT_REF)
|
|
41
|
+
return str(v) if v is not None else None
|
|
42
|
+
|
|
43
|
+
def get_value(self, obj: object, name: str | None = None) -> object:
|
|
44
|
+
"""Read this field's value from a backing object.
|
|
45
|
+
|
|
46
|
+
Dispatches on backing kind: a dict-backed ``ValueObject`` reads through
|
|
47
|
+
its map; any other object reads the attribute via ``getattr``. *name*
|
|
48
|
+
defaults to this field's own ``name`` (override to read a differently
|
|
49
|
+
keyed slot). Arrays are plain Python lists; nested objects are whatever
|
|
50
|
+
the consumer stored (typically another ValueObject / native instance).
|
|
51
|
+
"""
|
|
52
|
+
from ..object.value_object import ValueObject
|
|
53
|
+
|
|
54
|
+
key = name if name is not None else self.name
|
|
55
|
+
if isinstance(obj, ValueObject):
|
|
56
|
+
return obj.get(key)
|
|
57
|
+
return getattr(obj, key, None)
|
|
58
|
+
|
|
59
|
+
def set_value(
|
|
60
|
+
self, obj: object, value: object, name: str | None = None
|
|
61
|
+
) -> None:
|
|
62
|
+
"""Write *value* into a backing object under *name* (defaults to this
|
|
63
|
+
field's own ``name``).
|
|
64
|
+
|
|
65
|
+
ValueObject -> map set; any other object -> ``setattr`` typed property.
|
|
66
|
+
No coercion here. Nested objects / arrays are stored as-given (the
|
|
67
|
+
consumer recurses for nested OBJECT fields: resolve the child via
|
|
68
|
+
``object_ref`` + ``new_instance`` and ``set_value`` it).
|
|
69
|
+
"""
|
|
70
|
+
from ..object.value_object import ValueObject
|
|
71
|
+
|
|
72
|
+
key = name if name is not None else self.name
|
|
73
|
+
if isinstance(obj, ValueObject):
|
|
74
|
+
obj.set(key, value)
|
|
75
|
+
return
|
|
76
|
+
setattr(obj, key, value)
|
|
File without changes
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Identity subtype + attr vocabulary (colocated)."""
|
|
2
|
+
IDENTITY_SUBTYPE_PRIMARY = "primary"
|
|
3
|
+
IDENTITY_SUBTYPE_SECONDARY = "secondary"
|
|
4
|
+
IDENTITY_SUBTYPE_REFERENCE = "reference"
|
|
5
|
+
IDENTITY_SUBTYPES = (IDENTITY_SUBTYPE_PRIMARY, IDENTITY_SUBTYPE_SECONDARY, IDENTITY_SUBTYPE_REFERENCE)
|
|
6
|
+
|
|
7
|
+
IDENTITY_ATTR_FIELDS = "fields"
|
|
8
|
+
IDENTITY_ATTR_GENERATION = "generation"
|
|
9
|
+
IDENTITY_ATTR_UNIQUE = "unique"
|
|
10
|
+
|
|
11
|
+
# Allowed values for @generation on identity.primary
|
|
12
|
+
GENERATION_INCREMENT = "increment"
|
|
13
|
+
GENERATION_UUID = "uuid"
|
|
14
|
+
GENERATION_ASSIGNED = "assigned"
|
|
15
|
+
GENERATION_VALUES = (GENERATION_INCREMENT, GENERATION_UUID, GENERATION_ASSIGNED)
|
|
16
|
+
|
|
17
|
+
# identity.reference attrs: target entity and enforcement flag
|
|
18
|
+
IDENTITY_REFERENCE_ATTR_REFERENCES = "references"
|
|
19
|
+
IDENTITY_REFERENCE_ATTR_ENFORCE = "enforce"
|
|
File without changes
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""MetaObject — typed accessors over children + runtime instantiation."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from ...meta_data import MetaData
|
|
5
|
+
from ..field.meta_field import MetaField
|
|
6
|
+
from .object_class_registry import (
|
|
7
|
+
ObjectClassRegistry,
|
|
8
|
+
default_object_class_registry,
|
|
9
|
+
)
|
|
10
|
+
from .value_object import ValueObject
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class MetaObject(MetaData):
|
|
14
|
+
def fields(self) -> list[MetaField]:
|
|
15
|
+
"""Effective fields: own + inherited via super chain (uses children())."""
|
|
16
|
+
return [c for c in self.children() if isinstance(c, MetaField)]
|
|
17
|
+
|
|
18
|
+
def own_fields(self) -> list[MetaField]:
|
|
19
|
+
"""Own fields only — direct children, no inherited fields from super."""
|
|
20
|
+
return [c for c in self.own_children() if isinstance(c, MetaField)]
|
|
21
|
+
|
|
22
|
+
def find_field(self, name: str) -> MetaField | None:
|
|
23
|
+
"""Find a field by name in effective fields (own + inherited)."""
|
|
24
|
+
return next((f for f in self.fields() if f.name == name), None)
|
|
25
|
+
|
|
26
|
+
def get_meta_field(self, name: str) -> MetaField | None:
|
|
27
|
+
"""Alias for find_field — Java/TS parity (``getMetaField``)."""
|
|
28
|
+
return self.find_field(name)
|
|
29
|
+
|
|
30
|
+
def new_instance(self, registry: ObjectClassRegistry | None = None) -> object:
|
|
31
|
+
"""Instantiate a backing object for this MetaObject (Java/TS parity:
|
|
32
|
+
``newInstance``). Resolution order:
|
|
33
|
+
|
|
34
|
+
1. If ``registry.resolve(self.resolution_key())`` yields a factory,
|
|
35
|
+
call it; if the result is MetaObjectAware (has ``set_meta_data``),
|
|
36
|
+
attach this MetaObject as its back-reference.
|
|
37
|
+
2. Otherwise create a dict-backed ``ValueObject`` (the unbound default
|
|
38
|
+
for ``object.value``), which sets its own back-reference via the
|
|
39
|
+
constructor.
|
|
40
|
+
|
|
41
|
+
The registry key is this object's package-folded ``resolution_key()`` —
|
|
42
|
+
the SAME FQN form a nested field's ``@objectRef`` uses, so a factory
|
|
43
|
+
registered for an object's FQN is found whether instantiation is driven
|
|
44
|
+
top-down or via an object-ref. Reflection-free: only registered
|
|
45
|
+
factories are consulted.
|
|
46
|
+
"""
|
|
47
|
+
reg = registry if registry is not None else default_object_class_registry
|
|
48
|
+
factory = reg.resolve(self.resolution_key())
|
|
49
|
+
if factory is not None:
|
|
50
|
+
instance = factory(self)
|
|
51
|
+
set_back_ref = getattr(instance, "set_meta_data", None)
|
|
52
|
+
if callable(set_back_ref):
|
|
53
|
+
set_back_ref(self)
|
|
54
|
+
return instance
|
|
55
|
+
return ValueObject(self)
|
|
56
|
+
|
|
57
|
+
def primary_identity(self) -> "MetaIdentity | None": # type: ignore[name-defined]
|
|
58
|
+
"""Primary identity node from effective children (own or inherited)."""
|
|
59
|
+
from ..identity.meta_identity import MetaIdentity
|
|
60
|
+
from ..identity.identity_constants import IDENTITY_SUBTYPE_PRIMARY
|
|
61
|
+
return next(
|
|
62
|
+
(c for c in self.children()
|
|
63
|
+
if isinstance(c, MetaIdentity) and c.sub_type == IDENTITY_SUBTYPE_PRIMARY),
|
|
64
|
+
None,
|
|
65
|
+
)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""MetaObjectAware — the runtime back-reference contract.
|
|
2
|
+
|
|
3
|
+
Idiomatic Python port of Java's ``MetaObjectAware`` / the TS
|
|
4
|
+
``MetaObjectAware`` interface: a backing object that knows the ``MetaObject``
|
|
5
|
+
describing it. The default backing type (``ValueObject``) implements this;
|
|
6
|
+
generated / registered native types may implement it too so they carry a
|
|
7
|
+
back-reference after ``MetaObject.new_instance()``.
|
|
8
|
+
|
|
9
|
+
Reflection-free — no ``importlib``, no runtime type resolution. The protocol is
|
|
10
|
+
structural (``typing.Protocol``), so a native type only needs the two methods;
|
|
11
|
+
it does not have to inherit from anything.
|
|
12
|
+
"""
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from typing import TYPE_CHECKING, Protocol, runtime_checkable
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from .meta_object import MetaObject
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@runtime_checkable
|
|
22
|
+
class MetaObjectAware(Protocol):
|
|
23
|
+
"""A backing object carrying a reference to the MetaObject describing it."""
|
|
24
|
+
|
|
25
|
+
def get_meta_data(self) -> "MetaObject | None":
|
|
26
|
+
"""The MetaObject describing this instance, or None if not yet attached."""
|
|
27
|
+
...
|
|
28
|
+
|
|
29
|
+
def set_meta_data(self, mo: "MetaObject") -> None:
|
|
30
|
+
"""Attach the MetaObject describing this instance (back-reference)."""
|
|
31
|
+
...
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def is_meta_object_aware(obj: object) -> bool:
|
|
35
|
+
"""Structural check — True when *obj* satisfies the MetaObjectAware contract.
|
|
36
|
+
|
|
37
|
+
Mirrors the TS ``isMetaObjectAware`` type guard. Uses duck-typing rather
|
|
38
|
+
than ``isinstance(obj, MetaObjectAware)`` so it stays robust even when a
|
|
39
|
+
native type defines the two methods without importing the protocol.
|
|
40
|
+
"""
|
|
41
|
+
return callable(getattr(obj, "get_meta_data", None)) and callable(
|
|
42
|
+
getattr(obj, "set_meta_data", None)
|
|
43
|
+
)
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""ObjectClassRegistry — resolution-key -> factory binding for instantiation.
|
|
2
|
+
|
|
3
|
+
Idiomatic Python port of Java's ``ObjectClassRegistry`` / the TS
|
|
4
|
+
``ObjectClassRegistry``: maps an object's package-folded FQN (its
|
|
5
|
+
``MetaData.resolution_key()``, e.g. ``com::example::om::Person``) to a factory
|
|
6
|
+
that produces a native backing instance. Generated code self-registers at
|
|
7
|
+
import time (``registry.register(fqn, factory)``) — NO reflection (no
|
|
8
|
+
``importlib`` / ``Class.forName`` / ``Type.GetType`` equivalent), AOT/tree-shake
|
|
9
|
+
safe per ADR-0001.
|
|
10
|
+
|
|
11
|
+
DISTINCT from the metadata ``TypeRegistry``, which maps metamodel type/subType
|
|
12
|
+
names to MetaData node classes. THIS registry maps a DATA object's FQN to a
|
|
13
|
+
runtime BACKING instance factory.
|
|
14
|
+
"""
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from typing import TYPE_CHECKING, Callable, Optional
|
|
18
|
+
|
|
19
|
+
if TYPE_CHECKING:
|
|
20
|
+
from .meta_object import MetaObject
|
|
21
|
+
|
|
22
|
+
#: Produces a native backing instance for the given MetaObject.
|
|
23
|
+
ObjectFactory = Callable[["MetaObject"], object]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ObjectClassRegistry:
|
|
27
|
+
"""Binds a resolution-key (``package::name``) to a backing-instance factory."""
|
|
28
|
+
|
|
29
|
+
def __init__(self) -> None:
|
|
30
|
+
self._factories: dict[str, ObjectFactory] = {}
|
|
31
|
+
|
|
32
|
+
def register(self, fqn: str, factory: ObjectFactory) -> None:
|
|
33
|
+
"""Bind a resolution-key to a backing-instance factory."""
|
|
34
|
+
self._factories[fqn] = factory
|
|
35
|
+
|
|
36
|
+
def resolve(self, fqn: str) -> Optional[ObjectFactory]:
|
|
37
|
+
"""Resolve the factory bound to a resolution-key, or None if unbound."""
|
|
38
|
+
return self._factories.get(fqn)
|
|
39
|
+
|
|
40
|
+
def has(self, fqn: str) -> bool:
|
|
41
|
+
"""True if a factory is bound to the resolution-key."""
|
|
42
|
+
return fqn in self._factories
|
|
43
|
+
|
|
44
|
+
def unregister(self, fqn: str) -> bool:
|
|
45
|
+
"""Remove a binding (test/scoping convenience); True if present."""
|
|
46
|
+
if fqn in self._factories:
|
|
47
|
+
del self._factories[fqn]
|
|
48
|
+
return True
|
|
49
|
+
return False
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
#: The module-level default registry. Generated code self-registers here at
|
|
53
|
+
#: import time; ``MetaObject.new_instance()`` consults it when no explicit
|
|
54
|
+
#: registry is passed. Tests should construct their own
|
|
55
|
+
#: ``ObjectClassRegistry()`` to avoid leaking bindings across cases (scenario 6).
|
|
56
|
+
default_object_class_registry = ObjectClassRegistry()
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Object subtype vocabulary (colocated)."""
|
|
2
|
+
from ....shared.base_types import SUBTYPE_BASE
|
|
3
|
+
|
|
4
|
+
OBJECT_SUBTYPE_ENTITY = "entity"
|
|
5
|
+
OBJECT_SUBTYPE_VALUE = "value"
|
|
6
|
+
OBJECT_SUBTYPES = (SUBTYPE_BASE, OBJECT_SUBTYPE_ENTITY, OBJECT_SUBTYPE_VALUE)
|
|
7
|
+
|
|
8
|
+
# FR-014 — TPH (Table-per-Hierarchy single-table inheritance) discriminator attrs.
|
|
9
|
+
# On every object subtype (base/entity/value). Mirrors TS object-constants.ts.
|
|
10
|
+
# @discriminator — on a base entity: the field holding the subtype value.
|
|
11
|
+
# @discriminatorValue — on a subtype: the value identifying its rows.
|
|
12
|
+
OBJECT_ATTR_DISCRIMINATOR = "discriminator"
|
|
13
|
+
OBJECT_ATTR_DISCRIMINATOR_VALUE = "discriminatorValue"
|