metaobjects 1.0.11__tar.gz → 1.0.12__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-1.0.11 → metaobjects-1.0.12}/PKG-INFO +1 -1
- {metaobjects-1.0.11 → metaobjects-1.0.12}/pyproject.toml +1 -1
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/cli.py +151 -0
- metaobjects-1.0.12/src/metaobjects/fmt.py +214 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/render/extract/__init__.py +2 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/render/extract/extract.py +34 -2
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/render/extract/types.py +28 -1
- metaobjects-1.0.12/tests/codegen/test_cli_fmt.py +137 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_extractor_generator.py +41 -0
- metaobjects-1.0.12/tests/codegen/test_fmt_format_file.py +45 -0
- metaobjects-1.0.12/tests/conformance/test_fmt_conformance.py +54 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/render/extract/test_extract.py +100 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/uv.lock +1 -1
- {metaobjects-1.0.11 → metaobjects-1.0.12}/.gitignore +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/LICENSE +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/README.md +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/hatch_build.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/scripts/generate_embedded_library.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/__init__.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/agent_context/__init__.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/agent_context/scaffold.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/apidocs/__init__.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/apidocs/api_model.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/apidocs/builder.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/apidocs/naming.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/apidocs/paths.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/apidocs/renderer.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/attr_class_map.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/codegen/KNOWN_GAPS.md +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/codegen/__init__.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/codegen/collision_names.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/codegen/config.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/codegen/constants.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/codegen/eject.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/codegen/extract_delegate_emitter.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/codegen/extract_schema_emitter.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/codegen/format.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/codegen/fr010_field_mapping.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/codegen/generator.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/codegen/generator_registry.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/codegen/generators/__init__.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/codegen/generators/entity_model.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/codegen/generators/extractor_generator.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/codegen/generators/filter_allowlist_generator.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/codegen/generators/find_inbound.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/codegen/generators/fr019_shared_enum.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/codegen/generators/m2m_codegen.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/codegen/generators/names_generator.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/codegen/generators/output_parser_generator.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/codegen/generators/output_prompt_generator.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/codegen/generators/render_helper_generator.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/codegen/generators/router_generator.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/codegen/generators/template_generator.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/codegen/generators/tph_plan.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/codegen/generators/trace_helper_generator.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/codegen/instance_artifacts.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/codegen/metaobjects-config.schema.json +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/codegen/model_walk.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/codegen/output_format_spec_emitter.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/codegen/overwrite_policy.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/codegen/project_config.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/codegen/runner.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/codegen/runtime/__init__.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/codegen/runtime/constraint_errors.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/codegen/runtime/filter_parser.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/codegen/template_codegen/__init__.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/codegen/template_codegen/output_pattern.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/codegen/template_codegen/template_data.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/codegen/template_codegen/template_spec.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/codegen/type_map.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/codegen/value_objects.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/config/__init__.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/config/dependencies.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/config/neutral_config.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/config/source_resolver.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/core_types.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/datatype.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/documentation/__init__.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/documentation/doc_constants.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/documentation/doc_provider.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/documentation/doc_schema.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/errors.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/library/__init__.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/library/embedded_library.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/library/library_sources.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/loader/__init__.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/loader/merge.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/loader/meta_data_loader.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/loader/registered_validation.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/loader/sources/__init__.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/loader/sources/directory_source.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/loader/sources/file_source.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/loader/sources/meta_data_source.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/loader/sources/uri_source.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/loader/validate_discriminator.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/loader/validate_enum_normalize_ambiguity.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/loader/validate_field_mutability.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/loader/validate_source_escapes.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/loader/validate_source_parameter_ref.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/loader/validate_source_physical_names.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/loader/validation_passes.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/__init__.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/core/__init__.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/core/attr/__init__.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/core/attr/attr_constants.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/core/attr/meta_attr.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/core/field/__init__.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/core/field/field_constants.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/core/field/meta_field.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/core/identity/__init__.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/core/identity/identity_constants.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/core/identity/meta_identity.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/core/index/__init__.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/core/index/index_constants.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/core/index/meta_index.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/core/object/__init__.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/core/object/meta_object.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/core/object/meta_object_aware.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/core/object/object_class_registry.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/core/object/object_constants.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/core/object/object_extract.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/core/object/value_object.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/core/relationship/__init__.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/core/relationship/derive_m2m_fields.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/core/relationship/meta_relationship.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/core/relationship/relationship_constants.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/core/relationship/relationship_references.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/core/requirement/__init__.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/core/requirement/meta_requirement.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/core/requirement/requirement_constants.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/core/validator/__init__.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/core/validator/validator_constants.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/meta_data.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/meta_root.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/persistence/__init__.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/persistence/db/__init__.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/persistence/db/db_constants.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/persistence/db/db_provider.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/persistence/origin/__init__.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/persistence/origin/meta_origin.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/persistence/origin/origin_constants.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/persistence/source/__init__.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/persistence/source/meta_source.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/persistence/source/source_constants.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/presentation/__init__.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/presentation/layout/__init__.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/presentation/layout/layout_constants.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/presentation/layout/meta_layout.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/presentation/ui/__init__.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/presentation/ui/ui_provider.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/presentation/view/__init__.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/presentation/view/meta_view.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/presentation/view/view_constants.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/provider_extends.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/template/__init__.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/template/meta_template.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/template/prompt_provider.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta/template/template_constants.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/meta_endpoint.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/naming.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/naming_refs.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/parser.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/parser_yaml.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/provider.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/py.typed +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/registry.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/registry_manifest.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/render/__init__.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/render/email_document.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/render/escapers.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/render/extract/KNOWN_GAPS.md +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/render/extract/coerce.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/render/extract/extract_map.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/render/extract/json_forgiving_reader.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/render/extract/locate.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/render/extract/normalize.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/render/extract/strip.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/render/extract/xml_forgiving_reader.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/render/filesystem_provider.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/render/payload_accessors.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/render/prompt/__init__.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/render/prompt/output_format_renderer.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/render/prompt/output_format_spec.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/render/prompt/prompt_field.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/render/prompt/prompt_overrides.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/render/prompt/prompt_style.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/render/renderer.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/render/verify.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/runtime/__init__.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/runtime/llm_recorder.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/runtime/n2m_resolver.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/runtime/object_manager.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/runtime/tph.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/scope.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/serializer_json.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/shared/__init__.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/shared/base_types.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/shared/separators.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/shared/structural.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/source/__init__.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/source/error_source.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/source/json_path.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/source/semantic_diff.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/source/yaml_positions.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/source_resolution.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/spec_metamodel/__init__.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/spec_metamodel/attr.json +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/spec_metamodel/db.json +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/spec_metamodel/documentation.json +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/spec_metamodel/field.json +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/spec_metamodel/identity.json +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/spec_metamodel/index.json +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/spec_metamodel/layout.json +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/spec_metamodel/object.json +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/spec_metamodel/origin.json +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/spec_metamodel/prompt.json +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/spec_metamodel/relationship.json +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/spec_metamodel/requirement.json +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/spec_metamodel/source.json +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/spec_metamodel/template.json +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/spec_metamodel/ui-web.json +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/spec_metamodel/ui.json +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/spec_metamodel/validator.json +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/spec_metamodel/view.json +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/super_resolve.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/validation_types.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/src/metaobjects/yaml_desugar.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/__init__.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/__init__.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/gen_suite.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/golden/extends/expected/BaseEntity.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/golden/extends/expected/Program.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/golden/extends/meta.json +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/golden/nested-array/expected/AuthorBrief.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/golden/nested-array/expected/PostBrief.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/golden/nested-array/meta.json +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/golden/scalars/expected/Metric.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/golden/scalars/expected/Report.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/golden/scalars/meta.json +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/golden/vanilla/expected/Subscriber.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/golden/vanilla/meta.json +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_abstract_conformance.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_api_docs_builder.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_api_docs_paths.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_cli.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_cli_config_gen.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_cli_config_verify.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_cli_dependencies.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_cli_gen_unknown_attr.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_cli_providers.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_cli_registry.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_cli_render_helper_template_root.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_cli_staleness_nudge.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_cli_verify_strict.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_cli_verify_subverbs.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_codegen_compile_conformance.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_constants_config.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_eject.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_eject_runtime.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_entity_model.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_enum_conformance.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_extends_abstract_field_inheritance.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_extract_tier_collision.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_filter_allowlist_generator.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_format.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_fr010_nested_object_prompt.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_fr010_output_codegen.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_fr019_shared_provided_conformance.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_gen_state_keying.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_generator.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_generator_extension_seams.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_golden.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_inherit_without_restate_gate.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_inheritance_conformance.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_instance_artifacts.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_m2m_codegen.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_names_extends_chain.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_names_generator.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_no_magic_physical_names.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_origin_nullability.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_output_parser_generator.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_overwrite_policy.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_overwrite_policy_hash.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_project_config.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_projection_compile.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_recipe_generators.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_render_helper_conformance.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_render_helper_generator.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_reverse_finders.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_route_path_naming.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_router_autoset.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_router_generator.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_router_generator_m2m_target_tph.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_router_generator_tph_m2m.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_router_generator_tph_m2m_shadowed.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_runner.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_source_resolution.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_sourceless_entity_surface.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_sourceless_projection.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_template_data.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_template_generator.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_template_output_pattern.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_template_scope_helpers.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_template_scope_walk.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_template_spec.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_tph_codegen.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_trace_helper_generator.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_type_map.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_validation_conformance.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_value_objects.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/codegen/test_write_through.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/config/__init__.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/config/test_dependencies.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/config/test_library_package_guard.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/config/test_neutral_config.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/config/test_source_resolver.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/conformance/__init__.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/conformance/capabilities.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/conformance/conformance-expected-failures.json +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/conformance/conformance_adapter.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/conformance/corpus.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/conformance/expected_failures.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/conformance/fixture_discovery.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/conformance/navigator.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/conformance/test_api_docs_cross_port_conformance.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/conformance/test_conformance.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/conformance/test_dependency_conformance.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/conformance/test_extract_conformance.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/conformance/test_extract_object_array_bounds.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/conformance/test_extract_object_verdict.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/conformance/test_fr010_loader_attrs.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/conformance/test_fr011_attrs.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/conformance/test_generator_registry_conformance.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/conformance/test_object_model_conformance.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/conformance/test_provider_composition_conformance.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/conformance/test_registry_conformance.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/conformance/test_runner_hardfail.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/conformance/test_scope_conformance.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/conformance/test_source_resolution_conformance.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/conformance/test_spec_metamodel_embed.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/conformance/test_strict_attr_load.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/conformance/test_template_codegen_conformance.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/conformance/test_template_generator_conformance.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/conformance/test_yaml_conformance.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/conformance/yaml-conformance-expected-failures.json +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/integration/__init__.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/integration/api_contract_assertions.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/integration/api_contract_jsonb_server.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/integration/api_contract_m2m_server.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/integration/api_contract_server.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/integration/generated_jsonb_app.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/integration/generated_m2m_app.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/integration/generated_projection_app.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/integration/generated_router_app.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/integration/generated_tph_app.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/integration/meta_ai_trace.yaml +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/integration/normalization.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/integration/postgres_container.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/integration/query_runner.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/integration/scenarios.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/integration/test_api_contract.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/integration/test_api_contract_generated.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/integration/test_api_contract_jsonb.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/integration/test_api_contract_m2m.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/integration/test_api_contract_m2m_generated.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/integration/test_api_contract_projection.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/integration/test_api_contract_tph_generated.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/integration/test_generated_router_uuid_pk.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/integration/test_llm_call_trace.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/integration/test_m2m_target_tph_generated.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/integration/test_normalization.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/integration/test_query_scenarios.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/integration/test_runtime_return_types.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/integration/test_tph_m2m_generated.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/integration/test_tph_m2m_shadowed_generated.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/loader/__init__.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/loader/test_abstract_subtype_authored.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/loader/test_child_wrapper_body_shape.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/loader/test_enum_extends_values_conflict.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/loader/test_field_mutability_order.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/loader/test_shipped_library_ai.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/loader/test_template_payload_target_210.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/open_closed_proof_test.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/render/__init__.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/render/extract/__init__.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/render/extract/test_coerce.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/render/extract/test_extract_map.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/render/extract/test_json_forgiving_reader.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/render/extract/test_locate.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/render/extract/test_model.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/render/extract/test_normalize.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/render/extract/test_strip.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/render/extract/test_xml_forgiving_reader.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/render/prompt/__init__.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/render/prompt/test_output_format_renderer.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/render/test_email_document.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/render/test_filesystem_provider.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/render/test_output_format_renderer_nested.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/render/test_output_prompt_conformance.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/render/test_render_conformance.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/render/test_render_max_chars.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/render/test_render_model_payload.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/render/test_verify.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/render/test_verify_conformance.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/runtime/test_constraint_errors.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/runtime/test_object_manager_currency_read.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/runtime/test_object_manager_enum_intvaluemap.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/runtime/test_object_manager_table_name.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/source/__init__.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/source/test_fr5c_merge_attribution.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/source/test_fr5d_reference_resolution.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/source/test_fr5e_database_source_shape.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/source/test_json_path.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/source/test_semantic_diff.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/source/test_source_on_node.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/source/test_yaml_positions.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/test_api_docs_accuracy.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/test_meta_endpoint.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/__init__.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_agent_context_staleness.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_capabilities.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_column_naming_strategy.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_common_attrs.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_core_types.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_derive_m2m_declaring_entity.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_effective_children_sibling_shadowing.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_effective_package.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_errors.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_field_enum.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_field_enum_intvaluemap.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_field_map_validation.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_field_uuid_dbcolumntype.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_file_source_id.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_fr016_source_name_and_kind_aliases.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_index_lookup.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_llm_recorder.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_loader.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_loader_bom.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_loader_class.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_merge.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_meta_attr.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_meta_data.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_meta_source.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_module_shortcuts.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_n2m_resolver.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_n2m_resolver_inherited.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_no_dead_config_fields.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_object_manager_autoset_stamping.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_object_manager_uuid_coercion.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_object_manager_write_through.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_one_primary_source.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_parser.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_parser_yaml.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_provider.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_provider_extension.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_registry.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_registry_completeness.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_registry_extend.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_registry_sealed.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_relationship_m2m_validation.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_relationship_references.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_relationship_referential_actions.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_resolution_key.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_runtime_resolution_key_binding.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_serializer.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_serializer_shared_document.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_shared_constants.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_smoke.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_source_escapes.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_sources.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_strict_child_placement.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_super_resolve.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_template_toolcall.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_template_wrong_subtype_attrs.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_validation_attr_schema.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_validation_filter_values.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_validation_origin_195.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_validation_origin_paths.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_validation_sort_field.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_validation_warnings.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_write_coercion_arrays.py +0 -0
- {metaobjects-1.0.11 → metaobjects-1.0.12}/tests/unit/test_yaml_desugar.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: metaobjects
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.12
|
|
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 = "1.0.
|
|
3
|
+
version = "1.0.12"
|
|
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"
|
|
@@ -1069,6 +1069,132 @@ def _cmd_list(_args: argparse.Namespace) -> int:
|
|
|
1069
1069
|
return 0
|
|
1070
1070
|
|
|
1071
1071
|
|
|
1072
|
+
def _describe_fmt_report(report: "object", cwd: Path) -> str:
|
|
1073
|
+
from metaobjects import fmt
|
|
1074
|
+
|
|
1075
|
+
try:
|
|
1076
|
+
rel = report.path.relative_to(cwd)
|
|
1077
|
+
except ValueError:
|
|
1078
|
+
rel = report.path
|
|
1079
|
+
if report.status == fmt.STATUS_FORMATTED:
|
|
1080
|
+
return f" reformatted {rel}"
|
|
1081
|
+
if report.status == fmt.STATUS_WOULD_FORMAT:
|
|
1082
|
+
return f" not canonical {rel}"
|
|
1083
|
+
if report.status == fmt.STATUS_UNCHANGED:
|
|
1084
|
+
return f" ok {rel}"
|
|
1085
|
+
if report.status == fmt.STATUS_SKIPPED_YAML:
|
|
1086
|
+
return f" skipped (yaml) {rel} — {report.detail}"
|
|
1087
|
+
if report.status == fmt.STATUS_SKIPPED_OVERLAY:
|
|
1088
|
+
return (
|
|
1089
|
+
f" skipped (overlay) {rel} — this file declares an overlay fmt "
|
|
1090
|
+
"cannot resolve standalone"
|
|
1091
|
+
)
|
|
1092
|
+
if report.status == fmt.STATUS_ERROR:
|
|
1093
|
+
return f" error {rel} — {report.detail}"
|
|
1094
|
+
return f" {report.status} {rel}" # pragma: no cover — exhaustive in practice
|
|
1095
|
+
|
|
1096
|
+
|
|
1097
|
+
def _cmd_fmt(args: argparse.Namespace) -> int:
|
|
1098
|
+
"""``fmt`` (#304) — rewrite metadata files into canonical form; ``--check``
|
|
1099
|
+
lists non-canonical files and exits non-zero without changing anything.
|
|
1100
|
+
|
|
1101
|
+
Mirrors the TS reference surface exactly: no ``metadata_dir`` positional —
|
|
1102
|
+
files come from the neutral ``.metaobjects/config.json`` ladder
|
|
1103
|
+
(:func:`resolve_metadata_location`, rungs 3-4; this command does not read
|
|
1104
|
+
this port's own ``metaobjects.config.yaml`` rung 2, matching the scope the
|
|
1105
|
+
C# and Java ports also keep for `fmt`), optionally narrowed by explicit
|
|
1106
|
+
file arguments.
|
|
1107
|
+
"""
|
|
1108
|
+
from metaobjects import fmt
|
|
1109
|
+
from metaobjects.core_types import core_providers
|
|
1110
|
+
from metaobjects.library import library_sources
|
|
1111
|
+
from metaobjects.provider import compose_registry
|
|
1112
|
+
|
|
1113
|
+
providers_extra, providers_ok = _providers_from_args(args)
|
|
1114
|
+
if not providers_ok:
|
|
1115
|
+
return 1
|
|
1116
|
+
providers = [*core_providers, *providers_extra]
|
|
1117
|
+
registry = compose_registry(providers)
|
|
1118
|
+
|
|
1119
|
+
cwd = Path.cwd()
|
|
1120
|
+
collection = _resolve_metadata_location_or_print_error(None, cwd)
|
|
1121
|
+
if collection is None:
|
|
1122
|
+
return 1
|
|
1123
|
+
|
|
1124
|
+
all_files = list(collection.files)
|
|
1125
|
+
own_files = list(collection.own_files)
|
|
1126
|
+
|
|
1127
|
+
if args.files:
|
|
1128
|
+
wanted = [Path(f).resolve() for f in args.files]
|
|
1129
|
+
owned_set = {p.resolve() for p in own_files}
|
|
1130
|
+
missing = [str(p) for p in wanted if p not in owned_set]
|
|
1131
|
+
if missing:
|
|
1132
|
+
print(
|
|
1133
|
+
"error: not among this project's resolved metadata sources: "
|
|
1134
|
+
+ ", ".join(missing),
|
|
1135
|
+
file=sys.stderr,
|
|
1136
|
+
)
|
|
1137
|
+
print(
|
|
1138
|
+
"metaobjects fmt only formats files the metadata-location ladder "
|
|
1139
|
+
"already resolves — pass no arguments to format every one of them.",
|
|
1140
|
+
file=sys.stderr,
|
|
1141
|
+
)
|
|
1142
|
+
return 1
|
|
1143
|
+
wanted_set = {p for p in wanted}
|
|
1144
|
+
targets = [p for p in own_files if p.resolve() in wanted_set]
|
|
1145
|
+
else:
|
|
1146
|
+
targets = own_files
|
|
1147
|
+
|
|
1148
|
+
lib_sources = library_sources(list(collection.libraries)) if collection.libraries else []
|
|
1149
|
+
|
|
1150
|
+
result = fmt.run(
|
|
1151
|
+
all_files=all_files,
|
|
1152
|
+
target_files=targets,
|
|
1153
|
+
file_ids=dict(collection.file_ids),
|
|
1154
|
+
registry=registry,
|
|
1155
|
+
lib_sources=lib_sources,
|
|
1156
|
+
check=bool(args.check),
|
|
1157
|
+
providers=providers,
|
|
1158
|
+
)
|
|
1159
|
+
|
|
1160
|
+
if result.fatal is not None:
|
|
1161
|
+
print(f"error: {result.fatal}", file=sys.stderr)
|
|
1162
|
+
return 1
|
|
1163
|
+
|
|
1164
|
+
if not result.files:
|
|
1165
|
+
print("metaobjects fmt — no metadata files to format.")
|
|
1166
|
+
return 0
|
|
1167
|
+
|
|
1168
|
+
for report in result.files:
|
|
1169
|
+
print(_describe_fmt_report(report, cwd))
|
|
1170
|
+
|
|
1171
|
+
errored = sum(1 for r in result.files if r.status == fmt.STATUS_ERROR)
|
|
1172
|
+
needs_format = sum(1 for r in result.files if r.status == fmt.STATUS_WOULD_FORMAT)
|
|
1173
|
+
reformatted = sum(1 for r in result.files if r.status == fmt.STATUS_FORMATTED)
|
|
1174
|
+
|
|
1175
|
+
if args.check:
|
|
1176
|
+
if needs_format or errored:
|
|
1177
|
+
suffix = f", {errored} error(s)" if errored else ""
|
|
1178
|
+
print(
|
|
1179
|
+
f"metaobjects fmt --check — {needs_format} file(s) not canonical{suffix}. "
|
|
1180
|
+
"Run `metaobjects fmt` to fix.",
|
|
1181
|
+
file=sys.stderr,
|
|
1182
|
+
)
|
|
1183
|
+
return 1
|
|
1184
|
+
print("metaobjects fmt --check — every file is already canonical.")
|
|
1185
|
+
return 0
|
|
1186
|
+
|
|
1187
|
+
if errored:
|
|
1188
|
+
print(
|
|
1189
|
+
f"metaobjects fmt — {errored} file(s) could not be formatted safely (left unchanged).",
|
|
1190
|
+
file=sys.stderr,
|
|
1191
|
+
)
|
|
1192
|
+
return 1
|
|
1193
|
+
|
|
1194
|
+
print(f"metaobjects fmt — {reformatted} file(s) reformatted.")
|
|
1195
|
+
return 0
|
|
1196
|
+
|
|
1197
|
+
|
|
1072
1198
|
def _cmd_eject(args: argparse.Namespace) -> int:
|
|
1073
1199
|
"""Copy reference generators into ``codegen/generators/`` to own (ADR-0034 Am. 3)."""
|
|
1074
1200
|
root = Path(args.root) if args.root else Path.cwd()
|
|
@@ -2493,6 +2619,31 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
2493
2619
|
)
|
|
2494
2620
|
verify.set_defaults(func=_cmd_verify)
|
|
2495
2621
|
|
|
2622
|
+
fmt_p = sub.add_parser(
|
|
2623
|
+
"fmt",
|
|
2624
|
+
help="rewrite metadata files into canonical form (#304)",
|
|
2625
|
+
)
|
|
2626
|
+
fmt_p.add_argument(
|
|
2627
|
+
"files",
|
|
2628
|
+
nargs="*",
|
|
2629
|
+
metavar="FILE",
|
|
2630
|
+
help="optional explicit file(s) to format — must be members of this "
|
|
2631
|
+
"project's resolved metadata sources; omit to format every one of them",
|
|
2632
|
+
)
|
|
2633
|
+
fmt_p.add_argument(
|
|
2634
|
+
"--check",
|
|
2635
|
+
action="store_true",
|
|
2636
|
+
help="list files that are not canonical and exit non-zero; changes nothing",
|
|
2637
|
+
)
|
|
2638
|
+
fmt_p.add_argument(
|
|
2639
|
+
"--provider",
|
|
2640
|
+
action="append",
|
|
2641
|
+
default=None,
|
|
2642
|
+
metavar="MODULE:SYMBOL",
|
|
2643
|
+
help="consumer-registered Provider(s), repeatable (module:symbol)",
|
|
2644
|
+
)
|
|
2645
|
+
fmt_p.set_defaults(func=_cmd_fmt)
|
|
2646
|
+
|
|
2496
2647
|
eject_p = sub.add_parser(
|
|
2497
2648
|
"eject",
|
|
2498
2649
|
help="copy reference generators into codegen/generators/ to own and edit",
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
"""``metaobjects fmt`` (#304) — rewrite metadata files into the canonical form
|
|
2
|
+
the cross-port canonical serializer already produces. ``--check`` lists files
|
|
3
|
+
that are not canonical and exits non-zero without changing anything.
|
|
4
|
+
|
|
5
|
+
Mirrors the TS reference (``packages/metadata/src/fmt.ts`` +
|
|
6
|
+
``packages/cli/src/lib/fmt-engine.ts``) and the C#/Java ports exactly:
|
|
7
|
+
|
|
8
|
+
- Each file is formatted STANDALONE — own-mode, declared-here layer only
|
|
9
|
+
(ADR-0039). ``parse_document`` already builds a fresh, unmerged tree per
|
|
10
|
+
document (this port's loader does parse -> merge -> super-resolve as three
|
|
11
|
+
separate steps, unlike TS/C#'s accumulating-root parser) — calling it
|
|
12
|
+
directly and never running ``merge_roots``/``resolve_supers`` is what makes
|
|
13
|
+
a cross-file ``extends`` round-trip untouched (the raw ``super_ref`` string
|
|
14
|
+
is read directly by ``canonical_serialize``, never its resolution) and an
|
|
15
|
+
``overlay: true`` node never even attempt a merge.
|
|
16
|
+
- An ``overlay: true`` node is tagged (``is_overlay``) by the parser but never
|
|
17
|
+
resolved here — Python's overlay MERGE only happens in ``loader/merge.py``,
|
|
18
|
+
which this module never calls. A file containing one anywhere in its tree
|
|
19
|
+
(detected by :func:`_contains_overlay`) is reported as a skip rather than
|
|
20
|
+
guessed at — the concrete shape of "overlay files stay overlays".
|
|
21
|
+
- YAML is always skipped: no canonical YAML emitter exists (ADR-0006).
|
|
22
|
+
- Before writing, the whole project is reloaded with the candidate
|
|
23
|
+
substituted in via an in-memory source (never touching disk until proven
|
|
24
|
+
safe) and the write is refused unless that reload has no errors AND its
|
|
25
|
+
canonical form is byte-identical to the untouched baseline.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
from __future__ import annotations
|
|
29
|
+
|
|
30
|
+
import json
|
|
31
|
+
from dataclasses import dataclass, field
|
|
32
|
+
from pathlib import Path
|
|
33
|
+
from typing import Callable
|
|
34
|
+
|
|
35
|
+
from metaobjects.errors import MetaError
|
|
36
|
+
from metaobjects.loader.meta_data_loader import MetaDataLoader
|
|
37
|
+
from metaobjects.loader.sources.file_source import FileSource
|
|
38
|
+
from metaobjects.loader.sources.meta_data_source import InMemoryStringSource, MetaDataSource
|
|
39
|
+
from metaobjects.meta.meta_data import MetaData
|
|
40
|
+
from metaobjects.parser import parse_document
|
|
41
|
+
from metaobjects.registry import TypeRegistry
|
|
42
|
+
from metaobjects.serializer_json import canonical_serialize
|
|
43
|
+
|
|
44
|
+
#: Statuses a single file's run can report.
|
|
45
|
+
STATUS_FORMATTED = "formatted"
|
|
46
|
+
STATUS_WOULD_FORMAT = "would-format"
|
|
47
|
+
STATUS_UNCHANGED = "unchanged"
|
|
48
|
+
STATUS_SKIPPED_YAML = "skipped-yaml"
|
|
49
|
+
STATUS_SKIPPED_OVERLAY = "skipped-overlay"
|
|
50
|
+
STATUS_ERROR = "error"
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@dataclass
|
|
54
|
+
class FormatFileResult:
|
|
55
|
+
ok: bool
|
|
56
|
+
overlay: bool = False
|
|
57
|
+
text: str | None = None
|
|
58
|
+
message: str | None = None
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _contains_overlay(node: MetaData) -> bool:
|
|
62
|
+
"""Whether *node* or any descendant (own children only — this tree was
|
|
63
|
+
never merged, so every child already is its own) carries ``overlay: true``.
|
|
64
|
+
"""
|
|
65
|
+
if getattr(node, "is_overlay", False):
|
|
66
|
+
return True
|
|
67
|
+
return any(_contains_overlay(child) for child in node.own_children())
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def format_file(content: str, registry: TypeRegistry, source_id: str) -> FormatFileResult:
|
|
71
|
+
"""Format one file's own content. Parses *content* standalone via
|
|
72
|
+
:func:`parse_document` — never merged with any other file, super
|
|
73
|
+
resolution never run — and, on a clean parse with no overlay anywhere in
|
|
74
|
+
the tree, returns the canonical serialization of the resulting file root.
|
|
75
|
+
Never raises for a malformed file; that comes back as ``ok=False``.
|
|
76
|
+
"""
|
|
77
|
+
# Strip a UTF-8 BOM before parsing — json.loads rejects one outright
|
|
78
|
+
# ("Unexpected UTF-8 BOM (decode using utf-8-sig)"), and Java-authored
|
|
79
|
+
# files often carry one.
|
|
80
|
+
normalized = content[1:] if content.startswith("") else content
|
|
81
|
+
try:
|
|
82
|
+
doc = json.loads(normalized)
|
|
83
|
+
except json.JSONDecodeError as exc:
|
|
84
|
+
return FormatFileResult(ok=False, message=f"invalid JSON: {exc}")
|
|
85
|
+
|
|
86
|
+
try:
|
|
87
|
+
result = parse_document(doc, registry, source_id)
|
|
88
|
+
except Exception as exc: # a few structural failures raise rather than record
|
|
89
|
+
return FormatFileResult(ok=False, message=str(exc))
|
|
90
|
+
|
|
91
|
+
if result.errors:
|
|
92
|
+
message = "; ".join(e.message for e in result.errors)
|
|
93
|
+
return FormatFileResult(ok=False, message=message)
|
|
94
|
+
|
|
95
|
+
if _contains_overlay(result.root):
|
|
96
|
+
return FormatFileResult(
|
|
97
|
+
ok=False,
|
|
98
|
+
overlay=True,
|
|
99
|
+
message="this file declares an overlay fmt cannot resolve standalone",
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
return FormatFileResult(ok=True, text=canonical_serialize(result.root))
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@dataclass
|
|
106
|
+
class FileReport:
|
|
107
|
+
path: Path
|
|
108
|
+
status: str
|
|
109
|
+
detail: str | None = None
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
@dataclass
|
|
113
|
+
class RunResult:
|
|
114
|
+
files: list[FileReport] = field(default_factory=list)
|
|
115
|
+
fatal: str | None = None
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def run(
|
|
119
|
+
all_files: list[Path],
|
|
120
|
+
target_files: list[Path],
|
|
121
|
+
file_ids: dict[Path, str],
|
|
122
|
+
registry: TypeRegistry,
|
|
123
|
+
lib_sources: list[MetaDataSource],
|
|
124
|
+
check: bool,
|
|
125
|
+
providers: list[object] | None = None,
|
|
126
|
+
) -> RunResult:
|
|
127
|
+
"""Run fmt over *target_files* (a subset of *all_files* — the project's
|
|
128
|
+
FULL resolved file set, needed for the whole-project safety-check reload
|
|
129
|
+
regardless of which files are in scope to format).
|
|
130
|
+
|
|
131
|
+
*providers* must be the SAME provider list *registry* (used for every
|
|
132
|
+
standalone per-file parse) was composed from — the whole-project baseline
|
|
133
|
+
and safety-check reloads build their own `MetaDataLoader`, which recomposes
|
|
134
|
+
a registry from *providers* itself rather than taking `registry` directly,
|
|
135
|
+
so passing a different set here would silently check two vocabularies
|
|
136
|
+
against each other.
|
|
137
|
+
"""
|
|
138
|
+
|
|
139
|
+
def source_id(path: Path) -> str:
|
|
140
|
+
return file_ids.get(path) or path.name
|
|
141
|
+
|
|
142
|
+
def build_sources(
|
|
143
|
+
override_path: Path | None = None, override_text: str | None = None
|
|
144
|
+
) -> list[MetaDataSource]:
|
|
145
|
+
sources: list[MetaDataSource] = list(lib_sources)
|
|
146
|
+
for p in all_files:
|
|
147
|
+
if override_path is not None and p == override_path:
|
|
148
|
+
sources.append(InMemoryStringSource(override_text or "", id=source_id(p)))
|
|
149
|
+
else:
|
|
150
|
+
sources.append(FileSource(p, id=source_id(p)))
|
|
151
|
+
return sources
|
|
152
|
+
|
|
153
|
+
baseline = MetaDataLoader(providers=providers, strict=False).load(build_sources())
|
|
154
|
+
if baseline.errors:
|
|
155
|
+
detail = "\n".join(f" {e.code}: {e.message}" for e in baseline.errors)
|
|
156
|
+
return RunResult(
|
|
157
|
+
fatal=(
|
|
158
|
+
"this project's metadata does not currently load cleanly — fix the "
|
|
159
|
+
f"error(s) below, then re-run fmt:\n{detail}"
|
|
160
|
+
)
|
|
161
|
+
)
|
|
162
|
+
baseline_canonical = canonical_serialize(baseline.root)
|
|
163
|
+
|
|
164
|
+
reports: list[FileReport] = []
|
|
165
|
+
for path in target_files:
|
|
166
|
+
ext = path.suffix.lower()
|
|
167
|
+
if ext in (".yaml", ".yml"):
|
|
168
|
+
reports.append(
|
|
169
|
+
FileReport(
|
|
170
|
+
path,
|
|
171
|
+
STATUS_SKIPPED_YAML,
|
|
172
|
+
"no canonical YAML emitter exists (ADR-0006: JSON is the canonical "
|
|
173
|
+
"interchange form) — left untouched",
|
|
174
|
+
)
|
|
175
|
+
)
|
|
176
|
+
continue
|
|
177
|
+
|
|
178
|
+
content = path.read_text(encoding="utf-8")
|
|
179
|
+
formatted = format_file(content, registry, source_id(path))
|
|
180
|
+
if not formatted.ok:
|
|
181
|
+
reports.append(
|
|
182
|
+
FileReport(
|
|
183
|
+
path,
|
|
184
|
+
STATUS_SKIPPED_OVERLAY if formatted.overlay else STATUS_ERROR,
|
|
185
|
+
formatted.message,
|
|
186
|
+
)
|
|
187
|
+
)
|
|
188
|
+
continue
|
|
189
|
+
|
|
190
|
+
if formatted.text == content:
|
|
191
|
+
reports.append(FileReport(path, STATUS_UNCHANGED))
|
|
192
|
+
continue
|
|
193
|
+
|
|
194
|
+
test_load = MetaDataLoader(providers=providers, strict=False).load(
|
|
195
|
+
build_sources(path, formatted.text)
|
|
196
|
+
)
|
|
197
|
+
safe = not test_load.errors and canonical_serialize(test_load.root) == baseline_canonical
|
|
198
|
+
if not safe:
|
|
199
|
+
reports.append(
|
|
200
|
+
FileReport(
|
|
201
|
+
path,
|
|
202
|
+
STATUS_ERROR,
|
|
203
|
+
"formatting this file would change the loaded model's meaning — left unchanged",
|
|
204
|
+
)
|
|
205
|
+
)
|
|
206
|
+
continue
|
|
207
|
+
|
|
208
|
+
if check:
|
|
209
|
+
reports.append(FileReport(path, STATUS_WOULD_FORMAT))
|
|
210
|
+
else:
|
|
211
|
+
path.write_text(formatted.text, encoding="utf-8")
|
|
212
|
+
reports.append(FileReport(path, STATUS_FORMATTED))
|
|
213
|
+
|
|
214
|
+
return RunResult(files=reports)
|
|
@@ -25,6 +25,7 @@ from metaobjects.render.extract.types import (
|
|
|
25
25
|
Format,
|
|
26
26
|
Normalizer,
|
|
27
27
|
OnField,
|
|
28
|
+
OnLocate,
|
|
28
29
|
ExtractOptions,
|
|
29
30
|
ExtractionOutcome,
|
|
30
31
|
ExtractSchema,
|
|
@@ -45,6 +46,7 @@ __all__ = [
|
|
|
45
46
|
"JsonForgivingReader",
|
|
46
47
|
"Normalizer",
|
|
47
48
|
"OnField",
|
|
49
|
+
"OnLocate",
|
|
48
50
|
"ExtractOptions",
|
|
49
51
|
"ExtractionOutcome",
|
|
50
52
|
"ExtractSchema",
|
|
@@ -38,23 +38,55 @@ def extract(
|
|
|
38
38
|
stripped = _strip.strip(text)
|
|
39
39
|
ci = o.tolerance != Tolerance.STRICT
|
|
40
40
|
|
|
41
|
+
# FR-364: the document-level on_locate hook runs BEFORE the default locator. A non-None
|
|
42
|
+
# return is the payload span, fed into the same format-specific forgiving reader a
|
|
43
|
+
# default-located span would be — tolerance/coercion/normalizers/on_field downstream are
|
|
44
|
+
# unaffected. A None return (or no hook) falls through to the default locate strategy
|
|
45
|
+
# unchanged. A raised exception propagates (not swallowed), matching on_field.
|
|
46
|
+
located: str | None = None if o.on_locate is None else o.on_locate(text if text is not None else "", schema.format)
|
|
47
|
+
|
|
41
48
|
# XML rootless (opts.rootless): the payload's fields ARE the top-level elements — there
|
|
42
49
|
# is no enclosing root to locate — so parse the whole stripped text's top-level elements
|
|
43
50
|
# directly. Otherwise locate the <rootName> span as before. JSON is unaffected. Mirrors
|
|
44
51
|
# Java Extract.extract.
|
|
45
52
|
span: str | None
|
|
46
53
|
raw: dict[str, object]
|
|
47
|
-
|
|
54
|
+
# Whether the SELECTED region (not the original reply) was itself empty/blank — the signal
|
|
55
|
+
# mark_empty() keys on below. For the default path this is the pre-existing "nothing to
|
|
56
|
+
# parse" test (stripped == "" or span is None); for on_locate it must be judged against the
|
|
57
|
+
# hook's OWN return, not the original text — otherwise an empty located span (e.g. the hook
|
|
58
|
+
# deliberately returning "") reports "not empty" while every field is LOST, because the
|
|
59
|
+
# original reply was non-blank even though the thing on_locate chose to parse was.
|
|
60
|
+
region_blank: bool
|
|
61
|
+
if located is not None:
|
|
62
|
+
span = located
|
|
63
|
+
if schema.format == Format.JSON:
|
|
64
|
+
raw = JsonForgivingReader().read(located)
|
|
65
|
+
elif o.rootless:
|
|
66
|
+
raw = XmlForgivingReader().read_rootless(located, ci)
|
|
67
|
+
else:
|
|
68
|
+
raw = XmlForgivingReader().read(located, ci)
|
|
69
|
+
# `from_` is deliberately left empty rather than the original text: a document-level
|
|
70
|
+
# audit entry that copied the whole raw reply (which can be arbitrarily large) into the
|
|
71
|
+
# report on every call would make the report itself heavy for no benefit `to`'s length
|
|
72
|
+
# doesn't already give. `to` is the located span's LENGTH, not the span text, for the
|
|
73
|
+
# same reason.
|
|
74
|
+
report.add_coercion(Coercion("", "", str(len(located)), "onLocate"))
|
|
75
|
+
region_blank = located.strip() == ""
|
|
76
|
+
elif schema.format == Format.JSON:
|
|
48
77
|
span = _select_json(text, stripped, schema.fields, ci)
|
|
49
78
|
raw = {} if span is None else JsonForgivingReader().read(span)
|
|
79
|
+
region_blank = stripped == "" or span is None
|
|
50
80
|
elif o.rootless:
|
|
51
81
|
span = None if stripped == "" else stripped
|
|
52
82
|
raw = {} if span is None else XmlForgivingReader().read_rootless(stripped, ci)
|
|
83
|
+
region_blank = stripped == "" or span is None
|
|
53
84
|
else:
|
|
54
85
|
span = _locate.xml(stripped, schema.root_name, ci)
|
|
55
86
|
raw = {} if span is None else XmlForgivingReader().read(span, ci)
|
|
87
|
+
region_blank = stripped == "" or span is None
|
|
56
88
|
|
|
57
|
-
if not raw and
|
|
89
|
+
if not raw and region_blank:
|
|
58
90
|
report.mark_empty()
|
|
59
91
|
|
|
60
92
|
_extract(schema.fields, raw, "", data, report, o, ci)
|
|
@@ -27,6 +27,17 @@ T = TypeVar("T")
|
|
|
27
27
|
OnField = Callable[[str, str, "FieldSpec"], object | None]
|
|
28
28
|
Normalizer = Callable[[str], object | None]
|
|
29
29
|
|
|
30
|
+
# FR-364: the document-level locate hook: (text, format) -> payload substring | None.
|
|
31
|
+
# Receives the RAW reply text (never None; an absent document is normalized to ``""``
|
|
32
|
+
# before the hook runs) and the schema's declared ``Format``. Returning ``None`` falls
|
|
33
|
+
# back to the default locator (the fenced-then-first-object rule #363 fixed). A non-None
|
|
34
|
+
# return is parsed by the normal pipeline exactly like a default-located span —
|
|
35
|
+
# tolerance, coercion, ``normalizers``, and ``on_field`` downstream are unaffected — so
|
|
36
|
+
# this hook only decides WHICH text is the payload, never how it is parsed. The returned
|
|
37
|
+
# text need not be a literal substring of the input (the hook may synthesize it). A
|
|
38
|
+
# raised exception propagates (it is not swallowed), matching ``on_field``.
|
|
39
|
+
OnLocate = Callable[[str, "Format"], "str | None"]
|
|
40
|
+
|
|
30
41
|
|
|
31
42
|
class Format(Enum):
|
|
32
43
|
"""Document format the extract pipeline targets."""
|
|
@@ -248,7 +259,8 @@ class ExtractOptions:
|
|
|
248
259
|
"""Bounded runtime override surface (the "20%").
|
|
249
260
|
|
|
250
261
|
``aliases``/``normalizers`` are MERGED with the schema's, runtime winning on key
|
|
251
|
-
conflict. ``on_field`` is the single bespoke-coercion hook
|
|
262
|
+
conflict. ``on_field`` is the single per-field bespoke-coercion hook; ``on_locate``
|
|
263
|
+
is the single document-level (payload-location) hook.
|
|
252
264
|
|
|
253
265
|
``rootless`` (XML only): when ``True``, the input has NO enclosing root element —
|
|
254
266
|
the payload's fields ARE the top-level elements (a flat sequence like
|
|
@@ -259,6 +271,7 @@ class ExtractOptions:
|
|
|
259
271
|
aliases: dict[str, str] = field(default_factory=dict)
|
|
260
272
|
normalizers: dict[str, Normalizer] = field(default_factory=dict)
|
|
261
273
|
on_field: OnField | None = None
|
|
274
|
+
on_locate: OnLocate | None = None
|
|
262
275
|
rootless: bool = False
|
|
263
276
|
|
|
264
277
|
@staticmethod
|
|
@@ -271,6 +284,19 @@ class ExtractOptions:
|
|
|
271
284
|
aliases=dict(self.aliases),
|
|
272
285
|
normalizers=dict(self.normalizers),
|
|
273
286
|
on_field=self.on_field,
|
|
287
|
+
on_locate=self.on_locate,
|
|
288
|
+
rootless=self.rootless,
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
def with_on_locate(self, hook: OnLocate | None) -> "ExtractOptions":
|
|
292
|
+
"""The single document-level locate hook. See ``OnLocate``. Returns a copy
|
|
293
|
+
with ``on_locate`` set."""
|
|
294
|
+
return ExtractOptions(
|
|
295
|
+
tolerance=self.tolerance,
|
|
296
|
+
aliases=dict(self.aliases),
|
|
297
|
+
normalizers=dict(self.normalizers),
|
|
298
|
+
on_field=self.on_field,
|
|
299
|
+
on_locate=hook,
|
|
274
300
|
rootless=self.rootless,
|
|
275
301
|
)
|
|
276
302
|
|
|
@@ -282,6 +308,7 @@ class ExtractOptions:
|
|
|
282
308
|
aliases=dict(self.aliases),
|
|
283
309
|
normalizers=dict(self.normalizers),
|
|
284
310
|
on_field=self.on_field,
|
|
311
|
+
on_locate=self.on_locate,
|
|
285
312
|
rootless=r,
|
|
286
313
|
)
|
|
287
314
|
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"""`metaobjects fmt` (#304) — rewrite metadata files into canonical form.
|
|
2
|
+
|
|
3
|
+
Mirrors the TS reference's integration test and the C#/Java ports' Mojo/CLI
|
|
4
|
+
tests: a messy file reformats, an already-canonical file is untouched, an
|
|
5
|
+
overlay file with no local base is skipped, and a YAML file is always
|
|
6
|
+
skipped. No `metadata_dir` positional — matches the TS reference surface:
|
|
7
|
+
files come from the neutral `.metaobjects/config.json` ladder (here, just
|
|
8
|
+
the default `metaobjects/` directory, since no project in these tests
|
|
9
|
+
declares a config), optionally narrowed by explicit file arguments.
|
|
10
|
+
"""
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import json
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
import pytest
|
|
17
|
+
|
|
18
|
+
from metaobjects.cli import main
|
|
19
|
+
|
|
20
|
+
MESSY_WIDGET = """\
|
|
21
|
+
{ "metadata.root": { "package": "acme", "children": [
|
|
22
|
+
{ "object.entity": { "@description": "A widget", "name": "Widget", "children": [
|
|
23
|
+
{ "field.string": { "name": "sku", "@maxLength": 40, "@required": true } }
|
|
24
|
+
]}}
|
|
25
|
+
]}}
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
CANONICAL_GADGET = (
|
|
29
|
+
"{\n \"metadata.root\": {\n \"package\": \"acme\",\n \"children\": [\n"
|
|
30
|
+
" {\n \"object.entity\": {\n \"name\": \"Gadget\",\n"
|
|
31
|
+
" \"children\": [\n {\n \"field.string\": {\n"
|
|
32
|
+
" \"name\": \"label\"\n }\n }\n ]\n"
|
|
33
|
+
" }\n }\n ]\n }\n}\n"
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
OVERLAY_WIDGET_UI = """\
|
|
37
|
+
{ "metadata.root": { "package": "acme", "children": [
|
|
38
|
+
{ "object.entity": { "name": "Widget", "overlay": true, "children": [
|
|
39
|
+
{ "field.string": { "name": "notes" } }
|
|
40
|
+
]}}
|
|
41
|
+
]}}
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
EXTRA_YAML = "metadata:\n package: acme\n children: []\n"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _project(tmp_path: Path) -> Path:
|
|
48
|
+
meta = tmp_path / "metaobjects"
|
|
49
|
+
meta.mkdir()
|
|
50
|
+
(meta / "meta.base.json").write_text(MESSY_WIDGET, encoding="utf-8")
|
|
51
|
+
(meta / "meta.already-canonical.json").write_text(CANONICAL_GADGET, encoding="utf-8")
|
|
52
|
+
(meta / "meta.widget.ui.json").write_text(OVERLAY_WIDGET_UI, encoding="utf-8")
|
|
53
|
+
(meta / "meta.extra.yaml").write_text(EXTRA_YAML, encoding="utf-8")
|
|
54
|
+
return meta
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def test_reformats_messy_leaves_canonical_skips_overlay_and_yaml(tmp_path, monkeypatch, capsys) -> None:
|
|
58
|
+
meta = _project(tmp_path)
|
|
59
|
+
monkeypatch.chdir(tmp_path)
|
|
60
|
+
|
|
61
|
+
rc = main(["fmt"])
|
|
62
|
+
assert rc == 0
|
|
63
|
+
|
|
64
|
+
out = capsys.readouterr().out
|
|
65
|
+
assert "reformatted" in out
|
|
66
|
+
assert "skipped (yaml)" in out
|
|
67
|
+
assert "skipped (overlay)" in out
|
|
68
|
+
|
|
69
|
+
assert (meta / "meta.already-canonical.json").read_text(encoding="utf-8") == CANONICAL_GADGET
|
|
70
|
+
assert (meta / "meta.base.json").read_text(encoding="utf-8") != MESSY_WIDGET
|
|
71
|
+
assert (meta / "meta.widget.ui.json").read_text(encoding="utf-8") == OVERLAY_WIDGET_UI
|
|
72
|
+
assert (meta / "meta.extra.yaml").read_text(encoding="utf-8") == EXTRA_YAML
|
|
73
|
+
|
|
74
|
+
# The reformatted file round-trips to valid, equivalent JSON.
|
|
75
|
+
reformatted = json.loads((meta / "meta.base.json").read_text(encoding="utf-8"))
|
|
76
|
+
assert reformatted["metadata.root"]["package"] == "acme"
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def test_is_idempotent(tmp_path, monkeypatch) -> None:
|
|
80
|
+
_project(tmp_path)
|
|
81
|
+
monkeypatch.chdir(tmp_path)
|
|
82
|
+
assert main(["fmt"]) == 0
|
|
83
|
+
once = (tmp_path / "metaobjects" / "meta.base.json").read_text(encoding="utf-8")
|
|
84
|
+
assert main(["fmt"]) == 0
|
|
85
|
+
assert (tmp_path / "metaobjects" / "meta.base.json").read_text(encoding="utf-8") == once
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def test_check_mode_lists_drift_and_changes_nothing(tmp_path, monkeypatch, capsys) -> None:
|
|
89
|
+
meta = _project(tmp_path)
|
|
90
|
+
monkeypatch.chdir(tmp_path)
|
|
91
|
+
before = (meta / "meta.base.json").read_text(encoding="utf-8")
|
|
92
|
+
|
|
93
|
+
rc = main(["fmt", "--check"])
|
|
94
|
+
assert rc == 1
|
|
95
|
+
assert "not canonical" in capsys.readouterr().err
|
|
96
|
+
|
|
97
|
+
assert (meta / "meta.base.json").read_text(encoding="utf-8") == before
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def test_check_mode_passes_once_canonical(tmp_path, monkeypatch) -> None:
|
|
101
|
+
_project(tmp_path)
|
|
102
|
+
monkeypatch.chdir(tmp_path)
|
|
103
|
+
assert main(["fmt"]) == 0
|
|
104
|
+
assert main(["fmt", "--check"]) == 0
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def test_explicit_file_narrows_the_run(tmp_path, monkeypatch) -> None:
|
|
108
|
+
meta = _project(tmp_path)
|
|
109
|
+
monkeypatch.chdir(tmp_path)
|
|
110
|
+
before_base = (meta / "meta.base.json").read_text(encoding="utf-8")
|
|
111
|
+
|
|
112
|
+
rc = main(["fmt", "metaobjects/meta.already-canonical.json"])
|
|
113
|
+
assert rc == 0
|
|
114
|
+
# Untouched — only the named (already-canonical) file was in scope.
|
|
115
|
+
assert (meta / "meta.base.json").read_text(encoding="utf-8") == before_base
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def test_explicit_file_outside_resolved_sources_fails(tmp_path, monkeypatch, capsys) -> None:
|
|
119
|
+
_project(tmp_path)
|
|
120
|
+
monkeypatch.chdir(tmp_path)
|
|
121
|
+
(tmp_path / "outside.json").write_text("{}", encoding="utf-8")
|
|
122
|
+
|
|
123
|
+
rc = main(["fmt", "outside.json"])
|
|
124
|
+
assert rc == 1
|
|
125
|
+
assert "not among this project's resolved metadata sources" in capsys.readouterr().err
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def test_refuses_when_project_does_not_load_cleanly(tmp_path, monkeypatch, capsys) -> None:
|
|
129
|
+
meta = tmp_path / "metaobjects"
|
|
130
|
+
meta.mkdir()
|
|
131
|
+
(meta / "bad.json").write_text("{ this is not valid json", encoding="utf-8")
|
|
132
|
+
monkeypatch.chdir(tmp_path)
|
|
133
|
+
|
|
134
|
+
rc = main(["fmt"])
|
|
135
|
+
assert rc == 1
|
|
136
|
+
assert "does not currently load cleanly" in capsys.readouterr().err
|
|
137
|
+
assert (meta / "bad.json").read_text(encoding="utf-8") == "{ this is not valid json"
|