metaobjects 0.12.0__tar.gz → 0.13.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.0 → metaobjects-0.13.0}/PKG-INFO +1 -1
- {metaobjects-0.12.0 → metaobjects-0.13.0}/pyproject.toml +1 -1
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/cli.py +98 -10
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/codegen/generator.py +32 -0
- metaobjects-0.13.0/src/metaobjects/codegen/generators/template_generator.py +138 -0
- metaobjects-0.13.0/src/metaobjects/codegen/template_codegen/output_pattern.py +54 -0
- metaobjects-0.13.0/src/metaobjects/codegen/template_codegen/template_data.py +119 -0
- metaobjects-0.13.0/src/metaobjects/codegen/template_codegen/template_spec.py +90 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/loader/validation_passes.py +637 -59
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/render/escapers.py +4 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/codegen/test_cli.py +57 -0
- metaobjects-0.13.0/tests/codegen/test_projection_compile.py +55 -0
- metaobjects-0.13.0/tests/codegen/test_template_data.py +50 -0
- metaobjects-0.13.0/tests/codegen/test_template_output_pattern.py +32 -0
- metaobjects-0.13.0/tests/codegen/test_template_scope_helpers.py +52 -0
- metaobjects-0.13.0/tests/codegen/test_template_scope_walk.py +52 -0
- metaobjects-0.13.0/tests/codegen/test_template_spec.py +60 -0
- metaobjects-0.13.0/tests/conformance/conformance-expected-failures.json +6 -0
- metaobjects-0.13.0/tests/conformance/test_template_codegen_conformance.py +51 -0
- metaobjects-0.13.0/tests/unit/__init__.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/unit/test_fr016_source_name_and_kind_aliases.py +22 -14
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/unit/test_validation_origin_paths.py +15 -6
- {metaobjects-0.12.0 → metaobjects-0.13.0}/uv.lock +1 -1
- metaobjects-0.12.0/src/metaobjects/codegen/generators/template_generator.py +0 -70
- metaobjects-0.12.0/tests/conformance/conformance-expected-failures.json +0 -25
- {metaobjects-0.12.0 → metaobjects-0.13.0}/.gitignore +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/LICENSE +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/README.md +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/hatch_build.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/__init__.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/agent_context/__init__.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/agent_context/scaffold.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/apidocs/__init__.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/apidocs/api_model.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/apidocs/builder.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/apidocs/naming.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/apidocs/paths.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/apidocs/renderer.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/attr_class_map.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/codegen/KNOWN_GAPS.md +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/codegen/__init__.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/codegen/config.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/codegen/constants.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/codegen/extract_delegate_emitter.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/codegen/extract_schema_emitter.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/codegen/format.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/codegen/fr010_field_mapping.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/codegen/generator_registry.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/codegen/generators/__init__.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/codegen/generators/entity_model.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/codegen/generators/extractor_generator.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/codegen/generators/filter_allowlist_generator.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/codegen/generators/fr019_shared_enum.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/codegen/generators/m2m_codegen.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/codegen/generators/output_parser_generator.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/codegen/generators/output_prompt_generator.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/codegen/generators/payload_vo_generator.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/codegen/generators/render_helper_generator.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/codegen/generators/router_generator.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/codegen/generators/tph_plan.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/codegen/generators/trace_helper_generator.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/codegen/instance_artifacts.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/codegen/output_format_spec_emitter.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/codegen/overwrite_policy.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/codegen/runner.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/codegen/runtime/__init__.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/codegen/runtime/filter_parser.py +0 -0
- {metaobjects-0.12.0/src/metaobjects/loader → metaobjects-0.13.0/src/metaobjects/codegen/template_codegen}/__init__.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/codegen/type_map.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/core_types.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/datatype.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/documentation/__init__.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/documentation/doc_constants.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/documentation/doc_provider.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/documentation/doc_schema.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/errors.py +0 -0
- {metaobjects-0.12.0/src/metaobjects/meta/core → metaobjects-0.13.0/src/metaobjects/loader}/__init__.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/loader/merge.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/loader/meta_data_loader.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/loader/registered_validation.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/loader/sources/__init__.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/loader/sources/directory_source.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/loader/sources/file_source.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/loader/sources/meta_data_source.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/loader/sources/uri_source.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/loader/validate_discriminator.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/loader/validate_field_readonly.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/loader/validate_source_parameter_ref.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/loader/validate_source_physical_names.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/meta/__init__.py +0 -0
- {metaobjects-0.12.0/src/metaobjects/meta/core/attr → metaobjects-0.13.0/src/metaobjects/meta/core}/__init__.py +0 -0
- {metaobjects-0.12.0/src/metaobjects/meta/core/field → metaobjects-0.13.0/src/metaobjects/meta/core/attr}/__init__.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/meta/core/attr/attr_constants.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/meta/core/attr/meta_attr.py +0 -0
- {metaobjects-0.12.0/src/metaobjects/meta/core/identity → metaobjects-0.13.0/src/metaobjects/meta/core/field}/__init__.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/meta/core/field/field_constants.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/meta/core/field/meta_field.py +0 -0
- {metaobjects-0.12.0/src/metaobjects/meta/core/object → metaobjects-0.13.0/src/metaobjects/meta/core/identity}/__init__.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/meta/core/identity/identity_constants.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/meta/core/identity/meta_identity.py +0 -0
- {metaobjects-0.12.0/src/metaobjects/meta/core/relationship → metaobjects-0.13.0/src/metaobjects/meta/core/object}/__init__.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/meta/core/object/meta_object.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/meta/core/object/meta_object_aware.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/meta/core/object/object_class_registry.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/meta/core/object/object_constants.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/meta/core/object/object_extract.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/meta/core/object/value_object.py +0 -0
- {metaobjects-0.12.0/src/metaobjects/meta/core/validator → metaobjects-0.13.0/src/metaobjects/meta/core/relationship}/__init__.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/meta/core/relationship/derive_m2m_fields.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/meta/core/relationship/meta_relationship.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/meta/core/relationship/relationship_constants.py +0 -0
- {metaobjects-0.12.0/src/metaobjects/meta/persistence → metaobjects-0.13.0/src/metaobjects/meta/core/validator}/__init__.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/meta/core/validator/validator_constants.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/meta/meta_data.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/meta/meta_root.py +0 -0
- {metaobjects-0.12.0/src/metaobjects/meta/persistence/origin → metaobjects-0.13.0/src/metaobjects/meta/persistence}/__init__.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/meta/persistence/db/__init__.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/meta/persistence/db/db_constants.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/meta/persistence/db/db_provider.py +0 -0
- {metaobjects-0.12.0/src/metaobjects/meta/persistence/source → metaobjects-0.13.0/src/metaobjects/meta/persistence/origin}/__init__.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/meta/persistence/origin/meta_origin.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/meta/persistence/origin/origin_constants.py +0 -0
- {metaobjects-0.12.0/src/metaobjects/meta/presentation → metaobjects-0.13.0/src/metaobjects/meta/persistence/source}/__init__.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/meta/persistence/source/meta_source.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/meta/persistence/source/source_constants.py +0 -0
- {metaobjects-0.12.0/src/metaobjects/meta/presentation/layout → metaobjects-0.13.0/src/metaobjects/meta/presentation}/__init__.py +0 -0
- {metaobjects-0.12.0/src/metaobjects/meta/presentation/ui → metaobjects-0.13.0/src/metaobjects/meta/presentation/layout}/__init__.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/meta/presentation/layout/layout_constants.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/meta/presentation/layout/meta_layout.py +0 -0
- {metaobjects-0.12.0/src/metaobjects/meta/presentation/view → metaobjects-0.13.0/src/metaobjects/meta/presentation/ui}/__init__.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/meta/presentation/ui/ui_provider.py +0 -0
- {metaobjects-0.12.0/src/metaobjects/meta/template → metaobjects-0.13.0/src/metaobjects/meta/presentation/view}/__init__.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/meta/presentation/view/meta_view.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/meta/presentation/view/view_constants.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/meta/provider_extends.py +0 -0
- {metaobjects-0.12.0/src/metaobjects/shared → metaobjects-0.13.0/src/metaobjects/meta/template}/__init__.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/meta/template/meta_template.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/meta/template/prompt_provider.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/meta/template/template_constants.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/naming_refs.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/parser.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/parser_yaml.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/provider.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/py.typed +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/registry.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/registry_manifest.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/render/__init__.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/render/email_document.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/render/extract/KNOWN_GAPS.md +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/render/extract/__init__.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/render/extract/coerce.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/render/extract/extract.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/render/extract/extract_map.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/render/extract/json_forgiving_reader.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/render/extract/locate.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/render/extract/normalize.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/render/extract/strip.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/render/extract/types.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/render/extract/xml_forgiving_reader.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/render/filesystem_provider.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/render/prompt/__init__.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/render/prompt/output_format_renderer.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/render/prompt/output_format_spec.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/render/prompt/prompt_field.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/render/prompt/prompt_overrides.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/render/prompt/prompt_style.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/render/renderer.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/render/verify.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/runtime/__init__.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/runtime/llm_recorder.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/runtime/n2m_resolver.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/runtime/object_manager.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/runtime/tph.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/serializer_json.py +0 -0
- {metaobjects-0.12.0/tests → metaobjects-0.13.0/src/metaobjects/shared}/__init__.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/shared/base_types.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/shared/separators.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/shared/structural.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/source/__init__.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/source/error_source.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/source/json_path.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/source/semantic_diff.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/source/yaml_positions.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/spec_metamodel/__init__.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/spec_metamodel/attr.json +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/spec_metamodel/db.json +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/spec_metamodel/documentation.json +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/spec_metamodel/field.json +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/spec_metamodel/identity.json +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/spec_metamodel/layout.json +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/spec_metamodel/object.json +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/spec_metamodel/origin.json +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/spec_metamodel/prompt.json +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/spec_metamodel/relationship.json +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/spec_metamodel/source.json +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/spec_metamodel/template.json +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/spec_metamodel/ui.json +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/spec_metamodel/validator.json +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/spec_metamodel/view.json +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/super_resolve.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/validation_types.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/src/metaobjects/yaml_desugar.py +0 -0
- {metaobjects-0.12.0/tests/codegen → metaobjects-0.13.0/tests}/__init__.py +0 -0
- {metaobjects-0.12.0/tests/conformance → metaobjects-0.13.0/tests/codegen}/__init__.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/codegen/golden/extends/expected/BaseEntity.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/codegen/golden/extends/expected/Program.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/codegen/golden/extends/meta.json +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/codegen/golden/nested-array/expected/AuthorBrief.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/codegen/golden/nested-array/expected/PostBrief.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/codegen/golden/nested-array/meta.json +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/codegen/golden/scalars/expected/Metric.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/codegen/golden/scalars/expected/Report.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/codegen/golden/scalars/meta.json +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/codegen/golden/vanilla/expected/Subscriber.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/codegen/golden/vanilla/meta.json +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/codegen/test_abstract_conformance.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/codegen/test_api_docs_builder.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/codegen/test_api_docs_paths.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/codegen/test_cli_registry.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/codegen/test_cli_staleness_nudge.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/codegen/test_cli_verify_subverbs.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/codegen/test_constants_config.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/codegen/test_entity_model.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/codegen/test_enum_conformance.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/codegen/test_extractor_generator.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/codegen/test_filter_allowlist_generator.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/codegen/test_format.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/codegen/test_fr010_output_codegen.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/codegen/test_fr019_shared_provided_conformance.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/codegen/test_generator.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/codegen/test_generator_extension_seams.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/codegen/test_golden.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/codegen/test_inherit_without_restate_gate.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/codegen/test_inheritance_conformance.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/codegen/test_instance_artifacts.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/codegen/test_m2m_codegen.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/codegen/test_output_parser_generator.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/codegen/test_overwrite_policy.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/codegen/test_payload_vo_generator.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/codegen/test_render_helper_conformance.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/codegen/test_render_helper_generator.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/codegen/test_router_generator.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/codegen/test_runner.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/codegen/test_template_generator.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/codegen/test_tph_codegen.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/codegen/test_trace_helper_generator.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/codegen/test_type_map.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/codegen/test_validation_conformance.py +0 -0
- {metaobjects-0.12.0/tests/integration → metaobjects-0.13.0/tests/conformance}/__init__.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/conformance/capabilities.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/conformance/conformance_adapter.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/conformance/corpus.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/conformance/expected_failures.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/conformance/fixture_discovery.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/conformance/navigator.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/conformance/test_api_docs_cross_port_conformance.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/conformance/test_conformance.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/conformance/test_extract_conformance.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/conformance/test_extract_object_verdict.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/conformance/test_fr010_loader_attrs.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/conformance/test_fr011_attrs.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/conformance/test_generator_registry_conformance.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/conformance/test_object_model_conformance.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/conformance/test_registry_conformance.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/conformance/test_runner_hardfail.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/conformance/test_spec_metamodel_embed.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/conformance/test_strict_attr_load.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/conformance/test_template_generator_conformance.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/conformance/test_yaml_conformance.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/conformance/yaml-conformance-expected-failures.json +0 -0
- {metaobjects-0.12.0/tests/render → metaobjects-0.13.0/tests/integration}/__init__.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/integration/api_contract_assertions.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/integration/api_contract_m2m_server.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/integration/api_contract_server.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/integration/generated_m2m_app.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/integration/generated_router_app.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/integration/generated_tph_app.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/integration/meta_ai_trace.yaml +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/integration/normalization.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/integration/postgres_container.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/integration/query_runner.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/integration/scenarios.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/integration/test_api_contract.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/integration/test_api_contract_generated.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/integration/test_api_contract_m2m.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/integration/test_api_contract_m2m_generated.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/integration/test_api_contract_tph_generated.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/integration/test_llm_call_trace.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/integration/test_normalization.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/integration/test_query_scenarios.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/integration/test_runtime_return_types.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/open_closed_proof_test.py +0 -0
- {metaobjects-0.12.0/tests/render/extract → metaobjects-0.13.0/tests/render}/__init__.py +0 -0
- {metaobjects-0.12.0/tests/render/prompt → metaobjects-0.13.0/tests/render/extract}/__init__.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/render/extract/test_coerce.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/render/extract/test_extract.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/render/extract/test_extract_map.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/render/extract/test_json_forgiving_reader.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/render/extract/test_locate.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/render/extract/test_model.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/render/extract/test_normalize.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/render/extract/test_strip.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/render/extract/test_xml_forgiving_reader.py +0 -0
- {metaobjects-0.12.0/tests/source → metaobjects-0.13.0/tests/render/prompt}/__init__.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/render/prompt/test_output_format_renderer.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/render/test_email_document.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/render/test_filesystem_provider.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/render/test_output_format_renderer_nested.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/render/test_output_prompt_conformance.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/render/test_render_conformance.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/render/test_render_max_chars.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/render/test_verify.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/render/test_verify_conformance.py +0 -0
- {metaobjects-0.12.0/tests/unit → metaobjects-0.13.0/tests/source}/__init__.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/source/test_fr5c_merge_attribution.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/source/test_fr5d_reference_resolution.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/source/test_fr5e_database_source_shape.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/source/test_json_path.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/source/test_semantic_diff.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/source/test_source_on_node.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/source/test_yaml_positions.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/test_api_docs_accuracy.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/unit/test_agent_context_staleness.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/unit/test_capabilities.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/unit/test_common_attrs.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/unit/test_core_types.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/unit/test_effective_package.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/unit/test_errors.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/unit/test_field_enum.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/unit/test_field_map_validation.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/unit/test_field_uuid_dbcolumntype.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/unit/test_llm_recorder.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/unit/test_loader.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/unit/test_loader_bom.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/unit/test_loader_class.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/unit/test_merge.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/unit/test_meta_attr.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/unit/test_meta_data.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/unit/test_meta_source.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/unit/test_module_shortcuts.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/unit/test_n2m_resolver.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/unit/test_object_manager_uuid_coercion.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/unit/test_one_primary_source.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/unit/test_parser.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/unit/test_parser_yaml.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/unit/test_provider.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/unit/test_provider_extension.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/unit/test_registry.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/unit/test_registry_completeness.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/unit/test_registry_extend.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/unit/test_registry_sealed.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/unit/test_relationship_referential_actions.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/unit/test_resolution_key.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/unit/test_runtime_resolution_key_binding.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/unit/test_serializer.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/unit/test_shared_constants.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/unit/test_smoke.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/unit/test_sources.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/unit/test_strict_child_placement.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/unit/test_super_resolve.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/unit/test_template_toolcall.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/unit/test_template_wrong_subtype_attrs.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/unit/test_validation_attr_schema.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/unit/test_validation_filter_values.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/unit/test_validation_sort_field.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.0}/tests/unit/test_validation_warnings.py +0 -0
- {metaobjects-0.12.0 → metaobjects-0.13.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.13.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.13.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
|
|
@@ -138,11 +142,38 @@ def _parse_entities(value: str | None) -> list[str] | None:
|
|
|
138
142
|
return names or None
|
|
139
143
|
|
|
140
144
|
|
|
145
|
+
def _run_suite(
|
|
146
|
+
root: MetaData,
|
|
147
|
+
out_dir: str,
|
|
148
|
+
generators: list[Generator] | None = None,
|
|
149
|
+
entity_filter: list[str] | None = None,
|
|
150
|
+
emit_package_init: bool = True,
|
|
151
|
+
) -> list[str]:
|
|
152
|
+
"""Run a generator suite against an ALREADY-LOADED ``root`` into ``out_dir``.
|
|
153
|
+
|
|
154
|
+
Split out from :func:`_generate` so a single gen invocation can load the
|
|
155
|
+
metadata once and run multiple passes against it (the default Python suite +
|
|
156
|
+
the ``--template-spec`` pass). See :func:`_generate` for the parameter
|
|
157
|
+
semantics. Returns the written paths.
|
|
158
|
+
|
|
159
|
+
NOTE: ``run_gen``'s output-path collision guard is per-pass — a
|
|
160
|
+
``--template-spec`` ``outputPattern`` that collides with a default-suite path
|
|
161
|
+
is not flagged across the two passes (it would overwrite, since default files
|
|
162
|
+
carry the ``@generated`` header). Accepted marginal limitation (requires user
|
|
163
|
+
misconfiguration).
|
|
164
|
+
"""
|
|
165
|
+
config = GenConfig(out_dir=out_dir, emit_package_init=emit_package_init)
|
|
166
|
+
suite = generators if generators is not None else _default_generators()
|
|
167
|
+
result = run_gen(config, root, generators=suite, entity_filter=entity_filter)
|
|
168
|
+
return [path for path, status in result.files if status != "refused"]
|
|
169
|
+
|
|
170
|
+
|
|
141
171
|
def _generate(
|
|
142
172
|
metadata_dir: str,
|
|
143
173
|
out_dir: str,
|
|
144
174
|
generators: list[Generator] | None = None,
|
|
145
175
|
entity_filter: list[str] | None = None,
|
|
176
|
+
emit_package_init: bool = True,
|
|
146
177
|
) -> tuple[list[str], list[str]]:
|
|
147
178
|
"""Run the generator suite into ``out_dir``.
|
|
148
179
|
|
|
@@ -150,18 +181,17 @@ def _generate(
|
|
|
150
181
|
resolved subset for ``--generators``. ``entity_filter`` (the ``--entities``
|
|
151
182
|
allowlist) restricts which entities are EMITTED while the WHOLE model is still
|
|
152
183
|
loaded, so cross-entity references (``extends`` bases, ``@objectRef`` VOs)
|
|
153
|
-
resolve — emit a subset without splitting the metadata.
|
|
184
|
+
resolve — emit a subset without splitting the metadata. ``emit_package_init``
|
|
185
|
+
is ``False`` for the format-agnostic ``--template-spec`` generators (whose
|
|
186
|
+
output is text/markdown/csv/json/xml/html, never a Python package) so no
|
|
187
|
+
spurious ``__init__.py`` is scattered through their output tree. Returns
|
|
154
188
|
``(written_paths, errors)``. On a load error, ``errors`` is non-empty and no
|
|
155
189
|
files are written.
|
|
156
190
|
"""
|
|
157
191
|
root, errors = _load_root(metadata_dir)
|
|
158
192
|
if root is None:
|
|
159
193
|
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, []
|
|
194
|
+
return _run_suite(root, out_dir, generators, entity_filter, emit_package_init), []
|
|
165
195
|
|
|
166
196
|
|
|
167
197
|
#: The default api-surface subdir (the cross-port contract's ``api/python``).
|
|
@@ -293,13 +323,54 @@ def _cmd_gen(args: argparse.Namespace) -> int:
|
|
|
293
323
|
print(f" {msg}", file=sys.stderr)
|
|
294
324
|
return 1
|
|
295
325
|
|
|
326
|
+
# SP-1: declarative Mustache generators from a JSON template-spec. Their output
|
|
327
|
+
# is format-agnostic (text/markdown/csv/json/xml/html), so they run as a SECOND,
|
|
328
|
+
# SEPARATE pass with emit_package_init=False — no Python __init__.py is injected
|
|
329
|
+
# into their (possibly non-Python) output tree. Templates resolve under
|
|
330
|
+
# --templates via a FilesystemProvider.
|
|
331
|
+
spec_gens: list[Generator] = []
|
|
332
|
+
if getattr(args, "template_spec", None):
|
|
333
|
+
from metaobjects.codegen.template_codegen.template_spec import (
|
|
334
|
+
parse_template_spec,
|
|
335
|
+
template_spec_to_generators,
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
try:
|
|
339
|
+
spec = parse_template_spec(json.loads(Path(args.template_spec).read_text(encoding="utf-8")))
|
|
340
|
+
except (OSError, ValueError) as exc:
|
|
341
|
+
print(f"error: invalid --template-spec: {exc}", file=sys.stderr)
|
|
342
|
+
return 1
|
|
343
|
+
provider = FilesystemProvider(args.templates)
|
|
344
|
+
spec_gens = template_spec_to_generators(spec, provider)
|
|
345
|
+
|
|
296
346
|
entities = _parse_entities(getattr(args, "entities", None))
|
|
297
|
-
|
|
298
|
-
|
|
347
|
+
# Load the metadata ONCE; both the default suite and the (optional)
|
|
348
|
+
# --template-spec pass run against this single loaded root.
|
|
349
|
+
root, load_errors = _load_root(args.metadata_dir)
|
|
350
|
+
if root is None:
|
|
299
351
|
print("error: failed to load metadata:", file=sys.stderr)
|
|
300
|
-
for msg in
|
|
352
|
+
for msg in load_errors:
|
|
301
353
|
print(f" {msg}", file=sys.stderr)
|
|
302
354
|
return 1
|
|
355
|
+
written = _run_suite(root, args.out, generators, entities)
|
|
356
|
+
if spec_gens:
|
|
357
|
+
# The template-spec pass renders user-supplied templates: a bad ref or a
|
|
358
|
+
# wrong --templates dir raises RenderError (not OSError/ValueError, so it
|
|
359
|
+
# escapes the parse-time guard above). Report it as a clean error.
|
|
360
|
+
from metaobjects.render.renderer import RenderError
|
|
361
|
+
|
|
362
|
+
try:
|
|
363
|
+
spec_written = _run_suite(
|
|
364
|
+
root, args.out, spec_gens, entities, emit_package_init=False
|
|
365
|
+
)
|
|
366
|
+
except RenderError as exc:
|
|
367
|
+
print(
|
|
368
|
+
f"error: --template-spec render failed (check the template refs and "
|
|
369
|
+
f"--templates dir {args.templates!r}): {exc}",
|
|
370
|
+
file=sys.stderr,
|
|
371
|
+
)
|
|
372
|
+
return 1
|
|
373
|
+
written = list(written) + spec_written
|
|
303
374
|
for path in written:
|
|
304
375
|
print(path)
|
|
305
376
|
print(f"metaobjects gen: wrote {len(written)} file(s) to {args.out}")
|
|
@@ -581,6 +652,23 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
581
652
|
action="store_true",
|
|
582
653
|
help="list registered generators (stable name + description) and exit",
|
|
583
654
|
)
|
|
655
|
+
gen.add_argument(
|
|
656
|
+
"--template-spec",
|
|
657
|
+
dest="template_spec",
|
|
658
|
+
default=None,
|
|
659
|
+
help=(
|
|
660
|
+
"path to a JSON template-spec ({\"generators\":[{name,template,scope,"
|
|
661
|
+
"outputPattern,format?}]}) — declarative Mustache generators appended "
|
|
662
|
+
"to the suite (SP-1). Templates resolve under --templates. For output "
|
|
663
|
+
"to be regenerable, the template must emit the @generated header itself "
|
|
664
|
+
"(the codegen write path refuses to overwrite files lacking it)."
|
|
665
|
+
),
|
|
666
|
+
)
|
|
667
|
+
gen.add_argument(
|
|
668
|
+
"--templates",
|
|
669
|
+
default="templates",
|
|
670
|
+
help="templates root for --template-spec (default: templates)",
|
|
671
|
+
)
|
|
584
672
|
gen.add_argument(
|
|
585
673
|
"--package",
|
|
586
674
|
default=None,
|
|
@@ -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}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""The declarative JSON template-spec the CLI ports (Python/C#) consume.
|
|
2
|
+
|
|
3
|
+
The JSON shape is the cross-port contract (SP-1 §4) — a JSON Schema sits beside
|
|
4
|
+
the TS port at codegen-ts/src/template-codegen/template-spec.schema.json. This
|
|
5
|
+
module validates + maps it to runnable Generators.
|
|
6
|
+
|
|
7
|
+
Regenerability note: ``--template-spec`` output flows through the standard codegen
|
|
8
|
+
write path, which refuses to overwrite a file that lacks the ``@generated`` marker
|
|
9
|
+
(the hand-edit guard the rest of codegen and the TS port share). For a template's
|
|
10
|
+
output to be safely regenerable, the **template author** must emit the
|
|
11
|
+
``@generated`` header in the template body itself — the same author responsibility
|
|
12
|
+
the rest of the codegen pipeline relies on.
|
|
13
|
+
|
|
14
|
+
The Python port has no output-*target* concept (the codegen pipeline writes every
|
|
15
|
+
``EmittedFile`` relative to a single ``out_dir``). A per-generator ``target`` field
|
|
16
|
+
is therefore REJECTED rather than silently dropped, so a cross-port spec authored
|
|
17
|
+
with ``target`` fails loudly here instead of producing a different layout than TS.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
from typing import Any
|
|
23
|
+
|
|
24
|
+
from metaobjects.codegen.generator import Generator
|
|
25
|
+
from metaobjects.codegen.generators.template_generator import SCOPES, template_generator
|
|
26
|
+
from metaobjects.render import escapers
|
|
27
|
+
from metaobjects.render.verify import Provider
|
|
28
|
+
|
|
29
|
+
_FORMATS = escapers.FORMATS
|
|
30
|
+
|
|
31
|
+
_REQUIRED_STR = ("name", "template", "scope", "outputPattern")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def parse_template_spec(obj: object) -> dict[str, Any]:
|
|
35
|
+
"""Validate + return a normalized ``{"generators": [...]}`` dict. Raises
|
|
36
|
+
``ValueError`` on any shape violation."""
|
|
37
|
+
if not isinstance(obj, dict) or not isinstance(obj.get("generators"), list):
|
|
38
|
+
raise ValueError("template-spec: expected an object with a `generators` array")
|
|
39
|
+
generators: list[dict[str, Any]] = []
|
|
40
|
+
for i, raw in enumerate(obj["generators"]):
|
|
41
|
+
if not isinstance(raw, dict):
|
|
42
|
+
raise ValueError(f"template-spec generators[{i}]: expected an object")
|
|
43
|
+
for key in _REQUIRED_STR:
|
|
44
|
+
if not isinstance(raw.get(key), str) or raw[key] == "":
|
|
45
|
+
raise ValueError(
|
|
46
|
+
f"template-spec generators[{i}]: missing or empty required string {key!r}"
|
|
47
|
+
)
|
|
48
|
+
if raw["scope"] not in SCOPES:
|
|
49
|
+
raise ValueError(
|
|
50
|
+
f"template-spec generators[{i}]: scope must be one of "
|
|
51
|
+
f"{' | '.join(SCOPES)}, got {raw['scope']!r}"
|
|
52
|
+
)
|
|
53
|
+
entry: dict[str, Any] = {
|
|
54
|
+
"name": raw["name"],
|
|
55
|
+
"template": raw["template"],
|
|
56
|
+
"scope": raw["scope"],
|
|
57
|
+
"outputPattern": raw["outputPattern"],
|
|
58
|
+
}
|
|
59
|
+
if "format" in raw:
|
|
60
|
+
if raw["format"] not in _FORMATS:
|
|
61
|
+
raise ValueError(
|
|
62
|
+
f"template-spec generators[{i}]: format must be one of "
|
|
63
|
+
f"{' | '.join(_FORMATS)}, got {raw['format']!r}"
|
|
64
|
+
)
|
|
65
|
+
entry["format"] = raw["format"]
|
|
66
|
+
if "target" in raw:
|
|
67
|
+
raise ValueError(
|
|
68
|
+
f"template-spec generators[{i}]: target is not supported by the "
|
|
69
|
+
"Python port — it has no output-target concept"
|
|
70
|
+
)
|
|
71
|
+
generators.append(entry)
|
|
72
|
+
return {"generators": generators}
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def template_spec_to_generators(spec: dict[str, Any], provider: Provider) -> list[Generator]:
|
|
76
|
+
"""Map a parsed spec into runnable Generators (one per entry). The caller
|
|
77
|
+
supplies the ``provider`` (the CLI builds a ``FilesystemProvider``)."""
|
|
78
|
+
out: list[Generator] = []
|
|
79
|
+
for e in spec["generators"]:
|
|
80
|
+
out.append(
|
|
81
|
+
template_generator(
|
|
82
|
+
name=e["name"],
|
|
83
|
+
template=e["template"],
|
|
84
|
+
scope=e["scope"],
|
|
85
|
+
output_pattern=e["outputPattern"],
|
|
86
|
+
provider=provider,
|
|
87
|
+
format=e.get("format", escapers.FORMAT_TEXT),
|
|
88
|
+
)
|
|
89
|
+
)
|
|
90
|
+
return out
|