metaobjects 0.15.13__tar.gz → 0.17.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.15.13 → metaobjects-0.17.0}/PKG-INFO +1 -1
- {metaobjects-0.15.13 → metaobjects-0.17.0}/pyproject.toml +1 -1
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/codegen/KNOWN_GAPS.md +8 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/codegen/generators/entity_model.py +308 -32
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/codegen/generators/filter_allowlist_generator.py +6 -1
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/codegen/generators/router_generator.py +100 -10
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/core_types.py +72 -7
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/errors.py +21 -0
- metaobjects-0.17.0/src/metaobjects/loader/validate_source_escapes.py +205 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/loader/validation_passes.py +536 -9
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/core/attr/attr_constants.py +5 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/core/attr/meta_attr.py +12 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/core/field/meta_field.py +15 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/core/object/meta_object.py +22 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/core/object/object_constants.py +6 -0
- metaobjects-0.17.0/src/metaobjects/meta/persistence/origin/origin_constants.py +49 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/persistence/source/meta_source.py +20 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/persistence/source/source_constants.py +10 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/render/extract/extract.py +6 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/render/extract/types.py +31 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/runtime/object_manager.py +176 -11
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/spec_metamodel/attr.json +6 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/spec_metamodel/db.json +2 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/spec_metamodel/object.json +4 -1
- metaobjects-0.17.0/src/metaobjects/spec_metamodel/origin.json +67 -0
- metaobjects-0.17.0/tests/codegen/golden/extends/expected/BaseEntity.py +23 -0
- metaobjects-0.17.0/tests/codegen/golden/extends/expected/Program.py +27 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/golden/nested-array/expected/AuthorBrief.py +2 -2
- metaobjects-0.17.0/tests/codegen/golden/vanilla/expected/Subscriber.py +26 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/test_entity_model.py +128 -9
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/test_extends_abstract_field_inheritance.py +6 -3
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/test_inherit_without_restate_gate.py +8 -3
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/test_inheritance_conformance.py +6 -2
- metaobjects-0.17.0/tests/codegen/test_write_through.py +112 -0
- metaobjects-0.17.0/tests/integration/api_contract_jsonb_server.py +317 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/integration/api_contract_server.py +29 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/integration/generated_jsonb_app.py +25 -6
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/integration/generated_m2m_app.py +7 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/integration/generated_router_app.py +6 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/integration/generated_tph_app.py +7 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/integration/test_generated_router_uuid_pk.py +3 -0
- metaobjects-0.17.0/tests/unit/test_object_manager_autoset_stamping.py +239 -0
- metaobjects-0.17.0/tests/unit/test_object_manager_write_through.py +167 -0
- metaobjects-0.17.0/tests/unit/test_source_escapes.py +336 -0
- metaobjects-0.17.0/tests/unit/test_validation_origin_195.py +312 -0
- metaobjects-0.15.13/src/metaobjects/meta/persistence/origin/origin_constants.py +0 -28
- metaobjects-0.15.13/src/metaobjects/spec_metamodel/origin.json +0 -43
- metaobjects-0.15.13/tests/codegen/golden/extends/expected/BaseEntity.py +0 -11
- metaobjects-0.15.13/tests/codegen/golden/extends/expected/Program.py +0 -11
- metaobjects-0.15.13/tests/codegen/golden/vanilla/expected/Subscriber.py +0 -12
- metaobjects-0.15.13/tests/integration/api_contract_jsonb_server.py +0 -160
- {metaobjects-0.15.13 → metaobjects-0.17.0}/.gitignore +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/LICENSE +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/README.md +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/hatch_build.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/__init__.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/agent_context/__init__.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/agent_context/scaffold.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/apidocs/__init__.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/apidocs/api_model.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/apidocs/builder.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/apidocs/naming.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/apidocs/paths.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/apidocs/renderer.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/attr_class_map.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/cli.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/codegen/__init__.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/codegen/config.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/codegen/constants.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/codegen/extract_delegate_emitter.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/codegen/extract_schema_emitter.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/codegen/format.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/codegen/fr010_field_mapping.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/codegen/generator.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/codegen/generator_registry.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/codegen/generators/__init__.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/codegen/generators/extractor_generator.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/codegen/generators/fr019_shared_enum.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/codegen/generators/m2m_codegen.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/codegen/generators/output_parser_generator.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/codegen/generators/output_prompt_generator.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/codegen/generators/payload_vo_generator.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/codegen/generators/render_helper_generator.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/codegen/generators/template_generator.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/codegen/generators/tph_plan.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/codegen/generators/trace_helper_generator.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/codegen/instance_artifacts.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/codegen/output_format_spec_emitter.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/codegen/overwrite_policy.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/codegen/runner.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/codegen/runtime/__init__.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/codegen/runtime/filter_parser.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/codegen/template_codegen/__init__.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/codegen/template_codegen/output_pattern.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/codegen/template_codegen/template_data.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/codegen/template_codegen/template_spec.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/codegen/type_map.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/datatype.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/documentation/__init__.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/documentation/doc_constants.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/documentation/doc_provider.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/documentation/doc_schema.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/loader/__init__.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/loader/merge.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/loader/meta_data_loader.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/loader/registered_validation.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/loader/sources/__init__.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/loader/sources/directory_source.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/loader/sources/file_source.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/loader/sources/meta_data_source.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/loader/sources/uri_source.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/loader/validate_discriminator.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/loader/validate_field_readonly.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/loader/validate_source_parameter_ref.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/loader/validate_source_physical_names.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/__init__.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/core/__init__.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/core/attr/__init__.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/core/field/__init__.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/core/field/field_constants.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/core/identity/__init__.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/core/identity/identity_constants.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/core/identity/meta_identity.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/core/index/__init__.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/core/index/index_constants.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/core/index/meta_index.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/core/object/__init__.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/core/object/meta_object_aware.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/core/object/object_class_registry.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/core/object/object_extract.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/core/object/value_object.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/core/relationship/__init__.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/core/relationship/derive_m2m_fields.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/core/relationship/meta_relationship.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/core/relationship/relationship_constants.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/core/validator/__init__.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/core/validator/validator_constants.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/meta_data.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/meta_root.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/persistence/__init__.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/persistence/db/__init__.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/persistence/db/db_constants.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/persistence/db/db_provider.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/persistence/origin/__init__.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/persistence/origin/meta_origin.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/persistence/source/__init__.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/presentation/__init__.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/presentation/layout/__init__.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/presentation/layout/layout_constants.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/presentation/layout/meta_layout.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/presentation/ui/__init__.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/presentation/ui/ui_provider.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/presentation/view/__init__.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/presentation/view/meta_view.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/presentation/view/view_constants.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/provider_extends.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/template/__init__.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/template/meta_template.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/template/prompt_provider.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/meta/template/template_constants.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/naming_refs.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/parser.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/parser_yaml.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/provider.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/py.typed +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/registry.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/registry_manifest.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/render/__init__.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/render/email_document.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/render/escapers.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/render/extract/KNOWN_GAPS.md +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/render/extract/__init__.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/render/extract/coerce.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/render/extract/extract_map.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/render/extract/json_forgiving_reader.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/render/extract/locate.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/render/extract/normalize.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/render/extract/strip.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/render/extract/xml_forgiving_reader.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/render/filesystem_provider.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/render/prompt/__init__.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/render/prompt/output_format_renderer.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/render/prompt/output_format_spec.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/render/prompt/prompt_field.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/render/prompt/prompt_overrides.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/render/prompt/prompt_style.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/render/renderer.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/render/verify.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/runtime/__init__.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/runtime/llm_recorder.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/runtime/n2m_resolver.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/runtime/tph.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/serializer_json.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/shared/__init__.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/shared/base_types.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/shared/separators.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/shared/structural.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/source/__init__.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/source/error_source.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/source/json_path.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/source/semantic_diff.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/source/yaml_positions.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/spec_metamodel/__init__.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/spec_metamodel/documentation.json +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/spec_metamodel/field.json +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/spec_metamodel/identity.json +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/spec_metamodel/index.json +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/spec_metamodel/layout.json +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/spec_metamodel/prompt.json +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/spec_metamodel/relationship.json +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/spec_metamodel/source.json +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/spec_metamodel/template.json +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/spec_metamodel/ui.json +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/spec_metamodel/validator.json +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/spec_metamodel/view.json +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/super_resolve.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/validation_types.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/yaml_desugar.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/__init__.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/__init__.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/golden/extends/meta.json +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/golden/nested-array/expected/PostBrief.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/golden/nested-array/meta.json +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/golden/scalars/expected/Metric.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/golden/scalars/expected/Report.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/golden/scalars/meta.json +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/golden/vanilla/meta.json +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/test_abstract_conformance.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/test_api_docs_builder.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/test_api_docs_paths.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/test_cli.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/test_cli_providers.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/test_cli_registry.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/test_cli_staleness_nudge.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/test_cli_verify_strict.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/test_cli_verify_subverbs.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/test_constants_config.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/test_enum_conformance.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/test_extractor_generator.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/test_filter_allowlist_generator.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/test_format.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/test_fr010_nested_object_prompt.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/test_fr010_output_codegen.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/test_fr019_shared_provided_conformance.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/test_generator.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/test_generator_extension_seams.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/test_golden.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/test_instance_artifacts.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/test_m2m_codegen.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/test_output_parser_generator.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/test_overwrite_policy.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/test_payload_vo_generator.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/test_projection_compile.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/test_render_helper_conformance.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/test_render_helper_generator.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/test_reverse_finders.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/test_router_generator.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/test_runner.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/test_template_data.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/test_template_generator.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/test_template_output_pattern.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/test_template_scope_helpers.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/test_template_scope_walk.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/test_template_spec.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/test_tph_codegen.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/test_trace_helper_generator.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/test_type_map.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/codegen/test_validation_conformance.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/conformance/__init__.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/conformance/capabilities.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/conformance/conformance-expected-failures.json +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/conformance/conformance_adapter.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/conformance/corpus.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/conformance/expected_failures.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/conformance/fixture_discovery.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/conformance/navigator.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/conformance/test_api_docs_cross_port_conformance.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/conformance/test_conformance.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/conformance/test_extract_conformance.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/conformance/test_extract_object_verdict.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/conformance/test_fr010_loader_attrs.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/conformance/test_fr011_attrs.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/conformance/test_generator_registry_conformance.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/conformance/test_object_model_conformance.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/conformance/test_provider_composition_conformance.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/conformance/test_registry_conformance.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/conformance/test_runner_hardfail.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/conformance/test_spec_metamodel_embed.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/conformance/test_strict_attr_load.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/conformance/test_template_codegen_conformance.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/conformance/test_template_generator_conformance.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/conformance/test_yaml_conformance.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/conformance/yaml-conformance-expected-failures.json +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/integration/__init__.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/integration/api_contract_assertions.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/integration/api_contract_m2m_server.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/integration/meta_ai_trace.yaml +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/integration/normalization.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/integration/postgres_container.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/integration/query_runner.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/integration/scenarios.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/integration/test_api_contract.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/integration/test_api_contract_generated.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/integration/test_api_contract_jsonb.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/integration/test_api_contract_m2m.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/integration/test_api_contract_m2m_generated.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/integration/test_api_contract_tph_generated.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/integration/test_llm_call_trace.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/integration/test_normalization.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/integration/test_query_scenarios.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/integration/test_runtime_return_types.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/open_closed_proof_test.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/render/__init__.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/render/extract/__init__.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/render/extract/test_coerce.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/render/extract/test_extract.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/render/extract/test_extract_map.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/render/extract/test_json_forgiving_reader.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/render/extract/test_locate.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/render/extract/test_model.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/render/extract/test_normalize.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/render/extract/test_strip.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/render/extract/test_xml_forgiving_reader.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/render/prompt/__init__.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/render/prompt/test_output_format_renderer.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/render/test_email_document.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/render/test_filesystem_provider.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/render/test_output_format_renderer_nested.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/render/test_output_prompt_conformance.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/render/test_render_conformance.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/render/test_render_max_chars.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/render/test_verify.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/render/test_verify_conformance.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/source/__init__.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/source/test_fr5c_merge_attribution.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/source/test_fr5d_reference_resolution.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/source/test_fr5e_database_source_shape.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/source/test_json_path.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/source/test_semantic_diff.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/source/test_source_on_node.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/source/test_yaml_positions.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/test_api_docs_accuracy.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/__init__.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_agent_context_staleness.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_capabilities.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_common_attrs.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_core_types.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_effective_package.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_errors.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_field_enum.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_field_map_validation.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_field_uuid_dbcolumntype.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_fr016_source_name_and_kind_aliases.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_index_lookup.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_llm_recorder.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_loader.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_loader_bom.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_loader_class.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_merge.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_meta_attr.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_meta_data.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_meta_source.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_module_shortcuts.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_n2m_resolver.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_n2m_resolver_inherited.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_object_manager_uuid_coercion.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_one_primary_source.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_parser.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_parser_yaml.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_provider.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_provider_extension.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_registry.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_registry_completeness.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_registry_extend.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_registry_sealed.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_relationship_referential_actions.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_resolution_key.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_runtime_resolution_key_binding.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_serializer.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_shared_constants.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_smoke.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_sources.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_strict_child_placement.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_super_resolve.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_template_toolcall.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_template_wrong_subtype_attrs.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_validation_attr_schema.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_validation_filter_values.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_validation_origin_paths.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_validation_sort_field.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_validation_warnings.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_write_coercion_arrays.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/tests/unit/test_yaml_desugar.py +0 -0
- {metaobjects-0.15.13 → metaobjects-0.17.0}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: metaobjects
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.17.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.17.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"
|
|
@@ -32,6 +32,14 @@ The generated router takes `dto: dict[str, Any]` for `POST` / `PATCH` /
|
|
|
32
32
|
`PUT` request bodies and returns `Any` for responses. The repository
|
|
33
33
|
`Protocol` likewise uses `Any` for the row type.
|
|
34
34
|
|
|
35
|
+
**FR-036 update:** the router now DOES import the generated Pydantic models
|
|
36
|
+
(`from .<Entity> import <Entity>, <Entity>Patch`) and constructs them to enforce
|
|
37
|
+
field constraints over HTTP — POST validates `<Entity>(**dto)`, PATCH validates
|
|
38
|
+
the all-optional `<Entity>Patch(**dto)` (present values only) → `400 {"error":"validation"}`.
|
|
39
|
+
The repository seam still receives the original `dict` (preserving the FR-035
|
|
40
|
+
present-key tristate + wire values), so the strong-typing simplification below
|
|
41
|
+
remains for the repo/response types; only the validation import was added.
|
|
42
|
+
|
|
35
43
|
This keeps the router module decoupled from sibling generated files
|
|
36
44
|
(the entity-model generator emits one `@dataclass`/Pydantic model per
|
|
37
45
|
entity in a separate file — wiring router→entity-model would require
|
{metaobjects-0.15.13 → metaobjects-0.17.0}/src/metaobjects/codegen/generators/entity_model.py
RENAMED
|
@@ -5,9 +5,23 @@ import re
|
|
|
5
5
|
|
|
6
6
|
from metaobjects.meta.core.field.meta_field import MetaField
|
|
7
7
|
from metaobjects.meta.core.object.meta_object import MetaObject
|
|
8
|
+
from metaobjects.meta.core.object.object_constants import OBJECT_SUBTYPE_ENTITY
|
|
8
9
|
from metaobjects.meta.core.field import field_constants as fc
|
|
10
|
+
from metaobjects.meta.core.identity.identity_constants import (
|
|
11
|
+
GENERATION_INCREMENT,
|
|
12
|
+
GENERATION_UUID,
|
|
13
|
+
IDENTITY_ATTR_FIELDS,
|
|
14
|
+
IDENTITY_ATTR_GENERATION,
|
|
15
|
+
)
|
|
9
16
|
from metaobjects.meta.core.validator import validator_constants as vc
|
|
10
|
-
from metaobjects.
|
|
17
|
+
from metaobjects.meta.persistence.origin.origin_constants import (
|
|
18
|
+
AGG_ALL,
|
|
19
|
+
AGG_ANY,
|
|
20
|
+
AGG_COLLECT,
|
|
21
|
+
ORIGIN_ATTR_AGG,
|
|
22
|
+
ORIGIN_SUBTYPE_AGGREGATE,
|
|
23
|
+
)
|
|
24
|
+
from metaobjects.shared.base_types import TYPE_ORIGIN, TYPE_VALIDATOR
|
|
11
25
|
from metaobjects.shared.separators import PACKAGE_SEP
|
|
12
26
|
from metaobjects.codegen.config import GenConfig
|
|
13
27
|
from metaobjects.codegen.constants import generated_header
|
|
@@ -98,7 +112,10 @@ def _validator_constraints(field: MetaField) -> dict[str, object]:
|
|
|
98
112
|
max_len = _first_attr(field, vc.VALIDATOR_SUBTYPE_LENGTH, vc.VALIDATOR_ATTR_MAX)
|
|
99
113
|
field_max = field.attrs().get(fc.FIELD_ATTR_MAX_LENGTH)
|
|
100
114
|
if _is_int(field_max):
|
|
101
|
-
|
|
115
|
+
# FR-036 A3 — strictest-wins: when BOTH a validator.length @max and a field
|
|
116
|
+
# @maxLength cap a string, the smaller bound governs (never the field attr
|
|
117
|
+
# unconditionally, which could loosen a tighter validator).
|
|
118
|
+
max_len = field_max if max_len is None else min(max_len, field_max)
|
|
102
119
|
if min_len is not None:
|
|
103
120
|
kwargs["min_length"] = min_len
|
|
104
121
|
if max_len is not None:
|
|
@@ -127,7 +144,11 @@ def _validator_constraints(field: MetaField) -> dict[str, object]:
|
|
|
127
144
|
# located via the resolving field.children() in _validators().
|
|
128
145
|
pattern = v.attr(vc.VALIDATOR_ATTR_PATTERN)
|
|
129
146
|
if isinstance(pattern, str):
|
|
130
|
-
|
|
147
|
+
# FR-036 Pin 2 — a validator.regex is a FULL match. Pydantic v2 `pattern=`
|
|
148
|
+
# is search/partial (un-anchored), so wrap the authored expression in an
|
|
149
|
+
# anchored non-capturing group so alternation still spans the whole value
|
|
150
|
+
# (a redundant anchor on an already-anchored pattern matches identically).
|
|
151
|
+
kwargs["pattern"] = f"^(?:{pattern})$"
|
|
131
152
|
break
|
|
132
153
|
|
|
133
154
|
# ADR-0036/0037 Wave 3 — @stringFormat: hostname -> a codegen-owned canonical
|
|
@@ -137,60 +158,103 @@ def _validator_constraints(field: MetaField) -> dict[str, object]:
|
|
|
137
158
|
if field.attrs().get(fc.FIELD_ATTR_STRING_FORMAT) == fc.STRING_FORMAT_HOSTNAME:
|
|
138
159
|
kwargs["pattern"] = _HOSTNAME_REGEX
|
|
139
160
|
|
|
161
|
+
# FR-036 Pin 1 — a @required non-array string is non-empty: reject null / "" but
|
|
162
|
+
# ACCEPT whitespace-only. Emit an implicit min_length of 1, keeping the stricter
|
|
163
|
+
# floor if a validator.length @min already set one.
|
|
164
|
+
if (
|
|
165
|
+
field.sub_type == fc.FIELD_SUBTYPE_STRING
|
|
166
|
+
and not field_is_array(field)
|
|
167
|
+
and field.attrs().get(fc.FIELD_ATTR_REQUIRED) is True
|
|
168
|
+
):
|
|
169
|
+
existing_min = kwargs.get("min_length")
|
|
170
|
+
kwargs["min_length"] = max(1, existing_min) if isinstance(existing_min, int) else 1
|
|
171
|
+
|
|
140
172
|
return kwargs
|
|
141
173
|
|
|
142
174
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
175
|
+
# Constraint kwargs are emitted in this fixed order so generated output is deterministic.
|
|
176
|
+
_CONSTRAINT_ORDER = ("pattern", "ge", "le", "min_length", "max_length")
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _constraint_parts(constraints: dict[str, object]) -> list[str]:
|
|
180
|
+
"""The ``Field(...)`` constraint kwargs rendered in the stable, deterministic order."""
|
|
181
|
+
return [f"{k}={constraints[k]!r}" for k in _CONSTRAINT_ORDER if k in constraints]
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def _type_expr_for_field(
|
|
185
|
+
field: MetaField, imports: set[str], config: GenConfig
|
|
186
|
+
) -> tuple[str, str | None]:
|
|
187
|
+
"""The Python annotation text for *field* (arrays wrapped in ``list[...]``) plus the
|
|
188
|
+
shared-enum class name when the field resolves to one (used to render an enum
|
|
189
|
+
default). Collects any required imports into *imports*. Shared by the create-model
|
|
190
|
+
and patch-model emitters so both carry byte-identical typing.
|
|
191
|
+
|
|
192
|
+
FR-019: a field resolving to a package-level shared enum is typed by the materialized
|
|
193
|
+
standalone class (imported from the shared ``enums`` module) or — when @provided — an
|
|
194
|
+
external module; inline enums keep their ``Literal[...]`` (py_type_for)."""
|
|
148
195
|
shared = (
|
|
149
196
|
fr019.shared_enum_for_field(field)
|
|
150
197
|
if field.sub_type == fc.FIELD_SUBTYPE_ENUM
|
|
151
198
|
else None
|
|
152
199
|
)
|
|
200
|
+
enum_type_name: str | None = None
|
|
153
201
|
if shared is not None:
|
|
154
202
|
type_name, import_line = fr019.enum_type_and_import(shared, config)
|
|
155
203
|
imports.add(import_line)
|
|
204
|
+
enum_type_name = type_name
|
|
156
205
|
type_expr = f"list[{type_name}]" if field_is_array(field) else type_name
|
|
157
206
|
else:
|
|
158
207
|
pt = py_type_for(field)
|
|
159
208
|
imports.update(pt.imports)
|
|
160
209
|
type_expr = pt.expr
|
|
161
|
-
# ADR-0036/0037 Wave 3 — @stringFormat: email narrows a plain string to a
|
|
162
|
-
#
|
|
163
|
-
#
|
|
164
|
-
# pattern= (handled in _validator_constraints). The field stays a string.
|
|
210
|
+
# ADR-0036/0037 Wave 3 — @stringFormat: email narrows a plain string to a validated
|
|
211
|
+
# email (Pydantic EmailStr, the Python analogue of Zod .email()); hostname stays a
|
|
212
|
+
# plain str with a codegen-owned canonical pattern= (in _validator_constraints).
|
|
165
213
|
if (
|
|
166
214
|
field.sub_type == fc.FIELD_SUBTYPE_STRING
|
|
167
215
|
and field.attrs().get(fc.FIELD_ATTR_STRING_FORMAT) == fc.STRING_FORMAT_EMAIL
|
|
168
216
|
):
|
|
169
|
-
base_expr = "EmailStr"
|
|
170
217
|
imports.add("from pydantic import EmailStr")
|
|
171
|
-
type_expr =
|
|
218
|
+
type_expr = "list[EmailStr]" if field_is_array(field) else "EmailStr"
|
|
172
219
|
if field.sub_type in (fc.FIELD_SUBTYPE_OBJECT, fc.FIELD_SUBTYPE_MAP):
|
|
173
|
-
# A field.object (-> VO) and a field.map with @objectRef (-> dict[str, VO])
|
|
174
|
-
#
|
|
175
|
-
#
|
|
220
|
+
# A field.object (-> VO) and a field.map with @objectRef (-> dict[str, VO]) both
|
|
221
|
+
# reference a value-object by name; import it so the emitted annotation resolves.
|
|
222
|
+
# @objectRef is FQN-expanded at load; the generated VOs live flat in one package,
|
|
223
|
+
# so import by the bare class name.
|
|
176
224
|
ref = field.attrs().get(fc.FIELD_ATTR_OBJECT_REF)
|
|
177
225
|
if ref:
|
|
178
|
-
# @objectRef is FQN-expanded at load time; the generated VOs live
|
|
179
|
-
# flat in one package, so import by the bare class name.
|
|
180
226
|
ref_name = str(ref).split("::")[-1]
|
|
181
227
|
imports.add(f"from .{ref_name} import {ref_name}")
|
|
228
|
+
return type_expr, enum_type_name
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def origin_guaranteed_non_null(field: MetaField) -> bool:
|
|
232
|
+
"""#195 — a field derived by an origin.aggregate ``@agg:any|all|collect`` is
|
|
233
|
+
COALESCE-guaranteed non-null in the synthesized view (any→false, all→true,
|
|
234
|
+
collect→[]), so its read type is non-null even when the field is not ``@required``.
|
|
235
|
+
origin.first is deliberately NOT here — an empty related set selects no row (→ null);
|
|
236
|
+
origin.computed nullability is expression-dependent, so it stays the conservative
|
|
237
|
+
nullable default. Mirrors the TS originGuaranteedNonNull."""
|
|
238
|
+
# ADR-0039 sanctioned own — origin.* never inherits (ADR-0029), so own is correct.
|
|
239
|
+
for child in field.own_children():
|
|
240
|
+
if child.type == TYPE_ORIGIN and child.sub_type == ORIGIN_SUBTYPE_AGGREGATE:
|
|
241
|
+
agg = child.attr(ORIGIN_ATTR_AGG) # ADR-0039 own — origin attr
|
|
242
|
+
if agg in (AGG_ANY, AGG_ALL, AGG_COLLECT):
|
|
243
|
+
return True
|
|
244
|
+
return False
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def _field_line(field: MetaField, imports: set[str], config: GenConfig) -> tuple[str, bool]:
|
|
248
|
+
"""Return (source line, uses_field). Collects required imports into *imports*."""
|
|
249
|
+
type_expr, enum_type_name = _type_expr_for_field(field, imports, config)
|
|
182
250
|
required = field.attrs().get(fc.FIELD_ATTR_REQUIRED) is True
|
|
183
251
|
default_raw = field.attrs().get(fc.FIELD_ATTR_DEFAULT)
|
|
184
252
|
# A server-side expression default (now(), gen_random_uuid(), CURRENT_TIMESTAMP)
|
|
185
253
|
# is filled by the DB — the Python model carries no default for it (the field keeps
|
|
186
254
|
# its required/optional shape). Only literal defaults become Pydantic defaults.
|
|
187
255
|
has_default = default_raw is not None and not _is_sql_expr_default(default_raw)
|
|
188
|
-
enum_type_name = type_name if shared is not None else None
|
|
189
256
|
|
|
190
|
-
|
|
191
|
-
# Emit kwargs in a stable order so generated output is deterministic.
|
|
192
|
-
_order = ["pattern", "ge", "le", "min_length", "max_length"]
|
|
193
|
-
parts = [f"{k}={constraints[k]!r}" for k in _order if k in constraints]
|
|
257
|
+
parts = _constraint_parts(_validator_constraints(field))
|
|
194
258
|
uses_field = bool(parts)
|
|
195
259
|
|
|
196
260
|
# @default — render an enum member (Type.MEMBER, UPPER_CASE) or a Python literal.
|
|
@@ -202,15 +266,20 @@ def _field_line(field: MetaField, imports: set[str], config: GenConfig) -> tuple
|
|
|
202
266
|
else:
|
|
203
267
|
default_expr = None
|
|
204
268
|
|
|
205
|
-
#
|
|
206
|
-
|
|
269
|
+
# #195 — a field derived by an origin.aggregate any|all|collect always COALESCEs
|
|
270
|
+
# to a value (any→false, all→true, collect→[]), so its read type is mandatory
|
|
271
|
+
# non-null exactly like a @required field (no `| None`, no default). origin.first /
|
|
272
|
+
# origin.computed stay the conservative nullable default.
|
|
273
|
+
mandatory = required or origin_guaranteed_non_null(field)
|
|
274
|
+
# A field is Optional only when it is neither mandatory nor carries a @default.
|
|
275
|
+
annotation = type_expr if (mandatory or has_default) else f"{type_expr} | None"
|
|
207
276
|
if has_default and uses_field:
|
|
208
277
|
assignment = f" = Field(default={default_expr}, {', '.join(parts)})"
|
|
209
278
|
elif has_default:
|
|
210
279
|
assignment = f" = {default_expr}"
|
|
211
|
-
elif
|
|
280
|
+
elif mandatory and uses_field:
|
|
212
281
|
assignment = f" = Field({', '.join(parts)})"
|
|
213
|
-
elif
|
|
282
|
+
elif mandatory:
|
|
214
283
|
assignment = ""
|
|
215
284
|
elif uses_field:
|
|
216
285
|
assignment = f" = Field(default=None, {', '.join(parts)})"
|
|
@@ -226,6 +295,106 @@ def _field_line(field: MetaField, imports: set[str], config: GenConfig) -> tuple
|
|
|
226
295
|
return f" {py_name}: {annotation}{assignment}", uses_field
|
|
227
296
|
|
|
228
297
|
|
|
298
|
+
def _primary_identity_field_names(entity: MetaObject) -> list[str]:
|
|
299
|
+
"""The field names in the entity's primary identity (``identity.primary @fields``),
|
|
300
|
+
normalized to a list. Empty when there is no primary identity. Mirrors the TS
|
|
301
|
+
``primaryIdentityFieldNames``."""
|
|
302
|
+
primary = entity.primary_identity()
|
|
303
|
+
if primary is None:
|
|
304
|
+
return []
|
|
305
|
+
# ADR-0039: resolving (identity attr) — an identity.primary may be inherited via extends.
|
|
306
|
+
fields = primary.get_meta_attr(IDENTITY_ATTR_FIELDS)
|
|
307
|
+
if isinstance(fields, (list, tuple)):
|
|
308
|
+
return [str(f) for f in fields]
|
|
309
|
+
if isinstance(fields, str) and fields:
|
|
310
|
+
return [s.strip() for s in fields.split(",") if s.strip()]
|
|
311
|
+
return []
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def _auto_gen_pk_field_names(entity: MetaObject) -> set[str]:
|
|
315
|
+
"""Primary-key field names that the SERVER generates (``identity.primary
|
|
316
|
+
@generation: increment|uuid``) — excluded from the create-validation shape because
|
|
317
|
+
the caller never supplies them. An ``assigned`` (or ungenerated) PK is NOT excluded
|
|
318
|
+
(the caller provides it). Mirrors the TS ``autoGenPkFieldNames``."""
|
|
319
|
+
primary = entity.primary_identity()
|
|
320
|
+
if primary is None:
|
|
321
|
+
return set()
|
|
322
|
+
# ADR-0039: resolving (identity attr).
|
|
323
|
+
generation = primary.get_meta_attr(IDENTITY_ATTR_GENERATION)
|
|
324
|
+
if generation not in (GENERATION_INCREMENT, GENERATION_UUID):
|
|
325
|
+
return set()
|
|
326
|
+
return set(_primary_identity_field_names(entity))
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
def _create_field_line(
|
|
330
|
+
field: MetaField, imports: set[str], config: GenConfig
|
|
331
|
+
) -> tuple[str, bool]:
|
|
332
|
+
"""A CREATE-model field line (FR-036): keyed by the WIRE name (``field.name``) and
|
|
333
|
+
carrying the same per-field constraints as the read model. A ``@required`` field
|
|
334
|
+
that is SERVER-FILLED — it has ANY ``@default`` (a literal OR a SQL expression such
|
|
335
|
+
as ``now()`` / ``gen_random_uuid()`` / ``CURRENT_TIMESTAMP``) or an ``@autoSet`` — is
|
|
336
|
+
emitted OPTIONAL, because the DB / trigger provides it and a POST that omits it must
|
|
337
|
+
not 400 (mirrors the TS InsertSchema ``fieldWillBeOptional``). A literal ``@default``
|
|
338
|
+
is preserved as the Python default; a SQL-expression default / ``@autoSet`` carries
|
|
339
|
+
no Python default (the server fills it). Returns (source line, uses_field)."""
|
|
340
|
+
type_expr, enum_type_name = _type_expr_for_field(field, imports, config)
|
|
341
|
+
required = field.attrs().get(fc.FIELD_ATTR_REQUIRED) is True
|
|
342
|
+
default_raw = field.attrs().get(fc.FIELD_ATTR_DEFAULT)
|
|
343
|
+
literal_default = default_raw is not None and not _is_sql_expr_default(default_raw)
|
|
344
|
+
auto_set = field.attrs().get(fc.FIELD_ATTR_AUTO_SET)
|
|
345
|
+
is_auto_set = auto_set in (fc.AUTO_SET_ON_CREATE, fc.AUTO_SET_ON_UPDATE)
|
|
346
|
+
# A field is required in the CREATE shape only when @required AND not server-filled:
|
|
347
|
+
# ANY @default (literal or SQL-expr) or @autoSet means the DB fills it, so the wire
|
|
348
|
+
# body may omit it (the FR-036 #1 regression fix).
|
|
349
|
+
server_filled = default_raw is not None or is_auto_set
|
|
350
|
+
create_required = required and not server_filled
|
|
351
|
+
|
|
352
|
+
parts = _constraint_parts(_validator_constraints(field))
|
|
353
|
+
uses_field = bool(parts)
|
|
354
|
+
|
|
355
|
+
py_name = field.name # FR-036 #3 — validation models key by the WIRE name (field.name)
|
|
356
|
+
|
|
357
|
+
if literal_default:
|
|
358
|
+
# Preserve the literal @default (enum member or Python literal) exactly as the
|
|
359
|
+
# read model does — the caller may omit the field and the model fills the default.
|
|
360
|
+
if enum_type_name is not None and not field_is_array(field):
|
|
361
|
+
default_expr = f"{enum_type_name}.{str(default_raw).upper()}"
|
|
362
|
+
else:
|
|
363
|
+
default_expr = repr(default_raw)
|
|
364
|
+
annotation = type_expr
|
|
365
|
+
assignment = (
|
|
366
|
+
f" = Field(default={default_expr}, {', '.join(parts)})"
|
|
367
|
+
if uses_field
|
|
368
|
+
else f" = {default_expr}"
|
|
369
|
+
)
|
|
370
|
+
elif create_required:
|
|
371
|
+
annotation = type_expr
|
|
372
|
+
assignment = f" = Field({', '.join(parts)})" if uses_field else ""
|
|
373
|
+
else:
|
|
374
|
+
annotation = f"{type_expr} | None"
|
|
375
|
+
assignment = (
|
|
376
|
+
f" = Field(default=None, {', '.join(parts)})" if uses_field else " = None"
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
return f" {py_name}: {annotation}{assignment}", uses_field
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
def _patch_field_line(
|
|
383
|
+
field: MetaField, imports: set[str], config: GenConfig
|
|
384
|
+
) -> tuple[str, bool]:
|
|
385
|
+
"""A PATCH-model field line: the field is made Optional (default ``None``) yet
|
|
386
|
+
carries the SAME per-field constraints as the create model, so a present, non-null
|
|
387
|
+
value is validated with the create rules (FR-036) while an absent field is left
|
|
388
|
+
untouched. A ``@default`` is never emitted (a patch does not default a column).
|
|
389
|
+
Returns (source line, uses_field)."""
|
|
390
|
+
type_expr, _enum = _type_expr_for_field(field, imports, config)
|
|
391
|
+
parts = _constraint_parts(_validator_constraints(field))
|
|
392
|
+
py_name = field.name # FR-036 #3 — validation models key by the WIRE name (field.name)
|
|
393
|
+
if parts:
|
|
394
|
+
return f" {py_name}: {type_expr} | None = Field(default=None, {', '.join(parts)})", True
|
|
395
|
+
return f" {py_name}: {type_expr} | None = None", False
|
|
396
|
+
|
|
397
|
+
|
|
229
398
|
def _effective_fqn(entity: MetaObject) -> str:
|
|
230
399
|
"""`package::name` via the canonical :meth:`MetaData.resolution_key` — own
|
|
231
400
|
package, else the file-default package (captured at parse), else the nearest
|
|
@@ -295,6 +464,80 @@ class EntityModelGenerator:
|
|
|
295
464
|
lines.append(f" {d.relation_name}: list[{element}] = []")
|
|
296
465
|
return lines, uses_field
|
|
297
466
|
|
|
467
|
+
def _emit_create_lines(
|
|
468
|
+
self,
|
|
469
|
+
entity: MetaObject,
|
|
470
|
+
imports: set[str],
|
|
471
|
+
config: GenConfig | None = None,
|
|
472
|
+
) -> tuple[list[str], bool]:
|
|
473
|
+
"""The ``<Name>Create`` body — the CREATE-validation shape the generated router
|
|
474
|
+
validates a POST body against (FR-036). Restates every EFFECTIVE field (own +
|
|
475
|
+
inherited via ``extends``) EXCEPT the ones the server owns: an auto-generated PK
|
|
476
|
+
(``identity.primary @generation``; FR-036 #2) and any ``@readOnly`` field
|
|
477
|
+
(FR-013) are omitted, and a ``@required`` server-filled field (``@default`` /
|
|
478
|
+
``@autoSet``; FR-036 #1) is made optional. Keyed by the wire name; a TPH
|
|
479
|
+
subtype's discriminator is pinned to its ``Literal`` value (the route injects it
|
|
480
|
+
from the URL, so it stays optional with a default). M:N navigations are omitted
|
|
481
|
+
(a create body targets columns, not relations). Returns ``(lines, uses_field)``.
|
|
482
|
+
Override to add/transform create-model lines."""
|
|
483
|
+
cfg = config if config is not None else GenConfig(out_dir="")
|
|
484
|
+
auto_gen_pk = _auto_gen_pk_field_names(entity)
|
|
485
|
+
binding = tph_subtype_binding(entity)
|
|
486
|
+
disc_field = binding[0] if binding is not None else None
|
|
487
|
+
uses_field = False
|
|
488
|
+
lines: list[str] = []
|
|
489
|
+
# RESOLVING (fields()): the flat Create model subclasses BaseModel (not the entity
|
|
490
|
+
# base), so it restates inherited fields too — unlike the read model's own_fields
|
|
491
|
+
# subclass-emit.
|
|
492
|
+
for f in entity.fields():
|
|
493
|
+
if disc_field is not None and f.name == disc_field:
|
|
494
|
+
continue # pinned below (prepended), never as a normal field line
|
|
495
|
+
if f.name in auto_gen_pk:
|
|
496
|
+
continue # FR-036 #2 — a server-generated PK is never in the create body
|
|
497
|
+
if f.attrs().get(fc.FIELD_ATTR_READ_ONLY) is True:
|
|
498
|
+
continue # FR-013 — a read-only column is DB/owner-written, not create input
|
|
499
|
+
if f.is_derived():
|
|
500
|
+
continue # FR-024 §7 (#214) — a derived (origin.*) field is view-computed, not create input
|
|
501
|
+
line, used = _create_field_line(f, imports, cfg)
|
|
502
|
+
uses_field = uses_field or used
|
|
503
|
+
lines.append(line)
|
|
504
|
+
# FR-017 TPH: pin the inherited discriminator to this subtype's value (with a
|
|
505
|
+
# default so the URL-injected create body may omit it) — mirrors the read model.
|
|
506
|
+
if binding is not None:
|
|
507
|
+
disc_name, disc_value = binding
|
|
508
|
+
lines = [f' {disc_name}: Literal["{disc_value}"] = "{disc_value}"', *lines]
|
|
509
|
+
imports.add("from typing import Literal")
|
|
510
|
+
return lines, uses_field
|
|
511
|
+
|
|
512
|
+
def _emit_patch_lines(
|
|
513
|
+
self,
|
|
514
|
+
entity: MetaObject,
|
|
515
|
+
imports: set[str],
|
|
516
|
+
config: GenConfig | None = None,
|
|
517
|
+
) -> tuple[list[str], bool]:
|
|
518
|
+
"""The ``<Name>Patch`` body: every EFFECTIVE field (own + inherited via
|
|
519
|
+
``extends``) made Optional while retaining its create-model constraints, so the
|
|
520
|
+
generated PATCH handler can validate present, non-null values without
|
|
521
|
+
bean-validating absent ones (FR-036). The primary-key field(s) are excluded
|
|
522
|
+
(route-authoritative; FR-036 #4) and M:N navigations are omitted (a patch
|
|
523
|
+
targets columns, not relations). Returns ``(lines, uses_field)``. Override to
|
|
524
|
+
add/transform patch-model lines."""
|
|
525
|
+
cfg = config if config is not None else GenConfig(out_dir="")
|
|
526
|
+
pk_fields = set(_primary_identity_field_names(entity))
|
|
527
|
+
uses_field = False
|
|
528
|
+
lines: list[str] = []
|
|
529
|
+
# RESOLVING (fields()): the flat Patch model is self-contained (subclasses
|
|
530
|
+
# BaseModel, not the entity base), so it restates inherited fields too.
|
|
531
|
+
for f in entity.fields():
|
|
532
|
+
if f.name in pk_fields:
|
|
533
|
+
continue # FR-036 #4 — the PK is route-authoritative, never patched
|
|
534
|
+
if f.is_derived():
|
|
535
|
+
continue # FR-024 §7 (#214) — a derived (origin.*) field is view-computed, never patched
|
|
536
|
+
line, used = _patch_field_line(f, imports, cfg)
|
|
537
|
+
uses_field = uses_field or used
|
|
538
|
+
lines.append(line)
|
|
539
|
+
return lines, uses_field
|
|
540
|
+
|
|
298
541
|
def render_entity_model(
|
|
299
542
|
self,
|
|
300
543
|
entity: MetaObject,
|
|
@@ -333,11 +576,29 @@ class EntityModelGenerator:
|
|
|
333
576
|
imports.add("from typing import Literal")
|
|
334
577
|
body = lines if lines else [" pass"]
|
|
335
578
|
|
|
336
|
-
#
|
|
579
|
+
# FR-036 — an object.entity also emits two flat validation models the generated
|
|
580
|
+
# router binds: a ``<Name>Create`` (POST-body shape: auto-gen PK / @readOnly
|
|
581
|
+
# omitted, @default/@autoSet optional, other @required required) and an
|
|
582
|
+
# all-optional ``<Name>Patch`` (PATCH-body shape: PK excluded). Both key by the
|
|
583
|
+
# WIRE name and carry the field constraints, so present values are validated with
|
|
584
|
+
# the create rules. Value objects / projections are never routed, so they emit
|
|
585
|
+
# neither (only the read model). See findings #1–#4.
|
|
586
|
+
emit_validation_models = entity.sub_type == OBJECT_SUBTYPE_ENTITY
|
|
587
|
+
create_lines: list[str] = []
|
|
588
|
+
create_uses_field = False
|
|
589
|
+
patch_lines: list[str] = []
|
|
590
|
+
patch_uses_field = False
|
|
591
|
+
if emit_validation_models:
|
|
592
|
+
create_lines, create_uses_field = self._emit_create_lines(entity, imports, config)
|
|
593
|
+
patch_lines, patch_uses_field = self._emit_patch_lines(entity, imports, config)
|
|
594
|
+
|
|
595
|
+
# Import only the pydantic names actually referenced. The main model imports
|
|
596
|
+
# BaseModel only when it has no super, but the flat Create/Patch models always
|
|
597
|
+
# subclass BaseModel, so their presence forces the import regardless.
|
|
337
598
|
pyd_names: list[str] = []
|
|
338
|
-
if entity.super_data is None:
|
|
599
|
+
if entity.super_data is None or emit_validation_models:
|
|
339
600
|
pyd_names.append("BaseModel")
|
|
340
|
-
if uses_field:
|
|
601
|
+
if uses_field or create_uses_field or patch_uses_field:
|
|
341
602
|
pyd_names.append("Field")
|
|
342
603
|
|
|
343
604
|
parts: list[str] = [
|
|
@@ -350,7 +611,22 @@ class EntityModelGenerator:
|
|
|
350
611
|
parts += [*extra_imports, ""]
|
|
351
612
|
if pyd_names:
|
|
352
613
|
parts += [f"from pydantic import {', '.join(pyd_names)}", ""]
|
|
353
|
-
parts += ["", self._emit_class_header(entity, base_class), *body
|
|
614
|
+
parts += ["", self._emit_class_header(entity, base_class), *body]
|
|
615
|
+
if emit_validation_models:
|
|
616
|
+
parts += [
|
|
617
|
+
"",
|
|
618
|
+
"",
|
|
619
|
+
f"class {entity.name}Create(BaseModel):",
|
|
620
|
+
' """GENERATED — CREATE input: auto-gen PK / @readOnly omitted; '
|
|
621
|
+
'@default/@autoSet optional; present values validated (FR-036)."""',
|
|
622
|
+
*create_lines,
|
|
623
|
+
"",
|
|
624
|
+
"",
|
|
625
|
+
f"class {entity.name}Patch(BaseModel):",
|
|
626
|
+
' """GENERATED — PATCH input: all fields optional (PK excluded); present values validated (FR-036)."""',
|
|
627
|
+
*patch_lines,
|
|
628
|
+
]
|
|
629
|
+
parts += [""]
|
|
354
630
|
return "\n".join(parts)
|
|
355
631
|
|
|
356
632
|
def _render_shared_enums_module(self, entities: list[MetaObject]) -> EmittedFile | None:
|
|
@@ -250,7 +250,12 @@ class FilterAllowlistGenerator:
|
|
|
250
250
|
src = _primary_source_rdb(entity)
|
|
251
251
|
if src is None:
|
|
252
252
|
return None
|
|
253
|
-
|
|
253
|
+
# FR-024 §7 (#214): a write-through entity read-view is writable and its generated
|
|
254
|
+
# router imports this allowlist — so it MUST be emitted for write-through regardless of
|
|
255
|
+
# source declaration order (matching the router gate; else a view-source-first
|
|
256
|
+
# write-through router imports a never-generated module and FastAPI startup crashes). A
|
|
257
|
+
# projection (read-only source only) is not write-through, so it still skips.
|
|
258
|
+
if not entity.is_write_through() and src.effective_kind() != SOURCE_KIND_TABLE:
|
|
254
259
|
return None
|
|
255
260
|
|
|
256
261
|
short_name = entity.name
|