metaobjects 0.13.0__tar.gz → 0.15.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {metaobjects-0.13.0 → metaobjects-0.15.0}/PKG-INFO +1 -1
- {metaobjects-0.13.0 → metaobjects-0.15.0}/pyproject.toml +1 -1
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/apidocs/builder.py +6 -4
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/apidocs/naming.py +75 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/cli.py +52 -9
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/codegen/extract_delegate_emitter.py +6 -1
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/codegen/fr010_field_mapping.py +21 -13
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/codegen/generators/entity_model.py +40 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/codegen/generators/extractor_generator.py +3 -2
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/codegen/generators/filter_allowlist_generator.py +15 -3
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/codegen/generators/fr019_shared_enum.py +9 -3
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/codegen/generators/m2m_codegen.py +12 -5
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/codegen/generators/output_parser_generator.py +5 -2
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/codegen/generators/output_prompt_generator.py +3 -2
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/codegen/generators/payload_vo_generator.py +14 -6
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/codegen/generators/render_helper_generator.py +11 -8
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/codegen/generators/router_generator.py +84 -4
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/codegen/generators/tph_plan.py +13 -5
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/codegen/generators/trace_helper_generator.py +8 -2
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/codegen/output_format_spec_emitter.py +2 -2
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/codegen/runner.py +2 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/codegen/template_codegen/template_data.py +22 -14
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/codegen/type_map.py +26 -4
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/core_types.py +21 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/loader/merge.py +5 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/loader/registered_validation.py +10 -1
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/loader/validate_discriminator.py +15 -2
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/loader/validate_field_readonly.py +19 -4
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/loader/validate_source_parameter_ref.py +23 -6
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/loader/validate_source_physical_names.py +3 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/loader/validation_passes.py +189 -62
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/core/field/field_constants.py +27 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/core/field/meta_field.py +11 -1
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/core/object/meta_object.py +6 -1
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/core/object/object_extract.py +35 -21
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/core/relationship/derive_m2m_fields.py +9 -4
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/core/relationship/meta_relationship.py +14 -10
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/meta_data.py +46 -1
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/persistence/db/db_constants.py +24 -11
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/persistence/db/db_provider.py +32 -6
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/persistence/source/meta_source.py +14 -8
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/template/meta_template.py +12 -7
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/parser.py +6 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/registry_manifest.py +27 -13
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/runtime/n2m_resolver.py +5 -1
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/runtime/object_manager.py +33 -13
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/runtime/tph.py +6 -2
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/serializer_json.py +9 -3
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/spec_metamodel/db.json +8 -1
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/spec_metamodel/field.json +18 -1
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/super_resolve.py +4 -0
- metaobjects-0.15.0/tests/codegen/test_cli_verify_strict.py +139 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/test_entity_model.py +14 -0
- metaobjects-0.15.0/tests/codegen/test_extends_abstract_field_inheritance.py +97 -0
- metaobjects-0.15.0/tests/codegen/test_reverse_finders.py +204 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/test_type_map.py +32 -0
- metaobjects-0.15.0/tests/integration/api_contract_jsonb_server.py +160 -0
- metaobjects-0.15.0/tests/integration/generated_jsonb_app.py +135 -0
- metaobjects-0.15.0/tests/integration/test_api_contract_jsonb.py +153 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/unit/test_field_uuid_dbcolumntype.py +90 -1
- metaobjects-0.15.0/tests/unit/test_n2m_resolver_inherited.py +144 -0
- metaobjects-0.15.0/tests/unit/test_write_coercion_arrays.py +83 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/uv.lock +1 -1
- {metaobjects-0.13.0 → metaobjects-0.15.0}/.gitignore +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/LICENSE +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/README.md +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/hatch_build.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/__init__.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/agent_context/__init__.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/agent_context/scaffold.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/apidocs/__init__.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/apidocs/api_model.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/apidocs/paths.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/apidocs/renderer.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/attr_class_map.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/codegen/KNOWN_GAPS.md +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/codegen/__init__.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/codegen/config.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/codegen/constants.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/codegen/extract_schema_emitter.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/codegen/format.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/codegen/generator.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/codegen/generator_registry.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/codegen/generators/__init__.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/codegen/generators/template_generator.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/codegen/instance_artifacts.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/codegen/overwrite_policy.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/codegen/runtime/__init__.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/codegen/runtime/filter_parser.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/codegen/template_codegen/__init__.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/codegen/template_codegen/output_pattern.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/codegen/template_codegen/template_spec.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/datatype.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/documentation/__init__.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/documentation/doc_constants.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/documentation/doc_provider.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/documentation/doc_schema.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/errors.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/loader/__init__.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/loader/meta_data_loader.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/loader/sources/__init__.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/loader/sources/directory_source.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/loader/sources/file_source.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/loader/sources/meta_data_source.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/loader/sources/uri_source.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/__init__.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/core/__init__.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/core/attr/__init__.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/core/attr/attr_constants.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/core/attr/meta_attr.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/core/field/__init__.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/core/identity/__init__.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/core/identity/identity_constants.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/core/identity/meta_identity.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/core/object/__init__.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/core/object/meta_object_aware.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/core/object/object_class_registry.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/core/object/object_constants.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/core/object/value_object.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/core/relationship/__init__.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/core/relationship/relationship_constants.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/core/validator/__init__.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/core/validator/validator_constants.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/meta_root.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/persistence/__init__.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/persistence/db/__init__.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/persistence/origin/__init__.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/persistence/origin/meta_origin.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/persistence/origin/origin_constants.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/persistence/source/__init__.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/persistence/source/source_constants.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/presentation/__init__.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/presentation/layout/__init__.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/presentation/layout/layout_constants.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/presentation/layout/meta_layout.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/presentation/ui/__init__.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/presentation/ui/ui_provider.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/presentation/view/__init__.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/presentation/view/meta_view.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/presentation/view/view_constants.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/provider_extends.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/template/__init__.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/template/prompt_provider.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/meta/template/template_constants.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/naming_refs.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/parser_yaml.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/provider.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/py.typed +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/registry.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/render/__init__.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/render/email_document.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/render/escapers.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/render/extract/KNOWN_GAPS.md +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/render/extract/__init__.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/render/extract/coerce.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/render/extract/extract.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/render/extract/extract_map.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/render/extract/json_forgiving_reader.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/render/extract/locate.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/render/extract/normalize.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/render/extract/strip.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/render/extract/types.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/render/extract/xml_forgiving_reader.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/render/filesystem_provider.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/render/prompt/__init__.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/render/prompt/output_format_renderer.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/render/prompt/output_format_spec.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/render/prompt/prompt_field.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/render/prompt/prompt_overrides.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/render/prompt/prompt_style.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/render/renderer.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/render/verify.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/runtime/__init__.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/runtime/llm_recorder.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/shared/__init__.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/shared/base_types.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/shared/separators.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/shared/structural.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/source/__init__.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/source/error_source.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/source/json_path.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/source/semantic_diff.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/source/yaml_positions.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/spec_metamodel/__init__.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/spec_metamodel/attr.json +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/spec_metamodel/documentation.json +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/spec_metamodel/identity.json +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/spec_metamodel/layout.json +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/spec_metamodel/object.json +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/spec_metamodel/origin.json +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/spec_metamodel/prompt.json +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/spec_metamodel/relationship.json +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/spec_metamodel/source.json +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/spec_metamodel/template.json +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/spec_metamodel/ui.json +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/spec_metamodel/validator.json +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/spec_metamodel/view.json +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/validation_types.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/yaml_desugar.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/__init__.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/__init__.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/golden/extends/expected/BaseEntity.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/golden/extends/expected/Program.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/golden/extends/meta.json +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/golden/nested-array/expected/AuthorBrief.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/golden/nested-array/expected/PostBrief.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/golden/nested-array/meta.json +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/golden/scalars/expected/Metric.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/golden/scalars/expected/Report.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/golden/scalars/meta.json +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/golden/vanilla/expected/Subscriber.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/golden/vanilla/meta.json +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/test_abstract_conformance.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/test_api_docs_builder.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/test_api_docs_paths.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/test_cli.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/test_cli_registry.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/test_cli_staleness_nudge.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/test_cli_verify_subverbs.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/test_constants_config.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/test_enum_conformance.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/test_extractor_generator.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/test_filter_allowlist_generator.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/test_format.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/test_fr010_output_codegen.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/test_fr019_shared_provided_conformance.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/test_generator.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/test_generator_extension_seams.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/test_golden.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/test_inherit_without_restate_gate.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/test_inheritance_conformance.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/test_instance_artifacts.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/test_m2m_codegen.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/test_output_parser_generator.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/test_overwrite_policy.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/test_payload_vo_generator.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/test_projection_compile.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/test_render_helper_conformance.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/test_render_helper_generator.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/test_router_generator.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/test_runner.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/test_template_data.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/test_template_generator.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/test_template_output_pattern.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/test_template_scope_helpers.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/test_template_scope_walk.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/test_template_spec.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/test_tph_codegen.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/test_trace_helper_generator.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/codegen/test_validation_conformance.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/conformance/__init__.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/conformance/capabilities.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/conformance/conformance-expected-failures.json +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/conformance/conformance_adapter.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/conformance/corpus.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/conformance/expected_failures.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/conformance/fixture_discovery.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/conformance/navigator.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/conformance/test_api_docs_cross_port_conformance.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/conformance/test_conformance.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/conformance/test_extract_conformance.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/conformance/test_extract_object_verdict.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/conformance/test_fr010_loader_attrs.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/conformance/test_fr011_attrs.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/conformance/test_generator_registry_conformance.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/conformance/test_object_model_conformance.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/conformance/test_registry_conformance.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/conformance/test_runner_hardfail.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/conformance/test_spec_metamodel_embed.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/conformance/test_strict_attr_load.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/conformance/test_template_codegen_conformance.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/conformance/test_template_generator_conformance.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/conformance/test_yaml_conformance.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/conformance/yaml-conformance-expected-failures.json +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/integration/__init__.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/integration/api_contract_assertions.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/integration/api_contract_m2m_server.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/integration/api_contract_server.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/integration/generated_m2m_app.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/integration/generated_router_app.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/integration/generated_tph_app.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/integration/meta_ai_trace.yaml +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/integration/normalization.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/integration/postgres_container.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/integration/query_runner.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/integration/scenarios.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/integration/test_api_contract.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/integration/test_api_contract_generated.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/integration/test_api_contract_m2m.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/integration/test_api_contract_m2m_generated.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/integration/test_api_contract_tph_generated.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/integration/test_llm_call_trace.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/integration/test_normalization.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/integration/test_query_scenarios.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/integration/test_runtime_return_types.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/open_closed_proof_test.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/render/__init__.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/render/extract/__init__.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/render/extract/test_coerce.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/render/extract/test_extract.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/render/extract/test_extract_map.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/render/extract/test_json_forgiving_reader.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/render/extract/test_locate.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/render/extract/test_model.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/render/extract/test_normalize.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/render/extract/test_strip.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/render/extract/test_xml_forgiving_reader.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/render/prompt/__init__.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/render/prompt/test_output_format_renderer.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/render/test_email_document.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/render/test_filesystem_provider.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/render/test_output_format_renderer_nested.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/render/test_output_prompt_conformance.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/render/test_render_conformance.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/render/test_render_max_chars.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/render/test_verify.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/render/test_verify_conformance.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/source/__init__.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/source/test_fr5c_merge_attribution.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/source/test_fr5d_reference_resolution.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/source/test_fr5e_database_source_shape.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/source/test_json_path.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/source/test_semantic_diff.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/source/test_source_on_node.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/source/test_yaml_positions.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/test_api_docs_accuracy.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/unit/__init__.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/unit/test_agent_context_staleness.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/unit/test_capabilities.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/unit/test_common_attrs.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/unit/test_core_types.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/unit/test_effective_package.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/unit/test_errors.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/unit/test_field_enum.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/unit/test_field_map_validation.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/unit/test_fr016_source_name_and_kind_aliases.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/unit/test_llm_recorder.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/unit/test_loader.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/unit/test_loader_bom.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/unit/test_loader_class.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/unit/test_merge.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/unit/test_meta_attr.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/unit/test_meta_data.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/unit/test_meta_source.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/unit/test_module_shortcuts.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/unit/test_n2m_resolver.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/unit/test_object_manager_uuid_coercion.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/unit/test_one_primary_source.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/unit/test_parser.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/unit/test_parser_yaml.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/unit/test_provider.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/unit/test_provider_extension.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/unit/test_registry.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/unit/test_registry_completeness.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/unit/test_registry_extend.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/unit/test_registry_sealed.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/unit/test_relationship_referential_actions.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/unit/test_resolution_key.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/unit/test_runtime_resolution_key_binding.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/unit/test_serializer.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/unit/test_shared_constants.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/unit/test_smoke.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/unit/test_sources.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/unit/test_strict_child_placement.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/unit/test_super_resolve.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/unit/test_template_toolcall.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/unit/test_template_wrong_subtype_attrs.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/unit/test_validation_attr_schema.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/unit/test_validation_filter_values.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/unit/test_validation_origin_paths.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/unit/test_validation_sort_field.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/unit/test_validation_warnings.py +0 -0
- {metaobjects-0.13.0 → metaobjects-0.15.0}/tests/unit/test_yaml_desugar.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: metaobjects
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.15.0
|
|
4
4
|
Summary: Cross-language metadata standard: declare typed entities once, generate idiomatic drift-checked code across languages — Python port.
|
|
5
5
|
Project-URL: Homepage, https://metaobjects.dev
|
|
6
6
|
Project-URL: Repository, https://github.com/metaobjectsdev/metaobjects
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "metaobjects"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.15.0"
|
|
4
4
|
description = "Cross-language metadata standard: declare typed entities once, generate idiomatic drift-checked code across languages — Python port."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
|
@@ -85,21 +85,21 @@ def _is_writable_table_entity(obj: MetaObject, object_index: dict[str, MetaObjec
|
|
|
85
85
|
|
|
86
86
|
|
|
87
87
|
def _template_format(tmpl: MetaData) -> str:
|
|
88
|
-
fmt = tmpl.
|
|
88
|
+
fmt = tmpl.get_meta_attr(tc.TEMPLATE_ATTR_FORMAT) # ADR-0039: template attr resolves via extends (not origin; templates CAN extend)
|
|
89
89
|
return fmt if isinstance(fmt, str) and fmt else tc.TEMPLATE_FORMAT_DEFAULT
|
|
90
90
|
|
|
91
91
|
|
|
92
92
|
def _payload_resolves(tmpl: MetaData, root: MetaData) -> MetaObject | None:
|
|
93
93
|
"""The payload VO a template resolves to (``@payloadRef`` → ``object.value``),
|
|
94
94
|
or ``None`` — the shared gate for payload / render / prompt / parser / extractor."""
|
|
95
|
-
payload_ref = tmpl.
|
|
95
|
+
payload_ref = tmpl.get_meta_attr(tc.TEMPLATE_ATTR_PAYLOAD_REF) # ADR-0039: template attr resolves via extends (not origin; templates CAN extend)
|
|
96
96
|
if not isinstance(payload_ref, str) or not payload_ref:
|
|
97
97
|
return None
|
|
98
98
|
return resolve_payload_vo(root, payload_ref)
|
|
99
99
|
|
|
100
100
|
|
|
101
101
|
def _is_email_kind(tmpl: MetaData) -> bool:
|
|
102
|
-
kind = tmpl.
|
|
102
|
+
kind = tmpl.get_meta_attr(tc.TEMPLATE_ATTR_KIND) # ADR-0039: template attr resolves via extends (not origin; templates CAN extend)
|
|
103
103
|
return isinstance(kind, str) and kind.lower() == tc.TEMPLATE_KIND_EMAIL
|
|
104
104
|
|
|
105
105
|
|
|
@@ -113,6 +113,7 @@ class PythonApiModelBuilder:
|
|
|
113
113
|
|
|
114
114
|
def build(self, root: MetaData, project: str) -> ApiModel:
|
|
115
115
|
objects = [
|
|
116
|
+
# ADR-0039 sanctioned own: top-level scan on the loader ROOT (never extended, own == effective)
|
|
116
117
|
c
|
|
117
118
|
for c in root.own_children()
|
|
118
119
|
if c.type == TYPE_OBJECT and isinstance(c, MetaObject)
|
|
@@ -127,6 +128,7 @@ class PythonApiModelBuilder:
|
|
|
127
128
|
|
|
128
129
|
# Templates: only template.output is consumed by the payload/render/prompt/
|
|
129
130
|
# parser/extractor generators (the other template subtypes emit nothing).
|
|
131
|
+
# ADR-0039 sanctioned own: top-level scan on the loader ROOT (never extended, own == effective)
|
|
130
132
|
for tmpl in root.own_children():
|
|
131
133
|
if tmpl.type == TYPE_TEMPLATE and tmpl.sub_type == tc.TEMPLATE_SUBTYPE_OUTPUT:
|
|
132
134
|
units.append(self._build_template_unit(tmpl, root))
|
|
@@ -353,7 +355,7 @@ class PythonApiModelBuilder:
|
|
|
353
355
|
values = type_map.effective_enum_values(f)
|
|
354
356
|
if values:
|
|
355
357
|
note = "allowed: " + " | ".join(values)
|
|
356
|
-
required = f.
|
|
358
|
+
required = f.get_meta_attr(fc.FIELD_ATTR_REQUIRED) is True # ADR-0039: resolving (@required may be inherited)
|
|
357
359
|
rows.append(FieldShape(f.name, type_expr, optional=not required, note=note))
|
|
358
360
|
return rows
|
|
359
361
|
|
|
@@ -39,6 +39,9 @@ __all__ = [
|
|
|
39
39
|
"output_prompt_fn",
|
|
40
40
|
"output_parser_fn",
|
|
41
41
|
"extractor_fn",
|
|
42
|
+
"reverse_finder_fk_segment",
|
|
43
|
+
"reverse_finder_fn",
|
|
44
|
+
"reverse_finder_in_fn",
|
|
42
45
|
]
|
|
43
46
|
|
|
44
47
|
|
|
@@ -121,3 +124,75 @@ def output_parser_fn(template_name: str) -> str:
|
|
|
121
124
|
def extractor_fn(template_name: str) -> str:
|
|
122
125
|
"""``OrderSummary`` → ``extract_order_summary`` (the strict extractor fn)."""
|
|
123
126
|
return f"extract_{snake_case(template_name)}"
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
# ---------------------------------------------------------------------------
|
|
130
|
+
# ADR-0038 — reverse-relationship navigation as explicit FK finders.
|
|
131
|
+
#
|
|
132
|
+
# For each FK an entity ``E`` holds (an ``identity.reference`` over an FK field
|
|
133
|
+
# referencing entity ``T``), ``E``'s repository surface gains a finder returning
|
|
134
|
+
# the ``E`` rows matching a given ``T`` id — so ``T`` navigates to its referencing
|
|
135
|
+
# ``E`` rows by calling the finder with a ``T`` id. Two variants: a single-value
|
|
136
|
+
# finder and a batched (anti-N+1) ``…_in`` finder. Both are plain, framework-free
|
|
137
|
+
# single queries (``WHERE <fk> = ?`` / ``WHERE <fk> IN (…)``) — NOT lazy ORM
|
|
138
|
+
# collections (ADR-0038: lazy collections are impossible framework-free and are
|
|
139
|
+
# the canonical N+1 anti-pattern).
|
|
140
|
+
#
|
|
141
|
+
# CANONICAL NAMING (the cross-port contract — idiomatic Python snake_case spelling
|
|
142
|
+
# of the cross-port shape ``find<EPlural>By<FkField>`` / ``…In``):
|
|
143
|
+
#
|
|
144
|
+
# find_<e_plural>_by_<fk_segment>(value) → SELECT … FROM E WHERE <fk> = ?
|
|
145
|
+
# find_<e_plural>_by_<fk_segment>_in(values) → SELECT … FROM E WHERE <fk> IN (…)
|
|
146
|
+
#
|
|
147
|
+
# where:
|
|
148
|
+
# - <e_plural> is the source entity name pluralized then snake_cased
|
|
149
|
+
# (``GameSession`` → ``game_sessions``).
|
|
150
|
+
# - <fk_segment> is the FK FIELD name (NOT the relationship/navigation name and
|
|
151
|
+
# NOT the raw column), snake_cased, with a single trailing ``_id`` dropped if
|
|
152
|
+
# present. The FK field name is unique within an entity, so the finder name is
|
|
153
|
+
# unique by construction — this dissolves the same-pair collision and removes
|
|
154
|
+
# any need for a naming attribute.
|
|
155
|
+
#
|
|
156
|
+
# SAME-PAIR EXAMPLE (``GameSession`` has THREE FKs to ``Scene``):
|
|
157
|
+
# FK field ``currentSceneId`` → find_game_sessions_by_current_scene
|
|
158
|
+
# FK field ``lastOpeningNarrativeSceneId`` → find_game_sessions_by_last_opening_narrative_scene
|
|
159
|
+
# FK field ``transitioningFromSceneId`` → find_game_sessions_by_transitioning_from_scene
|
|
160
|
+
# Three distinct finders — no collision.
|
|
161
|
+
# ---------------------------------------------------------------------------
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def _pluralize(name: str) -> str:
|
|
165
|
+
"""Trivial cross-port pluralization (matches the TS ``pluralize`` /
|
|
166
|
+
``MetaSource._pluralize``), applied to a PascalCase entity name BEFORE
|
|
167
|
+
snake-casing (``GameSession`` → ``GameSessions``)."""
|
|
168
|
+
if not name:
|
|
169
|
+
return name
|
|
170
|
+
lower = name.lower()
|
|
171
|
+
if lower.endswith(("s", "x", "z", "ch", "sh")):
|
|
172
|
+
return name + "es"
|
|
173
|
+
if len(name) >= 2 and lower[-1] == "y" and lower[-2] not in "aeiou":
|
|
174
|
+
return name[:-1] + "ies"
|
|
175
|
+
return name + "s"
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def reverse_finder_fk_segment(fk_field_name: str) -> str:
|
|
179
|
+
"""Lower an FK field name to the ``<fk_segment>`` of a reverse finder name:
|
|
180
|
+
snake_case the field, then drop a single trailing ``_id`` if present.
|
|
181
|
+
E.g. ``currentSceneId`` → ``current_scene``, ``authorId`` → ``author``,
|
|
182
|
+
``scene`` → ``scene``."""
|
|
183
|
+
snake = snake_case(fk_field_name)
|
|
184
|
+
# Drop a single trailing "_id" (but not a bare "id" — that would yield "").
|
|
185
|
+
if len(snake) > 3 and snake.endswith("_id"):
|
|
186
|
+
return snake[:-3]
|
|
187
|
+
return snake
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def reverse_finder_fn(source_entity_name: str, fk_field_name: str) -> str:
|
|
191
|
+
"""Reverse single-value finder name: ``find_<e_plural>_by_<fk_segment>``."""
|
|
192
|
+
e_plural = snake_case(_pluralize(source_entity_name))
|
|
193
|
+
return f"find_{e_plural}_by_{reverse_finder_fk_segment(fk_field_name)}"
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def reverse_finder_in_fn(source_entity_name: str, fk_field_name: str) -> str:
|
|
197
|
+
"""Reverse batched finder name: ``find_<e_plural>_by_<fk_segment>_in``."""
|
|
198
|
+
return f"{reverse_finder_fn(source_entity_name, fk_field_name)}_in"
|
|
@@ -103,15 +103,33 @@ def _default_generators() -> list[Generator]:
|
|
|
103
103
|
]
|
|
104
104
|
|
|
105
105
|
|
|
106
|
-
def _load_root(
|
|
107
|
-
|
|
108
|
-
|
|
106
|
+
def _load_root(
|
|
107
|
+
metadata_dir: str, strict: bool = False
|
|
108
|
+
) -> tuple[MetaData | None, list[str]]:
|
|
109
|
+
"""Load metadata; return ``(root, error_messages)``. ``root`` is None on error.
|
|
110
|
+
|
|
111
|
+
``strict`` (ADR-0023, #96) — when True, an undeclared own ``@attr`` →
|
|
112
|
+
``ERR_UNKNOWN_ATTR``. Defaults False so ``gen`` / ``docs`` keep the legacy
|
|
113
|
+
open-attr load; only ``verify`` opts in (strict-by-default with a ``--lax``
|
|
114
|
+
escape).
|
|
115
|
+
"""
|
|
116
|
+
result = MetaDataLoader.from_directory(metadata_dir, strict=strict)
|
|
109
117
|
if result.errors:
|
|
110
118
|
msgs = [f"{e.code}: {e.message}" for e in result.errors]
|
|
111
119
|
return None, msgs
|
|
112
120
|
return result.root, []
|
|
113
121
|
|
|
114
122
|
|
|
123
|
+
def _strict_load_hint() -> str:
|
|
124
|
+
"""Actionable next-steps when strict verify rejects an undeclared @attr."""
|
|
125
|
+
return (
|
|
126
|
+
"verify is strict (ADR-0023): every authored @attr must be declared. Fix: "
|
|
127
|
+
"register the attr on a metadata provider, OR move arbitrary author-supplied "
|
|
128
|
+
"properties into an `attr.properties` bag, OR re-run with `--lax` to keep the "
|
|
129
|
+
"legacy open-attr load."
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
|
|
115
133
|
def _resolve_generators(names: str) -> tuple[list[Generator], list[str]]:
|
|
116
134
|
"""Resolve a comma-separated list of STABLE generator names via the registry.
|
|
117
135
|
|
|
@@ -174,6 +192,7 @@ def _generate(
|
|
|
174
192
|
generators: list[Generator] | None = None,
|
|
175
193
|
entity_filter: list[str] | None = None,
|
|
176
194
|
emit_package_init: bool = True,
|
|
195
|
+
strict: bool = False,
|
|
177
196
|
) -> tuple[list[str], list[str]]:
|
|
178
197
|
"""Run the generator suite into ``out_dir``.
|
|
179
198
|
|
|
@@ -186,9 +205,10 @@ def _generate(
|
|
|
186
205
|
output is text/markdown/csv/json/xml/html, never a Python package) so no
|
|
187
206
|
spurious ``__init__.py`` is scattered through their output tree. Returns
|
|
188
207
|
``(written_paths, errors)``. On a load error, ``errors`` is non-empty and no
|
|
189
|
-
files are written.
|
|
208
|
+
files are written. ``strict`` (ADR-0023, #96) is threaded to the load — the
|
|
209
|
+
``verify --codegen`` caller passes True; ``gen`` keeps the default False.
|
|
190
210
|
"""
|
|
191
|
-
root, errors = _load_root(metadata_dir)
|
|
211
|
+
root, errors = _load_root(metadata_dir, strict=strict)
|
|
192
212
|
if root is None:
|
|
193
213
|
return [], errors
|
|
194
214
|
return _run_suite(root, out_dir, generators, entity_filter, emit_package_init), []
|
|
@@ -408,13 +428,20 @@ def _verify_codegen(args: argparse.Namespace) -> int:
|
|
|
408
428
|
# Reuse the exact gen code path — regenerate into a throwaway temp dir. The
|
|
409
429
|
# --entities filter must match the `gen` that produced --out, or the diff
|
|
410
430
|
# reports the un-emitted entities as spurious drift.
|
|
431
|
+
# ADR-0023 strict-by-default (#96): verify loads strict unless --lax is given,
|
|
432
|
+
# so an undeclared/typo'd own @attr fails verify (matching Java's Maven goal).
|
|
433
|
+
strict = not getattr(args, "lax", False)
|
|
411
434
|
with tempfile.TemporaryDirectory() as tmp:
|
|
412
435
|
entities = _parse_entities(getattr(args, "entities", None))
|
|
413
|
-
written, errors = _generate(
|
|
436
|
+
written, errors = _generate(
|
|
437
|
+
args.metadata_dir, tmp, None, entities, strict=strict
|
|
438
|
+
)
|
|
414
439
|
if errors:
|
|
415
440
|
print("error: failed to load metadata:", file=sys.stderr)
|
|
416
441
|
for msg in errors:
|
|
417
442
|
print(f" {msg}", file=sys.stderr)
|
|
443
|
+
if strict and any("ERR_UNKNOWN_ATTR" in m for m in errors):
|
|
444
|
+
print(_strict_load_hint(), file=sys.stderr)
|
|
418
445
|
return 1
|
|
419
446
|
|
|
420
447
|
expected = _relative_set(Path(tmp))
|
|
@@ -478,17 +505,23 @@ def _verify_templates(args: argparse.Namespace) -> int:
|
|
|
478
505
|
)
|
|
479
506
|
return 2
|
|
480
507
|
|
|
481
|
-
|
|
508
|
+
# ADR-0023 strict-by-default (#96) — same as --codegen: an undeclared @attr
|
|
509
|
+
# fails verify unless --lax is passed.
|
|
510
|
+
strict = not getattr(args, "lax", False)
|
|
511
|
+
root, errors = _load_root(args.metadata_dir, strict=strict)
|
|
482
512
|
if root is None:
|
|
483
513
|
print("error: failed to load metadata:", file=sys.stderr)
|
|
484
514
|
for msg in errors:
|
|
485
515
|
print(f" {msg}", file=sys.stderr)
|
|
516
|
+
if strict and any("ERR_UNKNOWN_ATTR" in m for m in errors):
|
|
517
|
+
print(_strict_load_hint(), file=sys.stderr)
|
|
486
518
|
return 1
|
|
487
519
|
|
|
488
520
|
provider = FilesystemProvider(template_root)
|
|
489
521
|
# Toolcall templates have no renderable text body (the body IS the schema) —
|
|
490
522
|
# skip them; there is no {{field}} drift to check.
|
|
491
523
|
templates = [
|
|
524
|
+
# ADR-0039 sanctioned own: top-level scan on the loader ROOT (never extended, own == effective)
|
|
492
525
|
c
|
|
493
526
|
for c in root.own_children()
|
|
494
527
|
if c.type == TYPE_TEMPLATE and c.sub_type != tc.TEMPLATE_SUBTYPE_TOOLCALL
|
|
@@ -501,7 +534,7 @@ def _verify_templates(args: argparse.Namespace) -> int:
|
|
|
501
534
|
error_count = 0
|
|
502
535
|
checked = 0
|
|
503
536
|
for tmpl in sorted(templates, key=lambda c: c.name):
|
|
504
|
-
payload_ref = tmpl.
|
|
537
|
+
payload_ref = tmpl.get_meta_attr(tc.TEMPLATE_ATTR_PAYLOAD_REF) # ADR-0039: template attr resolves via extends (not origin; templates CAN extend)
|
|
505
538
|
if not isinstance(payload_ref, str) or not payload_ref:
|
|
506
539
|
print(f"error: [{tmpl.name}] missing @payloadRef.", file=sys.stderr)
|
|
507
540
|
error_count += 1
|
|
@@ -521,7 +554,8 @@ def _verify_templates(args: argparse.Namespace) -> int:
|
|
|
521
554
|
refs = [
|
|
522
555
|
val
|
|
523
556
|
for a in _TEMPLATE_TEXT_REF_ATTRS
|
|
524
|
-
|
|
557
|
+
# ADR-0039: template attr resolves via extends (not origin; templates CAN extend)
|
|
558
|
+
if isinstance(val := tmpl.get_meta_attr(a), str) and val
|
|
525
559
|
]
|
|
526
560
|
if not refs:
|
|
527
561
|
print(
|
|
@@ -760,6 +794,15 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
760
794
|
default=None,
|
|
761
795
|
help="comma-separated entity allowlist for --codegen drift (match `gen --entities`)",
|
|
762
796
|
)
|
|
797
|
+
verify.add_argument(
|
|
798
|
+
"--lax",
|
|
799
|
+
action="store_true",
|
|
800
|
+
help=(
|
|
801
|
+
"opt out of strict-attr load (ADR-0023, #96): verify is strict by "
|
|
802
|
+
"default — an undeclared/typo'd @attr fails. --lax restores the legacy "
|
|
803
|
+
"open-attr load."
|
|
804
|
+
),
|
|
805
|
+
)
|
|
763
806
|
verify.set_defaults(func=_cmd_verify)
|
|
764
807
|
|
|
765
808
|
agent_docs = sub.add_parser(
|
{metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/codegen/extract_delegate_emitter.py
RENAMED
|
@@ -36,7 +36,12 @@ from metaobjects.shared.separators import PACKAGE_SEP
|
|
|
36
36
|
|
|
37
37
|
|
|
38
38
|
def _find_object(root: MetaData, name: str) -> MetaData | None:
|
|
39
|
-
"""The
|
|
39
|
+
"""The top-level ``object.*`` node named *name*, or ``None``.
|
|
40
|
+
|
|
41
|
+
ADR-0039 sanctioned own: top-level object lookup on the loader ROOT
|
|
42
|
+
(metadata.root is never extended, so own == effective) — mirrors the TS
|
|
43
|
+
reference (``root.ownChildren()``).
|
|
44
|
+
"""
|
|
40
45
|
for c in root.own_children():
|
|
41
46
|
if c.type == TYPE_OBJECT and c.name == name:
|
|
42
47
|
return c
|
|
@@ -22,7 +22,6 @@ from metaobjects.meta.core.field import field_constants as fc
|
|
|
22
22
|
from metaobjects.meta.meta_data import MetaData
|
|
23
23
|
from metaobjects.meta.template.template_constants import TEMPLATE_ATTR_XML_TEXT
|
|
24
24
|
from metaobjects.shared.base_types import TYPE_FIELD
|
|
25
|
-
from metaobjects.shared.structural import KEY_IS_ARRAY
|
|
26
25
|
|
|
27
26
|
|
|
28
27
|
def fields(vo: MetaData) -> list[MetaData]:
|
|
@@ -32,9 +31,10 @@ def fields(vo: MetaData) -> list[MetaData]:
|
|
|
32
31
|
|
|
33
32
|
|
|
34
33
|
def is_array(field: MetaData) -> bool:
|
|
35
|
-
"""
|
|
36
|
-
``@isArray`` attr
|
|
37
|
-
|
|
34
|
+
"""Effective array-ness (ADR-0039 resolving): the native ``is_array`` flag OR the
|
|
35
|
+
``@isArray`` attr, resolved through the ``extends`` super chain so a concrete field
|
|
36
|
+
inheriting array-ness from an abstract parent is honored."""
|
|
37
|
+
return field.resolved_is_array()
|
|
38
38
|
|
|
39
39
|
|
|
40
40
|
def is_required(field: MetaData) -> bool:
|
|
@@ -63,31 +63,36 @@ def enum_values(field: MetaData) -> list[str]:
|
|
|
63
63
|
return []
|
|
64
64
|
|
|
65
65
|
|
|
66
|
-
def
|
|
67
|
-
"""The
|
|
68
|
-
|
|
66
|
+
def _attr_string(node: MetaData, name: str) -> str | None:
|
|
67
|
+
"""The RESOLVING string value of attr *name* (own + inherited via ``extends``),
|
|
68
|
+
or ``None``.
|
|
69
|
+
|
|
70
|
+
ADR-0039 — effective read: a concrete enum field extending an abstract enum may
|
|
71
|
+
inherit ``@coerceDefault``/``@default``/``@normalize`` from the abstract parent,
|
|
72
|
+
so these resolve through the super chain (``get_meta_attr``), never own-only."""
|
|
73
|
+
v = node.get_meta_attr(name)
|
|
69
74
|
return v if isinstance(v, str) else None
|
|
70
75
|
|
|
71
76
|
|
|
72
77
|
def coerce_default(field: MetaData) -> str | None:
|
|
73
|
-
"""FR-011: the enum field's
|
|
74
|
-
return
|
|
78
|
+
"""FR-011: the enum field's effective ``@coerceDefault`` member, or ``None``."""
|
|
79
|
+
return _attr_string(field, fc.FIELD_ATTR_COERCE_DEFAULT)
|
|
75
80
|
|
|
76
81
|
|
|
77
82
|
def default_value(field: MetaData) -> str | None:
|
|
78
|
-
"""FR-011: the enum field's
|
|
79
|
-
return
|
|
83
|
+
"""FR-011: the enum field's effective ``@default`` absent-fill member, or ``None``."""
|
|
84
|
+
return _attr_string(field, fc.FIELD_ATTR_DEFAULT)
|
|
80
85
|
|
|
81
86
|
|
|
82
87
|
def resolve_normalize(field: MetaData, owner: MetaData | None) -> str:
|
|
83
88
|
"""FR-011: resolve the enum normalization mode — field-level ``@normalize``, else the
|
|
84
89
|
owning ``object.value``'s ``@normalize`` (the per-object default), else the global
|
|
85
90
|
default (``"strip"``). Mirrors the Java/Kotlin/C#/TS ``resolveNormalize``."""
|
|
86
|
-
field_mode =
|
|
91
|
+
field_mode = _attr_string(field, fc.FIELD_ATTR_NORMALIZE)
|
|
87
92
|
if field_mode is not None:
|
|
88
93
|
return field_mode
|
|
89
94
|
if owner is not None:
|
|
90
|
-
owner_mode =
|
|
95
|
+
owner_mode = _attr_string(owner, fc.FIELD_ATTR_NORMALIZE)
|
|
91
96
|
if owner_mode is not None:
|
|
92
97
|
return owner_mode
|
|
93
98
|
return fc.NORMALIZE_DEFAULT
|
|
@@ -101,6 +106,9 @@ def scalar_kind(sub_type: str) -> str | None:
|
|
|
101
106
|
fc.FIELD_SUBTYPE_DATE,
|
|
102
107
|
fc.FIELD_SUBTYPE_TIME,
|
|
103
108
|
fc.FIELD_SUBTYPE_TIMESTAMP,
|
|
109
|
+
# ADR-0036/0037 Wave 3: uri/inet are string scalars on the wire.
|
|
110
|
+
fc.FIELD_SUBTYPE_URI,
|
|
111
|
+
fc.FIELD_SUBTYPE_INET,
|
|
104
112
|
):
|
|
105
113
|
return "STRING"
|
|
106
114
|
if sub_type == fc.FIELD_SUBTYPE_INT:
|
{metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/codegen/generators/entity_model.py
RENAMED
|
@@ -48,6 +48,18 @@ def _is_sql_expr_default(value: object) -> bool:
|
|
|
48
48
|
return isinstance(value, str) and any(p.search(value) for p in _SQL_EXPR_DEFAULT_PATTERNS)
|
|
49
49
|
|
|
50
50
|
|
|
51
|
+
# ADR-0036/0037 Wave 3 — the CANONICAL hostname matcher for @stringFormat: hostname.
|
|
52
|
+
# The matcher lives in codegen (NOT author validator.regex) so every port replicates
|
|
53
|
+
# the SAME canonical form — cross-language regex engines diverge, so this one
|
|
54
|
+
# expression is the byte-identical source of truth. RFC 1123 labels: 1–63 chars each,
|
|
55
|
+
# alphanumeric + internal hyphens, dot-separated; total length 1–253; anchored.
|
|
56
|
+
# Byte-identical to the TS zod-validators HOSTNAME_REGEX_LITERAL body.
|
|
57
|
+
_HOSTNAME_REGEX = (
|
|
58
|
+
r"^(?=.{1,253}$)([a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)"
|
|
59
|
+
r"(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$"
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
51
63
|
def _validators(field: MetaField, sub_type: str) -> list[MetaField]:
|
|
52
64
|
"""The field's own ``validator.<sub_type>`` children (effective, supers included)."""
|
|
53
65
|
return [
|
|
@@ -60,6 +72,10 @@ def _validators(field: MetaField, sub_type: str) -> list[MetaField]:
|
|
|
60
72
|
def _first_attr(field: MetaField, sub_type: str, attr_name: str) -> object | None:
|
|
61
73
|
"""First int-valued *attr_name* across the field's ``validator.<sub_type>`` children."""
|
|
62
74
|
for v in _validators(field, sub_type):
|
|
75
|
+
# ADR-0039 sanctioned own: a validator's @min/@max are its own declared
|
|
76
|
+
# bounds (never inherited across a validator extends chain) — matches the TS
|
|
77
|
+
# MetaValidator.min/.max ownAttr reads. The validator nodes are located via
|
|
78
|
+
# the resolving field.children() in _validators().
|
|
63
79
|
val = v.attr(attr_name)
|
|
64
80
|
if _is_int(val):
|
|
65
81
|
return val
|
|
@@ -106,11 +122,21 @@ def _validator_constraints(field: MetaField) -> dict[str, object]:
|
|
|
106
122
|
|
|
107
123
|
# Regex: validator.regex @pattern -> pattern.
|
|
108
124
|
for v in _validators(field, vc.VALIDATOR_SUBTYPE_REGEX):
|
|
125
|
+
# ADR-0039 sanctioned own: a validator's @pattern is its own declared value
|
|
126
|
+
# (matches the TS MetaValidator.pattern ownAttr read); the validator node is
|
|
127
|
+
# located via the resolving field.children() in _validators().
|
|
109
128
|
pattern = v.attr(vc.VALIDATOR_ATTR_PATTERN)
|
|
110
129
|
if isinstance(pattern, str):
|
|
111
130
|
kwargs["pattern"] = pattern
|
|
112
131
|
break
|
|
113
132
|
|
|
133
|
+
# ADR-0036/0037 Wave 3 — @stringFormat: hostname -> a codegen-owned canonical
|
|
134
|
+
# hostname pattern= (the matcher lives HERE, never author validator.regex, so
|
|
135
|
+
# every port replicates the byte-identical form). @stringFormat: email is bound
|
|
136
|
+
# as EmailStr in _field_line (a type, not a pattern), so it is not handled here.
|
|
137
|
+
if field.attrs().get(fc.FIELD_ATTR_STRING_FORMAT) == fc.STRING_FORMAT_HOSTNAME:
|
|
138
|
+
kwargs["pattern"] = _HOSTNAME_REGEX
|
|
139
|
+
|
|
114
140
|
return kwargs
|
|
115
141
|
|
|
116
142
|
|
|
@@ -132,6 +158,17 @@ def _field_line(field: MetaField, imports: set[str], config: GenConfig) -> tuple
|
|
|
132
158
|
pt = py_type_for(field)
|
|
133
159
|
imports.update(pt.imports)
|
|
134
160
|
type_expr = pt.expr
|
|
161
|
+
# ADR-0036/0037 Wave 3 — @stringFormat: email narrows a plain string to a
|
|
162
|
+
# validated email. Bind Pydantic's idiomatic EmailStr (the Python analogue of
|
|
163
|
+
# Zod .email()); hostname stays a plain str with a codegen-owned canonical
|
|
164
|
+
# pattern= (handled in _validator_constraints). The field stays a string.
|
|
165
|
+
if (
|
|
166
|
+
field.sub_type == fc.FIELD_SUBTYPE_STRING
|
|
167
|
+
and field.attrs().get(fc.FIELD_ATTR_STRING_FORMAT) == fc.STRING_FORMAT_EMAIL
|
|
168
|
+
):
|
|
169
|
+
base_expr = "EmailStr"
|
|
170
|
+
imports.add("from pydantic import EmailStr")
|
|
171
|
+
type_expr = f"list[{base_expr}]" if field_is_array(field) else base_expr
|
|
135
172
|
if field.sub_type in (fc.FIELD_SUBTYPE_OBJECT, fc.FIELD_SUBTYPE_MAP):
|
|
136
173
|
# A field.object (-> VO) and a field.map with @objectRef (-> dict[str, VO])
|
|
137
174
|
# both reference a value-object by name; import it so the annotation
|
|
@@ -238,6 +275,9 @@ class EntityModelGenerator:
|
|
|
238
275
|
cfg = config if config is not None else GenConfig(out_dir="")
|
|
239
276
|
uses_field = False
|
|
240
277
|
lines: list[str] = []
|
|
278
|
+
# ADR-0039 SANCTIONED OWN — codegen subclass-emit: the generated model
|
|
279
|
+
# `extends` its generated base, so only OWN fields are emitted here (the base
|
|
280
|
+
# class already declares inherited ones). Pinned by test_entity_model.py.
|
|
241
281
|
for f in entity.own_fields():
|
|
242
282
|
line, used = _field_line(f, imports, cfg)
|
|
243
283
|
uses_field = uses_field or used
|
{metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/codegen/generators/extractor_generator.py
RENAMED
|
@@ -151,14 +151,14 @@ def render_extractor(
|
|
|
151
151
|
Returns ``None`` when the ``@payloadRef`` can't be resolved to an ``object.value``,
|
|
152
152
|
or when the target ``@format`` is not json/xml (the extract tier requires the
|
|
153
153
|
tolerant extract API, which only the json/xml output-parsers emit)."""
|
|
154
|
-
payload_ref = template.
|
|
154
|
+
payload_ref = template.get_meta_attr(tc.TEMPLATE_ATTR_PAYLOAD_REF) # ADR-0039: template attr resolves via extends (not origin; templates CAN extend)
|
|
155
155
|
if not isinstance(payload_ref, str) or not payload_ref:
|
|
156
156
|
return None
|
|
157
157
|
payload = resolve_payload_vo(root, payload_ref)
|
|
158
158
|
if payload is None:
|
|
159
159
|
return None
|
|
160
160
|
|
|
161
|
-
fmt = template.
|
|
161
|
+
fmt = template.get_meta_attr(tc.TEMPLATE_ATTR_FORMAT) # ADR-0039: template attr resolves via extends (not origin; templates CAN extend)
|
|
162
162
|
fmt_str = fmt if isinstance(fmt, str) else tc.TEMPLATE_FORMAT_DEFAULT
|
|
163
163
|
if fmt_str.lower() not in _EXTRACT_FORMATS:
|
|
164
164
|
return None
|
|
@@ -278,6 +278,7 @@ class ExtractorGenerator:
|
|
|
278
278
|
files: list[EmittedFile] = []
|
|
279
279
|
outputs = sorted(
|
|
280
280
|
(
|
|
281
|
+
# ADR-0039 sanctioned own: top-level scan on the loader ROOT (never extended, own == effective)
|
|
281
282
|
c
|
|
282
283
|
for c in root.own_children()
|
|
283
284
|
if c.type == TYPE_TEMPLATE and c.sub_type == tc.TEMPLATE_SUBTYPE_OUTPUT
|
|
@@ -64,8 +64,12 @@ def _effective_fqn(entity: MetaObject) -> str:
|
|
|
64
64
|
|
|
65
65
|
|
|
66
66
|
def _primary_source_rdb(entity: MetaObject) -> MetaSource | None:
|
|
67
|
-
"""Return the entity's ``source.rdb`` child
|
|
68
|
-
|
|
67
|
+
"""Return the entity's ``source.rdb`` child, or ``None``.
|
|
68
|
+
|
|
69
|
+
ADR-0039 — RESOLVING (children()): an entity may inherit its source.rdb via
|
|
70
|
+
``extends`` (BaseEntity); own_children() would miss it. Mirrors TS dbTable.
|
|
71
|
+
"""
|
|
72
|
+
for c in entity.children():
|
|
69
73
|
if c.type == TYPE_SOURCE and isinstance(c, MetaSource):
|
|
70
74
|
return c
|
|
71
75
|
return None
|
|
@@ -91,7 +95,11 @@ def ops_for_subtype_ordered(sub_type: str | None) -> tuple[str, ...]:
|
|
|
91
95
|
return ()
|
|
92
96
|
if sub_type in (fc.FIELD_SUBTYPE_STRING, fc.FIELD_SUBTYPE_ENUM):
|
|
93
97
|
return _OPS_STRING
|
|
94
|
-
|
|
98
|
+
# ADR-0036/0037 Wave 3 — field.uri is string-like (substring searchable);
|
|
99
|
+
# field.inet is uuid-like (identity value, no substring search, no ordering).
|
|
100
|
+
if sub_type == fc.FIELD_SUBTYPE_URI:
|
|
101
|
+
return _OPS_STRING
|
|
102
|
+
if sub_type in (fc.FIELD_SUBTYPE_UUID, fc.FIELD_SUBTYPE_INET):
|
|
95
103
|
return _OPS_UUID
|
|
96
104
|
if sub_type in (
|
|
97
105
|
fc.FIELD_SUBTYPE_INT,
|
|
@@ -190,6 +198,10 @@ class FilterAllowlistGenerator:
|
|
|
190
198
|
plan = tph_plan_for(entity, object_index)
|
|
191
199
|
if plan is not None:
|
|
192
200
|
for st in plan.subtypes:
|
|
201
|
+
# ADR-0039 sanctioned own: TPH subtype delta — the base's
|
|
202
|
+
# effective fields are added above via entity.fields(); each
|
|
203
|
+
# subtype contributes ONLY its own additional fields (inherited
|
|
204
|
+
# base fields must not be re-added).
|
|
193
205
|
for f in st.entity.own_fields():
|
|
194
206
|
add(f)
|
|
195
207
|
return out
|
{metaobjects-0.13.0 → metaobjects-0.15.0}/src/metaobjects/codegen/generators/fr019_shared_enum.py
RENAMED
|
@@ -66,8 +66,12 @@ def resolve_shared_enum_decl(field: MetaField) -> MetaData | None:
|
|
|
66
66
|
|
|
67
67
|
|
|
68
68
|
def is_provided(decl: MetaData) -> bool:
|
|
69
|
-
"""
|
|
70
|
-
|
|
69
|
+
"""Effective ``@provided`` truth of an enum declaration.
|
|
70
|
+
|
|
71
|
+
ADR-0039 — resolves through ``extends`` (``get_meta_attr``): a concrete enum
|
|
72
|
+
extending an abstract ``@provided`` enum inherits the flag, so an own-only read
|
|
73
|
+
would misclassify it."""
|
|
74
|
+
return decl.get_meta_attr(fc.FIELD_ATTR_PROVIDED) is True
|
|
71
75
|
|
|
72
76
|
|
|
73
77
|
def _meta_package_of(decl: MetaData) -> str:
|
|
@@ -99,7 +103,9 @@ def collect_shared_enums(entities: list) -> list[SharedEnum]:
|
|
|
99
103
|
not materialized (no dangling type)."""
|
|
100
104
|
out: dict[str, SharedEnum] = {}
|
|
101
105
|
for entity in entities:
|
|
102
|
-
|
|
106
|
+
# ADR-0039 — RESOLVING (fields()): mirrors the TS collectSharedEnums
|
|
107
|
+
# (``entity.fields()``); an inherited field consuming a shared enum counts.
|
|
108
|
+
for f in entity.fields():
|
|
103
109
|
shared = shared_enum_for_field(f)
|
|
104
110
|
if shared is None:
|
|
105
111
|
continue
|
|
@@ -72,7 +72,10 @@ def plural_lowercase(name: str) -> str:
|
|
|
72
72
|
|
|
73
73
|
|
|
74
74
|
def _primary_source_rdb(entity: MetaObject) -> MetaSource | None:
|
|
75
|
-
|
|
75
|
+
# ADR-0039 — RESOLVING (children()): an entity may inherit its source.rdb from
|
|
76
|
+
# an abstract base via extends (BaseEntity pattern); own_children() would emit
|
|
77
|
+
# nothing for such an entity. Mirrors the TS dbTable source lookup.
|
|
78
|
+
for c in entity.children():
|
|
76
79
|
if c.type == TYPE_SOURCE and isinstance(c, MetaSource):
|
|
77
80
|
return c
|
|
78
81
|
return None
|
|
@@ -111,7 +114,7 @@ def _pk_field_name(entity: MetaObject) -> str:
|
|
|
111
114
|
"""The single PK field name (``identity.primary @fields``), default ``id``."""
|
|
112
115
|
for c in entity.children():
|
|
113
116
|
if c.type == TYPE_IDENTITY and c.sub_type == IDENTITY_SUBTYPE_PRIMARY:
|
|
114
|
-
fields = c.
|
|
117
|
+
fields = c.get_meta_attr(IDENTITY_ATTR_FIELDS) # ADR-0039: resolving (identity attr)
|
|
115
118
|
if isinstance(fields, (list, tuple)) and fields:
|
|
116
119
|
first = fields[0]
|
|
117
120
|
if isinstance(first, str):
|
|
@@ -122,10 +125,14 @@ def _pk_field_name(entity: MetaObject) -> str:
|
|
|
122
125
|
|
|
123
126
|
|
|
124
127
|
def m2m_relationships(entity: MetaObject) -> list[MetaRelationship]:
|
|
125
|
-
"""The entity's
|
|
126
|
-
(declaration order). 1:N relationships (no ``@through``) are excluded.
|
|
128
|
+
"""The entity's ``relationship.* @cardinality:"many" + @through`` children
|
|
129
|
+
(declaration order). 1:N relationships (no ``@through``) are excluded.
|
|
130
|
+
|
|
131
|
+
ADR-0039 — RESOLVING (children()): mirrors the TS ``relationships()`` (built on
|
|
132
|
+
the effective ``children()``); a relationship inherited via ``extends`` counts.
|
|
133
|
+
"""
|
|
127
134
|
out: list[MetaRelationship] = []
|
|
128
|
-
for c in entity.
|
|
135
|
+
for c in entity.children():
|
|
129
136
|
if not isinstance(c, MetaRelationship):
|
|
130
137
|
continue
|
|
131
138
|
if c.cardinality() != CARDINALITY_MANY:
|
|
@@ -54,7 +54,7 @@ def render_output_parser(template: MetaData, root: MetaData) -> str | None:
|
|
|
54
54
|
|
|
55
55
|
Returns ``None`` when the ``@payloadRef`` can't be resolved (defensive;
|
|
56
56
|
the loader's template-validation pass would normally catch this first)."""
|
|
57
|
-
payload_ref = template.
|
|
57
|
+
payload_ref = template.get_meta_attr(tc.TEMPLATE_ATTR_PAYLOAD_REF) # ADR-0039: template attr resolves via extends (not origin; templates CAN extend)
|
|
58
58
|
if not isinstance(payload_ref, str) or not payload_ref:
|
|
59
59
|
return None
|
|
60
60
|
payload = resolve_payload_vo(root, payload_ref)
|
|
@@ -77,7 +77,7 @@ def render_output_parser(template: MetaData, root: MetaData) -> str | None:
|
|
|
77
77
|
# template targets json/xml. Otherwise only the FR-006 strict parser is emitted
|
|
78
78
|
# (text-format outputs get no extract). The mirror is a nullable twin of the
|
|
79
79
|
# payload, so the strict ``parse_*`` is left exactly as FR-006 shipped it.
|
|
80
|
-
fmt = template.
|
|
80
|
+
fmt = template.get_meta_attr(tc.TEMPLATE_ATTR_FORMAT) # ADR-0039: template attr resolves via extends (not origin; templates CAN extend)
|
|
81
81
|
fmt_str = fmt if isinstance(fmt, str) else tc.TEMPLATE_FORMAT_DEFAULT
|
|
82
82
|
emit_extract_lenient = fmt_str.lower() in _EXTRACT_FORMATS
|
|
83
83
|
extracted_class = f"{payload_class}Extracted"
|
|
@@ -172,6 +172,8 @@ def render_output_parser(template: MetaData, root: MetaData) -> str | None:
|
|
|
172
172
|
lines.append(" :param root: a loaded ``MetaRoot`` that declares the")
|
|
173
173
|
lines.append(f' ``{payload.name}`` value-object."""')
|
|
174
174
|
lines.append(" mo = None")
|
|
175
|
+
# Emits a root-scan into generated code: root is the loader ROOT (never
|
|
176
|
+
# extended, so own == effective) — ADR-0039 sanctioned own in emitted code.
|
|
175
177
|
lines.append(" for child in root.own_children():")
|
|
176
178
|
lines.append(" if (")
|
|
177
179
|
lines.append(" isinstance(child, MetaObject)")
|
|
@@ -231,6 +233,7 @@ class OutputParserGenerator:
|
|
|
231
233
|
files: list[EmittedFile] = []
|
|
232
234
|
outputs = sorted(
|
|
233
235
|
(
|
|
236
|
+
# ADR-0039 sanctioned own: top-level scan on the loader ROOT (never extended, own == effective)
|
|
234
237
|
c
|
|
235
238
|
for c in root.own_children()
|
|
236
239
|
if c.type == TYPE_TEMPLATE and c.sub_type == tc.TEMPLATE_SUBTYPE_OUTPUT
|
|
@@ -60,12 +60,12 @@ def render_output_prompt(
|
|
|
60
60
|
Returns ``None`` when the format is unsupported (not json/xml) or the
|
|
61
61
|
``@payloadRef`` can't be resolved to an ``object.value`` (defensive — the loader
|
|
62
62
|
validation pass / the parser generator share this contract)."""
|
|
63
|
-
fmt = template.
|
|
63
|
+
fmt = template.get_meta_attr(tc.TEMPLATE_ATTR_FORMAT) # ADR-0039: template attr resolves via extends (not origin; templates CAN extend)
|
|
64
64
|
fmt_str = fmt if isinstance(fmt, str) else tc.TEMPLATE_FORMAT_DEFAULT
|
|
65
65
|
if fmt_str.lower() not in _PROMPT_FORMATS:
|
|
66
66
|
return None
|
|
67
67
|
|
|
68
|
-
payload_ref = template.
|
|
68
|
+
payload_ref = template.get_meta_attr(tc.TEMPLATE_ATTR_PAYLOAD_REF) # ADR-0039: template attr resolves via extends (not origin; templates CAN extend)
|
|
69
69
|
if not isinstance(payload_ref, str) or not payload_ref:
|
|
70
70
|
return None
|
|
71
71
|
payload = resolve_payload_vo(root, payload_ref)
|
|
@@ -153,6 +153,7 @@ class OutputPromptGenerator:
|
|
|
153
153
|
files: list[EmittedFile] = []
|
|
154
154
|
outputs = sorted(
|
|
155
155
|
(
|
|
156
|
+
# ADR-0039 sanctioned own: top-level scan on the loader ROOT (never extended, own == effective)
|
|
156
157
|
c
|
|
157
158
|
for c in root.own_children()
|
|
158
159
|
if c.type == TYPE_TEMPLATE and c.sub_type == tc.TEMPLATE_SUBTYPE_OUTPUT
|