codeplain 0.3.13.dev37__tar.gz → 0.3.13.dev39__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 (286) hide show
  1. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/PKG-INFO +2 -2
  2. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/module_renderer.py +2 -4
  3. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/pyproject.toml +1 -1
  4. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/render_machine/actions/exit_with_error.py +8 -7
  5. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/render_machine/actions/render_functional_requirement.py +10 -3
  6. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/render_machine/code_renderer.py +0 -3
  7. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/render_machine/render_context.py +1 -2
  8. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/render_machine/render_types.py +9 -11
  9. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/requirements.txt +1 -1
  10. codeplain-0.3.13.dev39/tests/test_render_stop_reason.py +210 -0
  11. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/.claude/settings.json +0 -0
  12. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/.flake8 +0 -0
  13. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/.github/dependabot.yml +0 -0
  14. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/.github/workflows/e2e.yml +0 -0
  15. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/.github/workflows/lint-and-test.yml +0 -0
  16. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/.github/workflows/lint-powershell.yml +0 -0
  17. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/.github/workflows/nofity-slack-on-main-merge.yml +0 -0
  18. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/.github/workflows/publish-dev-to-pypi.yml +0 -0
  19. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/.github/workflows/publish-install-script.yml +0 -0
  20. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/.github/workflows/publish-to-pypi.yml +0 -0
  21. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/.gitignore +0 -0
  22. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/.vscode/launch.json +0 -0
  23. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/.vscode/settings.json +0 -0
  24. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/CLAUDE.md +0 -0
  25. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/CONTRIBUTING.md +0 -0
  26. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/LICENSE +0 -0
  27. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/README.md +0 -0
  28. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/change_detection.py +0 -0
  29. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/cli_output/__init__.py +0 -0
  30. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/cli_output/dry_run.py +0 -0
  31. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/cli_output/render_summary.py +0 -0
  32. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/cli_output/status.py +0 -0
  33. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/codeplain_REST_api.py +0 -0
  34. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/concept_utils.py +0 -0
  35. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/config/__init__.py +0 -0
  36. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/config/system_config.yaml +0 -0
  37. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/conformance_fix_journal.py +0 -0
  38. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/diff_utils.py +0 -0
  39. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/docs/generate_cli.py +0 -0
  40. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/docs/plain2code_cli.md +0 -0
  41. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/docs/starting_a_plain_project_from_scratch.md +0 -0
  42. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/event_bus.py +0 -0
  43. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/examples/example_hello_world_golang/config.yaml +0 -0
  44. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/examples/example_hello_world_golang/harness_tests/hello_world_test.go +0 -0
  45. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/examples/example_hello_world_golang/hello_world_golang.plain +0 -0
  46. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/examples/example_hello_world_golang/run.sh +0 -0
  47. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/examples/example_hello_world_python/config.yaml +0 -0
  48. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/examples/example_hello_world_python/harness_tests/hello_world_display/test_hello_world.py +0 -0
  49. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/examples/example_hello_world_python/hello_world_python.plain +0 -0
  50. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/examples/example_hello_world_python/run.sh +0 -0
  51. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/examples/example_hello_world_react/config.yaml +0 -0
  52. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/examples/example_hello_world_react/harness_tests/hello_world_display/cypress/e2e/hello_world.cy.ts +0 -0
  53. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/examples/example_hello_world_react/harness_tests/hello_world_display/cypress/support/e2e.ts +0 -0
  54. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/examples/example_hello_world_react/harness_tests/hello_world_display/cypress.config.ts +0 -0
  55. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/examples/example_hello_world_react/harness_tests/hello_world_display/package.json +0 -0
  56. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/examples/example_hello_world_react/harness_tests/hello_world_display/tsconfig.json +0 -0
  57. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/examples/example_hello_world_react/hello_world_react.plain +0 -0
  58. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/examples/example_hello_world_react/run.sh +0 -0
  59. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/examples/run.sh +0 -0
  60. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/file_utils.py +0 -0
  61. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/git_preflight.py +0 -0
  62. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/git_utils.py +0 -0
  63. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/install/bash/examples.sh +0 -0
  64. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/install/bash/install.sh +0 -0
  65. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/install/bash/walkthrough.sh +0 -0
  66. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/install/powershell/examples.ps1 +0 -0
  67. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/install/powershell/install.ps1 +0 -0
  68. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/install/powershell/walkthrough.ps1 +0 -0
  69. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/memory_management.py +0 -0
  70. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/metadata_utils.py +0 -0
  71. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/partial_rendering.py +0 -0
  72. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/path_resolution.py +0 -0
  73. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/plain2code.py +0 -0
  74. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/plain2code_arguments.py +0 -0
  75. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/plain2code_cli.md +0 -0
  76. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/plain2code_console.py +0 -0
  77. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/plain2code_events.py +0 -0
  78. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/plain2code_exceptions.py +0 -0
  79. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/plain2code_logger.py +0 -0
  80. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/plain2code_nodes.py +0 -0
  81. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/plain2code_read_config.py +0 -0
  82. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/plain2code_state.py +0 -0
  83. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/plain2code_telemetry.py +0 -0
  84. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/plain2code_utils.py +0 -0
  85. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/plain_file.py +0 -0
  86. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/plain_modules.py +0 -0
  87. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/plain_spec.py +0 -0
  88. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/pytest.ini +0 -0
  89. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/render_machine/__init__.py +0 -0
  90. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/render_machine/actions/analyze_specification_ambiguity.py +0 -0
  91. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/render_machine/actions/base_action.py +0 -0
  92. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/render_machine/actions/commit_conformance_tests_changes.py +0 -0
  93. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/render_machine/actions/commit_implementation_code_changes.py +0 -0
  94. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/render_machine/actions/create_dist.py +0 -0
  95. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/render_machine/actions/distill_conformance_test_memory.py +0 -0
  96. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/render_machine/actions/finish_functional_requirement.py +0 -0
  97. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/render_machine/actions/fix_conformance_test.py +0 -0
  98. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/render_machine/actions/fix_unit_tests.py +0 -0
  99. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/render_machine/actions/prepare_repositories.py +0 -0
  100. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/render_machine/actions/prepare_testing_environment.py +0 -0
  101. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/render_machine/actions/refactor_code.py +0 -0
  102. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/render_machine/actions/render_conformance_tests.py +0 -0
  103. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/render_machine/actions/run_conformance_tests.py +0 -0
  104. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/render_machine/actions/run_unit_tests.py +0 -0
  105. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/render_machine/actions/summarize_conformance_tests.py +0 -0
  106. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/render_machine/agent_tools.py +0 -0
  107. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/render_machine/conformance_tests.py +0 -0
  108. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/render_machine/implementation_code_helpers.py +0 -0
  109. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/render_machine/render_utils.py +0 -0
  110. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/render_machine/state_machine_config.py +0 -0
  111. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/render_machine/states.py +0 -0
  112. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/render_machine/triggers.py +0 -0
  113. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/resources/codeplain_overview.png +0 -0
  114. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/resources/plain_example.png +0 -0
  115. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/standard_template_library/golang-console-app-template.plain +0 -0
  116. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/standard_template_library/python-console-app-template.plain +0 -0
  117. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/standard_template_library/typescript-react-app-boilerplate.plain +0 -0
  118. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/standard_template_library/typescript-react-app-template.plain +0 -0
  119. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/system_config.py +0 -0
  120. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/test_scripts/run_conformance_tests_cypress.ps1 +0 -0
  121. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/test_scripts/run_conformance_tests_cypress.sh +0 -0
  122. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/test_scripts/run_conformance_tests_golang.ps1 +0 -0
  123. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/test_scripts/run_conformance_tests_golang.sh +0 -0
  124. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/test_scripts/run_conformance_tests_python.ps1 +0 -0
  125. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/test_scripts/run_conformance_tests_python.sh +0 -0
  126. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/test_scripts/run_unittests_flutter.ps1 +0 -0
  127. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/test_scripts/run_unittests_flutter.sh +0 -0
  128. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/test_scripts/run_unittests_golang.ps1 +0 -0
  129. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/test_scripts/run_unittests_golang.sh +0 -0
  130. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/test_scripts/run_unittests_python.ps1 +0 -0
  131. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/test_scripts/run_unittests_python.sh +0 -0
  132. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/test_scripts/run_unittests_react.ps1 +0 -0
  133. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/test_scripts/run_unittests_react.sh +0 -0
  134. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/__init__.py +0 -0
  135. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/conftest.py +0 -0
  136. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/acceptance_tests_warning/main_requiring_acceptance_tests.plain +0 -0
  137. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/acceptance_tests_warning/required_with_acceptance_tests.plain +0 -0
  138. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/imports/circular_imports_1.plain +0 -0
  139. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/imports/circular_imports_2.plain +0 -0
  140. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/imports/circular_imports_main.plain +0 -0
  141. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/imports/diamond_import_1.plain +0 -0
  142. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/imports/diamond_import_2.plain +0 -0
  143. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/imports/diamond_import_common.plain +0 -0
  144. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/imports/diamond_imports_main.plain +0 -0
  145. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/imports/import_with_functionalities.plain +0 -0
  146. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/imports/imports_module_with_functionalities.plain +0 -0
  147. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/imports/non_existent_import.plain +0 -0
  148. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/partial_rendering/pr_code_var.plain +0 -0
  149. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/partial_rendering/pr_implement.plain +0 -0
  150. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/partial_rendering/pr_leaf.plain +0 -0
  151. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/partial_rendering/pr_middle.plain +0 -0
  152. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/partial_rendering/pr_root.plain +0 -0
  153. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/partial_rendering/pr_solo.plain +0 -0
  154. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfile/add_new_task_modal_specification.yaml +0 -0
  155. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfile/duplicate_specification_heading.plain +0 -0
  156. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfile/empty_functional_specs_section.plain +0 -0
  157. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfile/invalid_specification_order.plain +0 -0
  158. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfile/missing_non_functional_requirements.plain +0 -0
  159. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfile/no_functional_specs_section.plain +0 -0
  160. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfile/plain_source_with_absolute_link.plain +0 -0
  161. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfile/plain_source_with_missing_link.plain +0 -0
  162. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfile/plain_source_with_url_link.plain +0 -0
  163. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfile/task_list_ui_specification.yaml +0 -0
  164. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfile/task_manager_with_reference_links.plain +0 -0
  165. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfile/without_non_functional_requirement.plain +0 -0
  166. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfileparser/concept_validation_acceptance_tests.plain +0 -0
  167. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfileparser/concept_validation_acceptance_tests_nondefined.plain +0 -0
  168. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfileparser/concept_validation_defined_nondefined.plain +0 -0
  169. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfileparser/concept_validation_defined_nondefined_2.plain +0 -0
  170. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfileparser/concept_validation_definition.plain +0 -0
  171. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfileparser/concept_validation_noconcepts.plain +0 -0
  172. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfileparser/concept_validation_nonconcept.plain +0 -0
  173. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfileparser/concept_validation_nondefined.plain +0 -0
  174. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfileparser/concept_validation_redefinition.plain +0 -0
  175. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfileparser/concept_validation_several_concepts.plain +0 -0
  176. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfileparser/concept_validation_valid.plain +0 -0
  177. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfileparser/cyclic_definitions.plain +0 -0
  178. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfileparser/exported_concepts.plain +0 -0
  179. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfileparser/exported_concepts_base.plain +0 -0
  180. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfileparser/exported_concepts_defaults_exported.plain +0 -0
  181. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfileparser/exported_concepts_example.plain +0 -0
  182. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfileparser/exported_concepts_imported_exported.plain +0 -0
  183. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfileparser/exported_concepts_inherited_reexported.plain +0 -0
  184. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfileparser/exported_concepts_malformed.plain +0 -0
  185. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfileparser/exported_concepts_missing_definition.plain +0 -0
  186. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfileparser/exported_concepts_missing_definition_example.plain +0 -0
  187. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfileparser/exported_concepts_multiple_per_bullet.plain +0 -0
  188. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfileparser/exported_concepts_multiple_per_bullet_undefined.plain +0 -0
  189. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfileparser/exported_concepts_nested.plain +0 -0
  190. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfileparser/exported_concepts_nested_example.plain +0 -0
  191. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfileparser/exported_concepts_no_definitions.plain +0 -0
  192. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfileparser/exported_concepts_not_declared.plain +0 -0
  193. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfileparser/exported_concepts_not_declared_example.plain +0 -0
  194. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfileparser/exported_concepts_transitive_example.plain +0 -0
  195. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfileparser/exported_concepts_transitive_l1.plain +0 -0
  196. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfileparser/exported_concepts_transitive_l2.plain +0 -0
  197. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfileparser/plain_file_parser_with_comments.plain +0 -0
  198. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfileparser/plain_file_with_comments_indented.plain +0 -0
  199. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfileparser/regular_plain_source.plain +0 -0
  200. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfileparser/required_concepts.plain +0 -0
  201. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfileparser/required_concepts_defs.plain +0 -0
  202. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfileparser/required_concepts_example.plain +0 -0
  203. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfileparser/required_concepts_l1.plain +0 -0
  204. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfileparser/required_concepts_l2.plain +0 -0
  205. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfileparser/required_concepts_missing.plain +0 -0
  206. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfileparser/required_concepts_module.plain +0 -0
  207. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfileparser/required_concepts_partial.plain +0 -0
  208. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfileparser/required_concepts_partial_duplicate.plain +0 -0
  209. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfileparser/requires_without_definitions_example.plain +0 -0
  210. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfileparser/topological_sort.plain +0 -0
  211. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/plainfileparser/topological_sort_not_referenced.plain +0 -0
  212. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/requires/circular_requires_main.plain +0 -0
  213. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/requires/circular_requires_sub.plain +0 -0
  214. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/requires/diamond_requires_1.plain +0 -0
  215. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/requires/diamond_requires_2.plain +0 -0
  216. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/requires/diamond_requires_common.plain +0 -0
  217. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/requires/diamond_requires_main.plain +0 -0
  218. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/requires/independent_requires_1.plain +0 -0
  219. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/requires/independent_requires_2.plain +0 -0
  220. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/requires/independent_requires_main.plain +0 -0
  221. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/requires/non_existent_require.plain +0 -0
  222. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/requires/normal_requires_1.plain +0 -0
  223. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/requires/normal_requires_2.plain +0 -0
  224. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/requires/normal_requires_common.plain +0 -0
  225. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/requires/normal_requires_main.plain +0 -0
  226. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/sample_base64_image.txt +0 -0
  227. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/simple.plain +0 -0
  228. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/templates/block_level_include.plain +0 -0
  229. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/templates/code_variables.plain +0 -0
  230. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/templates/header.plain +0 -0
  231. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/templates/implement.plain +0 -0
  232. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/templates/implement_2.plain +0 -0
  233. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/templates/template_include.plain +0 -0
  234. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/data/templates/test_hardest_problem.plain +0 -0
  235. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/e2e/Dockerfile +0 -0
  236. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/e2e/conftest.py +0 -0
  237. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/e2e/test_hello_world_python.py +0 -0
  238. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/e2e/test_hello_world_python_windows.py +0 -0
  239. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/test_agent_tools.py +0 -0
  240. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/test_arg_provenance.py +0 -0
  241. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/test_change_detection.py +0 -0
  242. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/test_cli_output.py +0 -0
  243. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/test_conformance_fix_journal.py +0 -0
  244. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/test_console_log_styles.py +0 -0
  245. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/test_distill_conformance_test_memory.py +0 -0
  246. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/test_dry_run_link_validation.py +0 -0
  247. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/test_file_utils.py +0 -0
  248. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/test_fix_unit_tests_action.py +0 -0
  249. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/test_fix_unit_tests_conformance_context.py +0 -0
  250. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/test_folder_path_resolution.py +0 -0
  251. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/test_git_preflight.py +0 -0
  252. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/test_git_utils.py +0 -0
  253. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/test_headless_logging.py +0 -0
  254. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/test_imports.py +0 -0
  255. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/test_log_file_name_resolution.py +0 -0
  256. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/test_logging_config_path_resolution.py +0 -0
  257. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/test_partial_rendering.py +0 -0
  258. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/test_path_resolution.py +0 -0
  259. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/test_plain2code.py +0 -0
  260. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/test_plain2code_state.py +0 -0
  261. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/test_plain2code_utils.py +0 -0
  262. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/test_plain_modules.py +0 -0
  263. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/test_plainfile.py +0 -0
  264. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/test_plainfileparser.py +0 -0
  265. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/test_plainspec.py +0 -0
  266. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/test_prepare_repositories_layout.py +0 -0
  267. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/test_render_finished_report.py +0 -0
  268. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/test_requires.py +0 -0
  269. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/test_resolve_config_file.py +0 -0
  270. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/test_script_path_resolution.py +0 -0
  271. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/test_system_config.py +0 -0
  272. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/test_telemetry.py +0 -0
  273. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/test_terminal_attachment.py +0 -0
  274. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/test_tui_components.py +0 -0
  275. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/test_tui_usage.py +0 -0
  276. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tests/test_usage_summary.py +0 -0
  277. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tui/__init__.py +0 -0
  278. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tui/components.py +0 -0
  279. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tui/models.py +0 -0
  280. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tui/plain2code_tui.py +0 -0
  281. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tui/plain_module_render_choice_tui.py +0 -0
  282. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tui/spinner.py +0 -0
  283. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tui/state_handlers.py +0 -0
  284. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tui/styles.css +0 -0
  285. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/tui/widget_helpers.py +0 -0
  286. {codeplain-0.3.13.dev37 → codeplain-0.3.13.dev39}/usage_summary.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: codeplain
3
- Version: 0.3.13.dev37
3
+ Version: 0.3.13.dev39
4
4
  Summary: Transform plain language specifications into working code
5
5
  License-File: LICENSE
6
6
  Classifier: Environment :: Console
@@ -8,7 +8,7 @@ Classifier: Intended Audience :: Developers
8
8
  Classifier: Operating System :: OS Independent
9
9
  Classifier: Topic :: Software Development :: Code Generators
10
10
  Requires-Python: ==3.11.*
11
- Requires-Dist: gitpython==3.1.62
11
+ Requires-Dist: gitpython==3.2.1
12
12
  Requires-Dist: mistletoe==1.6.0
13
13
  Requires-Dist: networkx==3.6.1
14
14
  Requires-Dist: python-frontmatter==1.3.0
@@ -134,10 +134,8 @@ class ModuleRenderer:
134
134
 
135
135
  code_renderer.run()
136
136
  if code_renderer.render_context.state == States.RENDER_FAILED.value:
137
- error_message = RenderError.get_display_message(
138
- code_renderer.render_context.previous_action_payload,
139
- fallback_message=code_renderer.render_context.last_error_message,
140
- )
137
+ # ExitWithError resolved and logged the stop reason; the TUI shows the same text.
138
+ error_message = code_renderer.render_context.last_error_message or RenderError.get_display_message(None)
141
139
  code_renderer.render_context.event_bus.publish(RenderFailed(error_message=error_message))
142
140
  return False, True
143
141
 
@@ -20,7 +20,7 @@ dependencies = [
20
20
  "requests==2.34.2",
21
21
  "tiktoken==0.14.0",
22
22
  "PyYAML==6.0.3",
23
- "gitpython==3.1.62",
23
+ "gitpython==3.2.1",
24
24
  "transitions==0.9.3",
25
25
  "textual>=7.5.0",
26
26
  "rich==15.0.0",
@@ -10,7 +10,13 @@ class ExitWithError(BaseAction):
10
10
  SUCCESSFUL_OUTCOME = "error_handled"
11
11
 
12
12
  def execute(self, render_context: RenderContext, previous_action_payload: Any | None):
13
- console.error(previous_action_payload)
13
+ # The stop reason is the error payload of the failed action, or the message stored when the
14
+ # error was dispatched from a state callback. The log and the TUI both show this text.
15
+ error_message = RenderError.get_display_message(
16
+ previous_action_payload, fallback_message=render_context.last_error_message
17
+ )
18
+ render_context.last_error_message = error_message
19
+ console.error(error_message)
14
20
 
15
21
  render_context.codeplain_api.fail_functional_requirement(
16
22
  render_context.frid_context.frid,
@@ -27,9 +33,4 @@ class ExitWithError(BaseAction):
27
33
  if render_context.run_state.render_id is not None:
28
34
  console.info(f"Render ID: {render_context.run_state.render_id}")
29
35
 
30
- return (
31
- self.SUCCESSFUL_OUTCOME,
32
- RenderError.encode(
33
- message=render_context.last_error_message or "Unknown error",
34
- ).to_payload(),
35
- )
36
+ return self.SUCCESSFUL_OUTCOME, RenderError.encode(message=error_message).to_payload()
@@ -7,7 +7,7 @@ from plain2code_console import RETRY_COLOR, console
7
7
  from plain2code_exceptions import FunctionalRequirementTooComplex
8
8
  from render_machine.actions.base_action import BaseAction
9
9
  from render_machine.implementation_code_helpers import ImplementationCodeHelpers
10
- from render_machine.render_context import RenderContext
10
+ from render_machine.render_context import MAX_UNITTEST_FIX_ATTEMPTS, RenderContext
11
11
  from render_machine.render_types import RenderError
12
12
 
13
13
  MAX_CODE_GENERATION_RETRIES = 2
@@ -20,9 +20,16 @@ class RenderFunctionalRequirement(BaseAction):
20
20
 
21
21
  def execute(self, render_context: RenderContext, _previous_action_payload: Any | None):
22
22
  if render_context.frid_context.functional_requirement_render_attempts >= MAX_CODE_GENERATION_RETRIES:
23
- error_msg = f"Unittests could not be fixed after rendering the functionality {render_context.frid_context.frid} for the {MAX_CODE_GENERATION_RETRIES} times."
23
+ # A render attempt is only restarted once its unit tests used up every fix round.
24
+ error_msg = (
25
+ f"Functionality {render_context.frid_context.frid}: unit tests still fail after "
26
+ f"{MAX_CODE_GENERATION_RETRIES} render attempts, {MAX_UNITTEST_FIX_ATTEMPTS} fix rounds each."
27
+ )
28
+ last_output_path = render_context.script_execution_history.latest_unit_test_output_path
29
+ if last_output_path:
30
+ error_msg += f" Last unit tests output: {last_output_path}"
24
31
  render_context.last_error_message = error_msg
25
- return self.ITERATION_LIMIT_EXCEEDED_OUTCOME, None
32
+ return self.ITERATION_LIMIT_EXCEEDED_OUTCOME, RenderError.encode(message=error_msg).to_payload()
26
33
 
27
34
  render_context.frid_context.functional_requirement_render_attempts += 1
28
35
 
@@ -67,14 +67,11 @@ class CodeRenderer:
67
67
  self.render_context.run_state.current_render_state = self.render_context.state
68
68
  self.render_context.script_execution_history.should_update_script_outputs = False
69
69
 
70
- self.render_context.previous_action_payload = previous_action_payload
71
-
72
70
  outcome, previous_action_payload = self.action_map[self.render_context.state].execute(
73
71
  self.render_context, previous_action_payload
74
72
  )
75
73
 
76
74
  if self.render_context.state == States.RENDER_FAILED.value:
77
- self.render_context.last_error_message = previous_action_payload
78
75
  self.render_context.event_bus.publish(RenderModuleFailed(module_name=self.render_context.module_name))
79
76
  break
80
77
 
@@ -113,12 +113,11 @@ class RenderContext:
113
113
  self.machine = machine
114
114
 
115
115
  def dispatch_error(self, error_message: str):
116
- """Log error, store it, and dispatch HANDLE_ERROR trigger.
116
+ """Store the error and dispatch HANDLE_ERROR trigger. ExitWithError logs it and the TUI displays it.
117
117
 
118
118
  Args:
119
119
  error_message: The error message to log and display to the user.
120
120
  """
121
- console.error(error_message)
122
121
  self.last_error_message = error_message
123
122
  self.machine.dispatch(triggers.HANDLE_ERROR)
124
123
 
@@ -259,17 +259,15 @@ class RenderError:
259
259
  """Decode error from action payload.
260
260
 
261
261
  Expects standardized format: {"error": {"message": ..., "type": ..., "details": ...}}
262
+ Any other payload is not an error (e.g. the payload of the action that ran before an error was
263
+ dispatched from a state callback) and decodes to None.
262
264
  """
263
- if payload is None:
265
+ if not isinstance(payload, dict) or "error" not in payload:
264
266
  return None
265
267
 
266
- if isinstance(payload, dict) and "error" in payload:
267
- error_data = payload["error"]
268
- return cls(
269
- message=error_data.get("message", "Unknown error"),
270
- error_type=error_data.get("type"),
271
- details=error_data.get("details"),
272
- )
273
-
274
- # Unexpected format - log and return generic error
275
- return cls(message=f"Unexpected error format: {type(payload).__name__}")
268
+ error_data = payload["error"]
269
+ return cls(
270
+ message=error_data.get("message", "Unknown error"),
271
+ error_type=error_data.get("type"),
272
+ details=error_data.get("details"),
273
+ )
@@ -5,7 +5,7 @@ requests==2.34.2
5
5
  rich==15.0.0
6
6
  tiktoken==0.14.0
7
7
  PyYAML==6.0.3
8
- gitpython==3.1.62
8
+ gitpython==3.2.1
9
9
  pytest==9.1.1
10
10
  textual>=7.5.0
11
11
  networkx==3.6.1
@@ -0,0 +1,210 @@
1
+ """Tests for the reason a render gives when it stops on an error.
2
+
3
+ When a render stops, the reason is logged and shown in the TUI. These tests drive the
4
+ real render state machine through the last failing unit-test run of a functionality
5
+ and check that the stop reason names the cause, instead of the literal "None".
6
+ """
7
+
8
+ import logging
9
+ import os
10
+ import stat
11
+ import tempfile
12
+ import threading
13
+ from types import SimpleNamespace
14
+
15
+ import pytest
16
+
17
+ import plain2code_logger
18
+ from event_bus import EventBus
19
+ from memory_management import MemoryManager
20
+ from plain2code_state import RunState
21
+ from plain_modules import PlainModule
22
+ from render_machine.actions.exit_with_error import ExitWithError
23
+ from render_machine.actions.render_functional_requirement import MAX_CODE_GENERATION_RETRIES
24
+ from render_machine.code_renderer import CodeRenderer
25
+ from render_machine.render_context import (
26
+ MAX_FUNCTIONAL_REQUIREMENT_RENDER_ATTEMPTS_FAILED_UNIT_DURING_CONFORMANCE_TESTS,
27
+ MAX_UNITTEST_FIX_ATTEMPTS,
28
+ RenderContext,
29
+ )
30
+ from render_machine.render_types import RenderError, UnitTestsRunningContext
31
+ from render_machine.states import States
32
+
33
+
34
+ class FakeCodeplainAPI:
35
+ def __init__(self):
36
+ self.failed_frids = []
37
+
38
+ def fail_functional_requirement(self, frid, module_name, run_state):
39
+ self.failed_frids.append(frid)
40
+
41
+
42
+ @pytest.fixture
43
+ def workspace():
44
+ with tempfile.TemporaryDirectory() as folder:
45
+ build_folder = os.path.join(folder, "plain_modules")
46
+ os.makedirs(build_folder)
47
+
48
+ unittests_script = os.path.join(folder, "run_unittests.sh")
49
+ with open(unittests_script, "w", encoding="utf-8") as script:
50
+ script.write("#!/bin/sh\necho '1 failed, 0 passed'\nexit 1\n")
51
+ os.chmod(unittests_script, os.stat(unittests_script).st_mode | stat.S_IEXEC)
52
+
53
+ yield build_folder, unittests_script
54
+
55
+
56
+ def make_render_context(api, build_folder, unittests_script, fixtures_dir):
57
+ plain_module = PlainModule("pr_solo.plain", build_folder, [fixtures_dir])
58
+ os.makedirs(plain_module.module_build_folder, exist_ok=True)
59
+ return RenderContext(
60
+ api,
61
+ MemoryManager(api, plain_module.module_memory_folder),
62
+ plain_module,
63
+ build_folder=plain_module.module_build_folder,
64
+ build_dest="dist",
65
+ conformance_tests_dest="dist_tests",
66
+ unittests_script=unittests_script,
67
+ conformance_tests_script=None,
68
+ prepare_environment_script=None,
69
+ copy_build=False,
70
+ copy_conformance_tests=False,
71
+ render_range=None,
72
+ render_conformance_tests=False,
73
+ base_folder=fixtures_dir,
74
+ run_state=RunState(spec_filename="pr_solo.plain"),
75
+ event_bus=EventBus(),
76
+ enter_pause_event=threading.Event(),
77
+ )
78
+
79
+
80
+ def nested_state(*states: States) -> str:
81
+ return "_".join(state.value for state in states)
82
+
83
+
84
+ def error_log_messages(caplog) -> list[str]:
85
+ return [
86
+ record.getMessage()
87
+ for record in caplog.records
88
+ if record.name == plain2code_logger.LOGGER_NAME and record.levelno == logging.ERROR
89
+ ]
90
+
91
+
92
+ @pytest.fixture
93
+ def fixtures_dir(get_test_data_path):
94
+ return get_test_data_path("data/partial_rendering")
95
+
96
+
97
+ def test_unit_tests_failing_after_the_last_attempt_name_the_cause(workspace, fixtures_dir, caplog):
98
+ build_folder, unittests_script = workspace
99
+ api = FakeCodeplainAPI()
100
+ render_context = make_render_context(api, build_folder, unittests_script, fixtures_dir)
101
+ code_renderer = CodeRenderer(render_context)
102
+
103
+ # The last render attempt of functionality 1, after its last unit-test fix round.
104
+ render_context.start_implementing_frid()
105
+ render_context.frid_context.functional_requirement_render_attempts = MAX_CODE_GENERATION_RETRIES
106
+ render_context.unit_tests_running_context = UnitTestsRunningContext(fix_attempts=MAX_UNITTEST_FIX_ATTEMPTS)
107
+ code_renderer.machine.set_state(
108
+ nested_state(States.IMPLEMENTING_FRID, States.PROCESSING_UNIT_TESTS, States.UNIT_TESTS_READY)
109
+ )
110
+
111
+ with caplog.at_level(logging.DEBUG, logger=plain2code_logger.LOGGER_NAME):
112
+ code_renderer.run()
113
+
114
+ assert render_context.state == States.RENDER_FAILED.value
115
+ assert api.failed_frids == ["1"]
116
+
117
+ last_output_path = render_context.script_execution_history.latest_unit_test_output_path
118
+ assert last_output_path is not None and os.path.exists(last_output_path)
119
+
120
+ expected_message = (
121
+ f"Functionality 1: unit tests still fail after {MAX_CODE_GENERATION_RETRIES} render attempts, "
122
+ f"{MAX_UNITTEST_FIX_ATTEMPTS} fix rounds each. Last unit tests output: {last_output_path}"
123
+ )
124
+ assert error_log_messages(caplog) == [expected_message]
125
+ # The TUI shows the same stop reason as the log.
126
+ assert render_context.last_error_message == expected_message
127
+
128
+
129
+ def test_unit_tests_failing_again_during_conformance_tests_name_the_cause_once(workspace, fixtures_dir, caplog):
130
+ build_folder, unittests_script = workspace
131
+ api = FakeCodeplainAPI()
132
+ render_context = make_render_context(api, build_folder, unittests_script, fixtures_dir)
133
+ code_renderer = CodeRenderer(render_context)
134
+
135
+ # Unit tests broken by a conformance fix, failing their last fix round for the last allowed time.
136
+ render_context.start_implementing_frid()
137
+ render_context.start_conformance_tests_processing()
138
+ render_context.functional_requirements_render_attempts_failed_unit_during_conformance_tests = (
139
+ MAX_FUNCTIONAL_REQUIREMENT_RENDER_ATTEMPTS_FAILED_UNIT_DURING_CONFORMANCE_TESTS - 1
140
+ )
141
+ render_context.unit_tests_running_context = UnitTestsRunningContext(fix_attempts=MAX_UNITTEST_FIX_ATTEMPTS)
142
+ code_renderer.machine.set_state(
143
+ nested_state(
144
+ States.IMPLEMENTING_FRID,
145
+ States.PROCESSING_CONFORMANCE_TESTS,
146
+ States.PROCESSING_UNIT_TESTS,
147
+ States.UNIT_TESTS_READY,
148
+ )
149
+ )
150
+
151
+ with caplog.at_level(logging.DEBUG, logger=plain2code_logger.LOGGER_NAME):
152
+ code_renderer.run()
153
+
154
+ assert render_context.state == States.RENDER_FAILED.value
155
+ error_messages = error_log_messages(caplog)
156
+ assert len(error_messages) == 1
157
+ assert error_messages[0].startswith("Failed to adjust the unit tests")
158
+ assert "functionality 1" in error_messages[0]
159
+ # The TUI shows the same stop reason as the log, not the unit tests output of the payload.
160
+ assert render_context.last_error_message == error_messages[0]
161
+
162
+
163
+ class TestExitWithErrorMessage:
164
+ @staticmethod
165
+ def make_context(last_error_message=None):
166
+ render_context = RenderContext.__new__(RenderContext)
167
+ render_context.codeplain_api = FakeCodeplainAPI()
168
+ render_context.frid_context = SimpleNamespace(frid="1")
169
+ render_context.module_name = "mod"
170
+ render_context.run_state = RunState(spec_filename="mod.plain")
171
+ render_context.last_error_message = last_error_message
172
+ return render_context
173
+
174
+ def run_exit(self, render_context, payload, caplog):
175
+ with caplog.at_level(logging.DEBUG, logger=plain2code_logger.LOGGER_NAME):
176
+ ExitWithError().execute(render_context, payload)
177
+ return error_log_messages(caplog)
178
+
179
+ def test_logs_the_message_of_an_error_payload(self, caplog):
180
+ render_context = self.make_context()
181
+ payload = RenderError.encode(
182
+ message="Unit tests script failed due to problems in the environment setup.",
183
+ error_type="ENVIRONMENT_ERROR",
184
+ script="run_unittests.sh",
185
+ ).to_payload()
186
+
187
+ messages = self.run_exit(render_context, payload, caplog)
188
+
189
+ expected = (
190
+ "Unit tests script failed due to problems in the environment setup.\n\nDetails:\n Script: run_unittests.sh"
191
+ )
192
+ assert messages == [expected]
193
+ assert render_context.last_error_message == expected
194
+
195
+ def test_logs_the_stored_message_when_the_payload_is_not_an_error(self, caplog):
196
+ render_context = self.make_context(last_error_message="Conformance tests could not be fixed.")
197
+
198
+ messages = self.run_exit(render_context, {"previous_unittests_issue": "1 failed"}, caplog)
199
+
200
+ assert messages == ["Conformance tests could not be fixed."]
201
+ assert render_context.last_error_message == "Conformance tests could not be fixed."
202
+
203
+ def test_never_logs_none(self, caplog):
204
+ render_context = self.make_context()
205
+
206
+ messages = self.run_exit(render_context, None, caplog)
207
+
208
+ assert len(messages) == 1
209
+ assert "None" not in messages[0]
210
+ assert render_context.last_error_message == messages[0]