codeplain 0.2.14__tar.gz → 0.2.15__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.
- codeplain-0.2.15/.claude/settings.local.json +7 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/PKG-INFO +1 -1
- codeplain-0.2.15/_version.py +1 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/codeplain_REST_api.py +8 -8
- codeplain-0.2.15/docs/generate_cli.py +25 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/docs/plain2code_cli.md +38 -25
- {codeplain-0.2.14 → codeplain-0.2.15}/event_bus.py +2 -14
- {codeplain-0.2.14 → codeplain-0.2.15}/file_utils.py +7 -4
- {codeplain-0.2.14 → codeplain-0.2.15}/git_utils.py +6 -8
- {codeplain-0.2.14 → codeplain-0.2.15}/install/bash/install.sh +1 -1
- {codeplain-0.2.14 → codeplain-0.2.15}/install/powershell/install.ps1 +1 -1
- {codeplain-0.2.14 → codeplain-0.2.15}/memory_management.py +1 -1
- {codeplain-0.2.14 → codeplain-0.2.15}/module_renderer.py +8 -1
- {codeplain-0.2.14 → codeplain-0.2.15}/plain2code.py +34 -36
- {codeplain-0.2.14 → codeplain-0.2.15}/plain2code_arguments.py +23 -15
- {codeplain-0.2.14 → codeplain-0.2.15}/plain2code_console.py +15 -11
- {codeplain-0.2.14 → codeplain-0.2.15}/plain2code_events.py +6 -1
- {codeplain-0.2.14 → codeplain-0.2.15}/plain2code_exceptions.py +7 -1
- {codeplain-0.2.14 → codeplain-0.2.15}/plain2code_logger.py +2 -19
- {codeplain-0.2.14 → codeplain-0.2.15}/plain2code_utils.py +3 -3
- {codeplain-0.2.14 → codeplain-0.2.15}/plain_file.py +11 -11
- {codeplain-0.2.14 → codeplain-0.2.15}/plain_spec.py +3 -3
- {codeplain-0.2.14 → codeplain-0.2.15}/pyproject.toml +0 -5
- {codeplain-0.2.14 → codeplain-0.2.15}/render_machine/actions/analyze_specification_ambiguity.py +2 -4
- {codeplain-0.2.14 → codeplain-0.2.15}/render_machine/actions/create_dist.py +1 -1
- {codeplain-0.2.14 → codeplain-0.2.15}/render_machine/actions/exit_with_error.py +1 -1
- {codeplain-0.2.14 → codeplain-0.2.15}/render_machine/actions/fix_conformance_test.py +3 -3
- {codeplain-0.2.14 → codeplain-0.2.15}/render_machine/actions/fix_unit_tests.py +2 -2
- {codeplain-0.2.14 → codeplain-0.2.15}/render_machine/actions/prepare_repositories.py +3 -3
- {codeplain-0.2.14 → codeplain-0.2.15}/render_machine/actions/prepare_testing_environment.py +1 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/render_machine/actions/refactor_code.py +3 -3
- {codeplain-0.2.14 → codeplain-0.2.15}/render_machine/actions/render_conformance_tests.py +4 -4
- {codeplain-0.2.14 → codeplain-0.2.15}/render_machine/actions/render_functional_requirement.py +4 -4
- {codeplain-0.2.14 → codeplain-0.2.15}/render_machine/actions/run_conformance_tests.py +11 -7
- {codeplain-0.2.14 → codeplain-0.2.15}/render_machine/actions/run_unit_tests.py +9 -4
- {codeplain-0.2.14 → codeplain-0.2.15}/render_machine/actions/summarize_conformance_tests.py +2 -2
- {codeplain-0.2.14 → codeplain-0.2.15}/render_machine/code_renderer.py +11 -1
- {codeplain-0.2.14 → codeplain-0.2.15}/render_machine/conformance_tests.py +3 -3
- {codeplain-0.2.14 → codeplain-0.2.15}/render_machine/render_context.py +13 -8
- {codeplain-0.2.14 → codeplain-0.2.15}/render_machine/render_types.py +5 -1
- {codeplain-0.2.14 → codeplain-0.2.15}/render_machine/render_utils.py +102 -22
- {codeplain-0.2.14 → codeplain-0.2.15}/test_scripts/run_unittests_golang.sh +1 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/test_scripts/run_unittests_python.sh +1 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/test_scripts/run_unittests_react.sh +1 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/plainfile/invalid_specification_order.plain +1 -1
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/plainfile/missing_non_functional_requirements.plain +1 -1
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/plainfile/without_non_functional_requirement.plain +1 -1
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/plainfileparser/plain_file_parser_with_comments.plain +2 -2
- codeplain-0.2.15/tests/data/plainfileparser/plain_file_with_comments_indented.plain +10 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/plainfileparser/regular_plain_source.plain +2 -2
- codeplain-0.2.15/tests/data/requires/diamond_requires_common.plain +7 -0
- codeplain-0.2.15/tests/data/requires/normal_requires_common.plain +7 -0
- codeplain-0.2.15/tests/data/simple.plain +7 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/templates/code_variables.plain +2 -2
- codeplain-0.2.15/tests/data/templates/header.plain +7 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/test_git_utils.py +3 -3
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/test_plainfile.py +8 -8
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/test_plainfileparser.py +23 -23
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/test_plainspec.py +5 -5
- {codeplain-0.2.14 → codeplain-0.2.15}/tui/components.py +60 -27
- {codeplain-0.2.14 → codeplain-0.2.15}/tui/plain2code_tui.py +33 -5
- {codeplain-0.2.14 → codeplain-0.2.15}/tui/state_handlers.py +5 -5
- {codeplain-0.2.14 → codeplain-0.2.15}/tui/styles.css +21 -3
- {codeplain-0.2.14 → codeplain-0.2.15}/tui/widget_helpers.py +17 -11
- codeplain-0.2.14/_version.py +0 -1
- codeplain-0.2.14/docs/generate_cli.py +0 -20
- codeplain-0.2.14/tests/data/plainfileparser/plain_file_with_comments_indented.plain +0 -10
- codeplain-0.2.14/tests/data/requires/diamond_requires_common.plain +0 -7
- codeplain-0.2.14/tests/data/requires/normal_requires_common.plain +0 -7
- codeplain-0.2.14/tests/data/simple.plain +0 -7
- codeplain-0.2.14/tests/data/templates/header.plain +0 -7
- {codeplain-0.2.14 → codeplain-0.2.15}/.flake8 +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/.github/workflows/lint-and-test.yml +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/.github/workflows/nofity-slack-on-main-merge.yml +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/.github/workflows/publish-install-script.yml +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/.github/workflows/publish-to-pypi.yml +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/.gitignore +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/.vscode/launch.json +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/.vscode/settings.json +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/LICENSE +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/README.md +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/concept_utils.py +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/config/__init__.py +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/config/system_config.yaml +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/diff_utils.py +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/docs/starting_a_plain_project_from_scratch.md +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/examples/example_hello_world_golang/config.yaml +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/examples/example_hello_world_golang/harness_tests/hello_world_test.go +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/examples/example_hello_world_golang/hello_world_golang.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/examples/example_hello_world_golang/run.sh +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/examples/example_hello_world_python/config.yaml +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/examples/example_hello_world_python/harness_tests/hello_world_display/test_hello_world.py +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/examples/example_hello_world_python/hello_world_python.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/examples/example_hello_world_python/python-console-app-boilerplate.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/examples/example_hello_world_python/run.sh +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/examples/example_hello_world_react/config.yaml +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/examples/example_hello_world_react/harness_tests/hello_world_display/cypress/e2e/hello_world.cy.ts +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/examples/example_hello_world_react/harness_tests/hello_world_display/cypress/support/e2e.ts +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/examples/example_hello_world_react/harness_tests/hello_world_display/cypress.config.ts +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/examples/example_hello_world_react/harness_tests/hello_world_display/package.json +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/examples/example_hello_world_react/harness_tests/hello_world_display/tsconfig.json +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/examples/example_hello_world_react/hello_world_react.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/examples/example_hello_world_react/run.sh +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/examples/run.sh +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/hash_key.py +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/install/bash/examples.sh +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/install/bash/walkthrough.sh +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/install/powershell/examples.ps1 +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/install/powershell/walkthrough.ps1 +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/plain2code_nodes.py +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/plain2code_read_config.py +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/plain2code_state.py +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/plain_modules.py +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/pytest.ini +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/render_machine/__init__.py +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/render_machine/actions/base_action.py +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/render_machine/actions/commit_conformance_tests_changes.py +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/render_machine/actions/commit_implementation_code_changes.py +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/render_machine/actions/finish_functional_requirement.py +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/render_machine/conformance_test_helpers.py +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/render_machine/implementation_code_helpers.py +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/render_machine/state_machine_config.py +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/render_machine/states.py +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/render_machine/triggers.py +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/requirements.txt +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/resources/codeplain_overview.png +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/resources/plain_example.png +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/standard_template_library/__init__.py +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/standard_template_library/golang-console-app-template.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/standard_template_library/python-console-app-template.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/standard_template_library/typescript-react-app-boilerplate.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/standard_template_library/typescript-react-app-template.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/system_config.py +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/test_scripts/run_conformance_tests_cypress.sh +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/test_scripts/run_conformance_tests_golang.sh +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/test_scripts/run_conformance_tests_python.sh +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/__init__.py +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/conftest.py +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/imports/circular_imports_1.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/imports/circular_imports_2.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/imports/circular_imports_main.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/imports/diamond_import_1.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/imports/diamond_import_2.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/imports/diamond_import_common.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/imports/diamond_imports_main.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/imports/non_existent_import.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/plainfile/duplicate_specification_heading.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/plainfile/plain_source_with_absolute_link.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/plainfile/plain_source_with_url_link.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/plainfile/task_manager_with_reference_links.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/plainfileparser/concept_validation_acceptance_tests.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/plainfileparser/concept_validation_acceptance_tests_nondefined.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/plainfileparser/concept_validation_defined_nondefined.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/plainfileparser/concept_validation_defined_nondefined_2.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/plainfileparser/concept_validation_definition.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/plainfileparser/concept_validation_noconcepts.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/plainfileparser/concept_validation_nonconcept.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/plainfileparser/concept_validation_nondefined.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/plainfileparser/concept_validation_redefinition.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/plainfileparser/concept_validation_several_concepts.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/plainfileparser/concept_validation_valid.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/plainfileparser/exported_concepts.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/plainfileparser/exported_concepts_base.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/plainfileparser/exported_concepts_example.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/plainfileparser/exported_concepts_missing.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/plainfileparser/exported_concepts_missing_example.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/plainfileparser/exported_concepts_nested.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/plainfileparser/exported_concepts_nested_example.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/plainfileparser/exported_concepts_transitive_example.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/plainfileparser/exported_concepts_transitive_l1.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/plainfileparser/exported_concepts_transitive_l2.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/plainfileparser/required_concepts.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/plainfileparser/required_concepts_defs.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/plainfileparser/required_concepts_example.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/plainfileparser/required_concepts_l1.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/plainfileparser/required_concepts_l2.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/plainfileparser/required_concepts_missing.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/plainfileparser/required_concepts_module.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/plainfileparser/required_concepts_partial.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/plainfileparser/required_concepts_partial_duplicate.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/plainfileparser/topological_sort.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/plainfileparser/topological_sort_not_referenced.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/requires/circular_requires_main.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/requires/circular_requires_sub.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/requires/diamond_requires_1.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/requires/diamond_requires_2.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/requires/diamond_requires_main.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/requires/independent_requires_1.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/requires/independent_requires_2.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/requires/independent_requires_main.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/requires/non_existent_require.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/requires/normal_requires_1.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/requires/normal_requires_2.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/requires/normal_requires_main.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/templates/block_level_include.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/templates/implement.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/templates/implement_2.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/templates/template_include.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/data/templates/test_hardest_problem.plain +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/test_imports.py +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/test_requires.py +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tests/test_resolve_config_file.py +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tui/__init__.py +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tui/models.py +0 -0
- {codeplain-0.2.14 → codeplain-0.2.15}/tui/spinner.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.2.15"
|
|
@@ -18,7 +18,7 @@ RETRY_ERROR_CODES = [
|
|
|
18
18
|
ERROR_CODE_EXCEPTIONS = {
|
|
19
19
|
"FunctionalRequirementTooComplex": plain2code_exceptions.FunctionalRequirementTooComplex,
|
|
20
20
|
"ConflictingRequirements": plain2code_exceptions.ConflictingRequirements,
|
|
21
|
-
"CreditBalanceTooLow": plain2code_exceptions.
|
|
21
|
+
"CreditBalanceTooLow": plain2code_exceptions.RenderingCreditBalanceTooLow,
|
|
22
22
|
"LLMInternalError": plain2code_exceptions.LLMInternalError,
|
|
23
23
|
"MissingResource": plain2code_exceptions.MissingResource,
|
|
24
24
|
"PlainSyntaxError": plain2code_exceptions.PlainSyntaxError,
|
|
@@ -67,8 +67,8 @@ class CodeplainAPI:
|
|
|
67
67
|
connection_error_type = "Network error" if is_connection_error else "Error"
|
|
68
68
|
if attempt < num_retries:
|
|
69
69
|
if not silent:
|
|
70
|
-
self.console.
|
|
71
|
-
self.console.
|
|
70
|
+
self.console.debug(f"{connection_error_type} on attempt {attempt + 1}/{num_retries + 1}: {error}")
|
|
71
|
+
self.console.debug(f"Retrying in {retry_delay} seconds...")
|
|
72
72
|
time.sleep(retry_delay)
|
|
73
73
|
# Exponential backoff
|
|
74
74
|
return retry_delay * 2
|
|
@@ -117,7 +117,7 @@ class CodeplainAPI:
|
|
|
117
117
|
try:
|
|
118
118
|
response_json = response.json()
|
|
119
119
|
except requests.exceptions.JSONDecodeError as e:
|
|
120
|
-
|
|
120
|
+
self.console.debug(f"Failed to decode JSON response: {e}. Response text: {response.text}")
|
|
121
121
|
raise
|
|
122
122
|
|
|
123
123
|
if response.status_code == requests.codes.bad_request and "error_code" in response_json:
|
|
@@ -156,11 +156,11 @@ class CodeplainAPI:
|
|
|
156
156
|
run_state: RunState,
|
|
157
157
|
) -> dict[str, str]:
|
|
158
158
|
"""
|
|
159
|
-
Renders the content of a
|
|
159
|
+
Renders the content of a functionality based on the provided ID,
|
|
160
160
|
corresponding sections from a Plain document, and existing files' content.
|
|
161
161
|
|
|
162
162
|
Args:
|
|
163
|
-
frid (str): The unique identifier for the
|
|
163
|
+
frid (str): The unique identifier for the functionality to be rendered.
|
|
164
164
|
plain_source_tree (dict): A dictionary containing the plain source tree.
|
|
165
165
|
linked_resources (dict): A dictionary where the keys represent filenames of linked
|
|
166
166
|
resources and the values are dictionaries containing
|
|
@@ -170,7 +170,7 @@ class CodeplainAPI:
|
|
|
170
170
|
and the values are the content of those files.
|
|
171
171
|
memory_files_content (dict): A dictionary where the keys represent memory filenames
|
|
172
172
|
and the values are the content of those files.
|
|
173
|
-
module_name (str): The name of the module to render the
|
|
173
|
+
module_name (str): The name of the module to render the functionality for.
|
|
174
174
|
required_modules (dict): A dictionary where the keys represent module names
|
|
175
175
|
and the values are lists of functionalities implemented in those modules.
|
|
176
176
|
run_state (RunState): The current state of the rendering process.
|
|
@@ -390,7 +390,7 @@ class CodeplainAPI:
|
|
|
390
390
|
Renders acceptance tests based on the provided parameters.
|
|
391
391
|
|
|
392
392
|
Args:
|
|
393
|
-
frid (str): The unique identifier for the
|
|
393
|
+
frid (str): The unique identifier for the functionality.
|
|
394
394
|
plain_source_tree (dict): A dictionary containing the plain source tree.
|
|
395
395
|
linked_resources (dict): A dictionary where the keys represent resource names
|
|
396
396
|
and the values are the content of those resources.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
# Add the parent directory to the path so we can import plain2code_arguments
|
|
5
|
+
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def main():
|
|
9
|
+
from plain2code_arguments import create_parser
|
|
10
|
+
|
|
11
|
+
# Get the parser and generate help text
|
|
12
|
+
parser = create_parser(color=False) # Disable color for markdown output
|
|
13
|
+
help_text = parser.format_help()
|
|
14
|
+
|
|
15
|
+
# Create markdown
|
|
16
|
+
md = "# Plain2Code CLI Reference\n\n```text\n" + help_text + "\n```"
|
|
17
|
+
|
|
18
|
+
# Run generate_cli.py in the docs folder
|
|
19
|
+
|
|
20
|
+
with open("plain2code_cli.md", "w", encoding="utf-8") as f:
|
|
21
|
+
f.write(md)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
if __name__ == "__main__":
|
|
25
|
+
main()
|
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
# Plain2Code CLI Reference
|
|
2
2
|
|
|
3
3
|
```text
|
|
4
|
-
usage: generate_cli.py [-h] [--verbose] [--base-folder BASE_FOLDER] [--build-folder BUILD_FOLDER] [--log-to-file | --no-log-to-file]
|
|
5
|
-
[--
|
|
6
|
-
[--
|
|
7
|
-
[--
|
|
8
|
-
[--
|
|
4
|
+
usage: generate_cli.py [-h] [--verbose] [--base-folder BASE_FOLDER] [--build-folder BUILD_FOLDER] [--log-to-file | --no-log-to-file]
|
|
5
|
+
[--log-file-name LOG_FILE_NAME] [--config-name CONFIG_NAME] [--render-range RENDER_RANGE | --render-from RENDER_FROM]
|
|
6
|
+
[--force-render] [--unittests-script UNITTESTS_SCRIPT] [--conformance-tests-folder CONFORMANCE_TESTS_FOLDER]
|
|
7
|
+
[--conformance-tests-script CONFORMANCE_TESTS_SCRIPT] [--prepare-environment-script PREPARE_ENVIRONMENT_SCRIPT]
|
|
8
|
+
[--test-script-timeout TEST_SCRIPT_TIMEOUT] [--api [API]] [--api-key API_KEY] [--full-plain] [--dry-run]
|
|
9
|
+
[--replay-with REPLAY_WITH] [--template-dir TEMPLATE_DIR] [--copy-build] [--build-dest BUILD_DEST]
|
|
10
|
+
[--copy-conformance-tests] [--conformance-tests-dest CONFORMANCE_TESTS_DEST] [--render-machine-graph]
|
|
11
|
+
[--logging-config-path LOGGING_CONFIG_PATH] [--headless]
|
|
9
12
|
filename
|
|
10
13
|
|
|
11
14
|
Render plain code to target code.
|
|
12
15
|
|
|
13
16
|
positional arguments:
|
|
14
|
-
filename Path to the plain file to render. The directory containing this file has highest precedence for template loading, so
|
|
15
|
-
for more details about template
|
|
17
|
+
filename Path to the plain file to render. The directory containing this file has highest precedence for template loading, so
|
|
18
|
+
you can place custom templates here to override the defaults. See --template-dir for more details about template
|
|
19
|
+
loading.
|
|
16
20
|
|
|
17
21
|
options:
|
|
18
22
|
-h, --help show this help message and exit
|
|
@@ -24,49 +28,58 @@ options:
|
|
|
24
28
|
--log-to-file, --no-log-to-file
|
|
25
29
|
Enable logging to a file. Defaults to True. Set to False to disable.
|
|
26
30
|
--log-file-name LOG_FILE_NAME
|
|
27
|
-
Name of the log file. Defaults to 'codeplain.log'.Always resolved relative to the plain file directory.If file on
|
|
28
|
-
current logs.
|
|
31
|
+
Name of the log file. Defaults to 'codeplain.log'.Always resolved relative to the plain file directory.If file on
|
|
32
|
+
this path already exists, the already existing log file will be overwritten by the current logs.
|
|
29
33
|
--render-range RENDER_RANGE
|
|
30
|
-
Specify a range of
|
|
31
|
-
inclusive of both start and end IDs.
|
|
34
|
+
Specify a range of functionalities to render (e.g. `1` , `2`, `3`). Use comma to separate start and end IDs. If only
|
|
35
|
+
one functionality ID is provided, only that functionality is rendered. Range is inclusive of both start and end IDs.
|
|
32
36
|
--render-from RENDER_FROM
|
|
33
|
-
Continue generation starting from this specific
|
|
34
|
-
in your plain file.
|
|
37
|
+
Continue generation starting from this specific functionality (e.g. `2`). The functionality with this ID will be
|
|
38
|
+
included in the output. The functionality ID must match one of the functionalities in your plain file.
|
|
35
39
|
--force-render Force re-render of all the required modules.
|
|
36
40
|
--unittests-script UNITTESTS_SCRIPT
|
|
37
|
-
Shell script to run unit tests on generated code. Receives the build folder path as its first argument (default:
|
|
41
|
+
Shell script to run unit tests on generated code. Receives the build folder path as its first argument (default:
|
|
42
|
+
'plain_modules').
|
|
38
43
|
--conformance-tests-folder CONFORMANCE_TESTS_FOLDER
|
|
39
44
|
Folder for conformance test files
|
|
40
45
|
--conformance-tests-script CONFORMANCE_TESTS_SCRIPT
|
|
41
|
-
Path to conformance tests shell script. Every conformance test script should accept two arguments: 1) Path to a
|
|
42
|
-
|
|
46
|
+
Path to conformance tests shell script. Every conformance test script should accept two arguments: 1) Path to a
|
|
47
|
+
folder (e.g. `plain_modules/module_name`) containing generated source code, 2) Path to a subfolder of the conformance
|
|
48
|
+
tests folder (e.g. `conformance_tests/subfoldername`) containing test files.
|
|
43
49
|
--prepare-environment-script PREPARE_ENVIRONMENT_SCRIPT
|
|
44
|
-
Path to a shell script that prepares the testing environment. The script should accept the source code folder path as
|
|
50
|
+
Path to a shell script that prepares the testing environment. The script should accept the source code folder path as
|
|
51
|
+
its first argument.
|
|
45
52
|
--test-script-timeout TEST_SCRIPT_TIMEOUT
|
|
46
53
|
Timeout for test scripts in seconds. If not provided, the default timeout of 120 seconds is used.
|
|
47
54
|
--api [API] Alternative base URL for the API. Default: `https://api.codeplain.ai`
|
|
48
55
|
--api-key API_KEY API key used to access the API. If not provided, the `CODEPLAIN_API_KEY` environment variable is used.
|
|
49
|
-
--full-plain Full preview ***plain specification before code generation.Use when you want to preview context of all ***plain
|
|
56
|
+
--full-plain Full preview ***plain specification before code generation.Use when you want to preview context of all ***plain
|
|
57
|
+
primitives that are going to be included in order to render the given module.
|
|
50
58
|
--dry-run Dry run preview of the code generation (without actually making any changes).
|
|
51
59
|
--replay-with REPLAY_WITH
|
|
52
60
|
--template-dir TEMPLATE_DIR
|
|
53
|
-
Path to a custom template directory. Templates are searched in the following order: 1) Directory containing the plain
|
|
54
|
-
Built-in standard_template_library
|
|
55
|
-
|
|
61
|
+
Path to a custom template directory. Templates are searched in the following order: 1) Directory containing the plain
|
|
62
|
+
file, 2) Custom template directory (if provided through this argument), 3) Built-in standard_template_library
|
|
63
|
+
directory
|
|
64
|
+
--copy-build If set, copy the rendered contents of code in `--base-folder` folder to `--build-dest` folder after successful
|
|
65
|
+
rendering.
|
|
56
66
|
--build-dest BUILD_DEST
|
|
57
67
|
Target folder to copy rendered contents of code to (used only if --copy-build is set).
|
|
58
68
|
--copy-conformance-tests
|
|
59
|
-
If set, copy the conformance tests of code in `--conformance-tests-folder` folder to `--conformance-tests-dest`
|
|
69
|
+
If set, copy the conformance tests of code in `--conformance-tests-folder` folder to `--conformance-tests-dest`
|
|
70
|
+
folder successful rendering. Requires --conformance-tests-script.
|
|
60
71
|
--conformance-tests-dest CONFORMANCE_TESTS_DEST
|
|
61
72
|
Target folder to copy conformance tests of code to (used only if --copy-conformance-tests is set).
|
|
62
73
|
--render-machine-graph
|
|
63
74
|
If set, render the state machine graph.
|
|
64
|
-
--logging-config-path
|
|
75
|
+
--logging-config-path LOGGING_CONFIG_PATH
|
|
65
76
|
Path to the logging configuration file.
|
|
66
|
-
--headless Run in headless mode: no TUI, no terminal output except a single render-started message. All logs are written to the
|
|
77
|
+
--headless Run in headless mode: no TUI, no terminal output except a single render-started message. All logs are written to the
|
|
78
|
+
log file.
|
|
67
79
|
|
|
68
80
|
configuration:
|
|
69
81
|
--config-name CONFIG_NAME
|
|
70
|
-
|
|
82
|
+
Name of the config file to look for. Looked up in the plain file directory and the current working directory.
|
|
83
|
+
Defaults to config.yaml.
|
|
71
84
|
|
|
72
85
|
```
|
|
@@ -7,11 +7,6 @@ from plain2code_events import BaseEvent
|
|
|
7
7
|
class EventBus:
|
|
8
8
|
def __init__(self):
|
|
9
9
|
self._listeners: defaultdict[Type[BaseEvent], list[Callable[[Any], None]]] = defaultdict(list)
|
|
10
|
-
self._dispatch_wrapper: Callable[[Callable], None] | None = None
|
|
11
|
-
|
|
12
|
-
def register_dispatch_wrapper(self, fn: Callable[[Callable], None]):
|
|
13
|
-
"""Set a wrapper for dispatching listeners (e.g., Textual's app.call_from_thread)."""
|
|
14
|
-
self._dispatch_wrapper = fn
|
|
15
10
|
|
|
16
11
|
def subscribe(self, event_type: Type[BaseEvent], listener: Callable[[Any], None]):
|
|
17
12
|
"""Registers a listener for a specific event type."""
|
|
@@ -19,12 +14,5 @@ class EventBus:
|
|
|
19
14
|
|
|
20
15
|
def publish(self, event: BaseEvent):
|
|
21
16
|
"""Publishes an event to all registered listeners."""
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
for listener in self._listeners[type(event)]:
|
|
25
|
-
listener(event)
|
|
26
|
-
|
|
27
|
-
if self._dispatch_wrapper:
|
|
28
|
-
self._dispatch_wrapper(_dispatch)
|
|
29
|
-
else:
|
|
30
|
-
_dispatch()
|
|
17
|
+
for listener in self._listeners[type(event)]:
|
|
18
|
+
listener(event)
|
|
@@ -7,6 +7,7 @@ from liquid2 import Environment, FileSystemLoader, StrictUndefined
|
|
|
7
7
|
from liquid2.exceptions import UndefinedError
|
|
8
8
|
|
|
9
9
|
import plain_spec
|
|
10
|
+
from plain2code_console import console
|
|
10
11
|
from plain2code_nodes import Plain2CodeIncludeTag, Plain2CodeLoaderMixin
|
|
11
12
|
from plain_modules import CODEPLAIN_MEMORY_SUBFOLDER, CODEPLAIN_METADATA_FOLDER
|
|
12
13
|
|
|
@@ -79,7 +80,7 @@ def list_all_text_files(directory):
|
|
|
79
80
|
with open(os.path.join(root, filename), "rb") as f:
|
|
80
81
|
f.read().decode("utf-8")
|
|
81
82
|
except UnicodeDecodeError:
|
|
82
|
-
|
|
83
|
+
console.debug(f"WARNING! Not listing {filename} in {root}. File is not a text file. Skipping it.")
|
|
83
84
|
continue
|
|
84
85
|
|
|
85
86
|
all_files.append(os.path.join(modified_root, filename))
|
|
@@ -178,7 +179,7 @@ def get_existing_files_content(build_folder, existing_files):
|
|
|
178
179
|
try:
|
|
179
180
|
existing_files_content[file_name] = content.decode("utf-8")
|
|
180
181
|
except UnicodeDecodeError:
|
|
181
|
-
|
|
182
|
+
console.debug(f"WARNING! Error loading {file_name}. File is not a text file. Skipping it.")
|
|
182
183
|
|
|
183
184
|
return existing_files_content
|
|
184
185
|
|
|
@@ -193,7 +194,7 @@ def store_response_files(target_folder, response_files, existing_files):
|
|
|
193
194
|
os.remove(full_file_name)
|
|
194
195
|
existing_files.remove(file_name)
|
|
195
196
|
else:
|
|
196
|
-
|
|
197
|
+
console.debug(f"WARNING! Cannot delete file! File {full_file_name} does not exist.")
|
|
197
198
|
|
|
198
199
|
continue
|
|
199
200
|
|
|
@@ -219,7 +220,9 @@ def open_from(dirs, file_name):
|
|
|
219
220
|
try:
|
|
220
221
|
content_text = content.decode("utf-8")
|
|
221
222
|
except UnicodeDecodeError:
|
|
222
|
-
|
|
223
|
+
console.debug(
|
|
224
|
+
f"WARNING! Error loading {file_name} ({file_name}). File is not a text file. Skipping it."
|
|
225
|
+
)
|
|
223
226
|
return content_text
|
|
224
227
|
|
|
225
228
|
return None
|
|
@@ -7,22 +7,20 @@ from git import Repo
|
|
|
7
7
|
import file_utils
|
|
8
8
|
from plain2code_exceptions import InvalidGitRepositoryError
|
|
9
9
|
|
|
10
|
-
FUNCTIONAL_REQUIREMENT_IMPLEMENTED_COMMIT_MESSAGE =
|
|
11
|
-
|
|
12
|
-
)
|
|
13
|
-
REFACTORED_CODE_COMMIT_MESSAGE = "[Codeplain] Refactored code after implementing functional requirement {}"
|
|
10
|
+
FUNCTIONAL_REQUIREMENT_IMPLEMENTED_COMMIT_MESSAGE = "[Codeplain] Implemented code and unit tests for functionality {}"
|
|
11
|
+
REFACTORED_CODE_COMMIT_MESSAGE = "[Codeplain] Refactored code after implementing functionality {}"
|
|
14
12
|
CONFORMANCE_TESTS_PASSED_COMMIT_MESSAGE = (
|
|
15
13
|
"[Codeplain] Fixed issues in the implementation code identified during conformance testing"
|
|
16
14
|
)
|
|
17
15
|
|
|
18
16
|
# Following messages are used as checkpoints in the git history
|
|
19
17
|
# Changing them will break backwards compatibility so change them with care
|
|
20
|
-
FUNCTIONAL_REQUIREMENT_FINISHED_COMMIT_MESSAGE = "[Codeplain]
|
|
18
|
+
FUNCTIONAL_REQUIREMENT_FINISHED_COMMIT_MESSAGE = "[Codeplain] functionality ID (FRID):{} fully implemented"
|
|
21
19
|
INITIAL_COMMIT_MESSAGE = "[Codeplain] Initial module commit"
|
|
22
20
|
BASE_FOLDER_COMMIT_MESSAGE = "[Codeplain] Initialize build with Base Folder content"
|
|
23
21
|
|
|
24
22
|
|
|
25
|
-
RENDERED_FRID_MESSAGE = "Changes related to
|
|
23
|
+
RENDERED_FRID_MESSAGE = "Changes related to functionality ID (FRID): {}"
|
|
26
24
|
MODULE_NAME_MESSAGE = "Module name: {}"
|
|
27
25
|
RENDER_ID_MESSAGE = "Render ID: {}"
|
|
28
26
|
|
|
@@ -244,7 +242,7 @@ def diff(repo_path: Union[str, os.PathLike], previous_frid: str = None) -> dict:
|
|
|
244
242
|
|
|
245
243
|
Args:
|
|
246
244
|
repo_path (str | os.PathLike): Path to the git repository
|
|
247
|
-
previous_frid (str):
|
|
245
|
+
previous_frid (str): functionality ID (FRID) of the previous commit
|
|
248
246
|
|
|
249
247
|
Returns:
|
|
250
248
|
dict: Dictionary with file names as keys and their clean diff strings as values
|
|
@@ -285,7 +283,7 @@ def _get_commit_with_frid(repo: Repo, frid: str, module_name: Optional[str] = No
|
|
|
285
283
|
|
|
286
284
|
Args:
|
|
287
285
|
repo (Repo): Git repository object
|
|
288
|
-
frid (str):
|
|
286
|
+
frid (str): functionality ID
|
|
289
287
|
module_name (Optional[str]): Module name to filter by. If provided, only returns
|
|
290
288
|
commits that have both the FRID and module name.
|
|
291
289
|
|
|
@@ -223,7 +223,7 @@ echo -e " thank you for using ${YELLOW}*codeplain!${NC}"
|
|
|
223
223
|
echo ""
|
|
224
224
|
echo -e " ${BOLD}next steps:${NC}"
|
|
225
225
|
echo ""
|
|
226
|
-
echo -e " join our Discord community: ${YELLOW}https://discord.gg/
|
|
226
|
+
echo -e " join our Discord community: ${YELLOW}https://discord.gg/cgbynb9hFq${NC}"
|
|
227
227
|
echo ""
|
|
228
228
|
echo -e " learn more about ${YELLOW}***plain${NC} at ${YELLOW}https://plainlang.org/${NC}"
|
|
229
229
|
echo ""
|
|
@@ -255,7 +255,7 @@ Write-Host " thank you for using ${YELLOW}*codeplain!${NC}"
|
|
|
255
255
|
Write-Host ""
|
|
256
256
|
Write-Host " ${BOLD}next steps:${NC}"
|
|
257
257
|
Write-Host ""
|
|
258
|
-
Write-Host " join our Discord community: ${YELLOW}https://discord.gg/
|
|
258
|
+
Write-Host " join our Discord community: ${YELLOW}https://discord.gg/cgbynb9hFq${NC}"
|
|
259
259
|
Write-Host ""
|
|
260
260
|
Write-Host " learn more about ${YELLOW}***plain${NC} at ${YELLOW}https://plainlang.org/${NC}"
|
|
261
261
|
Write-Host ""
|
|
@@ -21,7 +21,7 @@ class MemoryManager:
|
|
|
21
21
|
return [], {}
|
|
22
22
|
memory_files = file_utils.list_all_text_files(memory_path)
|
|
23
23
|
memory_files_content = file_utils.get_existing_files_content(memory_path, memory_files)
|
|
24
|
-
console.
|
|
24
|
+
console.debug(f"Loaded {len(memory_files_content)} memory files.")
|
|
25
25
|
return memory_files, memory_files_content
|
|
26
26
|
|
|
27
27
|
def __init__(self, codeplain_api, module_build_folder: str):
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import argparse
|
|
2
2
|
import os
|
|
3
|
+
import threading
|
|
3
4
|
|
|
4
5
|
import git_utils
|
|
5
6
|
import plain_file
|
|
@@ -28,6 +29,8 @@ class ModuleRenderer:
|
|
|
28
29
|
args: argparse.Namespace,
|
|
29
30
|
run_state: RunState,
|
|
30
31
|
event_bus: EventBus,
|
|
32
|
+
stop_event: threading.Event | None = None,
|
|
33
|
+
enter_pause_event: threading.Event | None = None,
|
|
31
34
|
):
|
|
32
35
|
self.codeplainAPI = codeplainAPI
|
|
33
36
|
self.filename = filename
|
|
@@ -36,6 +39,8 @@ class ModuleRenderer:
|
|
|
36
39
|
self.args = args
|
|
37
40
|
self.run_state = run_state
|
|
38
41
|
self.event_bus = event_bus
|
|
42
|
+
self.stop_event = stop_event
|
|
43
|
+
self.enter_pause_event = enter_pause_event
|
|
39
44
|
|
|
40
45
|
def _ensure_module_folders_exist(self, module_name: str, first_render_frid: str) -> tuple[str, str]:
|
|
41
46
|
"""
|
|
@@ -177,6 +182,8 @@ class ModuleRenderer:
|
|
|
177
182
|
run_state=self.run_state,
|
|
178
183
|
event_bus=self.event_bus,
|
|
179
184
|
test_script_timeout=self.args.test_script_timeout,
|
|
185
|
+
stop_event=self.stop_event,
|
|
186
|
+
enter_pause_event=self.enter_pause_event,
|
|
180
187
|
)
|
|
181
188
|
|
|
182
189
|
def _render_module(
|
|
@@ -199,7 +206,7 @@ class ModuleRenderer:
|
|
|
199
206
|
required_modules = []
|
|
200
207
|
has_any_required_module_changed = False
|
|
201
208
|
if not self.args.render_machine_graph and required_modules_list:
|
|
202
|
-
console.
|
|
209
|
+
console.debug(f"Analyzing required modules of module {module_name}...")
|
|
203
210
|
for required_module_name in required_modules_list:
|
|
204
211
|
required_module_filename = required_module_name + plain_file.PLAIN_SOURCE_FILE_EXTENSION
|
|
205
212
|
has_module_changed, sub_required_modules, rendering_failed = self._render_module(
|
|
@@ -2,6 +2,7 @@ import importlib.resources
|
|
|
2
2
|
import logging
|
|
3
3
|
import logging.config
|
|
4
4
|
import os
|
|
5
|
+
import signal
|
|
5
6
|
import sys
|
|
6
7
|
import threading
|
|
7
8
|
from pathlib import Path
|
|
@@ -22,7 +23,6 @@ from plain2code_console import console
|
|
|
22
23
|
from plain2code_events import RenderFailed
|
|
23
24
|
from plain2code_exceptions import (
|
|
24
25
|
ConflictingRequirements,
|
|
25
|
-
CreditBalanceTooLow,
|
|
26
26
|
InternalClientError,
|
|
27
27
|
InternalServerError,
|
|
28
28
|
InvalidAPIKey,
|
|
@@ -35,11 +35,13 @@ from plain2code_exceptions import (
|
|
|
35
35
|
NetworkConnectionError,
|
|
36
36
|
OutdatedClientVersion,
|
|
37
37
|
PlainSyntaxError,
|
|
38
|
+
RenderCancelledError,
|
|
39
|
+
RenderingCreditBalanceTooLow,
|
|
38
40
|
)
|
|
39
41
|
from plain2code_logger import (
|
|
42
|
+
LOGGER_NAME,
|
|
40
43
|
CrashLogHandler,
|
|
41
44
|
IndentedFormatter,
|
|
42
|
-
RetryOnlyFilter,
|
|
43
45
|
TuiLoggingHandler,
|
|
44
46
|
dump_crash_logs,
|
|
45
47
|
get_log_file_path,
|
|
@@ -50,6 +52,7 @@ from system_config import system_config
|
|
|
50
52
|
from tui.plain2code_tui import Plain2CodeTUI
|
|
51
53
|
|
|
52
54
|
DEFAULT_TEMPLATE_DIRS = importlib.resources.files("standard_template_library")
|
|
55
|
+
RENDER_THREAD_SHUTDOWN_TIMEOUT = 0.7
|
|
53
56
|
|
|
54
57
|
|
|
55
58
|
def get_render_range(render_range, plain_source):
|
|
@@ -86,12 +89,12 @@ def _get_frids_range(plain_source, start, end=None):
|
|
|
86
89
|
start = str(start)
|
|
87
90
|
|
|
88
91
|
if start not in frids:
|
|
89
|
-
raise InvalidFridArgument(f"Invalid start
|
|
92
|
+
raise InvalidFridArgument(f"Invalid start functionality ID: {start}. Valid IDs are: {frids}.")
|
|
90
93
|
|
|
91
94
|
if end is not None:
|
|
92
95
|
end = str(end)
|
|
93
96
|
if end not in frids:
|
|
94
|
-
raise InvalidFridArgument(f"Invalid end
|
|
97
|
+
raise InvalidFridArgument(f"Invalid end functionality ID: {end}. Valid IDs are: {frids}.")
|
|
95
98
|
|
|
96
99
|
end_idx = frids.index(end) + 1
|
|
97
100
|
else:
|
|
@@ -99,9 +102,7 @@ def _get_frids_range(plain_source, start, end=None):
|
|
|
99
102
|
|
|
100
103
|
start_idx = frids.index(start)
|
|
101
104
|
if start_idx >= end_idx:
|
|
102
|
-
raise InvalidFridArgument(
|
|
103
|
-
f"Start functional requirement ID: {start} must be before end functional requirement ID: {end}."
|
|
104
|
-
)
|
|
105
|
+
raise InvalidFridArgument(f"Start functionality ID: {start} must be before end functionality ID: {end}.")
|
|
105
106
|
|
|
106
107
|
return frids[start_idx:end_idx]
|
|
107
108
|
|
|
@@ -112,20 +113,12 @@ def setup_logging(
|
|
|
112
113
|
log_to_file: bool,
|
|
113
114
|
log_file_name: str,
|
|
114
115
|
plain_file_path: Optional[str],
|
|
115
|
-
render_id: str,
|
|
116
116
|
headless: bool = False,
|
|
117
117
|
):
|
|
118
118
|
# Set default level to INFO for everything not explicitly configured
|
|
119
119
|
logging.getLogger().setLevel(logging.INFO)
|
|
120
|
-
logging.getLogger(
|
|
121
|
-
logging.getLogger("httpx").setLevel(logging.WARNING)
|
|
122
|
-
logging.getLogger("httpcore").setLevel(logging.WARNING)
|
|
123
|
-
logging.getLogger("anthropic").setLevel(logging.WARNING)
|
|
124
|
-
logging.getLogger("langsmith").setLevel(logging.WARNING)
|
|
120
|
+
logging.getLogger(LOGGER_NAME).setLevel(logging.INFO)
|
|
125
121
|
logging.getLogger("git").setLevel(logging.WARNING)
|
|
126
|
-
logging.getLogger("anthropic._base_client").setLevel(logging.WARNING)
|
|
127
|
-
logging.getLogger("services.langsmith.langsmith_service").setLevel(logging.WARNING)
|
|
128
|
-
logging.getLogger("repositories").setLevel(logging.WARNING)
|
|
129
122
|
logging.getLogger("transitions").setLevel(logging.ERROR)
|
|
130
123
|
logging.getLogger("transitions.extensions.diagrams").setLevel(logging.ERROR)
|
|
131
124
|
|
|
@@ -141,18 +134,11 @@ def setup_logging(
|
|
|
141
134
|
except Exception as e:
|
|
142
135
|
console.warning(f"Failed to load logging configuration from {args.logging_config_path}: {str(e)}")
|
|
143
136
|
|
|
144
|
-
# Allow detailed retry logs for anthropic if needed
|
|
145
|
-
logging.getLogger("anthropic._base_client").setLevel(logging.DEBUG)
|
|
146
|
-
if logging.getLogger("anthropic._base_client").level == logging.DEBUG:
|
|
147
|
-
logging.getLogger("anthropic._base_client").addFilter(RetryOnlyFilter())
|
|
148
|
-
|
|
149
137
|
# The IndentedFormatter provides better multiline log readability.
|
|
150
138
|
# We add the TuiLoggingHandler to the root logger.
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
root_logger
|
|
154
|
-
for h in root_logger.handlers[:]:
|
|
155
|
-
root_logger.removeHandler(h)
|
|
139
|
+
root_logger = logging.getLogger(LOGGER_NAME)
|
|
140
|
+
configured_log_level = root_logger.level
|
|
141
|
+
root_logger.setLevel(logging.DEBUG) # Capture all logs; handlers will filter levels as needed
|
|
156
142
|
|
|
157
143
|
formatter = IndentedFormatter("%(levelname)s:%(name)s:%(message)s")
|
|
158
144
|
|
|
@@ -165,6 +151,7 @@ def setup_logging(
|
|
|
165
151
|
try:
|
|
166
152
|
file_handler = logging.FileHandler(log_file_path, mode="w")
|
|
167
153
|
file_handler.setFormatter(formatter)
|
|
154
|
+
file_handler.setLevel(configured_log_level)
|
|
168
155
|
root_logger.addHandler(file_handler)
|
|
169
156
|
except Exception as e:
|
|
170
157
|
console.warning(f"Failed to setup file logging to {log_file_path}: {str(e)}")
|
|
@@ -173,10 +160,9 @@ def setup_logging(
|
|
|
173
160
|
# in case we need to dump them on crash.
|
|
174
161
|
crash_handler = CrashLogHandler()
|
|
175
162
|
crash_handler.setFormatter(formatter)
|
|
163
|
+
crash_handler.setLevel(configured_log_level)
|
|
176
164
|
root_logger.addHandler(crash_handler)
|
|
177
165
|
|
|
178
|
-
root_logger.info(f"Render ID: {render_id}") # Ensure render ID is logged in to codeplain.log file
|
|
179
|
-
|
|
180
166
|
|
|
181
167
|
def _check_connection(codeplainAPI: codeplain_api.CodeplainAPI):
|
|
182
168
|
"""Check API connectivity and validate API key and client version."""
|
|
@@ -216,6 +202,10 @@ def render(args, run_state: RunState, event_bus: EventBus): # noqa: C901
|
|
|
216
202
|
|
|
217
203
|
_check_connection(codeplainAPI)
|
|
218
204
|
|
|
205
|
+
stop_event = threading.Event()
|
|
206
|
+
enter_pause_event = threading.Event()
|
|
207
|
+
signal.signal(signal.SIGTERM, lambda _signum, _frame: stop_event.set())
|
|
208
|
+
|
|
219
209
|
module_renderer = ModuleRenderer(
|
|
220
210
|
codeplainAPI,
|
|
221
211
|
args.filename,
|
|
@@ -224,6 +214,8 @@ def render(args, run_state: RunState, event_bus: EventBus): # noqa: C901
|
|
|
224
214
|
args,
|
|
225
215
|
run_state,
|
|
226
216
|
event_bus,
|
|
217
|
+
stop_event=stop_event,
|
|
218
|
+
enter_pause_event=enter_pause_event,
|
|
227
219
|
)
|
|
228
220
|
|
|
229
221
|
render_error: list[Exception] = []
|
|
@@ -231,13 +223,19 @@ def render(args, run_state: RunState, event_bus: EventBus): # noqa: C901
|
|
|
231
223
|
def run_render():
|
|
232
224
|
try:
|
|
233
225
|
module_renderer.render_module()
|
|
226
|
+
console.info(f"[#79FC96]Render {run_state.render_id} completed successfully.[/#79FC96]")
|
|
227
|
+
except RenderCancelledError:
|
|
228
|
+
pass # TUI already closed, nothing to report
|
|
234
229
|
except Exception as e:
|
|
235
230
|
render_error.append(e)
|
|
236
231
|
event_bus.publish(RenderFailed(error_message=str(e)))
|
|
237
232
|
|
|
238
233
|
if args.headless:
|
|
239
|
-
|
|
240
|
-
|
|
234
|
+
console.info(f"Render started. Render ID: {run_state.render_id}")
|
|
235
|
+
try:
|
|
236
|
+
module_renderer.render_module()
|
|
237
|
+
except RenderCancelledError:
|
|
238
|
+
pass
|
|
241
239
|
return
|
|
242
240
|
else:
|
|
243
241
|
render_thread = threading.Thread(target=run_render, daemon=True)
|
|
@@ -249,10 +247,12 @@ def render(args, run_state: RunState, event_bus: EventBus): # noqa: C901
|
|
|
249
247
|
conformance_tests_script=args.conformance_tests_script,
|
|
250
248
|
prepare_environment_script=args.prepare_environment_script,
|
|
251
249
|
state_machine_version=system_config.client_version,
|
|
250
|
+
enter_pause_event=enter_pause_event,
|
|
252
251
|
css_path="styles.css",
|
|
253
252
|
)
|
|
254
253
|
app.run()
|
|
255
|
-
|
|
254
|
+
stop_event.set()
|
|
255
|
+
render_thread.join(timeout=RENDER_THREAD_SHUTDOWN_TIMEOUT)
|
|
256
256
|
|
|
257
257
|
if render_error:
|
|
258
258
|
raise render_error[0]
|
|
@@ -295,9 +295,7 @@ def main(): # noqa: C901
|
|
|
295
295
|
# Suppress Rich console output.
|
|
296
296
|
console.quiet = True
|
|
297
297
|
|
|
298
|
-
setup_logging(
|
|
299
|
-
args, event_bus, args.log_to_file, args.log_file_name, args.filename, run_state.render_id, args.headless
|
|
300
|
-
)
|
|
298
|
+
setup_logging(args, event_bus, args.log_to_file, args.log_file_name, args.filename, args.headless)
|
|
301
299
|
|
|
302
300
|
exc_info = None
|
|
303
301
|
try:
|
|
@@ -307,7 +305,7 @@ def main(): # noqa: C901
|
|
|
307
305
|
"API key is required. Please set the CODEPLAIN_API_KEY environment variable or provide it with the --api-key argument."
|
|
308
306
|
)
|
|
309
307
|
|
|
310
|
-
console.
|
|
308
|
+
console.info(f"Render ID: {run_state.render_id}")
|
|
311
309
|
render(args, run_state, event_bus)
|
|
312
310
|
except InvalidFridArgument as e:
|
|
313
311
|
exc_info = sys.exc_info()
|
|
@@ -351,7 +349,7 @@ def main(): # noqa: C901
|
|
|
351
349
|
exc_info = sys.exc_info()
|
|
352
350
|
console.error(f"Conflicting requirements: {str(e)}\n")
|
|
353
351
|
console.debug(f"Render ID: {run_state.render_id}")
|
|
354
|
-
except
|
|
352
|
+
except RenderingCreditBalanceTooLow as e:
|
|
355
353
|
exc_info = sys.exc_info()
|
|
356
354
|
console.error(f"Credit balance too low: {str(e)}\n")
|
|
357
355
|
console.debug(f"Render ID: {run_state.render_id}")
|