metaobjects 0.12.1__tar.gz → 0.14.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.12.1 → metaobjects-0.14.0}/PKG-INFO +1 -1
- {metaobjects-0.12.1 → metaobjects-0.14.0}/pyproject.toml +1 -1
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/cli.py +146 -17
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/generator.py +32 -0
- metaobjects-0.14.0/src/metaobjects/codegen/generators/template_generator.py +138 -0
- metaobjects-0.14.0/src/metaobjects/codegen/template_codegen/output_pattern.py +54 -0
- metaobjects-0.14.0/src/metaobjects/codegen/template_codegen/template_data.py +119 -0
- metaobjects-0.14.0/src/metaobjects/codegen/template_codegen/template_spec.py +90 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/type_map.py +12 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/render/escapers.py +4 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_cli.py +57 -0
- metaobjects-0.14.0/tests/codegen/test_cli_verify_strict.py +139 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_entity_model.py +14 -0
- metaobjects-0.14.0/tests/codegen/test_template_data.py +50 -0
- metaobjects-0.14.0/tests/codegen/test_template_output_pattern.py +32 -0
- metaobjects-0.14.0/tests/codegen/test_template_scope_helpers.py +52 -0
- metaobjects-0.14.0/tests/codegen/test_template_scope_walk.py +52 -0
- metaobjects-0.14.0/tests/codegen/test_template_spec.py +60 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_type_map.py +14 -0
- metaobjects-0.14.0/tests/conformance/test_template_codegen_conformance.py +51 -0
- metaobjects-0.14.0/tests/integration/api_contract_jsonb_server.py +160 -0
- metaobjects-0.14.0/tests/integration/generated_jsonb_app.py +135 -0
- metaobjects-0.14.0/tests/integration/test_api_contract_jsonb.py +153 -0
- metaobjects-0.14.0/tests/unit/__init__.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/uv.lock +1 -1
- metaobjects-0.12.1/src/metaobjects/codegen/generators/template_generator.py +0 -70
- {metaobjects-0.12.1 → metaobjects-0.14.0}/.gitignore +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/LICENSE +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/README.md +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/hatch_build.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/__init__.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/agent_context/__init__.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/agent_context/scaffold.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/apidocs/__init__.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/apidocs/api_model.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/apidocs/builder.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/apidocs/naming.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/apidocs/paths.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/apidocs/renderer.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/attr_class_map.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/KNOWN_GAPS.md +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/__init__.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/config.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/constants.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/extract_delegate_emitter.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/extract_schema_emitter.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/format.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/fr010_field_mapping.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/generator_registry.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/generators/__init__.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/generators/entity_model.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/generators/extractor_generator.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/generators/filter_allowlist_generator.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/generators/fr019_shared_enum.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/generators/m2m_codegen.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/generators/output_parser_generator.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/generators/output_prompt_generator.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/generators/payload_vo_generator.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/generators/render_helper_generator.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/generators/router_generator.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/generators/tph_plan.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/generators/trace_helper_generator.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/instance_artifacts.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/output_format_spec_emitter.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/overwrite_policy.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/runner.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/runtime/__init__.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/runtime/filter_parser.py +0 -0
- {metaobjects-0.12.1/src/metaobjects/loader → metaobjects-0.14.0/src/metaobjects/codegen/template_codegen}/__init__.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/core_types.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/datatype.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/documentation/__init__.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/documentation/doc_constants.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/documentation/doc_provider.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/documentation/doc_schema.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/errors.py +0 -0
- {metaobjects-0.12.1/src/metaobjects/meta/core → metaobjects-0.14.0/src/metaobjects/loader}/__init__.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/loader/merge.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/loader/meta_data_loader.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/loader/registered_validation.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/loader/sources/__init__.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/loader/sources/directory_source.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/loader/sources/file_source.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/loader/sources/meta_data_source.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/loader/sources/uri_source.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/loader/validate_discriminator.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/loader/validate_field_readonly.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/loader/validate_source_parameter_ref.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/loader/validate_source_physical_names.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/loader/validation_passes.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/__init__.py +0 -0
- {metaobjects-0.12.1/src/metaobjects/meta/core/attr → metaobjects-0.14.0/src/metaobjects/meta/core}/__init__.py +0 -0
- {metaobjects-0.12.1/src/metaobjects/meta/core/field → metaobjects-0.14.0/src/metaobjects/meta/core/attr}/__init__.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/core/attr/attr_constants.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/core/attr/meta_attr.py +0 -0
- {metaobjects-0.12.1/src/metaobjects/meta/core/identity → metaobjects-0.14.0/src/metaobjects/meta/core/field}/__init__.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/core/field/field_constants.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/core/field/meta_field.py +0 -0
- {metaobjects-0.12.1/src/metaobjects/meta/core/object → metaobjects-0.14.0/src/metaobjects/meta/core/identity}/__init__.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/core/identity/identity_constants.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/core/identity/meta_identity.py +0 -0
- {metaobjects-0.12.1/src/metaobjects/meta/core/relationship → metaobjects-0.14.0/src/metaobjects/meta/core/object}/__init__.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/core/object/meta_object.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/core/object/meta_object_aware.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/core/object/object_class_registry.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/core/object/object_constants.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/core/object/object_extract.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/core/object/value_object.py +0 -0
- {metaobjects-0.12.1/src/metaobjects/meta/core/validator → metaobjects-0.14.0/src/metaobjects/meta/core/relationship}/__init__.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/core/relationship/derive_m2m_fields.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/core/relationship/meta_relationship.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/core/relationship/relationship_constants.py +0 -0
- {metaobjects-0.12.1/src/metaobjects/meta/persistence → metaobjects-0.14.0/src/metaobjects/meta/core/validator}/__init__.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/core/validator/validator_constants.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/meta_data.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/meta_root.py +0 -0
- {metaobjects-0.12.1/src/metaobjects/meta/persistence/origin → metaobjects-0.14.0/src/metaobjects/meta/persistence}/__init__.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/persistence/db/__init__.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/persistence/db/db_constants.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/persistence/db/db_provider.py +0 -0
- {metaobjects-0.12.1/src/metaobjects/meta/persistence/source → metaobjects-0.14.0/src/metaobjects/meta/persistence/origin}/__init__.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/persistence/origin/meta_origin.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/persistence/origin/origin_constants.py +0 -0
- {metaobjects-0.12.1/src/metaobjects/meta/presentation → metaobjects-0.14.0/src/metaobjects/meta/persistence/source}/__init__.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/persistence/source/meta_source.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/persistence/source/source_constants.py +0 -0
- {metaobjects-0.12.1/src/metaobjects/meta/presentation/layout → metaobjects-0.14.0/src/metaobjects/meta/presentation}/__init__.py +0 -0
- {metaobjects-0.12.1/src/metaobjects/meta/presentation/ui → metaobjects-0.14.0/src/metaobjects/meta/presentation/layout}/__init__.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/presentation/layout/layout_constants.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/presentation/layout/meta_layout.py +0 -0
- {metaobjects-0.12.1/src/metaobjects/meta/presentation/view → metaobjects-0.14.0/src/metaobjects/meta/presentation/ui}/__init__.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/presentation/ui/ui_provider.py +0 -0
- {metaobjects-0.12.1/src/metaobjects/meta/template → metaobjects-0.14.0/src/metaobjects/meta/presentation/view}/__init__.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/presentation/view/meta_view.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/presentation/view/view_constants.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/provider_extends.py +0 -0
- {metaobjects-0.12.1/src/metaobjects/shared → metaobjects-0.14.0/src/metaobjects/meta/template}/__init__.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/template/meta_template.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/template/prompt_provider.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/template/template_constants.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/naming_refs.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/parser.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/parser_yaml.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/provider.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/py.typed +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/registry.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/registry_manifest.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/render/__init__.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/render/email_document.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/render/extract/KNOWN_GAPS.md +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/render/extract/__init__.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/render/extract/coerce.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/render/extract/extract.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/render/extract/extract_map.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/render/extract/json_forgiving_reader.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/render/extract/locate.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/render/extract/normalize.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/render/extract/strip.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/render/extract/types.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/render/extract/xml_forgiving_reader.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/render/filesystem_provider.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/render/prompt/__init__.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/render/prompt/output_format_renderer.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/render/prompt/output_format_spec.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/render/prompt/prompt_field.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/render/prompt/prompt_overrides.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/render/prompt/prompt_style.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/render/renderer.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/render/verify.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/runtime/__init__.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/runtime/llm_recorder.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/runtime/n2m_resolver.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/runtime/object_manager.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/runtime/tph.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/serializer_json.py +0 -0
- {metaobjects-0.12.1/tests → metaobjects-0.14.0/src/metaobjects/shared}/__init__.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/shared/base_types.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/shared/separators.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/shared/structural.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/source/__init__.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/source/error_source.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/source/json_path.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/source/semantic_diff.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/source/yaml_positions.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/spec_metamodel/__init__.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/spec_metamodel/attr.json +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/spec_metamodel/db.json +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/spec_metamodel/documentation.json +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/spec_metamodel/field.json +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/spec_metamodel/identity.json +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/spec_metamodel/layout.json +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/spec_metamodel/object.json +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/spec_metamodel/origin.json +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/spec_metamodel/prompt.json +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/spec_metamodel/relationship.json +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/spec_metamodel/source.json +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/spec_metamodel/template.json +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/spec_metamodel/ui.json +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/spec_metamodel/validator.json +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/spec_metamodel/view.json +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/super_resolve.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/validation_types.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/yaml_desugar.py +0 -0
- {metaobjects-0.12.1/tests/codegen → metaobjects-0.14.0/tests}/__init__.py +0 -0
- {metaobjects-0.12.1/tests/conformance → metaobjects-0.14.0/tests/codegen}/__init__.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/golden/extends/expected/BaseEntity.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/golden/extends/expected/Program.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/golden/extends/meta.json +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/golden/nested-array/expected/AuthorBrief.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/golden/nested-array/expected/PostBrief.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/golden/nested-array/meta.json +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/golden/scalars/expected/Metric.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/golden/scalars/expected/Report.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/golden/scalars/meta.json +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/golden/vanilla/expected/Subscriber.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/golden/vanilla/meta.json +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_abstract_conformance.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_api_docs_builder.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_api_docs_paths.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_cli_registry.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_cli_staleness_nudge.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_cli_verify_subverbs.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_constants_config.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_enum_conformance.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_extractor_generator.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_filter_allowlist_generator.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_format.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_fr010_output_codegen.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_fr019_shared_provided_conformance.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_generator.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_generator_extension_seams.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_golden.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_inherit_without_restate_gate.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_inheritance_conformance.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_instance_artifacts.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_m2m_codegen.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_output_parser_generator.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_overwrite_policy.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_payload_vo_generator.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_projection_compile.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_render_helper_conformance.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_render_helper_generator.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_router_generator.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_runner.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_template_generator.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_tph_codegen.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_trace_helper_generator.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_validation_conformance.py +0 -0
- {metaobjects-0.12.1/tests/integration → metaobjects-0.14.0/tests/conformance}/__init__.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/conformance/capabilities.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/conformance/conformance-expected-failures.json +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/conformance/conformance_adapter.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/conformance/corpus.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/conformance/expected_failures.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/conformance/fixture_discovery.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/conformance/navigator.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/conformance/test_api_docs_cross_port_conformance.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/conformance/test_conformance.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/conformance/test_extract_conformance.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/conformance/test_extract_object_verdict.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/conformance/test_fr010_loader_attrs.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/conformance/test_fr011_attrs.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/conformance/test_generator_registry_conformance.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/conformance/test_object_model_conformance.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/conformance/test_registry_conformance.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/conformance/test_runner_hardfail.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/conformance/test_spec_metamodel_embed.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/conformance/test_strict_attr_load.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/conformance/test_template_generator_conformance.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/conformance/test_yaml_conformance.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/conformance/yaml-conformance-expected-failures.json +0 -0
- {metaobjects-0.12.1/tests/render → metaobjects-0.14.0/tests/integration}/__init__.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/integration/api_contract_assertions.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/integration/api_contract_m2m_server.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/integration/api_contract_server.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/integration/generated_m2m_app.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/integration/generated_router_app.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/integration/generated_tph_app.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/integration/meta_ai_trace.yaml +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/integration/normalization.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/integration/postgres_container.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/integration/query_runner.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/integration/scenarios.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/integration/test_api_contract.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/integration/test_api_contract_generated.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/integration/test_api_contract_m2m.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/integration/test_api_contract_m2m_generated.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/integration/test_api_contract_tph_generated.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/integration/test_llm_call_trace.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/integration/test_normalization.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/integration/test_query_scenarios.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/integration/test_runtime_return_types.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/open_closed_proof_test.py +0 -0
- {metaobjects-0.12.1/tests/render/extract → metaobjects-0.14.0/tests/render}/__init__.py +0 -0
- {metaobjects-0.12.1/tests/render/prompt → metaobjects-0.14.0/tests/render/extract}/__init__.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/render/extract/test_coerce.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/render/extract/test_extract.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/render/extract/test_extract_map.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/render/extract/test_json_forgiving_reader.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/render/extract/test_locate.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/render/extract/test_model.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/render/extract/test_normalize.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/render/extract/test_strip.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/render/extract/test_xml_forgiving_reader.py +0 -0
- {metaobjects-0.12.1/tests/source → metaobjects-0.14.0/tests/render/prompt}/__init__.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/render/prompt/test_output_format_renderer.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/render/test_email_document.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/render/test_filesystem_provider.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/render/test_output_format_renderer_nested.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/render/test_output_prompt_conformance.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/render/test_render_conformance.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/render/test_render_max_chars.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/render/test_verify.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/render/test_verify_conformance.py +0 -0
- {metaobjects-0.12.1/tests/unit → metaobjects-0.14.0/tests/source}/__init__.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/source/test_fr5c_merge_attribution.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/source/test_fr5d_reference_resolution.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/source/test_fr5e_database_source_shape.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/source/test_json_path.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/source/test_semantic_diff.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/source/test_source_on_node.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/source/test_yaml_positions.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/test_api_docs_accuracy.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_agent_context_staleness.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_capabilities.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_common_attrs.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_core_types.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_effective_package.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_errors.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_field_enum.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_field_map_validation.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_field_uuid_dbcolumntype.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_fr016_source_name_and_kind_aliases.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_llm_recorder.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_loader.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_loader_bom.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_loader_class.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_merge.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_meta_attr.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_meta_data.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_meta_source.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_module_shortcuts.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_n2m_resolver.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_object_manager_uuid_coercion.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_one_primary_source.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_parser.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_parser_yaml.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_provider.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_provider_extension.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_registry.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_registry_completeness.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_registry_extend.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_registry_sealed.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_relationship_referential_actions.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_resolution_key.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_runtime_resolution_key_binding.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_serializer.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_shared_constants.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_smoke.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_sources.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_strict_child_placement.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_super_resolve.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_template_toolcall.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_template_wrong_subtype_attrs.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_validation_attr_schema.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_validation_filter_values.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_validation_origin_paths.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_validation_sort_field.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_validation_warnings.py +0 -0
- {metaobjects-0.12.1 → metaobjects-0.14.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.14.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.14.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"
|
|
@@ -3,9 +3,13 @@
|
|
|
3
3
|
Two subcommands:
|
|
4
4
|
|
|
5
5
|
metaobjects gen <metadataDir> --out <dir> [--package <pkg>]
|
|
6
|
+
[--template-spec <json> [--templates <dir>]]
|
|
6
7
|
Load metadata, run the Python codegen generator suite, and write files
|
|
7
8
|
under ``--out`` (guarded by the @generated header). Prints each written
|
|
8
|
-
file. Non-zero exit on a load error.
|
|
9
|
+
file. Non-zero exit on a load error. ``--template-spec`` appends the
|
|
10
|
+
declarative Mustache template generators (scope perEntity/perPackage/
|
|
11
|
+
perModel + outputPattern) described by a JSON spec, resolving template
|
|
12
|
+
refs under ``--templates`` (default ``templates``).
|
|
9
13
|
|
|
10
14
|
metaobjects verify <metadataDir> [--codegen] [--templates] [--db URL] ...
|
|
11
15
|
Drift gate with explicit subverbs (ADR-0021 D2 — one verify vocabulary
|
|
@@ -99,15 +103,33 @@ def _default_generators() -> list[Generator]:
|
|
|
99
103
|
]
|
|
100
104
|
|
|
101
105
|
|
|
102
|
-
def _load_root(
|
|
103
|
-
|
|
104
|
-
|
|
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)
|
|
105
117
|
if result.errors:
|
|
106
118
|
msgs = [f"{e.code}: {e.message}" for e in result.errors]
|
|
107
119
|
return None, msgs
|
|
108
120
|
return result.root, []
|
|
109
121
|
|
|
110
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
|
+
|
|
111
133
|
def _resolve_generators(names: str) -> tuple[list[Generator], list[str]]:
|
|
112
134
|
"""Resolve a comma-separated list of STABLE generator names via the registry.
|
|
113
135
|
|
|
@@ -138,11 +160,39 @@ def _parse_entities(value: str | None) -> list[str] | None:
|
|
|
138
160
|
return names or None
|
|
139
161
|
|
|
140
162
|
|
|
163
|
+
def _run_suite(
|
|
164
|
+
root: MetaData,
|
|
165
|
+
out_dir: str,
|
|
166
|
+
generators: list[Generator] | None = None,
|
|
167
|
+
entity_filter: list[str] | None = None,
|
|
168
|
+
emit_package_init: bool = True,
|
|
169
|
+
) -> list[str]:
|
|
170
|
+
"""Run a generator suite against an ALREADY-LOADED ``root`` into ``out_dir``.
|
|
171
|
+
|
|
172
|
+
Split out from :func:`_generate` so a single gen invocation can load the
|
|
173
|
+
metadata once and run multiple passes against it (the default Python suite +
|
|
174
|
+
the ``--template-spec`` pass). See :func:`_generate` for the parameter
|
|
175
|
+
semantics. Returns the written paths.
|
|
176
|
+
|
|
177
|
+
NOTE: ``run_gen``'s output-path collision guard is per-pass — a
|
|
178
|
+
``--template-spec`` ``outputPattern`` that collides with a default-suite path
|
|
179
|
+
is not flagged across the two passes (it would overwrite, since default files
|
|
180
|
+
carry the ``@generated`` header). Accepted marginal limitation (requires user
|
|
181
|
+
misconfiguration).
|
|
182
|
+
"""
|
|
183
|
+
config = GenConfig(out_dir=out_dir, emit_package_init=emit_package_init)
|
|
184
|
+
suite = generators if generators is not None else _default_generators()
|
|
185
|
+
result = run_gen(config, root, generators=suite, entity_filter=entity_filter)
|
|
186
|
+
return [path for path, status in result.files if status != "refused"]
|
|
187
|
+
|
|
188
|
+
|
|
141
189
|
def _generate(
|
|
142
190
|
metadata_dir: str,
|
|
143
191
|
out_dir: str,
|
|
144
192
|
generators: list[Generator] | None = None,
|
|
145
193
|
entity_filter: list[str] | None = None,
|
|
194
|
+
emit_package_init: bool = True,
|
|
195
|
+
strict: bool = False,
|
|
146
196
|
) -> tuple[list[str], list[str]]:
|
|
147
197
|
"""Run the generator suite into ``out_dir``.
|
|
148
198
|
|
|
@@ -150,18 +200,18 @@ def _generate(
|
|
|
150
200
|
resolved subset for ``--generators``. ``entity_filter`` (the ``--entities``
|
|
151
201
|
allowlist) restricts which entities are EMITTED while the WHOLE model is still
|
|
152
202
|
loaded, so cross-entity references (``extends`` bases, ``@objectRef`` VOs)
|
|
153
|
-
resolve — emit a subset without splitting the metadata.
|
|
203
|
+
resolve — emit a subset without splitting the metadata. ``emit_package_init``
|
|
204
|
+
is ``False`` for the format-agnostic ``--template-spec`` generators (whose
|
|
205
|
+
output is text/markdown/csv/json/xml/html, never a Python package) so no
|
|
206
|
+
spurious ``__init__.py`` is scattered through their output tree. Returns
|
|
154
207
|
``(written_paths, errors)``. On a load error, ``errors`` is non-empty and no
|
|
155
|
-
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.
|
|
156
210
|
"""
|
|
157
|
-
root, errors = _load_root(metadata_dir)
|
|
211
|
+
root, errors = _load_root(metadata_dir, strict=strict)
|
|
158
212
|
if root is None:
|
|
159
213
|
return [], errors
|
|
160
|
-
|
|
161
|
-
suite = generators if generators is not None else _default_generators()
|
|
162
|
-
result = run_gen(config, root, generators=suite, entity_filter=entity_filter)
|
|
163
|
-
written = [path for path, status in result.files if status != "refused"]
|
|
164
|
-
return written, []
|
|
214
|
+
return _run_suite(root, out_dir, generators, entity_filter, emit_package_init), []
|
|
165
215
|
|
|
166
216
|
|
|
167
217
|
#: The default api-surface subdir (the cross-port contract's ``api/python``).
|
|
@@ -293,13 +343,54 @@ def _cmd_gen(args: argparse.Namespace) -> int:
|
|
|
293
343
|
print(f" {msg}", file=sys.stderr)
|
|
294
344
|
return 1
|
|
295
345
|
|
|
346
|
+
# SP-1: declarative Mustache generators from a JSON template-spec. Their output
|
|
347
|
+
# is format-agnostic (text/markdown/csv/json/xml/html), so they run as a SECOND,
|
|
348
|
+
# SEPARATE pass with emit_package_init=False — no Python __init__.py is injected
|
|
349
|
+
# into their (possibly non-Python) output tree. Templates resolve under
|
|
350
|
+
# --templates via a FilesystemProvider.
|
|
351
|
+
spec_gens: list[Generator] = []
|
|
352
|
+
if getattr(args, "template_spec", None):
|
|
353
|
+
from metaobjects.codegen.template_codegen.template_spec import (
|
|
354
|
+
parse_template_spec,
|
|
355
|
+
template_spec_to_generators,
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
try:
|
|
359
|
+
spec = parse_template_spec(json.loads(Path(args.template_spec).read_text(encoding="utf-8")))
|
|
360
|
+
except (OSError, ValueError) as exc:
|
|
361
|
+
print(f"error: invalid --template-spec: {exc}", file=sys.stderr)
|
|
362
|
+
return 1
|
|
363
|
+
provider = FilesystemProvider(args.templates)
|
|
364
|
+
spec_gens = template_spec_to_generators(spec, provider)
|
|
365
|
+
|
|
296
366
|
entities = _parse_entities(getattr(args, "entities", None))
|
|
297
|
-
|
|
298
|
-
|
|
367
|
+
# Load the metadata ONCE; both the default suite and the (optional)
|
|
368
|
+
# --template-spec pass run against this single loaded root.
|
|
369
|
+
root, load_errors = _load_root(args.metadata_dir)
|
|
370
|
+
if root is None:
|
|
299
371
|
print("error: failed to load metadata:", file=sys.stderr)
|
|
300
|
-
for msg in
|
|
372
|
+
for msg in load_errors:
|
|
301
373
|
print(f" {msg}", file=sys.stderr)
|
|
302
374
|
return 1
|
|
375
|
+
written = _run_suite(root, args.out, generators, entities)
|
|
376
|
+
if spec_gens:
|
|
377
|
+
# The template-spec pass renders user-supplied templates: a bad ref or a
|
|
378
|
+
# wrong --templates dir raises RenderError (not OSError/ValueError, so it
|
|
379
|
+
# escapes the parse-time guard above). Report it as a clean error.
|
|
380
|
+
from metaobjects.render.renderer import RenderError
|
|
381
|
+
|
|
382
|
+
try:
|
|
383
|
+
spec_written = _run_suite(
|
|
384
|
+
root, args.out, spec_gens, entities, emit_package_init=False
|
|
385
|
+
)
|
|
386
|
+
except RenderError as exc:
|
|
387
|
+
print(
|
|
388
|
+
f"error: --template-spec render failed (check the template refs and "
|
|
389
|
+
f"--templates dir {args.templates!r}): {exc}",
|
|
390
|
+
file=sys.stderr,
|
|
391
|
+
)
|
|
392
|
+
return 1
|
|
393
|
+
written = list(written) + spec_written
|
|
303
394
|
for path in written:
|
|
304
395
|
print(path)
|
|
305
396
|
print(f"metaobjects gen: wrote {len(written)} file(s) to {args.out}")
|
|
@@ -337,13 +428,20 @@ def _verify_codegen(args: argparse.Namespace) -> int:
|
|
|
337
428
|
# Reuse the exact gen code path — regenerate into a throwaway temp dir. The
|
|
338
429
|
# --entities filter must match the `gen` that produced --out, or the diff
|
|
339
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)
|
|
340
434
|
with tempfile.TemporaryDirectory() as tmp:
|
|
341
435
|
entities = _parse_entities(getattr(args, "entities", None))
|
|
342
|
-
written, errors = _generate(
|
|
436
|
+
written, errors = _generate(
|
|
437
|
+
args.metadata_dir, tmp, None, entities, strict=strict
|
|
438
|
+
)
|
|
343
439
|
if errors:
|
|
344
440
|
print("error: failed to load metadata:", file=sys.stderr)
|
|
345
441
|
for msg in errors:
|
|
346
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)
|
|
347
445
|
return 1
|
|
348
446
|
|
|
349
447
|
expected = _relative_set(Path(tmp))
|
|
@@ -407,11 +505,16 @@ def _verify_templates(args: argparse.Namespace) -> int:
|
|
|
407
505
|
)
|
|
408
506
|
return 2
|
|
409
507
|
|
|
410
|
-
|
|
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)
|
|
411
512
|
if root is None:
|
|
412
513
|
print("error: failed to load metadata:", file=sys.stderr)
|
|
413
514
|
for msg in errors:
|
|
414
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)
|
|
415
518
|
return 1
|
|
416
519
|
|
|
417
520
|
provider = FilesystemProvider(template_root)
|
|
@@ -581,6 +684,23 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
581
684
|
action="store_true",
|
|
582
685
|
help="list registered generators (stable name + description) and exit",
|
|
583
686
|
)
|
|
687
|
+
gen.add_argument(
|
|
688
|
+
"--template-spec",
|
|
689
|
+
dest="template_spec",
|
|
690
|
+
default=None,
|
|
691
|
+
help=(
|
|
692
|
+
"path to a JSON template-spec ({\"generators\":[{name,template,scope,"
|
|
693
|
+
"outputPattern,format?}]}) — declarative Mustache generators appended "
|
|
694
|
+
"to the suite (SP-1). Templates resolve under --templates. For output "
|
|
695
|
+
"to be regenerable, the template must emit the @generated header itself "
|
|
696
|
+
"(the codegen write path refuses to overwrite files lacking it)."
|
|
697
|
+
),
|
|
698
|
+
)
|
|
699
|
+
gen.add_argument(
|
|
700
|
+
"--templates",
|
|
701
|
+
default="templates",
|
|
702
|
+
help="templates root for --template-spec (default: templates)",
|
|
703
|
+
)
|
|
584
704
|
gen.add_argument(
|
|
585
705
|
"--package",
|
|
586
706
|
default=None,
|
|
@@ -672,6 +792,15 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
672
792
|
default=None,
|
|
673
793
|
help="comma-separated entity allowlist for --codegen drift (match `gen --entities`)",
|
|
674
794
|
)
|
|
795
|
+
verify.add_argument(
|
|
796
|
+
"--lax",
|
|
797
|
+
action="store_true",
|
|
798
|
+
help=(
|
|
799
|
+
"opt out of strict-attr load (ADR-0023, #96): verify is strict by "
|
|
800
|
+
"default — an undeclared/typo'd @attr fails. --lax restores the legacy "
|
|
801
|
+
"open-attr load."
|
|
802
|
+
),
|
|
803
|
+
)
|
|
675
804
|
verify.set_defaults(func=_cmd_verify)
|
|
676
805
|
|
|
677
806
|
agent_docs = sub.add_parser(
|
|
@@ -60,3 +60,35 @@ def once_per_run(
|
|
|
60
60
|
return r if isinstance(r, list) else [r]
|
|
61
61
|
|
|
62
62
|
return run
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def per_package(
|
|
66
|
+
fn: Callable[[str, list[MetaObject], GenContext], "EmittedFile | list[EmittedFile]"],
|
|
67
|
+
) -> Callable[[GenContext], list[EmittedFile]]:
|
|
68
|
+
"""One-file-per-package convenience. Groups matched entities by effective
|
|
69
|
+
package, runs ``fn(pkg, entities, ctx)`` once per package (packages ascending,
|
|
70
|
+
entities keeping ``ctx.entities`` order). The package scope from
|
|
71
|
+
codegen-concepts §10 (object + model already exist via per_entity + per_model)."""
|
|
72
|
+
|
|
73
|
+
def run(ctx: GenContext) -> list[EmittedFile]:
|
|
74
|
+
# Local import avoids a module cycle (template_data imports nothing from here).
|
|
75
|
+
from metaobjects.codegen.template_codegen.template_data import package_of
|
|
76
|
+
|
|
77
|
+
by_pkg: dict[str, list[MetaObject]] = {}
|
|
78
|
+
for e in ctx.entities:
|
|
79
|
+
if not ctx.matches(e):
|
|
80
|
+
continue
|
|
81
|
+
by_pkg.setdefault(package_of(e), []).append(e)
|
|
82
|
+
out: list[EmittedFile] = []
|
|
83
|
+
for pkg in sorted(by_pkg):
|
|
84
|
+
r = fn(pkg, by_pkg[pkg], ctx)
|
|
85
|
+
out.extend(r if isinstance(r, list) else [r])
|
|
86
|
+
return out
|
|
87
|
+
|
|
88
|
+
return run
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
#: App-scope alias — run once over the whole model. The canonical name for the
|
|
92
|
+
#: one-shot scope (``once_per_run`` stays as a soft-deprecated alias; "run" is
|
|
93
|
+
#: ambiguous under multi-target output, ``per_model`` names the data scope).
|
|
94
|
+
per_model = once_per_run
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"""template_generator() — Python port of the TS rc.12 factory.
|
|
2
|
+
|
|
3
|
+
Walks the loaded MetaRoot -> renders shared Mustache templates via the
|
|
4
|
+
metaobjects.render engine -> returns EmittedFile[]. Same Generator Protocol
|
|
5
|
+
as the per-entity hand-coded generators; just adds the "Mustache template"
|
|
6
|
+
+ "walk that yields a data dict per output" primitives.
|
|
7
|
+
|
|
8
|
+
Design: spec/design-docs/2026-05-28-cross-port-template-generator.md.
|
|
9
|
+
Cross-port byte-equivalence verified via fixtures/render-conformance/template-generator/.
|
|
10
|
+
"""
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from dataclasses import dataclass
|
|
14
|
+
from typing import Any, Callable, Iterable, Sequence
|
|
15
|
+
|
|
16
|
+
from metaobjects.codegen.generator import EmittedFile, GenContext, Generator
|
|
17
|
+
from metaobjects.codegen.template_codegen.output_pattern import expand_output_pattern
|
|
18
|
+
from metaobjects.codegen.template_codegen.template_data import (
|
|
19
|
+
build_entity_template_data,
|
|
20
|
+
build_model_template_data,
|
|
21
|
+
build_package_template_data,
|
|
22
|
+
bare_name,
|
|
23
|
+
is_concrete,
|
|
24
|
+
package_of,
|
|
25
|
+
)
|
|
26
|
+
from metaobjects.render import escapers
|
|
27
|
+
from metaobjects.render.renderer import RenderRequest, render
|
|
28
|
+
from metaobjects.render.verify import Provider
|
|
29
|
+
|
|
30
|
+
#: The three built-in walk scopes (SP-1 §3.1).
|
|
31
|
+
SCOPES = ("perEntity", "perPackage", "perModel")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _scope_walk(scope: str, pattern: str) -> Callable[[Any], list[dict]]:
|
|
35
|
+
"""Derive a walk from a built-in scope + output pattern. Each scope yields the
|
|
36
|
+
neutral data dict for its unit and names the file via the pattern."""
|
|
37
|
+
|
|
38
|
+
def walk(root: Any) -> list[dict]:
|
|
39
|
+
from metaobjects.meta.core.object.meta_object import MetaObject
|
|
40
|
+
|
|
41
|
+
objects = [c for c in root.children() if isinstance(c, MetaObject)]
|
|
42
|
+
concrete = [o for o in objects if is_concrete(o)]
|
|
43
|
+
if scope == "perEntity":
|
|
44
|
+
return [
|
|
45
|
+
{
|
|
46
|
+
"data": build_entity_template_data(o),
|
|
47
|
+
"output_path": expand_output_pattern(pattern, bare_name(o), package_of(o)),
|
|
48
|
+
}
|
|
49
|
+
for o in concrete
|
|
50
|
+
]
|
|
51
|
+
if scope == "perPackage":
|
|
52
|
+
by_pkg: dict[str, list[Any]] = {}
|
|
53
|
+
for o in concrete:
|
|
54
|
+
by_pkg.setdefault(package_of(o), []).append(o)
|
|
55
|
+
return [
|
|
56
|
+
{
|
|
57
|
+
"data": build_package_template_data(pkg, by_pkg[pkg]),
|
|
58
|
+
"output_path": expand_output_pattern(pattern, None, pkg),
|
|
59
|
+
}
|
|
60
|
+
for pkg in sorted(by_pkg)
|
|
61
|
+
]
|
|
62
|
+
# perModel — one file over the whole model.
|
|
63
|
+
return [
|
|
64
|
+
{
|
|
65
|
+
"data": build_model_template_data(objects),
|
|
66
|
+
"output_path": expand_output_pattern(pattern, None, None),
|
|
67
|
+
}
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
return walk
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@dataclass
|
|
74
|
+
class _TemplateGenerator:
|
|
75
|
+
name: str
|
|
76
|
+
template: str
|
|
77
|
+
walk: Callable[[Any], Sequence[dict]]
|
|
78
|
+
provider: Provider
|
|
79
|
+
format: str = escapers.FORMAT_TEXT
|
|
80
|
+
|
|
81
|
+
def generate(self, ctx: GenContext) -> list[EmittedFile]:
|
|
82
|
+
walk_results: Iterable[dict] = self.walk(ctx.loaded_root)
|
|
83
|
+
files: list[EmittedFile] = []
|
|
84
|
+
for entry in walk_results:
|
|
85
|
+
content = render(
|
|
86
|
+
RenderRequest(
|
|
87
|
+
payload=entry["data"],
|
|
88
|
+
provider=self.provider,
|
|
89
|
+
ref=self.template,
|
|
90
|
+
format=self.format,
|
|
91
|
+
)
|
|
92
|
+
)
|
|
93
|
+
files.append(EmittedFile(path=entry["output_path"], content=content))
|
|
94
|
+
return files
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def template_generator(
|
|
98
|
+
*,
|
|
99
|
+
name: str,
|
|
100
|
+
template: str,
|
|
101
|
+
provider: Provider,
|
|
102
|
+
walk: Callable[[Any], Sequence[dict]] | None = None,
|
|
103
|
+
scope: str | None = None,
|
|
104
|
+
output_pattern: str | None = None,
|
|
105
|
+
format: str = escapers.FORMAT_TEXT,
|
|
106
|
+
) -> Generator:
|
|
107
|
+
"""Build a Generator that renders a Mustache template per walk entry.
|
|
108
|
+
|
|
109
|
+
Provide exactly one of ``walk`` (the power-user escape hatch) or
|
|
110
|
+
(``scope`` + ``output_pattern``) — the declarative built-in walk.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
name: kebab-case identifier; surfaces in diagnostics.
|
|
114
|
+
template: ref resolved by the provider (e.g. "custom/hello").
|
|
115
|
+
provider: ref-resolver for the template.
|
|
116
|
+
walk: callback taking the loaded MetaRoot, returning dicts shaped
|
|
117
|
+
{"data": <payload>, "output_path": <relative path>}.
|
|
118
|
+
scope: built-in walk scope (perEntity/perPackage/perModel).
|
|
119
|
+
output_pattern: output path pattern for the built-in scope walk.
|
|
120
|
+
format: render format ("text", "html", "markdown", ...). Defaults to text.
|
|
121
|
+
"""
|
|
122
|
+
has_walk = walk is not None
|
|
123
|
+
has_scope = scope is not None
|
|
124
|
+
if has_walk == has_scope:
|
|
125
|
+
raise ValueError(
|
|
126
|
+
f"template_generator({name}): provide exactly one of `walk` or "
|
|
127
|
+
"(`scope` + `output_pattern`)"
|
|
128
|
+
)
|
|
129
|
+
if has_scope and not output_pattern:
|
|
130
|
+
raise ValueError(f"template_generator({name}): `scope` requires `output_pattern`")
|
|
131
|
+
resolved_walk = walk if has_walk else _scope_walk(scope, output_pattern) # type: ignore[arg-type]
|
|
132
|
+
return _TemplateGenerator(
|
|
133
|
+
name=name,
|
|
134
|
+
template=template,
|
|
135
|
+
walk=resolved_walk,
|
|
136
|
+
provider=provider,
|
|
137
|
+
format=format,
|
|
138
|
+
)
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""Expands the cross-port output-pattern grammar (SP-1 §3.3): ``{name}``,
|
|
2
|
+
``{Name}`` (PascalCase), ``{package}`` (``::`` -> ``/``). An empty ``{package}``
|
|
3
|
+
collapses its trailing/leading slash so ``{package}/{name}`` with no package
|
|
4
|
+
yields just ``{name}``. Unknown placeholders raise.
|
|
5
|
+
|
|
6
|
+
Byte-equivalent to the TypeScript ``output-pattern.ts`` and the JVM
|
|
7
|
+
``OutputPattern``.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import re
|
|
13
|
+
|
|
14
|
+
_TOKEN = re.compile(r"\{(\w+)\}")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _pascal(s: str) -> str:
|
|
18
|
+
return "".join(w[:1].upper() + w[1:] for w in re.split(r"[^A-Za-z0-9]+", s) if w)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def expand_output_pattern(
|
|
22
|
+
pattern: str, name: str | None, package: str | None
|
|
23
|
+
) -> str:
|
|
24
|
+
"""Expand ``pattern``. ``name`` may be ``None`` (perPackage/perModel);
|
|
25
|
+
``package`` may be ``None``/empty."""
|
|
26
|
+
pkg_empty = False
|
|
27
|
+
|
|
28
|
+
def repl(m: re.Match[str]) -> str:
|
|
29
|
+
nonlocal pkg_empty
|
|
30
|
+
token = m.group(1)
|
|
31
|
+
if token == "package":
|
|
32
|
+
p = (package or "").replace("::", "/")
|
|
33
|
+
if p == "":
|
|
34
|
+
pkg_empty = True
|
|
35
|
+
return p
|
|
36
|
+
if token == "name":
|
|
37
|
+
if name is None:
|
|
38
|
+
raise ValueError(
|
|
39
|
+
f"output pattern {pattern!r} uses {{name}} but no entity name is in scope"
|
|
40
|
+
)
|
|
41
|
+
return name
|
|
42
|
+
if token == "Name":
|
|
43
|
+
if name is None:
|
|
44
|
+
raise ValueError(
|
|
45
|
+
f"output pattern {pattern!r} uses {{Name}} but no entity name is in scope"
|
|
46
|
+
)
|
|
47
|
+
return _pascal(name)
|
|
48
|
+
raise ValueError(f"unknown placeholder {{{token}}} in output pattern {pattern!r}")
|
|
49
|
+
|
|
50
|
+
out = _TOKEN.sub(repl, pattern)
|
|
51
|
+
if pkg_empty:
|
|
52
|
+
out = re.sub(r"^/+", "", out)
|
|
53
|
+
out = re.sub(r"/{2,}", "/", out)
|
|
54
|
+
return out
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"""The NEUTRAL, structural codegen template data dict (SP-1 §3.2) for Python.
|
|
2
|
+
|
|
3
|
+
Plain ``dict``/``list`` mirroring the TS ``EntityTemplateData`` keys EXACTLY — a
|
|
4
|
+
byte-gated cross-port contract (verified against the TS-produced
|
|
5
|
+
``fixtures/template-codegen-conformance/expected/``). Optional keys
|
|
6
|
+
(``maxLength``, ``enumValues``) are OMITTED when absent so a ``{{#maxLength}}``
|
|
7
|
+
section gates identically to TS.
|
|
8
|
+
|
|
9
|
+
Own-vs-effective discipline (matching the TS ``ownAttr`` semantics, per the JVM
|
|
10
|
+
review): ``is_abstract`` per-node; ``@required`` attr + ``maxLength`` read from
|
|
11
|
+
``own_attrs()`` (own-only); the required-*validator* branch effective; enum
|
|
12
|
+
``values`` from ``attrs()`` (effective); effective package derived from
|
|
13
|
+
``resolution_key()`` (matching the TS ``effectivePackage``).
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from typing import Any
|
|
19
|
+
|
|
20
|
+
from metaobjects.shared.base_types import (
|
|
21
|
+
TYPE_IDENTITY,
|
|
22
|
+
TYPE_RELATIONSHIP,
|
|
23
|
+
TYPE_VALIDATOR,
|
|
24
|
+
)
|
|
25
|
+
from metaobjects.shared.separators import PACKAGE_SEP
|
|
26
|
+
|
|
27
|
+
_SUBTYPE_ENUM = "enum"
|
|
28
|
+
_VALIDATOR_REQUIRED = "required"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def bare_name(o: Any) -> str:
|
|
32
|
+
"""The object name. Python objects already carry the bare leaf name."""
|
|
33
|
+
return o.name
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def package_of(o: Any) -> str:
|
|
37
|
+
"""Effective package, matching the TS ``effectivePackage(resolutionKey)``.
|
|
38
|
+
|
|
39
|
+
Derived from ``resolution_key()`` (own ``package`` → ``file_default_package`` →
|
|
40
|
+
nearest ancestor's ``package``) by stripping the trailing ``::<name>`` suffix —
|
|
41
|
+
so programmatically-built / plugin trees (no ``file_default_package``, package
|
|
42
|
+
only on an ancestor) resolve the ancestor package like TS does. "" when the
|
|
43
|
+
resolution key carries no package segment.
|
|
44
|
+
"""
|
|
45
|
+
key = o.resolution_key()
|
|
46
|
+
idx = key.rfind(PACKAGE_SEP)
|
|
47
|
+
return key[:idx] if idx >= 0 else ""
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def is_concrete(o: Any) -> bool:
|
|
51
|
+
return not o.is_abstract
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _is_required(f: Any) -> bool:
|
|
55
|
+
if f.own_attrs().get(_VALIDATOR_REQUIRED) is True:
|
|
56
|
+
return True
|
|
57
|
+
return any(
|
|
58
|
+
c.type == TYPE_VALIDATOR and c.sub_type == _VALIDATOR_REQUIRED
|
|
59
|
+
for c in f.children()
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _field_data(f: Any) -> dict[str, Any]:
|
|
64
|
+
d: dict[str, Any] = {
|
|
65
|
+
"name": f.name,
|
|
66
|
+
"type": f.sub_type,
|
|
67
|
+
"required": _is_required(f),
|
|
68
|
+
"isArray": bool(f.is_array),
|
|
69
|
+
}
|
|
70
|
+
own = f.own_attrs()
|
|
71
|
+
if "maxLength" in own:
|
|
72
|
+
d["maxLength"] = int(own["maxLength"])
|
|
73
|
+
if f.sub_type == _SUBTYPE_ENUM:
|
|
74
|
+
values = f.attrs().get("values")
|
|
75
|
+
if isinstance(values, list):
|
|
76
|
+
d["enumValues"] = [str(v) for v in values]
|
|
77
|
+
return d
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def build_entity_template_data(o: Any) -> dict[str, Any]:
|
|
81
|
+
fields = [_field_data(f) for f in o.fields()]
|
|
82
|
+
identities: list[dict[str, Any]] = []
|
|
83
|
+
relationships: list[dict[str, Any]] = []
|
|
84
|
+
for c in o.children():
|
|
85
|
+
if c.type == TYPE_IDENTITY:
|
|
86
|
+
id_fields = c.own_attrs().get("fields")
|
|
87
|
+
identities.append(
|
|
88
|
+
{"kind": c.sub_type, "fields": list(id_fields) if isinstance(id_fields, list) else []}
|
|
89
|
+
)
|
|
90
|
+
elif c.type == TYPE_RELATIONSHIP:
|
|
91
|
+
own = c.own_attrs()
|
|
92
|
+
relationships.append(
|
|
93
|
+
{
|
|
94
|
+
"name": c.name,
|
|
95
|
+
"cardinality": own.get("cardinality", "") or "",
|
|
96
|
+
"targetRef": own.get("objectRef", "") or "",
|
|
97
|
+
}
|
|
98
|
+
)
|
|
99
|
+
return {
|
|
100
|
+
"name": bare_name(o),
|
|
101
|
+
"package": package_of(o),
|
|
102
|
+
"fields": fields,
|
|
103
|
+
"identities": identities,
|
|
104
|
+
"relationships": relationships,
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def build_package_template_data(pkg: str, entities: list[Any]) -> dict[str, Any]:
|
|
109
|
+
return {"package": pkg, "entities": [build_entity_template_data(o) for o in entities]}
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def build_model_template_data(objects: list[Any]) -> dict[str, Any]:
|
|
113
|
+
by_pkg: dict[str, list[Any]] = {}
|
|
114
|
+
for o in objects:
|
|
115
|
+
if not is_concrete(o):
|
|
116
|
+
continue
|
|
117
|
+
by_pkg.setdefault(package_of(o), []).append(o)
|
|
118
|
+
packages = [build_package_template_data(p, by_pkg[p]) for p in sorted(by_pkg)]
|
|
119
|
+
return {"packages": packages}
|