modwire 9.2.0__tar.gz → 9.2.2__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 (366) hide show
  1. {modwire-9.2.0 → modwire-9.2.2}/.dev/modwire.egg-info/SOURCES.txt +3 -2
  2. {modwire-9.2.0 → modwire-9.2.2}/PKG-INFO +1 -1
  3. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/application.py +2 -1
  4. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/boundaries/services/no_reentry_flow_analyzer.py +34 -11
  5. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/cache/application.py +17 -23
  6. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/cache/models/cache_outcome.py +1 -10
  7. modwire-9.2.2/src/modwire/cli/cache/models/cache_stage.py +9 -0
  8. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/cache/models/cached_result.py +2 -1
  9. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/cache/services/cache_identity.py +1 -1
  10. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/pipeline/domain.py +1 -1
  11. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/pipeline/services/batch_source_reader.py +2 -2
  12. {modwire-9.2.0 → modwire-9.2.2}/tests/architecture/base_test.py +1 -1
  13. modwire-9.2.2/tests/architecture/boundaries/test_04_paths.py +143 -0
  14. {modwire-9.2.0 → modwire-9.2.2}/tests/architecture/insights/base_test.py +2 -2
  15. {modwire-9.2.0 → modwire-9.2.2}/tests/architecture/insights/test_02_invariants.py +19 -10
  16. {modwire-9.2.0 → modwire-9.2.2}/tests/architecture/shape/base_test.py +3 -1
  17. modwire-9.2.2/tests/architecture/shape/test_01_limits.py +178 -0
  18. {modwire-9.2.0 → modwire-9.2.2}/tests/shared/code/test_02_invariants.py +12 -9
  19. {modwire-9.2.0 → modwire-9.2.2}/tests/support/code_map.py +17 -24
  20. modwire-9.2.0/tests/architecture/boundaries/test_04_paths.py +0 -66
  21. modwire-9.2.0/tests/architecture/shape/test_01_limits.py +0 -126
  22. {modwire-9.2.0 → modwire-9.2.2}/.github/workflows/ci.yml +0 -0
  23. {modwire-9.2.0 → modwire-9.2.2}/.github/workflows/release.yml +0 -0
  24. {modwire-9.2.0 → modwire-9.2.2}/.gitignore +0 -0
  25. {modwire-9.2.0 → modwire-9.2.2}/AGENTS.md +0 -0
  26. {modwire-9.2.0 → modwire-9.2.2}/LICENSE +0 -0
  27. {modwire-9.2.0 → modwire-9.2.2}/MANIFEST.in +0 -0
  28. {modwire-9.2.0 → modwire-9.2.2}/Makefile +0 -0
  29. {modwire-9.2.0 → modwire-9.2.2}/README.md +0 -0
  30. {modwire-9.2.0 → modwire-9.2.2}/docs/target-language-ownership.md +0 -0
  31. {modwire-9.2.0 → modwire-9.2.2}/pyproject.toml +0 -0
  32. {modwire-9.2.0 → modwire-9.2.2}/scripts/check_extractors.py +0 -0
  33. {modwire-9.2.0 → modwire-9.2.2}/scripts/generate_docs.py +0 -0
  34. {modwire-9.2.0 → modwire-9.2.2}/scripts/verify_consumer.py +0 -0
  35. {modwire-9.2.0 → modwire-9.2.2}/setup.cfg +0 -0
  36. {modwire-9.2.0 → modwire-9.2.2}/setup.py +0 -0
  37. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/__init__.py +0 -0
  38. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/__main__.py +0 -0
  39. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/__init__.py +0 -0
  40. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/boundaries/__init__.py +0 -0
  41. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/boundaries/application.py +0 -0
  42. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/boundaries/domain.py +0 -0
  43. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/boundaries/models/__init__.py +0 -0
  44. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/boundaries/models/edge_rule_violation.py +0 -0
  45. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/boundaries/models/flow_report.py +0 -0
  46. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/boundaries/models/flow_violation.py +0 -0
  47. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/boundaries/models/types.py +0 -0
  48. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/boundaries/services/__init__.py +0 -0
  49. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/boundaries/services/backward_flow_analyzer.py +0 -0
  50. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/boundaries/services/boundaries_flow_analyzer.py +0 -0
  51. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/boundaries/services/module_boundary_analyzer.py +0 -0
  52. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/boundaries/services/no_cycles_flow_analyzer.py +0 -0
  53. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/config/__init__.py +0 -0
  54. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/config/application.py +0 -0
  55. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/config/models/__init__.py +0 -0
  56. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/config/models/architecture_config.py +0 -0
  57. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/config/models/boundaries_config.py +0 -0
  58. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/config/models/boundary_rule.py +0 -0
  59. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/config/models/configuration_value.py +0 -0
  60. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/config/models/flow_realm.py +0 -0
  61. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/config/models/flow_rules.py +0 -0
  62. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/config/models/shape_config.py +0 -0
  63. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/config/models/shape_realm.py +0 -0
  64. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/config/models/shape_rules.py +0 -0
  65. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/config/models/tag_rule.py +0 -0
  66. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/facade.py +0 -0
  67. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/insights/__init__.py +0 -0
  68. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/insights/application.py +0 -0
  69. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/insights/domain.py +0 -0
  70. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/insights/models/__init__.py +0 -0
  71. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/insights/models/callable_report_entry.py +0 -0
  72. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/insights/models/callables_report.py +0 -0
  73. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/insights/models/clusters_report.py +0 -0
  74. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/insights/models/clusters_report_item.py +0 -0
  75. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/insights/models/coherence_report.py +0 -0
  76. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/insights/models/exports_report.py +0 -0
  77. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/insights/models/exports_report_item.py +0 -0
  78. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/insights/models/hotspots_report.py +0 -0
  79. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/insights/models/hotspots_report_item.py +0 -0
  80. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/insights/models/insight_report.py +0 -0
  81. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/insights/services/__init__.py +0 -0
  82. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/insights/services/callables_reporter.py +0 -0
  83. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/insights/services/clusters_reporter.py +0 -0
  84. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/insights/services/coherence_reporter.py +0 -0
  85. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/insights/services/exports_reporter.py +0 -0
  86. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/insights/services/hotspots_reporter.py +0 -0
  87. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/insights/services/insight_report_field_map.py +0 -0
  88. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/map/__init__.py +0 -0
  89. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/map/application.py +0 -0
  90. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/map/domain.py +0 -0
  91. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/map/models/__init__.py +0 -0
  92. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/map/models/architecture_map.py +0 -0
  93. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/map/models/architecture_realm.py +0 -0
  94. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/map/models/tag_map.py +0 -0
  95. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/map/models/tag_match.py +0 -0
  96. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/map/services/__init__.py +0 -0
  97. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/map/services/architecture_map_loader.py +0 -0
  98. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/map/services/tag_matcher.py +0 -0
  99. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/report/__init__.py +0 -0
  100. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/report/application.py +0 -0
  101. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/report/domain.py +0 -0
  102. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/report/models/__init__.py +0 -0
  103. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/report/models/architecture_group.py +0 -0
  104. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/report/models/map_report.py +0 -0
  105. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/report/models/report_catalog.py +0 -0
  106. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/report/models/report_descriptor.py +0 -0
  107. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/report/models/report_item.py +0 -0
  108. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/report/models/report_metadata.py +0 -0
  109. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/report/models/report_node.py +0 -0
  110. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/report/models/report_section.py +0 -0
  111. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/report/services/__init__.py +0 -0
  112. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/report/services/map_report_collector.py +0 -0
  113. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/shape/__init__.py +0 -0
  114. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/shape/application.py +0 -0
  115. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/shape/domain.py +0 -0
  116. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/shape/models/__init__.py +0 -0
  117. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/shape/models/shape_realm_architecture_map.py +0 -0
  118. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/shape/models/shape_report.py +0 -0
  119. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/shape/models/shape_violation.py +0 -0
  120. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/shape/services/__init__.py +0 -0
  121. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/shape/services/abstract_class_resolver.py +0 -0
  122. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/shape/services/callable_resolver.py +0 -0
  123. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/shape/services/class_resolver.py +0 -0
  124. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/shape/services/file_resolver.py +0 -0
  125. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/shape/services/import_resolver.py +0 -0
  126. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/shape/services/property_resolver.py +0 -0
  127. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/shape/services/signature_resolver.py +0 -0
  128. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/architecture/shape/services/symbol_resolver.py +0 -0
  129. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/autowiring.py +0 -0
  130. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/__init__.py +0 -0
  131. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/cache/__init__.py +0 -0
  132. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/cache/domain.py +0 -0
  133. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/cache/models/__init__.py +0 -0
  134. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/cache/models/cache_entry.py +0 -0
  135. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/cache/models/cache_key.py +0 -0
  136. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/cache/models/cache_options.py +0 -0
  137. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/cache/models/extraction_snapshot.py +0 -0
  138. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/cache/services/__init__.py +0 -0
  139. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/cache/services/cache_codec.py +0 -0
  140. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/cache/services/filesystem_cache_storage.py +0 -0
  141. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/documentation/__init__.py +0 -0
  142. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/documentation/application.py +0 -0
  143. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/documentation/services/__init__.py +0 -0
  144. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/documentation/services/documentation_generator.py +0 -0
  145. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/facade.py +0 -0
  146. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/initialization/__init__.py +0 -0
  147. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/initialization/application.py +0 -0
  148. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/initialization/models/__init__.py +0 -0
  149. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/initialization/models/initialization_result.py +0 -0
  150. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/initialization/services/__init__.py +0 -0
  151. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/initialization/services/initialization_service.py +0 -0
  152. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/pipeline/__init__.py +0 -0
  153. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/pipeline/application.py +0 -0
  154. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/pipeline/models/__init__.py +0 -0
  155. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/pipeline/models/command_request.py +0 -0
  156. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/pipeline/models/extractor_batch_input.py +0 -0
  157. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/pipeline/models/extractor_command_input.py +0 -0
  158. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/pipeline/models/extractor_source_input.py +0 -0
  159. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/pipeline/models/report_pipeline_context.py +0 -0
  160. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/pipeline/models/scan_policy.py +0 -0
  161. {modwire-9.2.0/src/modwire/cli/cache → modwire-9.2.2/src/modwire/cli/pipeline}/models/source_entry.py +0 -0
  162. {modwire-9.2.0/src/modwire/cli/cache → modwire-9.2.2/src/modwire/cli/pipeline}/models/source_inventory.py +0 -0
  163. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/pipeline/services/__init__.py +0 -0
  164. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/pipeline/services/callables_insight.py +0 -0
  165. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/pipeline/services/clusters_insight.py +0 -0
  166. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/pipeline/services/coherence_insight.py +0 -0
  167. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/pipeline/services/command_line.py +0 -0
  168. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/pipeline/services/configuration_loader.py +0 -0
  169. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/pipeline/services/extractor_command.py +0 -0
  170. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/pipeline/services/flow_violations.py +0 -0
  171. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/pipeline/services/hotspots_insight.py +0 -0
  172. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/pipeline/services/map_tree.py +0 -0
  173. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/pipeline/services/output_console.py +0 -0
  174. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/pipeline/services/shape_violations.py +0 -0
  175. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/pipeline/services/success.py +0 -0
  176. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/pipeline/services/unknown_files_warning.py +0 -0
  177. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/pipeline/services/unused_exports_insight.py +0 -0
  178. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/resources/__init__.py +0 -0
  179. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/resources/application.py +0 -0
  180. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/resources/models/__init__.py +0 -0
  181. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/resources/models/defaults.py +0 -0
  182. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/resources/models/init_asset.py +0 -0
  183. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/resources/templates/AGENTS.md +0 -0
  184. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/resources/templates/ARCHITECTURE.md +0 -0
  185. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/resources/templates/INDEX.md +0 -0
  186. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/resources/templates/MODWIRE.md +0 -0
  187. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/resources/templates/RULES.md +0 -0
  188. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/resources/templates/TESTING.md +0 -0
  189. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/resources/templates/WORKFLOW.md +0 -0
  190. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/cli/resources/templates/architecture.yaml +0 -0
  191. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/extraction/__init__.py +0 -0
  192. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/extraction/dependency/__init__.py +0 -0
  193. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/extraction/dependency/application.py +0 -0
  194. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/extraction/dependency/domain.py +0 -0
  195. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/extraction/dependency/services/__init__.py +0 -0
  196. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/extraction/dependency/services/graph_builder.py +0 -0
  197. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/extraction/dependency/services/import_resolver.py +0 -0
  198. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/extraction/dependency/services/suffix_index.py +0 -0
  199. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/extraction/extractors/__init__.py +0 -0
  200. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/extraction/extractors/application.py +0 -0
  201. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/extraction/extractors/domain.py +0 -0
  202. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/extraction/extractors/models/__init__.py +0 -0
  203. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/extraction/extractors/models/batch_config.py +0 -0
  204. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/extraction/extractors/models/extraction_request.py +0 -0
  205. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/extraction/extractors/models/extractor_resource.py +0 -0
  206. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/extraction/extractors/models/extractor_runtime.py +0 -0
  207. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/extraction/extractors/models/python_call_context.py +0 -0
  208. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/extraction/extractors/resources/__init__.py +0 -0
  209. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/extraction/extractors/resources/php/build.php +0 -0
  210. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/extraction/extractors/resources/php/build.py +0 -0
  211. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/extraction/extractors/resources/php/composer.json +0 -0
  212. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/extraction/extractors/resources/php/composer.lock +0 -0
  213. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/extraction/extractors/resources/php/script.php +0 -0
  214. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/extraction/extractors/resources/php/script.src.php +0 -0
  215. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/extraction/extractors/resources/python/__init__.py +0 -0
  216. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/extraction/extractors/resources/python/script.py +0 -0
  217. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/extraction/extractors/resources/typescript/build.mjs +0 -0
  218. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/extraction/extractors/resources/typescript/build.py +0 -0
  219. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/extraction/extractors/resources/typescript/package-lock.json +0 -0
  220. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/extraction/extractors/resources/typescript/package.json +0 -0
  221. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/extraction/extractors/resources/typescript/script.js +0 -0
  222. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/extraction/extractors/resources/typescript/script.ts +0 -0
  223. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/extraction/extractors/resources/typescript/tsconfig.json +0 -0
  224. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/extraction/extractors/services/__init__.py +0 -0
  225. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/extraction/extractors/services/php_extractor.py +0 -0
  226. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/extraction/extractors/services/python_call_reader.py +0 -0
  227. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/extraction/extractors/services/python_extractor.py +0 -0
  228. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/extraction/extractors/services/python_syntax_parser.py +0 -0
  229. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/extraction/extractors/services/type_script_extractor.py +0 -0
  230. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/extraction/facade.py +0 -0
  231. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/py.typed +0 -0
  232. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/shared/__init__.py +0 -0
  233. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/shared/code/__init__.py +0 -0
  234. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/shared/code/application.py +0 -0
  235. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/shared/code/domain.py +0 -0
  236. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/shared/code/models/__init__.py +0 -0
  237. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/shared/code/models/code_map.py +0 -0
  238. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/shared/code/models/code_package.py +0 -0
  239. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/shared/code/models/dependency_edge_result.py +0 -0
  240. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/shared/code/models/dependency_graph.py +0 -0
  241. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/shared/code/models/dependency_node_result.py +0 -0
  242. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/shared/code/models/duplicate_identity_error.py +0 -0
  243. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/shared/code/models/edge.py +0 -0
  244. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/shared/code/models/edge_resolution.py +0 -0
  245. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/shared/code/models/identity.py +0 -0
  246. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/shared/code/models/node.py +0 -0
  247. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/shared/code/models/query_builder.py +0 -0
  248. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/shared/code/models/query_types.py +0 -0
  249. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/shared/code/models/queryable_code_map.py +0 -0
  250. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/shared/code/models/source_abstract_class.py +0 -0
  251. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/shared/code/models/source_call.py +0 -0
  252. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/shared/code/models/source_callable.py +0 -0
  253. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/shared/code/models/source_callable_symbol.py +0 -0
  254. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/shared/code/models/source_class.py +0 -0
  255. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/shared/code/models/source_class_method.py +0 -0
  256. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/shared/code/models/source_class_property.py +0 -0
  257. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/shared/code/models/source_export.py +0 -0
  258. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/shared/code/models/source_extraction.py +0 -0
  259. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/shared/code/models/source_file.py +0 -0
  260. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/shared/code/models/source_file_result.py +0 -0
  261. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/shared/code/models/source_function.py +0 -0
  262. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/shared/code/models/source_import.py +0 -0
  263. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/shared/code/models/source_imported_symbol.py +0 -0
  264. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/shared/code/models/source_interface.py +0 -0
  265. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/shared/code/models/source_item_result.py +0 -0
  266. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/shared/code/models/source_parameter.py +0 -0
  267. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/shared/code/models/source_signature.py +0 -0
  268. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/shared/code/models/source_symbol.py +0 -0
  269. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/shared/code/models/source_type.py +0 -0
  270. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/shared/code/models/source_value.py +0 -0
  271. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/shared/code/models/types.py +0 -0
  272. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/shared/code/services/__init__.py +0 -0
  273. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/shared/code/services/glob_path_matcher.py +0 -0
  274. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/shared/code/services/identity_resolver.py +0 -0
  275. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/shared/values/__init__.py +0 -0
  276. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/shared/values/models/__init__.py +0 -0
  277. {modwire-9.2.0 → modwire-9.2.2}/src/modwire/shared/values/models/value_model.py +0 -0
  278. {modwire-9.2.0 → modwire-9.2.2}/tests/__init__.py +0 -0
  279. {modwire-9.2.0 → modwire-9.2.2}/tests/application/__init__.py +0 -0
  280. {modwire-9.2.0 → modwire-9.2.2}/tests/application/base_test.py +0 -0
  281. {modwire-9.2.0 → modwire-9.2.2}/tests/application/test_01_attacks.py +0 -0
  282. {modwire-9.2.0 → modwire-9.2.2}/tests/application/test_06_happy_path.py +0 -0
  283. {modwire-9.2.0 → modwire-9.2.2}/tests/application/test_07_cache.py +0 -0
  284. {modwire-9.2.0 → modwire-9.2.2}/tests/architecture/__init__.py +0 -0
  285. {modwire-9.2.0 → modwire-9.2.2}/tests/architecture/boundaries/__init__.py +0 -0
  286. {modwire-9.2.0 → modwire-9.2.2}/tests/architecture/boundaries/base_test.py +0 -0
  287. {modwire-9.2.0 → modwire-9.2.2}/tests/architecture/boundaries/test_01_attacks.py +0 -0
  288. {modwire-9.2.0 → modwire-9.2.2}/tests/architecture/boundaries/test_02_invariants.py +0 -0
  289. {modwire-9.2.0 → modwire-9.2.2}/tests/architecture/boundaries/test_03_public_api.py +0 -0
  290. {modwire-9.2.0 → modwire-9.2.2}/tests/architecture/config/__init__.py +0 -0
  291. {modwire-9.2.0 → modwire-9.2.2}/tests/architecture/config/test_01_attacks.py +0 -0
  292. {modwire-9.2.0 → modwire-9.2.2}/tests/architecture/config/test_02_invariants.py +0 -0
  293. {modwire-9.2.0 → modwire-9.2.2}/tests/architecture/insights/__init__.py +0 -0
  294. {modwire-9.2.0 → modwire-9.2.2}/tests/architecture/map/__init__.py +0 -0
  295. {modwire-9.2.0 → modwire-9.2.2}/tests/architecture/map/test_02_patterns.py +0 -0
  296. {modwire-9.2.0 → modwire-9.2.2}/tests/architecture/report/__init__.py +0 -0
  297. {modwire-9.2.0 → modwire-9.2.2}/tests/architecture/report/test_02_invariants.py +0 -0
  298. {modwire-9.2.0 → modwire-9.2.2}/tests/architecture/shape/__init__.py +0 -0
  299. {modwire-9.2.0 → modwire-9.2.2}/tests/architecture/shape/test_02_invariants.py +0 -0
  300. {modwire-9.2.0 → modwire-9.2.2}/tests/cli/__init__.py +0 -0
  301. {modwire-9.2.0 → modwire-9.2.2}/tests/cli/base_test.py +0 -0
  302. {modwire-9.2.0 → modwire-9.2.2}/tests/cli/documentation/__init__.py +0 -0
  303. {modwire-9.2.0 → modwire-9.2.2}/tests/cli/documentation/test_02_invariants.py +0 -0
  304. {modwire-9.2.0 → modwire-9.2.2}/tests/cli/initialization/__init__.py +0 -0
  305. {modwire-9.2.0 → modwire-9.2.2}/tests/cli/initialization/test_01_attacks.py +0 -0
  306. {modwire-9.2.0 → modwire-9.2.2}/tests/cli/initialization/test_02_invariants.py +0 -0
  307. {modwire-9.2.0 → modwire-9.2.2}/tests/cli/initialization/test_03_interruption.py +0 -0
  308. {modwire-9.2.0 → modwire-9.2.2}/tests/cli/initialization/test_04_cleanup.py +0 -0
  309. {modwire-9.2.0 → modwire-9.2.2}/tests/cli/initialization/test_05_recovery.py +0 -0
  310. {modwire-9.2.0 → modwire-9.2.2}/tests/cli/pipeline/__init__.py +0 -0
  311. {modwire-9.2.0 → modwire-9.2.2}/tests/cli/pipeline/base_test.py +0 -0
  312. {modwire-9.2.0 → modwire-9.2.2}/tests/cli/pipeline/test_01_attacks.py +0 -0
  313. {modwire-9.2.0 → modwire-9.2.2}/tests/cli/pipeline/test_02_invariants.py +0 -0
  314. {modwire-9.2.0 → modwire-9.2.2}/tests/cli/pipeline/test_03_interruption.py +0 -0
  315. {modwire-9.2.0 → modwire-9.2.2}/tests/cli/pipeline/test_04_command_transport.py +0 -0
  316. {modwire-9.2.0 → modwire-9.2.2}/tests/cli/pipeline/test_05_recovery.py +0 -0
  317. {modwire-9.2.0 → modwire-9.2.2}/tests/cli/pipeline/test_06_happy_path.py +0 -0
  318. {modwire-9.2.0 → modwire-9.2.2}/tests/conftest.py +0 -0
  319. {modwire-9.2.0 → modwire-9.2.2}/tests/extraction/__init__.py +0 -0
  320. {modwire-9.2.0 → modwire-9.2.2}/tests/extraction/base_test.py +0 -0
  321. {modwire-9.2.0 → modwire-9.2.2}/tests/extraction/big_projects/__init__.py +0 -0
  322. {modwire-9.2.0 → modwire-9.2.2}/tests/extraction/big_projects/project.py +0 -0
  323. {modwire-9.2.0 → modwire-9.2.2}/tests/extraction/big_projects/projects.json +0 -0
  324. {modwire-9.2.0 → modwire-9.2.2}/tests/extraction/big_projects/resolution_results.json +0 -0
  325. {modwire-9.2.0 → modwire-9.2.2}/tests/extraction/big_projects/results.json +0 -0
  326. {modwire-9.2.0 → modwire-9.2.2}/tests/extraction/big_projects/run.py +0 -0
  327. {modwire-9.2.0 → modwire-9.2.2}/tests/extraction/cache_benchmark/__init__.py +0 -0
  328. {modwire-9.2.0 → modwire-9.2.2}/tests/extraction/cache_benchmark/run.py +0 -0
  329. {modwire-9.2.0 → modwire-9.2.2}/tests/extraction/dependency/__init__.py +0 -0
  330. {modwire-9.2.0 → modwire-9.2.2}/tests/extraction/dependency/test_02_invariants.py +0 -0
  331. {modwire-9.2.0 → modwire-9.2.2}/tests/extraction/extractors/__init__.py +0 -0
  332. {modwire-9.2.0 → modwire-9.2.2}/tests/extraction/extractors/test_01_attacks.py +0 -0
  333. {modwire-9.2.0 → modwire-9.2.2}/tests/extraction/extractors/test_02_invariants.py +0 -0
  334. {modwire-9.2.0 → modwire-9.2.2}/tests/extraction/extractors/test_03_native_symbols.py +0 -0
  335. {modwire-9.2.0 → modwire-9.2.2}/tests/extraction/scan_benchmark/__init__.py +0 -0
  336. {modwire-9.2.0 → modwire-9.2.2}/tests/extraction/scan_benchmark/prepare.py +0 -0
  337. {modwire-9.2.0 → modwire-9.2.2}/tests/extraction/scan_benchmark/results.json +0 -0
  338. {modwire-9.2.0 → modwire-9.2.2}/tests/extraction/scan_benchmark/run.py +0 -0
  339. {modwire-9.2.0 → modwire-9.2.2}/tests/fixtures/languages/php/ignored/example_generated.php +0 -0
  340. {modwire-9.2.0 → modwire-9.2.2}/tests/fixtures/languages/php/src/example_application/example_actions/example_action.php +0 -0
  341. {modwire-9.2.0 → modwire-9.2.2}/tests/fixtures/languages/php/src/example_domain/example_model/example_record.php +0 -0
  342. {modwire-9.2.0 → modwire-9.2.2}/tests/fixtures/languages/php/src/example_domain/example_services/example_policy.php +0 -0
  343. {modwire-9.2.0 → modwire-9.2.2}/tests/fixtures/languages/php/src/example_interfaces/example_http/example_controller.php +0 -0
  344. {modwire-9.2.0 → modwire-9.2.2}/tests/fixtures/languages/python/ignored/example_generated.py +0 -0
  345. {modwire-9.2.0 → modwire-9.2.2}/tests/fixtures/languages/python/src/example_application/example_actions/example_action.py +0 -0
  346. {modwire-9.2.0 → modwire-9.2.2}/tests/fixtures/languages/python/src/example_domain/example_model/example_record.py +0 -0
  347. {modwire-9.2.0 → modwire-9.2.2}/tests/fixtures/languages/python/src/example_domain/example_services/example_policy.py +0 -0
  348. {modwire-9.2.0 → modwire-9.2.2}/tests/fixtures/languages/python/src/example_interfaces/example_http/example_controller.py +0 -0
  349. {modwire-9.2.0 → modwire-9.2.2}/tests/fixtures/languages/typescript/ignored/example_generated.ts +0 -0
  350. {modwire-9.2.0 → modwire-9.2.2}/tests/fixtures/languages/typescript/src/example_application/example_actions/example_action.ts +0 -0
  351. {modwire-9.2.0 → modwire-9.2.2}/tests/fixtures/languages/typescript/src/example_domain/example_model/example_record.ts +0 -0
  352. {modwire-9.2.0 → modwire-9.2.2}/tests/fixtures/languages/typescript/src/example_domain/example_model/example_view.tsx +0 -0
  353. {modwire-9.2.0 → modwire-9.2.2}/tests/fixtures/languages/typescript/src/example_domain/example_services/example_audit.js +0 -0
  354. {modwire-9.2.0 → modwire-9.2.2}/tests/fixtures/languages/typescript/src/example_domain/example_services/example_callables.ts +0 -0
  355. {modwire-9.2.0 → modwire-9.2.2}/tests/fixtures/languages/typescript/src/example_domain/example_services/example_policy.ts +0 -0
  356. {modwire-9.2.0 → modwire-9.2.2}/tests/fixtures/languages/typescript/src/example_interfaces/example_http/example_controller.ts +0 -0
  357. {modwire-9.2.0 → modwire-9.2.2}/tests/fixtures/languages/typescript/src/example_interfaces/example_http/example_view.jsx +0 -0
  358. {modwire-9.2.0 → modwire-9.2.2}/tests/fixtures/syntax/php/example_shapes.php +0 -0
  359. {modwire-9.2.0 → modwire-9.2.2}/tests/fixtures/syntax/python/example_shapes.py +0 -0
  360. {modwire-9.2.0 → modwire-9.2.2}/tests/fixtures/syntax/typescript/example_shapes.ts +0 -0
  361. {modwire-9.2.0 → modwire-9.2.2}/tests/shared/__init__.py +0 -0
  362. {modwire-9.2.0 → modwire-9.2.2}/tests/shared/code/__init__.py +0 -0
  363. {modwire-9.2.0 → modwire-9.2.2}/tests/support/__init__.py +0 -0
  364. {modwire-9.2.0 → modwire-9.2.2}/tests/support/base_test.py +0 -0
  365. {modwire-9.2.0 → modwire-9.2.2}/tests/support/service_test.py +0 -0
  366. {modwire-9.2.0 → modwire-9.2.2}/uv.lock +0 -0
@@ -120,10 +120,9 @@ src/modwire/cli/cache/models/cache_entry.py
120
120
  src/modwire/cli/cache/models/cache_key.py
121
121
  src/modwire/cli/cache/models/cache_options.py
122
122
  src/modwire/cli/cache/models/cache_outcome.py
123
+ src/modwire/cli/cache/models/cache_stage.py
123
124
  src/modwire/cli/cache/models/cached_result.py
124
125
  src/modwire/cli/cache/models/extraction_snapshot.py
125
- src/modwire/cli/cache/models/source_entry.py
126
- src/modwire/cli/cache/models/source_inventory.py
127
126
  src/modwire/cli/cache/services/__init__.py
128
127
  src/modwire/cli/cache/services/cache_codec.py
129
128
  src/modwire/cli/cache/services/cache_identity.py
@@ -148,6 +147,8 @@ src/modwire/cli/pipeline/models/extractor_command_input.py
148
147
  src/modwire/cli/pipeline/models/extractor_source_input.py
149
148
  src/modwire/cli/pipeline/models/report_pipeline_context.py
150
149
  src/modwire/cli/pipeline/models/scan_policy.py
150
+ src/modwire/cli/pipeline/models/source_entry.py
151
+ src/modwire/cli/pipeline/models/source_inventory.py
151
152
  src/modwire/cli/pipeline/services/__init__.py
152
153
  src/modwire/cli/pipeline/services/batch_source_reader.py
153
154
  src/modwire/cli/pipeline/services/callables_insight.py
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modwire
3
- Version: 9.2.0
3
+ Version: 9.2.2
4
4
  Summary: Code extraction, architecture analysis and the Modwire CLI.
5
5
  Author: Tomasz Szpak
6
6
  License-Expression: MIT
@@ -11,7 +11,8 @@ from .architecture.facade import ArchitectureFacade
11
11
  from .architecture.report.models.report_catalog import ReportCatalog
12
12
  from .architecture.report.models.report_node import ReportNode
13
13
  from .cli.cache.models.cache_options import CacheOptions
14
- from .cli.cache.models.cache_outcome import CacheOutcome, CacheStage
14
+ from .cli.cache.models.cache_outcome import CacheOutcome
15
+ from .cli.cache.models.cache_stage import CacheStage
15
16
  from .cli.cache.models.cached_result import CachedResult
16
17
  from .cli.facade import CliFacade
17
18
  from .cli.pipeline.models.scan_policy import ScanPolicy
@@ -9,6 +9,12 @@ from ..domain import BaseFlowAnalyzer, FlowAnalyzerInterface
9
9
  from ..models.flow_violation import FlowViolation
10
10
 
11
11
 
12
+ @dataclass(frozen=True)
13
+ class NoReentryTraversalState:
14
+ source_id: str
15
+ exited_modules: frozenset[str]
16
+
17
+
12
18
  @injectable(as_type=FlowAnalyzerInterface, qualifier="no_reentry")
13
19
  @dataclass(frozen=True)
14
20
  class NoReentryFlowAnalyzer(FlowAnalyzerInterface, BaseFlowAnalyzer):
@@ -24,12 +30,14 @@ class NoReentryFlowAnalyzer(FlowAnalyzerInterface, BaseFlowAnalyzer):
24
30
  if not architecture_map.realm.module_tag:
25
31
  return ()
26
32
  violations: list[FlowViolation] = []
33
+ visited: set[NoReentryTraversalState] = set()
27
34
  for root in self.roots(architecture_map):
28
35
  self.walk_dependencies(
29
36
  architecture_map=architecture_map,
30
37
  source_id=root,
31
38
  path=(root,),
32
39
  exited_modules=frozenset(),
40
+ visited=visited,
33
41
  violations=violations,
34
42
  )
35
43
  return self.dedupe(violations)
@@ -41,8 +49,8 @@ class NoReentryFlowAnalyzer(FlowAnalyzerInterface, BaseFlowAnalyzer):
41
49
  if architecture_map.code_map.incoming_dependencies(FileId(source_id)).count() == 0
42
50
  )
43
51
  if roots:
44
- return roots
45
- return architecture_map.code_map.source_ids()
52
+ return tuple(sorted(roots))
53
+ return tuple(sorted(architecture_map.code_map.source_ids()))
46
54
 
47
55
  def walk_dependencies(
48
56
  self,
@@ -50,10 +58,23 @@ class NoReentryFlowAnalyzer(FlowAnalyzerInterface, BaseFlowAnalyzer):
50
58
  source_id: str,
51
59
  path: tuple[str, ...],
52
60
  exited_modules: frozenset[str],
61
+ visited: set[NoReentryTraversalState],
53
62
  violations: list[FlowViolation],
54
63
  ) -> None:
64
+ state = NoReentryTraversalState(source_id=source_id, exited_modules=exited_modules)
65
+ if state in visited:
66
+ return
67
+ visited.add(state)
55
68
  source_module = self.module_for(architecture_map, source_id)
56
- for dependency in architecture_map.code_map.outgoing_dependencies(FileId(source_id)).all():
69
+ dependencies = sorted(
70
+ architecture_map.code_map.outgoing_dependencies(FileId(source_id)).all(),
71
+ key=lambda dependency: (
72
+ dependency.edge.to_id or "",
73
+ dependency.edge.specifier,
74
+ dependency.edge.kind,
75
+ ),
76
+ )
77
+ for dependency in dependencies:
57
78
  target_id = dependency.edge.to_id
58
79
  if target_id is None:
59
80
  continue
@@ -69,14 +90,16 @@ class NoReentryFlowAnalyzer(FlowAnalyzerInterface, BaseFlowAnalyzer):
69
90
  violation_index=len(path),
70
91
  rule_name=self.rule_name(architecture_map),
71
92
  message="module layer re-entered after exit",
93
+ source_module=source_module,
94
+ target_module=target_module,
72
95
  )
73
96
  )
74
97
  continue
75
- if target_id not in path:
76
- self.walk_dependencies(
77
- architecture_map=architecture_map,
78
- source_id=target_id,
79
- path=(*path, target_id),
80
- exited_modules=next_exited,
81
- violations=violations,
82
- )
98
+ self.walk_dependencies(
99
+ architecture_map=architecture_map,
100
+ source_id=target_id,
101
+ path=(*path, target_id),
102
+ exited_modules=next_exited,
103
+ visited=visited,
104
+ violations=violations,
105
+ )
@@ -18,19 +18,14 @@ from ..pipeline.models.scan_policy import ScanPolicy
18
18
  from .domain import CacheStorage
19
19
  from .models.cache_key import CacheKey
20
20
  from .models.cache_options import CacheOptions
21
- from .models.cache_outcome import CacheOutcome, CacheStage
21
+ from .models.cache_outcome import CacheOutcome
22
+ from .models.cache_stage import CacheStage
22
23
  from .models.cached_result import CachedResult
23
24
  from .models.extraction_snapshot import ExtractionSnapshot
24
25
  from .services.cache_codec import CacheCodec
25
26
  from .services.cache_identity import CacheIdentity
26
27
 
27
28
 
28
- @dataclass(frozen=True)
29
- class _CacheRead:
30
- value: object | None
31
- invalidated: bool
32
-
33
-
34
29
  @injectable
35
30
  @dataclass(frozen=True)
36
31
  class CacheApplication:
@@ -57,17 +52,17 @@ class CacheApplication:
57
52
  invalidated = 0
58
53
  for entry in inventory.entries:
59
54
  key = keys[entry.source_id]
60
- cached = self._read(options, key)
55
+ cached_value, cached_invalidated = self._read(options, key)
61
56
  try:
62
- source_file = SourceFile.model_validate(cached.value)
57
+ source_file = SourceFile.model_validate(cached_value)
63
58
  if source_file.file_id != entry.source_id:
64
59
  raise ValueError("Cached source identity does not match its key.")
65
60
  files[entry.source_id] = source_file
66
61
  hits += 1
67
62
  except (TypeError, ValueError, ValidationError):
68
- if cached.value is not None and not cached.invalidated:
63
+ if cached_value is not None and not cached_invalidated:
69
64
  self.storage.delete(options, key)
70
- invalidated += int(cached.invalidated or cached.value is not None)
65
+ invalidated += int(cached_invalidated or cached_value is not None)
71
66
  misses.append(entry.source_id)
72
67
 
73
68
  if misses or not inventory.entries:
@@ -128,19 +123,19 @@ class CacheApplication:
128
123
 
129
124
  def code_map(self, snapshot: ExtractionSnapshot, options: CacheOptions) -> CachedResult[CodeMap | None]:
130
125
  key = self.identity.code_map("", snapshot.manifest)
131
- cached = self._read(options, key)
126
+ cached_value, cached_invalidated = self._read(options, key)
132
127
  try:
133
- value = CodeMap.model_validate(cached.value)
128
+ value = CodeMap.model_validate(cached_value)
134
129
  outcome = CacheOutcome(stage=CacheStage.CODE_MAP, namespace=options.namespace, hits=1)
135
130
  except (TypeError, ValueError, ValidationError):
136
- if cached.value is not None and not cached.invalidated:
131
+ if cached_value is not None and not cached_invalidated:
137
132
  self.storage.delete(options, key)
138
133
  value = None
139
134
  outcome = CacheOutcome(
140
135
  stage=CacheStage.CODE_MAP,
141
136
  namespace=options.namespace,
142
137
  misses=1,
143
- invalidated=int(cached.invalidated or cached.value is not None),
138
+ invalidated=int(cached_invalidated or cached_value is not None),
144
139
  )
145
140
  return CachedResult[CodeMap | None](value=value, outcomes=(outcome,))
146
141
 
@@ -152,9 +147,8 @@ class CacheApplication:
152
147
  self, code_map: CodeMap, config: ArchitectureConfig, options: CacheOptions
153
148
  ) -> CachedResult[tuple[ReportNode, ...] | None]:
154
149
  key = self.identity.reports(code_map, config)
155
- cached = self._read(options, key)
156
- cached_value: object | None = cached.value
157
- invalidated = cached.invalidated or cached_value is not None
150
+ cached_value, cached_invalidated = self._read(options, key)
151
+ invalidated = cached_invalidated or cached_value is not None
158
152
  try:
159
153
  if not isinstance(cached_value, list):
160
154
  raise ValueError("Cached reports must be a list.")
@@ -162,7 +156,7 @@ class CacheApplication:
162
156
  value = tuple(sorted(reports, key=lambda item: (item.metadata.order, item.metadata.id)))
163
157
  outcome = CacheOutcome(stage=CacheStage.REPORTS, namespace=options.namespace, hits=1)
164
158
  except (AttributeError, ImportError, KeyError, TypeError, ValueError, ValidationError):
165
- if cached_value is not None and not cached.invalidated:
159
+ if cached_value is not None and not cached_invalidated:
166
160
  self.storage.delete(options, key)
167
161
  value = None
168
162
  outcome = CacheOutcome(
@@ -202,15 +196,15 @@ class CacheApplication:
202
196
  self.storage.delete(options, entry.key)
203
197
  total -= entry.size
204
198
 
205
- def _read(self, options: CacheOptions, key: CacheKey) -> _CacheRead:
199
+ def _read(self, options: CacheOptions, key: CacheKey) -> tuple[object | None, bool]:
206
200
  payload = self.storage.read(options, key)
207
201
  if payload is None:
208
- return _CacheRead(value=None, invalidated=False)
202
+ return None, False
209
203
  value = self.codec.decode(key, payload)
210
204
  if value is None:
211
205
  self.storage.delete(options, key)
212
- return _CacheRead(value=None, invalidated=True)
213
- return _CacheRead(value=value, invalidated=False)
206
+ return None, True
207
+ return value, False
214
208
 
215
209
  def _write(self, options: CacheOptions, key: CacheKey, value: object) -> None:
216
210
  self.storage.write(options, key, self.codec.encode(key, value))
@@ -1,16 +1,7 @@
1
- from enum import StrEnum
2
-
3
1
  from pydantic import field_validator
4
2
 
5
3
  from ....shared.values.models.value_model import ValueModel
6
-
7
-
8
- class CacheStage(StrEnum):
9
- """Public stages reported by cached Modwire operations."""
10
-
11
- EXTRACTION = "extraction"
12
- CODE_MAP = "code-map"
13
- REPORTS = "reports"
4
+ from .cache_stage import CacheStage
14
5
 
15
6
 
16
7
  class CacheOutcome(ValueModel):
@@ -0,0 +1,9 @@
1
+ from enum import StrEnum
2
+
3
+
4
+ class CacheStage(StrEnum):
5
+ """Public stages reported by cached Modwire operations."""
6
+
7
+ EXTRACTION = "extraction"
8
+ CODE_MAP = "code-map"
9
+ REPORTS = "reports"
@@ -1,7 +1,8 @@
1
1
  from pydantic import field_validator
2
2
 
3
3
  from ....shared.values.models.value_model import ValueModel
4
- from .cache_outcome import CacheOutcome, CacheStage
4
+ from .cache_outcome import CacheOutcome
5
+ from .cache_stage import CacheStage
5
6
 
6
7
 
7
8
  class CachedResult[Result](ValueModel):
@@ -11,8 +11,8 @@ from ....architecture.config.models.architecture_config import ArchitectureConfi
11
11
  from ....extraction.extractors.models.extraction_request import ExtractionRequest
12
12
  from ....shared.code.models.code_map import CodeMap
13
13
  from ...cache.models.cache_key import CacheKey, CacheKind
14
- from ...cache.models.source_entry import SourceEntry
15
14
  from ...pipeline.models.scan_policy import ScanPolicy
15
+ from ...pipeline.models.source_entry import SourceEntry
16
16
 
17
17
 
18
18
  @injectable
@@ -5,9 +5,9 @@ from ...extraction.extractors.models.extractor_runtime import ExtractorRuntime
5
5
  from ...shared.code.models.identity import FileId
6
6
  from ...shared.code.models.source_extraction import SourceExtraction
7
7
  from ...shared.code.models.source_file import SourceFile
8
- from ..cache.models.source_inventory import SourceInventory
9
8
  from .models.report_pipeline_context import ReportPipelineContext
10
9
  from .models.scan_policy import ScanPolicy
10
+ from .models.source_inventory import SourceInventory
11
11
 
12
12
 
13
13
  class ReportPipelineStep(ABC):
@@ -20,10 +20,10 @@ from ....shared.code.models.duplicate_identity_error import DuplicateIdentityErr
20
20
  from ....shared.code.models.identity import FileId, ModuleId
21
21
  from ....shared.code.models.source_extraction import SourceExtraction
22
22
  from ....shared.code.models.source_file import SourceFile
23
- from ...cache.models.source_entry import SourceEntry
24
- from ...cache.models.source_inventory import SourceInventory
25
23
  from ..domain import SourceReader
26
24
  from ..models.scan_policy import ScanPolicy
25
+ from ..models.source_entry import SourceEntry
26
+ from ..models.source_inventory import SourceInventory
27
27
 
28
28
 
29
29
  @injectable(as_type=SourceReader)
@@ -10,4 +10,4 @@ class ArchitectureTestCase(ServiceTestCase):
10
10
  return {}
11
11
 
12
12
  def queryable_map(self, paths: tuple[str, ...], edges: tuple[tuple[str, str | None, str, str], ...]):
13
- return CodeMapFactory.queryable({path: self.source_file(path) for path in paths}, edges)
13
+ return CodeMapFactory().queryable({path: self.source_file(path) for path in paths}, edges)
@@ -0,0 +1,143 @@
1
+ import time
2
+
3
+ import pytest
4
+
5
+ from .base_test import BoundaryTestCase
6
+
7
+
8
+ class TestFlowPaths(BoundaryTestCase):
9
+ @pytest.mark.parametrize("reverse", (False, True))
10
+ def test_layer_order_distinguishes_forward_and_backward_edges(self, reverse: bool) -> None:
11
+ config = self.application.configure(
12
+ {
13
+ "boundaries": {
14
+ "tags": [
15
+ {"name": "example-entry", "match": "src/example_entry"},
16
+ {"name": "example-core", "match": "src/example_core"},
17
+ ],
18
+ "flow": {
19
+ "layers": ["example-entry", "example-core"],
20
+ "analyzers": ["backward-flow"],
21
+ },
22
+ },
23
+ "shape": {"realms": [{"name": "example-source", "match": "src"}]},
24
+ }
25
+ )
26
+ paths = ("src/example_entry/example_entry.source", "src/example_core/example_core.source")
27
+ source, target = tuple(reversed(paths)) if reverse else paths
28
+ result = self.violations(config, source, target)
29
+ assert result == ((source, target),) if reverse else result == ()
30
+
31
+ @pytest.mark.parametrize("reenters", (False, True))
32
+ def test_reentry_reports_the_complete_dependency_path(self, reenters: bool) -> None:
33
+ config = self.application.configure(
34
+ {
35
+ "boundaries": {
36
+ "tags": [{"name": "example-module", "match": "src/*"}],
37
+ "flow": {"module_tag": "example-module", "analyzers": ["no-reentry"]},
38
+ },
39
+ "shape": {"realms": [{"name": "example-source", "match": "src"}]},
40
+ }
41
+ )
42
+ target_module = "example_first" if reenters else "example_third"
43
+ paths = (
44
+ "src/example_first/example_entry.source",
45
+ "src/example_second/example_middle.source",
46
+ f"src/{target_module}/example_target.source",
47
+ )
48
+ edges = tuple((paths[a], paths[b], "resolved", "example.target") for a, b in ((0, 1), (1, 2)))
49
+ report = self.report("architecture.violations.flow", config, self.queryable_map(paths, edges))
50
+ assert tuple(item.path for item in report.violations) == ((paths,) if reenters else ())
51
+ if reenters:
52
+ assert report.violations[0].violation_index == 2
53
+ assert report.violations[0].violation_type == "no-reentry"
54
+ assert report.violations[0].source_module == "example_second"
55
+ assert report.violations[0].target_module == "example_first"
56
+
57
+ def test_reentry_analysis_bounds_converging_dependency_paths(self) -> None:
58
+ config = self.application.configure(
59
+ {
60
+ "boundaries": {
61
+ "tags": [{"name": "example-module", "match": "src/*"}],
62
+ "flow": {"module_tag": "example-module", "analyzers": ["no-reentry"]},
63
+ },
64
+ "shape": {"realms": [{"name": "example-source", "match": "src"}]},
65
+ }
66
+ )
67
+ paths = ["src/example_module/example_root.source"]
68
+ levels: list[tuple[str, str]] = []
69
+ for level in range(18):
70
+ nodes = (
71
+ f"src/example_module/example_{level}_a.source",
72
+ f"src/example_module/example_{level}_b.source",
73
+ )
74
+ paths.extend(nodes)
75
+ levels.append(nodes)
76
+ paths.append("src/example_module/example_target.source")
77
+ edges: list[tuple[str, str | None, str, str]] = []
78
+ previous = (paths[0],)
79
+ for current in levels:
80
+ edges.extend((source, target, "resolved", "example.target") for source in previous for target in current)
81
+ previous = current
82
+ edges.extend((source, paths[-1], "resolved", "example.target") for source in previous)
83
+ code_map = self.queryable_map(tuple(paths), tuple(edges))
84
+ started = time.perf_counter()
85
+ report = self.report("architecture.violations.flow", config, code_map)
86
+ elapsed = time.perf_counter() - started
87
+ assert not report.violations
88
+ assert elapsed < 1.0
89
+
90
+ def test_reentry_analysis_preserves_distinct_states_across_repeated_dependencies_and_cycles(self) -> None:
91
+ config = self.application.configure(
92
+ {
93
+ "boundaries": {
94
+ "tags": [{"name": "example-module", "match": "src/*"}],
95
+ "flow": {"module_tag": "example-module", "analyzers": ["no-reentry"]},
96
+ },
97
+ "shape": {"realms": [{"name": "example-source", "match": "src"}]},
98
+ }
99
+ )
100
+ root = "src/example_entry/example_root.source"
101
+ first = "src/example_first/example_branch.source"
102
+ second = "src/example_second/example_branch.source"
103
+ shared = "src/example_shared/example_shared.source"
104
+ first_target = "src/example_first/example_target.source"
105
+ second_target = "src/example_second/example_target.source"
106
+ loop_one = "src/example_entry/example_loop_one.source"
107
+ loop_two = "src/example_entry/example_loop_two.source"
108
+ paths = (root, first, second, shared, first_target, second_target, loop_one, loop_two)
109
+ edges = (
110
+ (root, first, "resolved", "example.first"),
111
+ (root, first, "resolved", "example.first"),
112
+ (root, second, "resolved", "example.second"),
113
+ (first, shared, "resolved", "example.shared"),
114
+ (second, shared, "resolved", "example.shared"),
115
+ (shared, first_target, "resolved", "example.first_target"),
116
+ (shared, second_target, "resolved", "example.second_target"),
117
+ (root, loop_one, "resolved", "example.loop_one"),
118
+ (loop_one, loop_two, "resolved", "example.loop_two"),
119
+ (loop_two, loop_one, "resolved", "example.loop_one"),
120
+ )
121
+ report = self.report("architecture.violations.flow", config, self.queryable_map(paths, edges))
122
+ assert tuple(
123
+ (violation.path, violation.violation_index, violation.source_module, violation.target_module)
124
+ for violation in report.violations
125
+ ) == (
126
+ ((root, first, shared, first_target), 3, "example_shared", "example_first"),
127
+ ((root, second, shared, second_target), 3, "example_shared", "example_second"),
128
+ )
129
+
130
+ def test_cycles_are_reported_once_without_a_graph_root(self) -> None:
131
+ config = self.application.configure(
132
+ {
133
+ "boundaries": {
134
+ "tags": [{"name": "example-module", "match": "src/*"}],
135
+ "flow": {"module_tag": "example-module", "analyzers": ["no-cycles"]},
136
+ },
137
+ "shape": {"realms": [{"name": "example-source", "match": "src"}]},
138
+ }
139
+ )
140
+ paths = ("src/example_first/example_first.source", "src/example_second/example_second.source")
141
+ edges = tuple((paths[a], paths[b], "resolved", "example.target") for a, b in ((0, 1), (1, 0)))
142
+ report = self.report("architecture.violations.flow", config, self.queryable_map(paths, edges))
143
+ assert tuple(item.path for item in report.violations) == (("example_first", "example_second", "example_first"),)
@@ -6,7 +6,7 @@ class InsightTestCase(ArchitectureTestCase):
6
6
  def insight(
7
7
  self,
8
8
  files: dict[str, dict[str, object]],
9
- edges: tuple[tuple[str, str | None, str, str], ...] = (),
9
+ edges: tuple[tuple[str, str | None, str, str], ...],
10
10
  ):
11
- code_map = CodeMapFactory.queryable(files, edges)
11
+ code_map = CodeMapFactory().queryable(files, edges)
12
12
  return self.report("architecture.insights", self.example_configuration(), code_map)
@@ -34,14 +34,22 @@ class TestInsightInvariants(InsightTestCase):
34
34
  {
35
35
  "example_values.source": {
36
36
  "exports": [
37
- CodeMapFactory.source_export("ExampleUsed"),
38
- CodeMapFactory.source_export("ExampleUnused"),
37
+ CodeMapFactory().source_export("ExampleUsed"),
38
+ CodeMapFactory().source_export("ExampleUnused"),
39
39
  ]
40
40
  },
41
41
  "example_consumer.source": {
42
- "imports": [CodeMapFactory.source_import("example_values", imported_name="ExampleUsed")]
42
+ "imports": [
43
+ CodeMapFactory().source_import(
44
+ "example_values",
45
+ imported_name="ExampleUsed",
46
+ is_aliased=False,
47
+ imported_symbols=(),
48
+ )
49
+ ]
43
50
  },
44
- }
51
+ },
52
+ (),
45
53
  ).exports
46
54
  assert tuple((item.source_id, item.name) for item in result.unused_exports) == (
47
55
  ("example_values.source", "ExampleUnused"),
@@ -53,15 +61,16 @@ class TestInsightInvariants(InsightTestCase):
53
61
  {
54
62
  source_id: {
55
63
  "callables": [
56
- CodeMapFactory.source_callable(source_id, "example_target"),
57
- CodeMapFactory.source_callable(source_id, "example_caller"),
64
+ CodeMapFactory().source_callable(source_id, "example_target"),
65
+ CodeMapFactory().source_callable(source_id, "example_caller"),
58
66
  ],
59
67
  "calls": [
60
- CodeMapFactory.source_call(source_id, "example_caller", "example_target"),
61
- CodeMapFactory.source_call(source_id, "example_caller", "example_target"),
68
+ CodeMapFactory().source_call(source_id, "example_caller", "example_target"),
69
+ CodeMapFactory().source_call(source_id, "example_caller", "example_target"),
62
70
  ],
63
71
  }
64
- }
72
+ },
73
+ (),
65
74
  ).callables
66
75
  entries = {item.source_callable: item for item in result.entries}
67
76
  caller = next(item for key, item in entries.items() if key.endswith("example_caller"))
@@ -71,7 +80,7 @@ class TestInsightInvariants(InsightTestCase):
71
80
  assert target.calls == ()
72
81
 
73
82
  def test_empty_project_has_empty_insights(self) -> None:
74
- result = self.insight({})
83
+ result = self.insight({}, ())
75
84
  assert result.clusters.clusters == ()
76
85
  assert result.hotspots.hotspots == ()
77
86
  assert result.callables.entries == ()
@@ -4,4 +4,6 @@ from ..base_test import ArchitectureTestCase
4
4
 
5
5
  class ShapeTestCase(ArchitectureTestCase):
6
6
  def source_file(self, file_id: str) -> dict[str, object]:
7
- return {"functions": [CodeMapFactory.symbol("example_function")]}
7
+ return {
8
+ "functions": [CodeMapFactory().symbol("example_function", line_count=1, declared_args=0, optional_args=0)]
9
+ }