metaobjects 0.15.12__tar.gz → 0.16.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.
Files changed (386) hide show
  1. {metaobjects-0.15.12 → metaobjects-0.16.0}/PKG-INFO +1 -1
  2. {metaobjects-0.15.12 → metaobjects-0.16.0}/pyproject.toml +1 -1
  3. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/codegen/KNOWN_GAPS.md +8 -0
  4. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/codegen/generators/entity_model.py +271 -27
  5. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/codegen/generators/m2m_codegen.py +3 -3
  6. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/codegen/generators/router_generator.py +158 -35
  7. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/render/extract/extract.py +6 -0
  8. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/render/extract/types.py +31 -0
  9. metaobjects-0.16.0/tests/codegen/golden/extends/expected/BaseEntity.py +23 -0
  10. metaobjects-0.16.0/tests/codegen/golden/extends/expected/Program.py +27 -0
  11. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/golden/nested-array/expected/AuthorBrief.py +2 -2
  12. metaobjects-0.16.0/tests/codegen/golden/vanilla/expected/Subscriber.py +26 -0
  13. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/test_entity_model.py +128 -9
  14. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/test_extends_abstract_field_inheritance.py +6 -3
  15. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/test_generator_extension_seams.py +2 -2
  16. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/test_inherit_without_restate_gate.py +8 -3
  17. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/test_inheritance_conformance.py +6 -2
  18. metaobjects-0.16.0/tests/codegen/test_router_generator.py +357 -0
  19. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/integration/api_contract_server.py +29 -0
  20. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/integration/generated_jsonb_app.py +3 -0
  21. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/integration/generated_m2m_app.py +7 -0
  22. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/integration/generated_router_app.py +6 -0
  23. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/integration/generated_tph_app.py +7 -0
  24. metaobjects-0.16.0/tests/integration/test_generated_router_uuid_pk.py +224 -0
  25. {metaobjects-0.15.12 → metaobjects-0.16.0}/uv.lock +1 -1
  26. metaobjects-0.15.12/tests/codegen/golden/extends/expected/BaseEntity.py +0 -11
  27. metaobjects-0.15.12/tests/codegen/golden/extends/expected/Program.py +0 -11
  28. metaobjects-0.15.12/tests/codegen/golden/vanilla/expected/Subscriber.py +0 -12
  29. metaobjects-0.15.12/tests/codegen/test_router_generator.py +0 -160
  30. {metaobjects-0.15.12 → metaobjects-0.16.0}/.gitignore +0 -0
  31. {metaobjects-0.15.12 → metaobjects-0.16.0}/LICENSE +0 -0
  32. {metaobjects-0.15.12 → metaobjects-0.16.0}/README.md +0 -0
  33. {metaobjects-0.15.12 → metaobjects-0.16.0}/hatch_build.py +0 -0
  34. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/__init__.py +0 -0
  35. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/agent_context/__init__.py +0 -0
  36. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/agent_context/scaffold.py +0 -0
  37. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/apidocs/__init__.py +0 -0
  38. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/apidocs/api_model.py +0 -0
  39. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/apidocs/builder.py +0 -0
  40. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/apidocs/naming.py +0 -0
  41. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/apidocs/paths.py +0 -0
  42. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/apidocs/renderer.py +0 -0
  43. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/attr_class_map.py +0 -0
  44. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/cli.py +0 -0
  45. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/codegen/__init__.py +0 -0
  46. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/codegen/config.py +0 -0
  47. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/codegen/constants.py +0 -0
  48. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/codegen/extract_delegate_emitter.py +0 -0
  49. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/codegen/extract_schema_emitter.py +0 -0
  50. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/codegen/format.py +0 -0
  51. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/codegen/fr010_field_mapping.py +0 -0
  52. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/codegen/generator.py +0 -0
  53. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/codegen/generator_registry.py +0 -0
  54. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/codegen/generators/__init__.py +0 -0
  55. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/codegen/generators/extractor_generator.py +0 -0
  56. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/codegen/generators/filter_allowlist_generator.py +0 -0
  57. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/codegen/generators/fr019_shared_enum.py +0 -0
  58. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/codegen/generators/output_parser_generator.py +0 -0
  59. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/codegen/generators/output_prompt_generator.py +0 -0
  60. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/codegen/generators/payload_vo_generator.py +0 -0
  61. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/codegen/generators/render_helper_generator.py +0 -0
  62. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/codegen/generators/template_generator.py +0 -0
  63. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/codegen/generators/tph_plan.py +0 -0
  64. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/codegen/generators/trace_helper_generator.py +0 -0
  65. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/codegen/instance_artifacts.py +0 -0
  66. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/codegen/output_format_spec_emitter.py +0 -0
  67. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/codegen/overwrite_policy.py +0 -0
  68. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/codegen/runner.py +0 -0
  69. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/codegen/runtime/__init__.py +0 -0
  70. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/codegen/runtime/filter_parser.py +0 -0
  71. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/codegen/template_codegen/__init__.py +0 -0
  72. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/codegen/template_codegen/output_pattern.py +0 -0
  73. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/codegen/template_codegen/template_data.py +0 -0
  74. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/codegen/template_codegen/template_spec.py +0 -0
  75. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/codegen/type_map.py +0 -0
  76. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/core_types.py +0 -0
  77. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/datatype.py +0 -0
  78. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/documentation/__init__.py +0 -0
  79. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/documentation/doc_constants.py +0 -0
  80. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/documentation/doc_provider.py +0 -0
  81. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/documentation/doc_schema.py +0 -0
  82. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/errors.py +0 -0
  83. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/loader/__init__.py +0 -0
  84. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/loader/merge.py +0 -0
  85. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/loader/meta_data_loader.py +0 -0
  86. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/loader/registered_validation.py +0 -0
  87. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/loader/sources/__init__.py +0 -0
  88. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/loader/sources/directory_source.py +0 -0
  89. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/loader/sources/file_source.py +0 -0
  90. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/loader/sources/meta_data_source.py +0 -0
  91. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/loader/sources/uri_source.py +0 -0
  92. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/loader/validate_discriminator.py +0 -0
  93. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/loader/validate_field_readonly.py +0 -0
  94. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/loader/validate_source_parameter_ref.py +0 -0
  95. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/loader/validate_source_physical_names.py +0 -0
  96. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/loader/validation_passes.py +0 -0
  97. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/__init__.py +0 -0
  98. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/core/__init__.py +0 -0
  99. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/core/attr/__init__.py +0 -0
  100. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/core/attr/attr_constants.py +0 -0
  101. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/core/attr/meta_attr.py +0 -0
  102. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/core/field/__init__.py +0 -0
  103. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/core/field/field_constants.py +0 -0
  104. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/core/field/meta_field.py +0 -0
  105. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/core/identity/__init__.py +0 -0
  106. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/core/identity/identity_constants.py +0 -0
  107. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/core/identity/meta_identity.py +0 -0
  108. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/core/index/__init__.py +0 -0
  109. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/core/index/index_constants.py +0 -0
  110. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/core/index/meta_index.py +0 -0
  111. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/core/object/__init__.py +0 -0
  112. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/core/object/meta_object.py +0 -0
  113. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/core/object/meta_object_aware.py +0 -0
  114. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/core/object/object_class_registry.py +0 -0
  115. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/core/object/object_constants.py +0 -0
  116. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/core/object/object_extract.py +0 -0
  117. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/core/object/value_object.py +0 -0
  118. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/core/relationship/__init__.py +0 -0
  119. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/core/relationship/derive_m2m_fields.py +0 -0
  120. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/core/relationship/meta_relationship.py +0 -0
  121. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/core/relationship/relationship_constants.py +0 -0
  122. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/core/validator/__init__.py +0 -0
  123. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/core/validator/validator_constants.py +0 -0
  124. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/meta_data.py +0 -0
  125. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/meta_root.py +0 -0
  126. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/persistence/__init__.py +0 -0
  127. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/persistence/db/__init__.py +0 -0
  128. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/persistence/db/db_constants.py +0 -0
  129. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/persistence/db/db_provider.py +0 -0
  130. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/persistence/origin/__init__.py +0 -0
  131. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/persistence/origin/meta_origin.py +0 -0
  132. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/persistence/origin/origin_constants.py +0 -0
  133. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/persistence/source/__init__.py +0 -0
  134. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/persistence/source/meta_source.py +0 -0
  135. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/persistence/source/source_constants.py +0 -0
  136. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/presentation/__init__.py +0 -0
  137. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/presentation/layout/__init__.py +0 -0
  138. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/presentation/layout/layout_constants.py +0 -0
  139. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/presentation/layout/meta_layout.py +0 -0
  140. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/presentation/ui/__init__.py +0 -0
  141. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/presentation/ui/ui_provider.py +0 -0
  142. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/presentation/view/__init__.py +0 -0
  143. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/presentation/view/meta_view.py +0 -0
  144. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/presentation/view/view_constants.py +0 -0
  145. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/provider_extends.py +0 -0
  146. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/template/__init__.py +0 -0
  147. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/template/meta_template.py +0 -0
  148. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/template/prompt_provider.py +0 -0
  149. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/meta/template/template_constants.py +0 -0
  150. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/naming_refs.py +0 -0
  151. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/parser.py +0 -0
  152. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/parser_yaml.py +0 -0
  153. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/provider.py +0 -0
  154. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/py.typed +0 -0
  155. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/registry.py +0 -0
  156. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/registry_manifest.py +0 -0
  157. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/render/__init__.py +0 -0
  158. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/render/email_document.py +0 -0
  159. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/render/escapers.py +0 -0
  160. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/render/extract/KNOWN_GAPS.md +0 -0
  161. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/render/extract/__init__.py +0 -0
  162. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/render/extract/coerce.py +0 -0
  163. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/render/extract/extract_map.py +0 -0
  164. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/render/extract/json_forgiving_reader.py +0 -0
  165. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/render/extract/locate.py +0 -0
  166. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/render/extract/normalize.py +0 -0
  167. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/render/extract/strip.py +0 -0
  168. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/render/extract/xml_forgiving_reader.py +0 -0
  169. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/render/filesystem_provider.py +0 -0
  170. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/render/prompt/__init__.py +0 -0
  171. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/render/prompt/output_format_renderer.py +0 -0
  172. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/render/prompt/output_format_spec.py +0 -0
  173. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/render/prompt/prompt_field.py +0 -0
  174. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/render/prompt/prompt_overrides.py +0 -0
  175. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/render/prompt/prompt_style.py +0 -0
  176. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/render/renderer.py +0 -0
  177. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/render/verify.py +0 -0
  178. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/runtime/__init__.py +0 -0
  179. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/runtime/llm_recorder.py +0 -0
  180. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/runtime/n2m_resolver.py +0 -0
  181. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/runtime/object_manager.py +0 -0
  182. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/runtime/tph.py +0 -0
  183. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/serializer_json.py +0 -0
  184. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/shared/__init__.py +0 -0
  185. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/shared/base_types.py +0 -0
  186. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/shared/separators.py +0 -0
  187. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/shared/structural.py +0 -0
  188. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/source/__init__.py +0 -0
  189. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/source/error_source.py +0 -0
  190. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/source/json_path.py +0 -0
  191. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/source/semantic_diff.py +0 -0
  192. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/source/yaml_positions.py +0 -0
  193. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/spec_metamodel/__init__.py +0 -0
  194. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/spec_metamodel/attr.json +0 -0
  195. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/spec_metamodel/db.json +0 -0
  196. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/spec_metamodel/documentation.json +0 -0
  197. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/spec_metamodel/field.json +0 -0
  198. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/spec_metamodel/identity.json +0 -0
  199. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/spec_metamodel/index.json +0 -0
  200. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/spec_metamodel/layout.json +0 -0
  201. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/spec_metamodel/object.json +0 -0
  202. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/spec_metamodel/origin.json +0 -0
  203. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/spec_metamodel/prompt.json +0 -0
  204. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/spec_metamodel/relationship.json +0 -0
  205. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/spec_metamodel/source.json +0 -0
  206. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/spec_metamodel/template.json +0 -0
  207. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/spec_metamodel/ui.json +0 -0
  208. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/spec_metamodel/validator.json +0 -0
  209. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/spec_metamodel/view.json +0 -0
  210. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/super_resolve.py +0 -0
  211. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/validation_types.py +0 -0
  212. {metaobjects-0.15.12 → metaobjects-0.16.0}/src/metaobjects/yaml_desugar.py +0 -0
  213. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/__init__.py +0 -0
  214. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/__init__.py +0 -0
  215. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/golden/extends/meta.json +0 -0
  216. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/golden/nested-array/expected/PostBrief.py +0 -0
  217. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/golden/nested-array/meta.json +0 -0
  218. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/golden/scalars/expected/Metric.py +0 -0
  219. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/golden/scalars/expected/Report.py +0 -0
  220. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/golden/scalars/meta.json +0 -0
  221. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/golden/vanilla/meta.json +0 -0
  222. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/test_abstract_conformance.py +0 -0
  223. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/test_api_docs_builder.py +0 -0
  224. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/test_api_docs_paths.py +0 -0
  225. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/test_cli.py +0 -0
  226. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/test_cli_providers.py +0 -0
  227. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/test_cli_registry.py +0 -0
  228. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/test_cli_staleness_nudge.py +0 -0
  229. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/test_cli_verify_strict.py +0 -0
  230. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/test_cli_verify_subverbs.py +0 -0
  231. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/test_constants_config.py +0 -0
  232. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/test_enum_conformance.py +0 -0
  233. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/test_extractor_generator.py +0 -0
  234. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/test_filter_allowlist_generator.py +0 -0
  235. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/test_format.py +0 -0
  236. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/test_fr010_nested_object_prompt.py +0 -0
  237. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/test_fr010_output_codegen.py +0 -0
  238. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/test_fr019_shared_provided_conformance.py +0 -0
  239. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/test_generator.py +0 -0
  240. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/test_golden.py +0 -0
  241. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/test_instance_artifacts.py +0 -0
  242. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/test_m2m_codegen.py +0 -0
  243. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/test_output_parser_generator.py +0 -0
  244. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/test_overwrite_policy.py +0 -0
  245. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/test_payload_vo_generator.py +0 -0
  246. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/test_projection_compile.py +0 -0
  247. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/test_render_helper_conformance.py +0 -0
  248. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/test_render_helper_generator.py +0 -0
  249. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/test_reverse_finders.py +0 -0
  250. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/test_runner.py +0 -0
  251. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/test_template_data.py +0 -0
  252. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/test_template_generator.py +0 -0
  253. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/test_template_output_pattern.py +0 -0
  254. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/test_template_scope_helpers.py +0 -0
  255. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/test_template_scope_walk.py +0 -0
  256. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/test_template_spec.py +0 -0
  257. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/test_tph_codegen.py +0 -0
  258. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/test_trace_helper_generator.py +0 -0
  259. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/test_type_map.py +0 -0
  260. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/codegen/test_validation_conformance.py +0 -0
  261. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/conformance/__init__.py +0 -0
  262. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/conformance/capabilities.py +0 -0
  263. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/conformance/conformance-expected-failures.json +0 -0
  264. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/conformance/conformance_adapter.py +0 -0
  265. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/conformance/corpus.py +0 -0
  266. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/conformance/expected_failures.py +0 -0
  267. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/conformance/fixture_discovery.py +0 -0
  268. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/conformance/navigator.py +0 -0
  269. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/conformance/test_api_docs_cross_port_conformance.py +0 -0
  270. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/conformance/test_conformance.py +0 -0
  271. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/conformance/test_extract_conformance.py +0 -0
  272. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/conformance/test_extract_object_verdict.py +0 -0
  273. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/conformance/test_fr010_loader_attrs.py +0 -0
  274. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/conformance/test_fr011_attrs.py +0 -0
  275. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/conformance/test_generator_registry_conformance.py +0 -0
  276. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/conformance/test_object_model_conformance.py +0 -0
  277. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/conformance/test_provider_composition_conformance.py +0 -0
  278. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/conformance/test_registry_conformance.py +0 -0
  279. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/conformance/test_runner_hardfail.py +0 -0
  280. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/conformance/test_spec_metamodel_embed.py +0 -0
  281. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/conformance/test_strict_attr_load.py +0 -0
  282. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/conformance/test_template_codegen_conformance.py +0 -0
  283. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/conformance/test_template_generator_conformance.py +0 -0
  284. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/conformance/test_yaml_conformance.py +0 -0
  285. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/conformance/yaml-conformance-expected-failures.json +0 -0
  286. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/integration/__init__.py +0 -0
  287. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/integration/api_contract_assertions.py +0 -0
  288. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/integration/api_contract_jsonb_server.py +0 -0
  289. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/integration/api_contract_m2m_server.py +0 -0
  290. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/integration/meta_ai_trace.yaml +0 -0
  291. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/integration/normalization.py +0 -0
  292. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/integration/postgres_container.py +0 -0
  293. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/integration/query_runner.py +0 -0
  294. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/integration/scenarios.py +0 -0
  295. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/integration/test_api_contract.py +0 -0
  296. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/integration/test_api_contract_generated.py +0 -0
  297. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/integration/test_api_contract_jsonb.py +0 -0
  298. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/integration/test_api_contract_m2m.py +0 -0
  299. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/integration/test_api_contract_m2m_generated.py +0 -0
  300. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/integration/test_api_contract_tph_generated.py +0 -0
  301. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/integration/test_llm_call_trace.py +0 -0
  302. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/integration/test_normalization.py +0 -0
  303. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/integration/test_query_scenarios.py +0 -0
  304. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/integration/test_runtime_return_types.py +0 -0
  305. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/open_closed_proof_test.py +0 -0
  306. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/render/__init__.py +0 -0
  307. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/render/extract/__init__.py +0 -0
  308. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/render/extract/test_coerce.py +0 -0
  309. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/render/extract/test_extract.py +0 -0
  310. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/render/extract/test_extract_map.py +0 -0
  311. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/render/extract/test_json_forgiving_reader.py +0 -0
  312. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/render/extract/test_locate.py +0 -0
  313. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/render/extract/test_model.py +0 -0
  314. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/render/extract/test_normalize.py +0 -0
  315. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/render/extract/test_strip.py +0 -0
  316. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/render/extract/test_xml_forgiving_reader.py +0 -0
  317. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/render/prompt/__init__.py +0 -0
  318. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/render/prompt/test_output_format_renderer.py +0 -0
  319. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/render/test_email_document.py +0 -0
  320. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/render/test_filesystem_provider.py +0 -0
  321. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/render/test_output_format_renderer_nested.py +0 -0
  322. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/render/test_output_prompt_conformance.py +0 -0
  323. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/render/test_render_conformance.py +0 -0
  324. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/render/test_render_max_chars.py +0 -0
  325. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/render/test_verify.py +0 -0
  326. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/render/test_verify_conformance.py +0 -0
  327. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/source/__init__.py +0 -0
  328. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/source/test_fr5c_merge_attribution.py +0 -0
  329. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/source/test_fr5d_reference_resolution.py +0 -0
  330. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/source/test_fr5e_database_source_shape.py +0 -0
  331. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/source/test_json_path.py +0 -0
  332. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/source/test_semantic_diff.py +0 -0
  333. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/source/test_source_on_node.py +0 -0
  334. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/source/test_yaml_positions.py +0 -0
  335. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/test_api_docs_accuracy.py +0 -0
  336. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/__init__.py +0 -0
  337. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_agent_context_staleness.py +0 -0
  338. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_capabilities.py +0 -0
  339. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_common_attrs.py +0 -0
  340. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_core_types.py +0 -0
  341. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_effective_package.py +0 -0
  342. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_errors.py +0 -0
  343. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_field_enum.py +0 -0
  344. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_field_map_validation.py +0 -0
  345. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_field_uuid_dbcolumntype.py +0 -0
  346. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_fr016_source_name_and_kind_aliases.py +0 -0
  347. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_index_lookup.py +0 -0
  348. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_llm_recorder.py +0 -0
  349. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_loader.py +0 -0
  350. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_loader_bom.py +0 -0
  351. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_loader_class.py +0 -0
  352. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_merge.py +0 -0
  353. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_meta_attr.py +0 -0
  354. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_meta_data.py +0 -0
  355. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_meta_source.py +0 -0
  356. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_module_shortcuts.py +0 -0
  357. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_n2m_resolver.py +0 -0
  358. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_n2m_resolver_inherited.py +0 -0
  359. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_object_manager_uuid_coercion.py +0 -0
  360. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_one_primary_source.py +0 -0
  361. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_parser.py +0 -0
  362. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_parser_yaml.py +0 -0
  363. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_provider.py +0 -0
  364. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_provider_extension.py +0 -0
  365. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_registry.py +0 -0
  366. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_registry_completeness.py +0 -0
  367. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_registry_extend.py +0 -0
  368. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_registry_sealed.py +0 -0
  369. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_relationship_referential_actions.py +0 -0
  370. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_resolution_key.py +0 -0
  371. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_runtime_resolution_key_binding.py +0 -0
  372. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_serializer.py +0 -0
  373. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_shared_constants.py +0 -0
  374. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_smoke.py +0 -0
  375. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_sources.py +0 -0
  376. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_strict_child_placement.py +0 -0
  377. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_super_resolve.py +0 -0
  378. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_template_toolcall.py +0 -0
  379. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_template_wrong_subtype_attrs.py +0 -0
  380. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_validation_attr_schema.py +0 -0
  381. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_validation_filter_values.py +0 -0
  382. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_validation_origin_paths.py +0 -0
  383. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_validation_sort_field.py +0 -0
  384. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_validation_warnings.py +0 -0
  385. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_write_coercion_arrays.py +0 -0
  386. {metaobjects-0.15.12 → metaobjects-0.16.0}/tests/unit/test_yaml_desugar.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: metaobjects
3
- Version: 0.15.12
3
+ Version: 0.16.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.15.12"
3
+ version = "0.16.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
@@ -5,7 +5,14 @@ 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
17
  from metaobjects.shared.base_types import TYPE_VALIDATOR
11
18
  from metaobjects.shared.separators import PACKAGE_SEP
@@ -98,7 +105,10 @@ def _validator_constraints(field: MetaField) -> dict[str, object]:
98
105
  max_len = _first_attr(field, vc.VALIDATOR_SUBTYPE_LENGTH, vc.VALIDATOR_ATTR_MAX)
99
106
  field_max = field.attrs().get(fc.FIELD_ATTR_MAX_LENGTH)
100
107
  if _is_int(field_max):
101
- max_len = field_max
108
+ # FR-036 A3 — strictest-wins: when BOTH a validator.length @max and a field
109
+ # @maxLength cap a string, the smaller bound governs (never the field attr
110
+ # unconditionally, which could loosen a tighter validator).
111
+ max_len = field_max if max_len is None else min(max_len, field_max)
102
112
  if min_len is not None:
103
113
  kwargs["min_length"] = min_len
104
114
  if max_len is not None:
@@ -127,7 +137,11 @@ def _validator_constraints(field: MetaField) -> dict[str, object]:
127
137
  # located via the resolving field.children() in _validators().
128
138
  pattern = v.attr(vc.VALIDATOR_ATTR_PATTERN)
129
139
  if isinstance(pattern, str):
130
- kwargs["pattern"] = pattern
140
+ # FR-036 Pin 2 — a validator.regex is a FULL match. Pydantic v2 `pattern=`
141
+ # is search/partial (un-anchored), so wrap the authored expression in an
142
+ # anchored non-capturing group so alternation still spans the whole value
143
+ # (a redundant anchor on an already-anchored pattern matches identically).
144
+ kwargs["pattern"] = f"^(?:{pattern})$"
131
145
  break
132
146
 
133
147
  # ADR-0036/0037 Wave 3 — @stringFormat: hostname -> a codegen-owned canonical
@@ -137,60 +151,87 @@ def _validator_constraints(field: MetaField) -> dict[str, object]:
137
151
  if field.attrs().get(fc.FIELD_ATTR_STRING_FORMAT) == fc.STRING_FORMAT_HOSTNAME:
138
152
  kwargs["pattern"] = _HOSTNAME_REGEX
139
153
 
154
+ # FR-036 Pin 1 — a @required non-array string is non-empty: reject null / "" but
155
+ # ACCEPT whitespace-only. Emit an implicit min_length of 1, keeping the stricter
156
+ # floor if a validator.length @min already set one.
157
+ if (
158
+ field.sub_type == fc.FIELD_SUBTYPE_STRING
159
+ and not field_is_array(field)
160
+ and field.attrs().get(fc.FIELD_ATTR_REQUIRED) is True
161
+ ):
162
+ existing_min = kwargs.get("min_length")
163
+ kwargs["min_length"] = max(1, existing_min) if isinstance(existing_min, int) else 1
164
+
140
165
  return kwargs
141
166
 
142
167
 
143
- def _field_line(field: MetaField, imports: set[str], config: GenConfig) -> tuple[str, bool]:
144
- """Return (source line, uses_field). Collects required imports into *imports*."""
145
- # FR-019: a field resolving to a package-level shared enum is typed by the materialized
146
- # standalone class (imported from the shared `enums` module) or — when @provided — an
147
- # external module from per-port config; inline enums keep their `Literal[...]` (py_type_for).
168
+ # Constraint kwargs are emitted in this fixed order so generated output is deterministic.
169
+ _CONSTRAINT_ORDER = ("pattern", "ge", "le", "min_length", "max_length")
170
+
171
+
172
+ def _constraint_parts(constraints: dict[str, object]) -> list[str]:
173
+ """The ``Field(...)`` constraint kwargs rendered in the stable, deterministic order."""
174
+ return [f"{k}={constraints[k]!r}" for k in _CONSTRAINT_ORDER if k in constraints]
175
+
176
+
177
+ def _type_expr_for_field(
178
+ field: MetaField, imports: set[str], config: GenConfig
179
+ ) -> tuple[str, str | None]:
180
+ """The Python annotation text for *field* (arrays wrapped in ``list[...]``) plus the
181
+ shared-enum class name when the field resolves to one (used to render an enum
182
+ default). Collects any required imports into *imports*. Shared by the create-model
183
+ and patch-model emitters so both carry byte-identical typing.
184
+
185
+ FR-019: a field resolving to a package-level shared enum is typed by the materialized
186
+ standalone class (imported from the shared ``enums`` module) or — when @provided — an
187
+ external module; inline enums keep their ``Literal[...]`` (py_type_for)."""
148
188
  shared = (
149
189
  fr019.shared_enum_for_field(field)
150
190
  if field.sub_type == fc.FIELD_SUBTYPE_ENUM
151
191
  else None
152
192
  )
193
+ enum_type_name: str | None = None
153
194
  if shared is not None:
154
195
  type_name, import_line = fr019.enum_type_and_import(shared, config)
155
196
  imports.add(import_line)
197
+ enum_type_name = type_name
156
198
  type_expr = f"list[{type_name}]" if field_is_array(field) else type_name
157
199
  else:
158
200
  pt = py_type_for(field)
159
201
  imports.update(pt.imports)
160
202
  type_expr = pt.expr
161
- # ADR-0036/0037 Wave 3 — @stringFormat: email narrows a plain string to a
162
- # validated email. Bind Pydantic's idiomatic EmailStr (the Python analogue of
163
- # Zod .email()); hostname stays a plain str with a codegen-owned canonical
164
- # pattern= (handled in _validator_constraints). The field stays a string.
203
+ # ADR-0036/0037 Wave 3 — @stringFormat: email narrows a plain string to a validated
204
+ # email (Pydantic EmailStr, the Python analogue of Zod .email()); hostname stays a
205
+ # plain str with a codegen-owned canonical pattern= (in _validator_constraints).
165
206
  if (
166
207
  field.sub_type == fc.FIELD_SUBTYPE_STRING
167
208
  and field.attrs().get(fc.FIELD_ATTR_STRING_FORMAT) == fc.STRING_FORMAT_EMAIL
168
209
  ):
169
- base_expr = "EmailStr"
170
210
  imports.add("from pydantic import EmailStr")
171
- type_expr = f"list[{base_expr}]" if field_is_array(field) else base_expr
211
+ type_expr = "list[EmailStr]" if field_is_array(field) else "EmailStr"
172
212
  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
- # both reference a value-object by name; import it so the annotation
175
- # py_type_for emitted resolves.
213
+ # A field.object (-> VO) and a field.map with @objectRef (-> dict[str, VO]) both
214
+ # reference a value-object by name; import it so the emitted annotation resolves.
215
+ # @objectRef is FQN-expanded at load; the generated VOs live flat in one package,
216
+ # so import by the bare class name.
176
217
  ref = field.attrs().get(fc.FIELD_ATTR_OBJECT_REF)
177
218
  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
219
  ref_name = str(ref).split("::")[-1]
181
220
  imports.add(f"from .{ref_name} import {ref_name}")
221
+ return type_expr, enum_type_name
222
+
223
+
224
+ def _field_line(field: MetaField, imports: set[str], config: GenConfig) -> tuple[str, bool]:
225
+ """Return (source line, uses_field). Collects required imports into *imports*."""
226
+ type_expr, enum_type_name = _type_expr_for_field(field, imports, config)
182
227
  required = field.attrs().get(fc.FIELD_ATTR_REQUIRED) is True
183
228
  default_raw = field.attrs().get(fc.FIELD_ATTR_DEFAULT)
184
229
  # A server-side expression default (now(), gen_random_uuid(), CURRENT_TIMESTAMP)
185
230
  # is filled by the DB — the Python model carries no default for it (the field keeps
186
231
  # its required/optional shape). Only literal defaults become Pydantic defaults.
187
232
  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
233
 
190
- constraints = _validator_constraints(field)
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]
234
+ parts = _constraint_parts(_validator_constraints(field))
194
235
  uses_field = bool(parts)
195
236
 
196
237
  # @default — render an enum member (Type.MEMBER, UPPER_CASE) or a Python literal.
@@ -226,6 +267,106 @@ def _field_line(field: MetaField, imports: set[str], config: GenConfig) -> tuple
226
267
  return f" {py_name}: {annotation}{assignment}", uses_field
227
268
 
228
269
 
270
+ def _primary_identity_field_names(entity: MetaObject) -> list[str]:
271
+ """The field names in the entity's primary identity (``identity.primary @fields``),
272
+ normalized to a list. Empty when there is no primary identity. Mirrors the TS
273
+ ``primaryIdentityFieldNames``."""
274
+ primary = entity.primary_identity()
275
+ if primary is None:
276
+ return []
277
+ # ADR-0039: resolving (identity attr) — an identity.primary may be inherited via extends.
278
+ fields = primary.get_meta_attr(IDENTITY_ATTR_FIELDS)
279
+ if isinstance(fields, (list, tuple)):
280
+ return [str(f) for f in fields]
281
+ if isinstance(fields, str) and fields:
282
+ return [s.strip() for s in fields.split(",") if s.strip()]
283
+ return []
284
+
285
+
286
+ def _auto_gen_pk_field_names(entity: MetaObject) -> set[str]:
287
+ """Primary-key field names that the SERVER generates (``identity.primary
288
+ @generation: increment|uuid``) — excluded from the create-validation shape because
289
+ the caller never supplies them. An ``assigned`` (or ungenerated) PK is NOT excluded
290
+ (the caller provides it). Mirrors the TS ``autoGenPkFieldNames``."""
291
+ primary = entity.primary_identity()
292
+ if primary is None:
293
+ return set()
294
+ # ADR-0039: resolving (identity attr).
295
+ generation = primary.get_meta_attr(IDENTITY_ATTR_GENERATION)
296
+ if generation not in (GENERATION_INCREMENT, GENERATION_UUID):
297
+ return set()
298
+ return set(_primary_identity_field_names(entity))
299
+
300
+
301
+ def _create_field_line(
302
+ field: MetaField, imports: set[str], config: GenConfig
303
+ ) -> tuple[str, bool]:
304
+ """A CREATE-model field line (FR-036): keyed by the WIRE name (``field.name``) and
305
+ carrying the same per-field constraints as the read model. A ``@required`` field
306
+ that is SERVER-FILLED — it has ANY ``@default`` (a literal OR a SQL expression such
307
+ as ``now()`` / ``gen_random_uuid()`` / ``CURRENT_TIMESTAMP``) or an ``@autoSet`` — is
308
+ emitted OPTIONAL, because the DB / trigger provides it and a POST that omits it must
309
+ not 400 (mirrors the TS InsertSchema ``fieldWillBeOptional``). A literal ``@default``
310
+ is preserved as the Python default; a SQL-expression default / ``@autoSet`` carries
311
+ no Python default (the server fills it). Returns (source line, uses_field)."""
312
+ type_expr, enum_type_name = _type_expr_for_field(field, imports, config)
313
+ required = field.attrs().get(fc.FIELD_ATTR_REQUIRED) is True
314
+ default_raw = field.attrs().get(fc.FIELD_ATTR_DEFAULT)
315
+ literal_default = default_raw is not None and not _is_sql_expr_default(default_raw)
316
+ auto_set = field.attrs().get(fc.FIELD_ATTR_AUTO_SET)
317
+ is_auto_set = auto_set in (fc.AUTO_SET_ON_CREATE, fc.AUTO_SET_ON_UPDATE)
318
+ # A field is required in the CREATE shape only when @required AND not server-filled:
319
+ # ANY @default (literal or SQL-expr) or @autoSet means the DB fills it, so the wire
320
+ # body may omit it (the FR-036 #1 regression fix).
321
+ server_filled = default_raw is not None or is_auto_set
322
+ create_required = required and not server_filled
323
+
324
+ parts = _constraint_parts(_validator_constraints(field))
325
+ uses_field = bool(parts)
326
+
327
+ py_name = field.name # FR-036 #3 — validation models key by the WIRE name (field.name)
328
+
329
+ if literal_default:
330
+ # Preserve the literal @default (enum member or Python literal) exactly as the
331
+ # read model does — the caller may omit the field and the model fills the default.
332
+ if enum_type_name is not None and not field_is_array(field):
333
+ default_expr = f"{enum_type_name}.{str(default_raw).upper()}"
334
+ else:
335
+ default_expr = repr(default_raw)
336
+ annotation = type_expr
337
+ assignment = (
338
+ f" = Field(default={default_expr}, {', '.join(parts)})"
339
+ if uses_field
340
+ else f" = {default_expr}"
341
+ )
342
+ elif create_required:
343
+ annotation = type_expr
344
+ assignment = f" = Field({', '.join(parts)})" if uses_field else ""
345
+ else:
346
+ annotation = f"{type_expr} | None"
347
+ assignment = (
348
+ f" = Field(default=None, {', '.join(parts)})" if uses_field else " = None"
349
+ )
350
+
351
+ return f" {py_name}: {annotation}{assignment}", uses_field
352
+
353
+
354
+ def _patch_field_line(
355
+ field: MetaField, imports: set[str], config: GenConfig
356
+ ) -> tuple[str, bool]:
357
+ """A PATCH-model field line: the field is made Optional (default ``None``) yet
358
+ carries the SAME per-field constraints as the create model, so a present, non-null
359
+ value is validated with the create rules (FR-036) while an absent field is left
360
+ untouched. A ``@default`` is never emitted (a patch does not default a column).
361
+ Returns (source line, uses_field)."""
362
+ type_expr, _enum = _type_expr_for_field(field, imports, config)
363
+ parts = _constraint_parts(_validator_constraints(field))
364
+ py_name = field.name # FR-036 #3 — validation models key by the WIRE name (field.name)
365
+ if parts:
366
+ return f" {py_name}: {type_expr} | None = Field(default=None, {', '.join(parts)})", True
367
+ return f" {py_name}: {type_expr} | None = None", False
368
+
369
+
229
370
  def _effective_fqn(entity: MetaObject) -> str:
230
371
  """`package::name` via the canonical :meth:`MetaData.resolution_key` — own
231
372
  package, else the file-default package (captured at parse), else the nearest
@@ -295,6 +436,76 @@ class EntityModelGenerator:
295
436
  lines.append(f" {d.relation_name}: list[{element}] = []")
296
437
  return lines, uses_field
297
438
 
439
+ def _emit_create_lines(
440
+ self,
441
+ entity: MetaObject,
442
+ imports: set[str],
443
+ config: GenConfig | None = None,
444
+ ) -> tuple[list[str], bool]:
445
+ """The ``<Name>Create`` body — the CREATE-validation shape the generated router
446
+ validates a POST body against (FR-036). Restates every EFFECTIVE field (own +
447
+ inherited via ``extends``) EXCEPT the ones the server owns: an auto-generated PK
448
+ (``identity.primary @generation``; FR-036 #2) and any ``@readOnly`` field
449
+ (FR-013) are omitted, and a ``@required`` server-filled field (``@default`` /
450
+ ``@autoSet``; FR-036 #1) is made optional. Keyed by the wire name; a TPH
451
+ subtype's discriminator is pinned to its ``Literal`` value (the route injects it
452
+ from the URL, so it stays optional with a default). M:N navigations are omitted
453
+ (a create body targets columns, not relations). Returns ``(lines, uses_field)``.
454
+ Override to add/transform create-model lines."""
455
+ cfg = config if config is not None else GenConfig(out_dir="")
456
+ auto_gen_pk = _auto_gen_pk_field_names(entity)
457
+ binding = tph_subtype_binding(entity)
458
+ disc_field = binding[0] if binding is not None else None
459
+ uses_field = False
460
+ lines: list[str] = []
461
+ # RESOLVING (fields()): the flat Create model subclasses BaseModel (not the entity
462
+ # base), so it restates inherited fields too — unlike the read model's own_fields
463
+ # subclass-emit.
464
+ for f in entity.fields():
465
+ if disc_field is not None and f.name == disc_field:
466
+ continue # pinned below (prepended), never as a normal field line
467
+ if f.name in auto_gen_pk:
468
+ continue # FR-036 #2 — a server-generated PK is never in the create body
469
+ if f.attrs().get(fc.FIELD_ATTR_READ_ONLY) is True:
470
+ continue # FR-013 — a read-only column is DB/owner-written, not create input
471
+ line, used = _create_field_line(f, imports, cfg)
472
+ uses_field = uses_field or used
473
+ lines.append(line)
474
+ # FR-017 TPH: pin the inherited discriminator to this subtype's value (with a
475
+ # default so the URL-injected create body may omit it) — mirrors the read model.
476
+ if binding is not None:
477
+ disc_name, disc_value = binding
478
+ lines = [f' {disc_name}: Literal["{disc_value}"] = "{disc_value}"', *lines]
479
+ imports.add("from typing import Literal")
480
+ return lines, uses_field
481
+
482
+ def _emit_patch_lines(
483
+ self,
484
+ entity: MetaObject,
485
+ imports: set[str],
486
+ config: GenConfig | None = None,
487
+ ) -> tuple[list[str], bool]:
488
+ """The ``<Name>Patch`` body: every EFFECTIVE field (own + inherited via
489
+ ``extends``) made Optional while retaining its create-model constraints, so the
490
+ generated PATCH handler can validate present, non-null values without
491
+ bean-validating absent ones (FR-036). The primary-key field(s) are excluded
492
+ (route-authoritative; FR-036 #4) and M:N navigations are omitted (a patch
493
+ targets columns, not relations). Returns ``(lines, uses_field)``. Override to
494
+ add/transform patch-model lines."""
495
+ cfg = config if config is not None else GenConfig(out_dir="")
496
+ pk_fields = set(_primary_identity_field_names(entity))
497
+ uses_field = False
498
+ lines: list[str] = []
499
+ # RESOLVING (fields()): the flat Patch model is self-contained (subclasses
500
+ # BaseModel, not the entity base), so it restates inherited fields too.
501
+ for f in entity.fields():
502
+ if f.name in pk_fields:
503
+ continue # FR-036 #4 — the PK is route-authoritative, never patched
504
+ line, used = _patch_field_line(f, imports, cfg)
505
+ uses_field = uses_field or used
506
+ lines.append(line)
507
+ return lines, uses_field
508
+
298
509
  def render_entity_model(
299
510
  self,
300
511
  entity: MetaObject,
@@ -333,11 +544,29 @@ class EntityModelGenerator:
333
544
  imports.add("from typing import Literal")
334
545
  body = lines if lines else [" pass"]
335
546
 
336
- # Import only the pydantic names actually referenced.
547
+ # FR-036 an object.entity also emits two flat validation models the generated
548
+ # router binds: a ``<Name>Create`` (POST-body shape: auto-gen PK / @readOnly
549
+ # omitted, @default/@autoSet optional, other @required required) and an
550
+ # all-optional ``<Name>Patch`` (PATCH-body shape: PK excluded). Both key by the
551
+ # WIRE name and carry the field constraints, so present values are validated with
552
+ # the create rules. Value objects / projections are never routed, so they emit
553
+ # neither (only the read model). See findings #1–#4.
554
+ emit_validation_models = entity.sub_type == OBJECT_SUBTYPE_ENTITY
555
+ create_lines: list[str] = []
556
+ create_uses_field = False
557
+ patch_lines: list[str] = []
558
+ patch_uses_field = False
559
+ if emit_validation_models:
560
+ create_lines, create_uses_field = self._emit_create_lines(entity, imports, config)
561
+ patch_lines, patch_uses_field = self._emit_patch_lines(entity, imports, config)
562
+
563
+ # Import only the pydantic names actually referenced. The main model imports
564
+ # BaseModel only when it has no super, but the flat Create/Patch models always
565
+ # subclass BaseModel, so their presence forces the import regardless.
337
566
  pyd_names: list[str] = []
338
- if entity.super_data is None:
567
+ if entity.super_data is None or emit_validation_models:
339
568
  pyd_names.append("BaseModel")
340
- if uses_field:
569
+ if uses_field or create_uses_field or patch_uses_field:
341
570
  pyd_names.append("Field")
342
571
 
343
572
  parts: list[str] = [
@@ -350,7 +579,22 @@ class EntityModelGenerator:
350
579
  parts += [*extra_imports, ""]
351
580
  if pyd_names:
352
581
  parts += [f"from pydantic import {', '.join(pyd_names)}", ""]
353
- parts += ["", self._emit_class_header(entity, base_class), *body, ""]
582
+ parts += ["", self._emit_class_header(entity, base_class), *body]
583
+ if emit_validation_models:
584
+ parts += [
585
+ "",
586
+ "",
587
+ f"class {entity.name}Create(BaseModel):",
588
+ ' """GENERATED — CREATE input: auto-gen PK / @readOnly omitted; '
589
+ '@default/@autoSet optional; present values validated (FR-036)."""',
590
+ *create_lines,
591
+ "",
592
+ "",
593
+ f"class {entity.name}Patch(BaseModel):",
594
+ ' """GENERATED — PATCH input: all fields optional (PK excluded); present values validated (FR-036)."""',
595
+ *patch_lines,
596
+ ]
597
+ parts += [""]
354
598
  return "\n".join(parts)
355
599
 
356
600
  def _render_shared_enums_module(self, entities: list[MetaObject]) -> EmittedFile | None:
@@ -110,7 +110,7 @@ def _field_named(entity: MetaObject, field_name: str) -> MetaField | None:
110
110
  return None
111
111
 
112
112
 
113
- def _pk_field_name(entity: MetaObject) -> str:
113
+ def pk_field_name(entity: MetaObject) -> str:
114
114
  """The single PK field name (``identity.primary @fields``), default ``id``."""
115
115
  for c in entity.children():
116
116
  if c.type == TYPE_IDENTITY and c.sub_type == IDENTITY_SUBTYPE_PRIMARY:
@@ -184,8 +184,8 @@ def resolve_m2m_descriptors(
184
184
  source_column=_column_of(source_fk, fields.source_field),
185
185
  target_column=_column_of(target_fk, fields.target_field),
186
186
  target_pk_column=_column_of(
187
- _field_named(target, _pk_field_name(target)),
188
- _pk_field_name(target),
187
+ _field_named(target, pk_field_name(target)),
188
+ pk_field_name(target),
189
189
  ),
190
190
  symmetric=rel.symmetric(),
191
191
  )