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.
Files changed (181) hide show
  1. metaobjects/__init__.py +75 -0
  2. metaobjects/agent_context/__init__.py +55 -0
  3. metaobjects/agent_context/_content/README.md +14 -0
  4. metaobjects/agent_context/_content/servers/csharp.meta.json +5 -0
  5. metaobjects/agent_context/_content/servers/java.meta.json +5 -0
  6. metaobjects/agent_context/_content/servers/kotlin.meta.json +5 -0
  7. metaobjects/agent_context/_content/servers/python.meta.json +5 -0
  8. metaobjects/agent_context/_content/servers/typescript.meta.json +5 -0
  9. metaobjects/agent_context/_content/skills/metaobjects-authoring/SKILL.md +301 -0
  10. metaobjects/agent_context/_content/skills/metaobjects-codegen/SKILL.md +99 -0
  11. metaobjects/agent_context/_content/skills/metaobjects-codegen/references/csharp.md +87 -0
  12. metaobjects/agent_context/_content/skills/metaobjects-codegen/references/java.md +94 -0
  13. metaobjects/agent_context/_content/skills/metaobjects-codegen/references/kotlin.md +110 -0
  14. metaobjects/agent_context/_content/skills/metaobjects-codegen/references/typescript.md +135 -0
  15. metaobjects/agent_context/_content/skills/metaobjects-prompts/SKILL.md +148 -0
  16. metaobjects/agent_context/_content/skills/metaobjects-prompts/references/csharp.md +110 -0
  17. metaobjects/agent_context/_content/skills/metaobjects-prompts/references/java.md +108 -0
  18. metaobjects/agent_context/_content/skills/metaobjects-prompts/references/kotlin.md +130 -0
  19. metaobjects/agent_context/_content/skills/metaobjects-prompts/references/python.md +116 -0
  20. metaobjects/agent_context/_content/skills/metaobjects-prompts/references/typescript.md +150 -0
  21. metaobjects/agent_context/_content/skills/metaobjects-runtime-ui/SKILL.md +130 -0
  22. metaobjects/agent_context/_content/skills/metaobjects-runtime-ui/references/java.md +96 -0
  23. metaobjects/agent_context/_content/skills/metaobjects-runtime-ui/references/kotlin.md +99 -0
  24. metaobjects/agent_context/_content/skills/metaobjects-runtime-ui/references/react.md +86 -0
  25. metaobjects/agent_context/_content/skills/metaobjects-runtime-ui/references/tanstack.md +119 -0
  26. metaobjects/agent_context/_content/skills/metaobjects-runtime-ui/references/typescript.md +92 -0
  27. metaobjects/agent_context/_content/skills/metaobjects-verify/SKILL.md +107 -0
  28. metaobjects/agent_context/_content/skills/metaobjects-verify/references/migration.md +72 -0
  29. metaobjects/agent_context/_content/templates/always-on.md.mustache +27 -0
  30. metaobjects/agent_context/assemble.py +133 -0
  31. metaobjects/agent_context/content_root.py +54 -0
  32. metaobjects/agent_context/scaffold.py +191 -0
  33. metaobjects/agent_context/types.py +44 -0
  34. metaobjects/attr_class_map.py +23 -0
  35. metaobjects/cli.py +696 -0
  36. metaobjects/codegen/__init__.py +0 -0
  37. metaobjects/codegen/config.py +11 -0
  38. metaobjects/codegen/constants.py +13 -0
  39. metaobjects/codegen/extract_delegate_emitter.py +384 -0
  40. metaobjects/codegen/extract_schema_emitter.py +139 -0
  41. metaobjects/codegen/format.py +31 -0
  42. metaobjects/codegen/fr010_field_mapping.py +220 -0
  43. metaobjects/codegen/generator.py +62 -0
  44. metaobjects/codegen/generator_registry.py +163 -0
  45. metaobjects/codegen/generators/__init__.py +0 -0
  46. metaobjects/codegen/generators/entity_model.py +263 -0
  47. metaobjects/codegen/generators/extractor_generator.py +317 -0
  48. metaobjects/codegen/generators/filter_allowlist_generator.py +309 -0
  49. metaobjects/codegen/generators/m2m_codegen.py +192 -0
  50. metaobjects/codegen/generators/output_parser_generator.py +272 -0
  51. metaobjects/codegen/generators/output_prompt_generator.py +192 -0
  52. metaobjects/codegen/generators/payload_vo_generator.py +672 -0
  53. metaobjects/codegen/generators/render_helper_generator.py +451 -0
  54. metaobjects/codegen/generators/router_generator.py +635 -0
  55. metaobjects/codegen/generators/template_generator.py +70 -0
  56. metaobjects/codegen/generators/tph_plan.py +120 -0
  57. metaobjects/codegen/generators/trace_helper_generator.py +336 -0
  58. metaobjects/codegen/instance_artifacts.py +15 -0
  59. metaobjects/codegen/output_format_spec_emitter.py +79 -0
  60. metaobjects/codegen/overwrite_policy.py +27 -0
  61. metaobjects/codegen/runner.py +110 -0
  62. metaobjects/codegen/runtime/__init__.py +6 -0
  63. metaobjects/codegen/runtime/filter_parser.py +193 -0
  64. metaobjects/codegen/type_map.py +84 -0
  65. metaobjects/core_types.py +809 -0
  66. metaobjects/datatype.py +19 -0
  67. metaobjects/documentation/__init__.py +28 -0
  68. metaobjects/documentation/doc_constants.py +20 -0
  69. metaobjects/documentation/doc_provider.py +20 -0
  70. metaobjects/documentation/doc_schema.py +24 -0
  71. metaobjects/errors.py +124 -0
  72. metaobjects/loader/__init__.py +0 -0
  73. metaobjects/loader/merge.py +287 -0
  74. metaobjects/loader/meta_data_loader.py +245 -0
  75. metaobjects/loader/sources/__init__.py +24 -0
  76. metaobjects/loader/sources/directory_source.py +50 -0
  77. metaobjects/loader/sources/file_source.py +41 -0
  78. metaobjects/loader/sources/meta_data_source.py +67 -0
  79. metaobjects/loader/sources/uri_source.py +56 -0
  80. metaobjects/loader/validate_discriminator.py +181 -0
  81. metaobjects/loader/validate_field_readonly.py +146 -0
  82. metaobjects/loader/validate_source_parameter_ref.py +159 -0
  83. metaobjects/loader/validate_source_physical_names.py +140 -0
  84. metaobjects/loader/validation_passes.py +1513 -0
  85. metaobjects/meta/__init__.py +1 -0
  86. metaobjects/meta/core/__init__.py +0 -0
  87. metaobjects/meta/core/attr/__init__.py +0 -0
  88. metaobjects/meta/core/attr/attr_constants.py +31 -0
  89. metaobjects/meta/core/attr/meta_attr.py +136 -0
  90. metaobjects/meta/core/field/__init__.py +0 -0
  91. metaobjects/meta/core/field/field_constants.py +105 -0
  92. metaobjects/meta/core/field/meta_field.py +76 -0
  93. metaobjects/meta/core/identity/__init__.py +0 -0
  94. metaobjects/meta/core/identity/identity_constants.py +19 -0
  95. metaobjects/meta/core/identity/meta_identity.py +8 -0
  96. metaobjects/meta/core/object/__init__.py +0 -0
  97. metaobjects/meta/core/object/meta_object.py +65 -0
  98. metaobjects/meta/core/object/meta_object_aware.py +43 -0
  99. metaobjects/meta/core/object/object_class_registry.py +56 -0
  100. metaobjects/meta/core/object/object_constants.py +13 -0
  101. metaobjects/meta/core/object/object_extract.py +400 -0
  102. metaobjects/meta/core/object/value_object.py +70 -0
  103. metaobjects/meta/core/relationship/__init__.py +0 -0
  104. metaobjects/meta/core/relationship/derive_m2m_fields.py +180 -0
  105. metaobjects/meta/core/relationship/meta_relationship.py +54 -0
  106. metaobjects/meta/core/relationship/relationship_constants.py +51 -0
  107. metaobjects/meta/core/validator/__init__.py +0 -0
  108. metaobjects/meta/core/validator/validator_constants.py +18 -0
  109. metaobjects/meta/meta_data.py +206 -0
  110. metaobjects/meta/meta_root.py +8 -0
  111. metaobjects/meta/persistence/__init__.py +0 -0
  112. metaobjects/meta/persistence/db/__init__.py +1 -0
  113. metaobjects/meta/persistence/db/db_constants.py +41 -0
  114. metaobjects/meta/persistence/db/db_provider.py +60 -0
  115. metaobjects/meta/persistence/origin/__init__.py +0 -0
  116. metaobjects/meta/persistence/origin/meta_origin.py +8 -0
  117. metaobjects/meta/persistence/origin/origin_constants.py +20 -0
  118. metaobjects/meta/persistence/source/__init__.py +0 -0
  119. metaobjects/meta/persistence/source/meta_source.py +137 -0
  120. metaobjects/meta/persistence/source/source_constants.py +115 -0
  121. metaobjects/meta/presentation/__init__.py +0 -0
  122. metaobjects/meta/presentation/layout/__init__.py +0 -0
  123. metaobjects/meta/presentation/layout/layout_constants.py +13 -0
  124. metaobjects/meta/presentation/layout/meta_layout.py +8 -0
  125. metaobjects/meta/presentation/view/__init__.py +0 -0
  126. metaobjects/meta/presentation/view/meta_view.py +8 -0
  127. metaobjects/meta/presentation/view/view_constants.py +22 -0
  128. metaobjects/meta/template/__init__.py +0 -0
  129. metaobjects/meta/template/meta_template.py +46 -0
  130. metaobjects/meta/template/template_constants.py +112 -0
  131. metaobjects/meta/template/template_provider.py +43 -0
  132. metaobjects/parser.py +380 -0
  133. metaobjects/parser_yaml.py +82 -0
  134. metaobjects/provider.py +111 -0
  135. metaobjects/py.typed +0 -0
  136. metaobjects/registry.py +210 -0
  137. metaobjects/registry_manifest.py +223 -0
  138. metaobjects/render/__init__.py +74 -0
  139. metaobjects/render/email_document.py +14 -0
  140. metaobjects/render/escapers.py +109 -0
  141. metaobjects/render/extract/__init__.py +59 -0
  142. metaobjects/render/extract/coerce.py +279 -0
  143. metaobjects/render/extract/extract.py +211 -0
  144. metaobjects/render/extract/extract_map.py +61 -0
  145. metaobjects/render/extract/json_forgiving_reader.py +203 -0
  146. metaobjects/render/extract/locate.py +65 -0
  147. metaobjects/render/extract/normalize.py +96 -0
  148. metaobjects/render/extract/strip.py +20 -0
  149. metaobjects/render/extract/types.py +332 -0
  150. metaobjects/render/extract/xml_forgiving_reader.py +162 -0
  151. metaobjects/render/filesystem_provider.py +51 -0
  152. metaobjects/render/prompt/__init__.py +32 -0
  153. metaobjects/render/prompt/output_format_renderer.py +340 -0
  154. metaobjects/render/prompt/output_format_spec.py +28 -0
  155. metaobjects/render/prompt/prompt_field.py +29 -0
  156. metaobjects/render/prompt/prompt_overrides.py +29 -0
  157. metaobjects/render/prompt/prompt_style.py +38 -0
  158. metaobjects/render/renderer.py +358 -0
  159. metaobjects/render/verify.py +266 -0
  160. metaobjects/runtime/__init__.py +39 -0
  161. metaobjects/runtime/llm_recorder.py +210 -0
  162. metaobjects/runtime/n2m_resolver.py +155 -0
  163. metaobjects/runtime/object_manager.py +715 -0
  164. metaobjects/runtime/tph.py +50 -0
  165. metaobjects/serializer_json.py +172 -0
  166. metaobjects/shared/__init__.py +0 -0
  167. metaobjects/shared/base_types.py +16 -0
  168. metaobjects/shared/separators.py +4 -0
  169. metaobjects/shared/structural.py +9 -0
  170. metaobjects/source/__init__.py +79 -0
  171. metaobjects/source/error_source.py +266 -0
  172. metaobjects/source/json_path.py +106 -0
  173. metaobjects/source/semantic_diff.py +98 -0
  174. metaobjects/source/yaml_positions.py +174 -0
  175. metaobjects/super_resolve.py +128 -0
  176. metaobjects/yaml_desugar.py +481 -0
  177. metaobjects-0.9.0.dist-info/METADATA +97 -0
  178. metaobjects-0.9.0.dist-info/RECORD +181 -0
  179. metaobjects-0.9.0.dist-info/WHEEL +4 -0
  180. metaobjects-0.9.0.dist-info/entry_points.txt +2 -0
  181. 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"
@@ -0,0 +1,8 @@
1
+ """MetaIdentity — primary/secondary."""
2
+ from __future__ import annotations
3
+
4
+ from ...meta_data import MetaData
5
+
6
+
7
+ class MetaIdentity(MetaData):
8
+ pass
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"