codeplain 0.3.1__tar.gz → 0.3.3__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.3.1 → codeplain-0.3.3}/.claude/settings.json +5 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/PKG-INFO +25 -23
- {codeplain-0.3.1 → codeplain-0.3.3}/README.md +23 -22
- codeplain-0.3.3/_version.py +1 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/cli_output/render_summary.py +6 -1
- {codeplain-0.3.1 → codeplain-0.3.3}/cli_output/status.py +14 -9
- {codeplain-0.3.1 → codeplain-0.3.3}/codeplain_REST_api.py +1 -1
- {codeplain-0.3.1 → codeplain-0.3.3}/concept_utils.py +0 -1
- {codeplain-0.3.1 → codeplain-0.3.3}/file_utils.py +18 -62
- {codeplain-0.3.1 → codeplain-0.3.3}/plain2code.py +34 -25
- {codeplain-0.3.1 → codeplain-0.3.3}/plain2code_exceptions.py +6 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/plain2code_state.py +8 -7
- codeplain-0.3.3/plain2code_telemetry.py +114 -0
- codeplain-0.3.3/plain2code_utils.py +22 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/plain_file.py +6 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/plain_modules.py +6 -2
- {codeplain-0.3.1 → codeplain-0.3.3}/pyproject.toml +1 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/render_machine/actions/run_conformance_tests.py +4 -2
- {codeplain-0.3.1 → codeplain-0.3.3}/render_machine/actions/run_unit_tests.py +2 -2
- {codeplain-0.3.1 → codeplain-0.3.3}/render_machine/code_renderer.py +2 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/render_machine/render_context.py +30 -46
- {codeplain-0.3.1 → codeplain-0.3.3}/render_machine/render_types.py +0 -10
- {codeplain-0.3.1 → codeplain-0.3.3}/render_machine/state_machine_config.py +22 -63
- {codeplain-0.3.1 → codeplain-0.3.3}/render_machine/triggers.py +1 -1
- {codeplain-0.3.1 → codeplain-0.3.3}/requirements.txt +1 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/system_config.py +0 -6
- codeplain-0.3.3/tests/data/plainfile/add_new_task_modal_specification.yaml +1 -0
- codeplain-0.3.3/tests/data/plainfile/task_list_ui_specification.yaml +1 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/plainfile/task_manager_with_reference_links.plain +3 -3
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/test_cli_output.py +128 -26
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/test_plainfileparser.py +4 -4
- codeplain-0.3.3/tests/test_telemetry.py +204 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tui/components.py +0 -6
- {codeplain-0.3.1 → codeplain-0.3.3}/tui/plain2code_tui.py +4 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tui/plain_module_render_choice_tui.py +9 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tui/styles.css +1 -1
- {codeplain-0.3.1 → codeplain-0.3.3}/tui/widget_helpers.py +0 -18
- codeplain-0.3.1/.claude/settings.local.json +0 -66
- codeplain-0.3.1/_version.py +0 -1
- codeplain-0.3.1/hash_key.py +0 -29
- codeplain-0.3.1/plain2code_utils.py +0 -56
- codeplain-0.3.1/render_machine/conformance_test_helpers.py +0 -68
- {codeplain-0.3.1 → codeplain-0.3.3}/.flake8 +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/.github/dependabot.yml +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/.github/workflows/e2e.yml +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/.github/workflows/lint-and-test.yml +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/.github/workflows/nofity-slack-on-main-merge.yml +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/.github/workflows/publish-install-script.yml +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/.github/workflows/publish-to-pypi.yml +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/.gitignore +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/.vscode/launch.json +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/.vscode/settings.json +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/CLAUDE.md +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/LICENSE +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/cli_output/__init__.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/cli_output/dry_run.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/config/__init__.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/config/system_config.yaml +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/diff_utils.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/docs/generate_cli.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/docs/plain2code_cli.md +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/docs/starting_a_plain_project_from_scratch.md +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/event_bus.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/examples/example_hello_world_golang/config.yaml +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/examples/example_hello_world_golang/harness_tests/hello_world_test.go +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/examples/example_hello_world_golang/hello_world_golang.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/examples/example_hello_world_golang/run.sh +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/examples/example_hello_world_python/config.yaml +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/examples/example_hello_world_python/harness_tests/hello_world_display/test_hello_world.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/examples/example_hello_world_python/hello_world_python.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/examples/example_hello_world_python/run.sh +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/examples/example_hello_world_react/config.yaml +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/examples/example_hello_world_react/harness_tests/hello_world_display/cypress/e2e/hello_world.cy.ts +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/examples/example_hello_world_react/harness_tests/hello_world_display/cypress/support/e2e.ts +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/examples/example_hello_world_react/harness_tests/hello_world_display/cypress.config.ts +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/examples/example_hello_world_react/harness_tests/hello_world_display/package.json +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/examples/example_hello_world_react/harness_tests/hello_world_display/tsconfig.json +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/examples/example_hello_world_react/hello_world_react.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/examples/example_hello_world_react/run.sh +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/examples/run.sh +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/git_utils.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/install/bash/examples.sh +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/install/bash/install.sh +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/install/bash/walkthrough.sh +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/install/powershell/examples.ps1 +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/install/powershell/install.ps1 +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/install/powershell/walkthrough.ps1 +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/memory_management.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/module_renderer.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/partial_rendering.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/plain2code_arguments.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/plain2code_console.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/plain2code_events.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/plain2code_logger.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/plain2code_nodes.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/plain2code_read_config.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/plain_spec.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/pytest.ini +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/render_machine/__init__.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/render_machine/actions/analyze_specification_ambiguity.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/render_machine/actions/base_action.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/render_machine/actions/commit_conformance_tests_changes.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/render_machine/actions/commit_implementation_code_changes.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/render_machine/actions/create_dist.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/render_machine/actions/exit_with_error.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/render_machine/actions/finish_functional_requirement.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/render_machine/actions/fix_conformance_test.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/render_machine/actions/fix_unit_tests.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/render_machine/actions/prepare_repositories.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/render_machine/actions/prepare_testing_environment.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/render_machine/actions/refactor_code.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/render_machine/actions/render_conformance_tests.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/render_machine/actions/render_functional_requirement.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/render_machine/actions/summarize_conformance_tests.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/render_machine/conformance_tests.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/render_machine/implementation_code_helpers.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/render_machine/render_utils.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/render_machine/states.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/resources/codeplain_overview.png +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/resources/plain_example.png +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/standard_template_library/__init__.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/standard_template_library/golang-console-app-template.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/standard_template_library/python-console-app-template.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/standard_template_library/typescript-react-app-boilerplate.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/standard_template_library/typescript-react-app-template.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/test_scripts/run_conformance_tests_cypress.ps1 +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/test_scripts/run_conformance_tests_cypress.sh +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/test_scripts/run_conformance_tests_golang.ps1 +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/test_scripts/run_conformance_tests_golang.sh +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/test_scripts/run_conformance_tests_python.ps1 +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/test_scripts/run_conformance_tests_python.sh +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/test_scripts/run_unittests_flutter.ps1 +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/test_scripts/run_unittests_golang.ps1 +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/test_scripts/run_unittests_golang.sh +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/test_scripts/run_unittests_python.ps1 +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/test_scripts/run_unittests_python.sh +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/test_scripts/run_unittests_react.ps1 +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/test_scripts/run_unittests_react.sh +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/__init__.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/conftest.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/imports/circular_imports_1.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/imports/circular_imports_2.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/imports/circular_imports_main.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/imports/diamond_import_1.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/imports/diamond_import_2.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/imports/diamond_import_common.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/imports/diamond_imports_main.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/imports/non_existent_import.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/partial_rendering/pr_leaf.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/partial_rendering/pr_middle.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/partial_rendering/pr_root.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/partial_rendering/pr_solo.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/plainfile/duplicate_specification_heading.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/plainfile/invalid_specification_order.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/plainfile/missing_non_functional_requirements.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/plainfile/plain_source_with_absolute_link.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/plainfile/plain_source_with_url_link.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/plainfile/without_non_functional_requirement.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/plainfileparser/concept_validation_acceptance_tests.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/plainfileparser/concept_validation_acceptance_tests_nondefined.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/plainfileparser/concept_validation_defined_nondefined.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/plainfileparser/concept_validation_defined_nondefined_2.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/plainfileparser/concept_validation_definition.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/plainfileparser/concept_validation_noconcepts.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/plainfileparser/concept_validation_nonconcept.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/plainfileparser/concept_validation_nondefined.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/plainfileparser/concept_validation_redefinition.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/plainfileparser/concept_validation_several_concepts.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/plainfileparser/concept_validation_valid.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/plainfileparser/exported_concepts.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/plainfileparser/exported_concepts_base.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/plainfileparser/exported_concepts_example.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/plainfileparser/exported_concepts_missing.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/plainfileparser/exported_concepts_missing_example.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/plainfileparser/exported_concepts_nested.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/plainfileparser/exported_concepts_nested_example.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/plainfileparser/exported_concepts_transitive_example.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/plainfileparser/exported_concepts_transitive_l1.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/plainfileparser/exported_concepts_transitive_l2.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/plainfileparser/plain_file_parser_with_comments.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/plainfileparser/plain_file_with_comments_indented.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/plainfileparser/regular_plain_source.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/plainfileparser/required_concepts.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/plainfileparser/required_concepts_defs.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/plainfileparser/required_concepts_example.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/plainfileparser/required_concepts_l1.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/plainfileparser/required_concepts_l2.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/plainfileparser/required_concepts_missing.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/plainfileparser/required_concepts_module.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/plainfileparser/required_concepts_partial.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/plainfileparser/required_concepts_partial_duplicate.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/plainfileparser/topological_sort.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/plainfileparser/topological_sort_not_referenced.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/requires/circular_requires_main.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/requires/circular_requires_sub.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/requires/diamond_requires_1.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/requires/diamond_requires_2.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/requires/diamond_requires_common.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/requires/diamond_requires_main.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/requires/independent_requires_1.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/requires/independent_requires_2.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/requires/independent_requires_main.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/requires/non_existent_require.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/requires/normal_requires_1.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/requires/normal_requires_2.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/requires/normal_requires_common.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/requires/normal_requires_main.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/simple.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/templates/block_level_include.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/templates/code_variables.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/templates/header.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/templates/implement.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/templates/implement_2.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/templates/template_include.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/data/templates/test_hardest_problem.plain +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/e2e/Dockerfile +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/e2e/conftest.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/e2e/test_hello_world_python.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/e2e/test_hello_world_python_windows.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/test_file_utils.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/test_git_utils.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/test_imports.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/test_partial_rendering.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/test_plain_modules.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/test_plainfile.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/test_plainspec.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/test_requires.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tests/test_resolve_config_file.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tui/__init__.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tui/models.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tui/spinner.py +0 -0
- {codeplain-0.3.1 → codeplain-0.3.3}/tui/state_handlers.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codeplain
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.3
|
|
4
4
|
Summary: Transform plain language specifications into working code
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Classifier: Environment :: Console
|
|
@@ -16,6 +16,7 @@ Requires-Dist: python-liquid2==0.3.0
|
|
|
16
16
|
Requires-Dist: pyyaml==6.0.3
|
|
17
17
|
Requires-Dist: requests==2.34.0
|
|
18
18
|
Requires-Dist: rich==15.0.0
|
|
19
|
+
Requires-Dist: sentry-sdk==2.62.0
|
|
19
20
|
Requires-Dist: textual>=7.5.0
|
|
20
21
|
Requires-Dist: tiktoken==0.12.0
|
|
21
22
|
Requires-Dist: transitions==0.9.3
|
|
@@ -29,31 +30,28 @@ Description-Content-Type: text/markdown
|
|
|
29
30
|
|
|
30
31
|
# Codeplain plain2code renderer
|
|
31
32
|
|
|
32
|
-
Render
|
|
33
|
+
Render \*\*\*plain source to software code using the Codeplain API.
|
|
33
34
|
|
|
34
35
|
## Codeplain.ai - Code Generation as a Service
|
|
35
36
|
|
|
36
|
-
Codeplain is a platform that generates software code using large language models based on requirements you specify in
|
|
37
|
+
Codeplain is a platform that generates software code using large language models based on requirements you specify in \*\*\*plain specification language.
|
|
37
38
|
|
|
38
39
|
Schematic overview of the Codeplain's code generation service
|
|
39
40
|
|
|
40
41
|
<img src="https://raw.githubusercontent.com/Codeplain-ai/plain2code_client/main/resources/codeplain_overview.png">
|
|
41
42
|
|
|
42
|
-
### Abstracting Away Code Generation Complexity with
|
|
43
|
+
### Abstracting Away Code Generation Complexity with \*\*\*plain
|
|
43
44
|
|
|
45
|
+
\*\*\*plain is a novel specification language that helps abstracting away complexity of using large language models for code generation.
|
|
44
46
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
An example application in ***plain
|
|
47
|
+
An example application in \*\*\*plain
|
|
48
48
|
|
|
49
49
|
<img src="resources/plain_example.png" width="70%" height="70%">
|
|
50
50
|
|
|
51
|
-
|
|
52
51
|
## Getting started
|
|
53
52
|
|
|
54
53
|
### Prerequisites
|
|
55
54
|
|
|
56
|
-
|
|
57
55
|
#### System requirements
|
|
58
56
|
|
|
59
57
|
To run the plain2code client, you need Python 3.11 or a later version.
|
|
@@ -92,18 +90,24 @@ export CODEPLAIN_API_KEY="your_actual_api_key_here"
|
|
|
92
90
|
After completing the installation steps above, you can immediately test the system with a simple "Hello World" example:
|
|
93
91
|
|
|
94
92
|
- Change to the example folder and run the example:
|
|
95
|
-
```
|
|
96
|
-
cd examples/example_hello_world_python
|
|
97
|
-
python ../../plain2code.py hello_world_python.plain
|
|
98
|
-
```
|
|
99
93
|
|
|
100
|
-
|
|
94
|
+
```
|
|
95
|
+
cd examples/example_hello_world_python
|
|
96
|
+
python ../../plain2code.py hello_world_python.plain
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
_Note: Rendering will take a few minutes to complete._
|
|
101
100
|
|
|
102
101
|
- The system will generate a Python application in the `build` directory. You can run it with:
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
102
|
+
```
|
|
103
|
+
cd build
|
|
104
|
+
python hello_world.py
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Crash reporting
|
|
108
|
+
|
|
109
|
+
If the plain2code client crashes unexpectedly, it sends an anonymous crash report to Codeplain to help improve the tool. To disable crash reporting, set the `CODEPLAIN_NO_TELEMETRY=1` flag.
|
|
110
|
+
|
|
107
111
|
|
|
108
112
|
## Releasing
|
|
109
113
|
|
|
@@ -112,7 +116,7 @@ Releases are built and published with [uv](https://docs.astral.sh/uv/). The vers
|
|
|
112
116
|
1. Bump the version in `_version.py` and commit/tag (`git tag v<version>`).
|
|
113
117
|
2. Build the distributions:
|
|
114
118
|
```bash
|
|
115
|
-
uv build
|
|
119
|
+
uv build --python 3.11
|
|
116
120
|
```
|
|
117
121
|
This produces `dist/codeplain-<version>-py3-none-any.whl` and `dist/codeplain-<version>.tar.gz`.
|
|
118
122
|
3. (Optional) Smoke-test the wheel:
|
|
@@ -135,13 +139,11 @@ Releases are built and published with [uv](https://docs.astral.sh/uv/). The vers
|
|
|
135
139
|
### Examples and Sample Projects
|
|
136
140
|
|
|
137
141
|
- See the [examples](examples) folder for sample projects in Golang, Python, and React.
|
|
138
|
-
- For example application how to implement task manager in
|
|
139
|
-
- For example application how to implement SaaS connectors in
|
|
142
|
+
- For example application how to implement task manager in \*\*\*plain see [example-task-manager](https://github.com/Codeplain-ai/example-task-manager) repository.
|
|
143
|
+
- For example application how to implement SaaS connectors in \*\*\*plain see [example-saas-connectors](https://github.com/Codeplain-ai/example-saas-connectors) repository.
|
|
140
144
|
|
|
141
145
|
### Documentation
|
|
142
146
|
|
|
143
147
|
- For more details on the ***plain format, see the [***plain language specification](https://www.plainlang.org/docs/).
|
|
144
148
|
- For step-by-step instructions for creating your first ***plain project see the [Kickstart your ***plain project](docs/starting_a_plain_project_from_scratch.md).
|
|
145
149
|
- For complete CLI documentation and usage examples, see [plain2code CLI documentation](docs/plain2code_cli.md).
|
|
146
|
-
|
|
147
|
-
|
|
@@ -1,30 +1,27 @@
|
|
|
1
1
|
# Codeplain plain2code renderer
|
|
2
2
|
|
|
3
|
-
Render
|
|
3
|
+
Render \*\*\*plain source to software code using the Codeplain API.
|
|
4
4
|
|
|
5
5
|
## Codeplain.ai - Code Generation as a Service
|
|
6
6
|
|
|
7
|
-
Codeplain is a platform that generates software code using large language models based on requirements you specify in
|
|
7
|
+
Codeplain is a platform that generates software code using large language models based on requirements you specify in \*\*\*plain specification language.
|
|
8
8
|
|
|
9
9
|
Schematic overview of the Codeplain's code generation service
|
|
10
10
|
|
|
11
11
|
<img src="https://raw.githubusercontent.com/Codeplain-ai/plain2code_client/main/resources/codeplain_overview.png">
|
|
12
12
|
|
|
13
|
-
### Abstracting Away Code Generation Complexity with
|
|
13
|
+
### Abstracting Away Code Generation Complexity with \*\*\*plain
|
|
14
14
|
|
|
15
|
+
\*\*\*plain is a novel specification language that helps abstracting away complexity of using large language models for code generation.
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
An example application in ***plain
|
|
17
|
+
An example application in \*\*\*plain
|
|
19
18
|
|
|
20
19
|
<img src="resources/plain_example.png" width="70%" height="70%">
|
|
21
20
|
|
|
22
|
-
|
|
23
21
|
## Getting started
|
|
24
22
|
|
|
25
23
|
### Prerequisites
|
|
26
24
|
|
|
27
|
-
|
|
28
25
|
#### System requirements
|
|
29
26
|
|
|
30
27
|
To run the plain2code client, you need Python 3.11 or a later version.
|
|
@@ -63,18 +60,24 @@ export CODEPLAIN_API_KEY="your_actual_api_key_here"
|
|
|
63
60
|
After completing the installation steps above, you can immediately test the system with a simple "Hello World" example:
|
|
64
61
|
|
|
65
62
|
- Change to the example folder and run the example:
|
|
66
|
-
```
|
|
67
|
-
cd examples/example_hello_world_python
|
|
68
|
-
python ../../plain2code.py hello_world_python.plain
|
|
69
|
-
```
|
|
70
63
|
|
|
71
|
-
|
|
64
|
+
```
|
|
65
|
+
cd examples/example_hello_world_python
|
|
66
|
+
python ../../plain2code.py hello_world_python.plain
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
_Note: Rendering will take a few minutes to complete._
|
|
72
70
|
|
|
73
71
|
- The system will generate a Python application in the `build` directory. You can run it with:
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
72
|
+
```
|
|
73
|
+
cd build
|
|
74
|
+
python hello_world.py
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Crash reporting
|
|
78
|
+
|
|
79
|
+
If the plain2code client crashes unexpectedly, it sends an anonymous crash report to Codeplain to help improve the tool. To disable crash reporting, set the `CODEPLAIN_NO_TELEMETRY=1` flag.
|
|
80
|
+
|
|
78
81
|
|
|
79
82
|
## Releasing
|
|
80
83
|
|
|
@@ -83,7 +86,7 @@ Releases are built and published with [uv](https://docs.astral.sh/uv/). The vers
|
|
|
83
86
|
1. Bump the version in `_version.py` and commit/tag (`git tag v<version>`).
|
|
84
87
|
2. Build the distributions:
|
|
85
88
|
```bash
|
|
86
|
-
uv build
|
|
89
|
+
uv build --python 3.11
|
|
87
90
|
```
|
|
88
91
|
This produces `dist/codeplain-<version>-py3-none-any.whl` and `dist/codeplain-<version>.tar.gz`.
|
|
89
92
|
3. (Optional) Smoke-test the wheel:
|
|
@@ -106,13 +109,11 @@ Releases are built and published with [uv](https://docs.astral.sh/uv/). The vers
|
|
|
106
109
|
### Examples and Sample Projects
|
|
107
110
|
|
|
108
111
|
- See the [examples](examples) folder for sample projects in Golang, Python, and React.
|
|
109
|
-
- For example application how to implement task manager in
|
|
110
|
-
- For example application how to implement SaaS connectors in
|
|
112
|
+
- For example application how to implement task manager in \*\*\*plain see [example-task-manager](https://github.com/Codeplain-ai/example-task-manager) repository.
|
|
113
|
+
- For example application how to implement SaaS connectors in \*\*\*plain see [example-saas-connectors](https://github.com/Codeplain-ai/example-saas-connectors) repository.
|
|
111
114
|
|
|
112
115
|
### Documentation
|
|
113
116
|
|
|
114
117
|
- For more details on the ***plain format, see the [***plain language specification](https://www.plainlang.org/docs/).
|
|
115
118
|
- For step-by-step instructions for creating your first ***plain project see the [Kickstart your ***plain project](docs/starting_a_plain_project_from_scratch.md).
|
|
116
119
|
- For complete CLI documentation and usage examples, see [plain2code CLI documentation](docs/plain2code_cli.md).
|
|
117
|
-
|
|
118
|
-
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.3.3"
|
|
@@ -15,7 +15,12 @@ def print_exit_summary(
|
|
|
15
15
|
"""Print render outcome after the TUI exits (terminal restored)."""
|
|
16
16
|
console.quiet = False
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
if run_state.render_succeeded:
|
|
19
|
+
msg = "\n[#79FC96]✓ rendering completed\n\n"
|
|
20
|
+
elif run_state.render_cancelled:
|
|
21
|
+
msg = "\n[#FFFFFF]— rendering canceled\n\n"
|
|
22
|
+
else:
|
|
23
|
+
msg = "\n[#FF6B6B]✗ rendering failed\n\n"
|
|
19
24
|
msg += f" [#8E8F91]render id:\t\t\t[#FFFFFF]{run_state.render_id}\n"
|
|
20
25
|
msg += f" [#8E8F91]input file:\t\t\t[#FFFFFF]{spec_filename}\n"
|
|
21
26
|
msg += f" [#8E8F91]generated code folder:\t[#FFFFFF]{run_state.render_generated_code_path or '-'}\n\n"
|
|
@@ -50,8 +50,8 @@ def _display_credit_line(plan_credits: dict) -> None:
|
|
|
50
50
|
console.print(f" {plan_label:10} {bar} {remaining:2} of {total} remaining expires {formatted_date}")
|
|
51
51
|
|
|
52
52
|
|
|
53
|
-
def
|
|
54
|
-
"""Display a
|
|
53
|
+
def _display_bucket_credit_line(bucket: dict, label: str) -> None:
|
|
54
|
+
"""Display a credit bucket line (purchased or promo) with progress bar."""
|
|
55
55
|
remaining = bucket["remaining"]
|
|
56
56
|
total = bucket["total"]
|
|
57
57
|
expiry_date = bucket["expiry_date"]
|
|
@@ -71,12 +71,12 @@ def _display_purchased_credit_line(bucket: dict) -> None:
|
|
|
71
71
|
bar = _create_progress_bar(remaining, total, width=30)
|
|
72
72
|
|
|
73
73
|
if is_expired:
|
|
74
|
-
console.print(f"
|
|
74
|
+
console.print(f" {label:10} {bar} expired {formatted_date}")
|
|
75
75
|
else:
|
|
76
|
-
console.print(f"
|
|
76
|
+
console.print(f" {label:10} {bar} {remaining:2} of {total} remaining expires {formatted_date}")
|
|
77
77
|
|
|
78
78
|
|
|
79
|
-
def _display_status_message(plan_credits: Optional[dict], purchased_credits: list) -> None:
|
|
79
|
+
def _display_status_message(plan_credits: Optional[dict], purchased_credits: list, promo_credits: list) -> None:
|
|
80
80
|
"""Display appropriate status message based on credit state."""
|
|
81
81
|
has_remaining = False
|
|
82
82
|
|
|
@@ -93,8 +93,8 @@ def _display_status_message(plan_credits: Optional[dict], purchased_credits: lis
|
|
|
93
93
|
if remaining > 0 and period_end > now:
|
|
94
94
|
has_remaining = True
|
|
95
95
|
|
|
96
|
-
# Check if any purchased credits have remaining balance and not expired
|
|
97
|
-
for bucket in purchased_credits:
|
|
96
|
+
# Check if any purchased or promo credits have remaining balance and not expired
|
|
97
|
+
for bucket in [*purchased_credits, *promo_credits]:
|
|
98
98
|
dt_str = bucket["expiry_date"].replace("Z", "+00:00")
|
|
99
99
|
expiry_date = datetime.fromisoformat(dt_str)
|
|
100
100
|
# If naive datetime, assume UTC
|
|
@@ -127,6 +127,7 @@ def print_status(api_key: str, api_url: str, client_version: str) -> None:
|
|
|
127
127
|
org_owner = response.get("organization_owner_email")
|
|
128
128
|
plan_credits = response.get("plan_credits")
|
|
129
129
|
purchased_credits = response.get("purchased_credits", [])
|
|
130
|
+
promo_credits = response.get("promo_credits", [])
|
|
130
131
|
|
|
131
132
|
# Display header information
|
|
132
133
|
if client_version_valid:
|
|
@@ -153,8 +154,12 @@ def print_status(api_key: str, api_url: str, client_version: str) -> None:
|
|
|
153
154
|
|
|
154
155
|
# Display purchased credits
|
|
155
156
|
for bucket in purchased_credits:
|
|
156
|
-
|
|
157
|
+
_display_bucket_credit_line(bucket, "Purchased")
|
|
158
|
+
|
|
159
|
+
# Display promo credits
|
|
160
|
+
for bucket in promo_credits:
|
|
161
|
+
_display_bucket_credit_line(bucket, "Promo")
|
|
157
162
|
|
|
158
163
|
# Display status messages and management link
|
|
159
|
-
_display_status_message(plan_credits, purchased_credits)
|
|
164
|
+
_display_status_message(plan_credits, purchased_credits, promo_credits)
|
|
160
165
|
console.print("\nTo manage your plan navigate to https://platform.codeplain.ai/plans")
|
|
@@ -108,7 +108,7 @@ class CodeplainAPI:
|
|
|
108
108
|
if error_code == "InternalServerError":
|
|
109
109
|
raise plain2code_exceptions.InternalServerError(
|
|
110
110
|
"Internal server error.\n\n"
|
|
111
|
-
|
|
111
|
+
"Please report the error to support@codeplain.ai with the attached .log file."
|
|
112
112
|
)
|
|
113
113
|
exception_class = ERROR_CODE_EXCEPTIONS[error_code]
|
|
114
114
|
raise exception_class(message)
|
|
@@ -54,15 +54,6 @@ def is_system_folder_path(file_path: str) -> bool:
|
|
|
54
54
|
return bool(parts) and parts[0] in SYSTEM_FOLDERS
|
|
55
55
|
|
|
56
56
|
|
|
57
|
-
def get_file_type(file_name):
|
|
58
|
-
|
|
59
|
-
# Extract the file extension
|
|
60
|
-
ext = Path(file_name).suffix.lower() # Convert to lowercase to handle case-insensitive matching
|
|
61
|
-
|
|
62
|
-
# Use the dictionary to get the file type, defaulting to 'unknown' if the extension is not found
|
|
63
|
-
return FILE_EXTENSION_MAPPING.get(ext, "unknown")
|
|
64
|
-
|
|
65
|
-
|
|
66
57
|
def list_all_text_files(directory):
|
|
67
58
|
all_files = []
|
|
68
59
|
for root, dirs, files in os.walk(directory, topdown=True):
|
|
@@ -99,64 +90,29 @@ def list_folders_in_directory(directory):
|
|
|
99
90
|
return folders
|
|
100
91
|
|
|
101
92
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
delete_files_and_subfolders(folder_name)
|
|
107
|
-
_make_writable(folder_name)
|
|
108
|
-
os.rmdir(folder_name)
|
|
93
|
+
def _on_rm_error(func, path, _exc_info):
|
|
94
|
+
"""On Windows, clear read-only flag and retry the removal."""
|
|
95
|
+
os.chmod(path, stat.S_IWRITE)
|
|
96
|
+
func(path)
|
|
109
97
|
|
|
110
98
|
|
|
111
|
-
def
|
|
112
|
-
"""
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
if is_windows:
|
|
116
|
-
try:
|
|
117
|
-
mode = os.stat(path).st_mode
|
|
118
|
-
os.chmod(path, mode | stat.S_IWRITE)
|
|
119
|
-
except OSError:
|
|
120
|
-
pass
|
|
99
|
+
def delete_folder(folder_name):
|
|
100
|
+
"""Delete a folder and all its subfolders and files."""
|
|
101
|
+
if os.path.exists(folder_name):
|
|
102
|
+
shutil.rmtree(folder_name, onerror=_on_rm_error)
|
|
121
103
|
|
|
122
104
|
|
|
123
105
|
def delete_files_and_subfolders(directory):
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
total_files_deleted += 1
|
|
135
|
-
|
|
136
|
-
# Delete directories
|
|
137
|
-
for dir_ in dirs:
|
|
138
|
-
dir_path = os.path.join(root, dir_)
|
|
139
|
-
_make_writable(dir_path)
|
|
140
|
-
os.rmdir(dir_path)
|
|
141
|
-
total_folders_deleted += 1
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
def copy_file(source_path, destination_path):
|
|
145
|
-
# Ensure the destination directory exists
|
|
146
|
-
os.makedirs(os.path.dirname(destination_path), exist_ok=True)
|
|
147
|
-
|
|
148
|
-
# Open the source file in read-binary ('rb') mode
|
|
149
|
-
with open(source_path, "rb") as source_file:
|
|
150
|
-
# Open the destination file in write-binary ('wb') mode
|
|
151
|
-
with open(destination_path, "wb") as destination_file:
|
|
152
|
-
# Copy the content from source to destination
|
|
153
|
-
while True:
|
|
154
|
-
# Read a chunk of the source file
|
|
155
|
-
chunk = source_file.read(4096) # Reading in chunks of 4KB
|
|
156
|
-
if not chunk:
|
|
157
|
-
break # End of file reached
|
|
158
|
-
# Write the chunk to the destination file
|
|
159
|
-
destination_file.write(chunk)
|
|
106
|
+
"""Delete all contents of a directory but keep the directory itself."""
|
|
107
|
+
for entry in os.scandir(directory):
|
|
108
|
+
if entry.is_dir(follow_symlinks=False):
|
|
109
|
+
shutil.rmtree(entry.path, onerror=_on_rm_error)
|
|
110
|
+
else:
|
|
111
|
+
try:
|
|
112
|
+
os.remove(entry.path)
|
|
113
|
+
except PermissionError:
|
|
114
|
+
os.chmod(entry.path, stat.S_IWRITE)
|
|
115
|
+
os.remove(entry.path)
|
|
160
116
|
|
|
161
117
|
|
|
162
118
|
def add_current_path_if_no_path(filename):
|
|
@@ -25,6 +25,7 @@ from plain2code_console import console
|
|
|
25
25
|
from plain2code_events import RenderFailed
|
|
26
26
|
from plain2code_exceptions import (
|
|
27
27
|
ConflictingRequirements,
|
|
28
|
+
GitNotInstalledError,
|
|
28
29
|
InvalidAPIKey,
|
|
29
30
|
InvalidFridArgument,
|
|
30
31
|
MissingAPIKey,
|
|
@@ -48,6 +49,7 @@ from plain2code_logger import (
|
|
|
48
49
|
get_log_file_path,
|
|
49
50
|
)
|
|
50
51
|
from plain2code_state import RunState
|
|
52
|
+
from plain2code_telemetry import capture_crash, initialize_telemetry
|
|
51
53
|
from system_config import system_config
|
|
52
54
|
from tui.plain2code_tui import Plain2CodeTUI
|
|
53
55
|
from tui.plain_module_render_choice_tui import PlainModuleRenderChoiceTUI
|
|
@@ -55,6 +57,27 @@ from tui.plain_module_render_choice_tui import PlainModuleRenderChoiceTUI
|
|
|
55
57
|
DEFAULT_TEMPLATE_DIRS = importlib.resources.files("standard_template_library")
|
|
56
58
|
RENDER_THREAD_SHUTDOWN_TIMEOUT = 0.7
|
|
57
59
|
|
|
60
|
+
# Exceptions that represent expected, user-facing error conditions. They are
|
|
61
|
+
# reported to the user directly and must never be sent to Sentry as crashes.
|
|
62
|
+
EXPECTED_EXCEPTIONS = (
|
|
63
|
+
InvalidFridArgument,
|
|
64
|
+
FileNotFoundError,
|
|
65
|
+
MissingResource,
|
|
66
|
+
TemplateNotFoundError,
|
|
67
|
+
PlainSyntaxError,
|
|
68
|
+
MissingPreviousFunctionalitiesError,
|
|
69
|
+
MissingAPIKey,
|
|
70
|
+
InvalidAPIKey,
|
|
71
|
+
OutdatedClientVersion,
|
|
72
|
+
ConflictingRequirements,
|
|
73
|
+
RenderingCreditBalanceTooLow,
|
|
74
|
+
NetworkConnectionError,
|
|
75
|
+
ModuleDoesNotExistError,
|
|
76
|
+
UnsupportedResourceType,
|
|
77
|
+
GitNotInstalledError,
|
|
78
|
+
SystemExit,
|
|
79
|
+
)
|
|
80
|
+
|
|
58
81
|
|
|
59
82
|
def setup_logging(
|
|
60
83
|
args,
|
|
@@ -181,6 +204,7 @@ def render(args, run_state: RunState, event_bus: EventBus, default_log_level: st
|
|
|
181
204
|
render_choices,
|
|
182
205
|
system_config.client_version,
|
|
183
206
|
run_state.render_id,
|
|
207
|
+
on_cancel=run_state.set_render_cancelled,
|
|
184
208
|
css_path="styles.css",
|
|
185
209
|
)
|
|
186
210
|
render_choice = app.run()
|
|
@@ -189,6 +213,7 @@ def render(args, run_state: RunState, event_bus: EventBus, default_log_level: st
|
|
|
189
213
|
and render_choice.render_range is None
|
|
190
214
|
and render_choice.choice_type == "quit"
|
|
191
215
|
):
|
|
216
|
+
run_state.set_render_cancelled()
|
|
192
217
|
sys.exit(0)
|
|
193
218
|
elif ask_user and args.headless:
|
|
194
219
|
# ignore the default choice if it requires user input due to headless mode
|
|
@@ -217,7 +242,7 @@ def render(args, run_state: RunState, event_bus: EventBus, default_log_level: st
|
|
|
217
242
|
try:
|
|
218
243
|
module_renderer.render_module()
|
|
219
244
|
except RenderCancelledError:
|
|
220
|
-
|
|
245
|
+
run_state.set_render_cancelled() # TUI already closed, nothing to report
|
|
221
246
|
except Exception as e:
|
|
222
247
|
run_state.set_render_succeeded(False)
|
|
223
248
|
render_error.append(e)
|
|
@@ -228,8 +253,7 @@ def render(args, run_state: RunState, event_bus: EventBus, default_log_level: st
|
|
|
228
253
|
try:
|
|
229
254
|
module_renderer.render_module()
|
|
230
255
|
except RenderCancelledError:
|
|
231
|
-
run_state.
|
|
232
|
-
pass
|
|
256
|
+
run_state.set_render_cancelled()
|
|
233
257
|
return
|
|
234
258
|
else:
|
|
235
259
|
render_thread = threading.Thread(target=run_render, daemon=True)
|
|
@@ -242,6 +266,7 @@ def render(args, run_state: RunState, event_bus: EventBus, default_log_level: st
|
|
|
242
266
|
prepare_environment_script=args.prepare_environment_script,
|
|
243
267
|
state_machine_version=system_config.client_version,
|
|
244
268
|
enter_pause_event=enter_pause_event,
|
|
269
|
+
on_cancel=run_state.set_render_cancelled,
|
|
245
270
|
default_log_level=default_log_level,
|
|
246
271
|
css_path="styles.css",
|
|
247
272
|
)
|
|
@@ -321,6 +346,8 @@ def main(): # noqa: C901
|
|
|
321
346
|
args, event_bus, run_state, args.log_to_file, args.log_file_name, args.filename, args.headless
|
|
322
347
|
)
|
|
323
348
|
|
|
349
|
+
initialize_telemetry()
|
|
350
|
+
|
|
324
351
|
exc_info = None
|
|
325
352
|
error_message = None
|
|
326
353
|
|
|
@@ -337,35 +364,17 @@ def main(): # noqa: C901
|
|
|
337
364
|
else:
|
|
338
365
|
error_message = str(e) if str(e) else repr(e)
|
|
339
366
|
|
|
340
|
-
if not isinstance(
|
|
341
|
-
e,
|
|
342
|
-
(
|
|
343
|
-
InvalidFridArgument,
|
|
344
|
-
FileNotFoundError,
|
|
345
|
-
MissingResource,
|
|
346
|
-
TemplateNotFoundError,
|
|
347
|
-
PlainSyntaxError,
|
|
348
|
-
MissingPreviousFunctionalitiesError,
|
|
349
|
-
MissingAPIKey,
|
|
350
|
-
InvalidAPIKey,
|
|
351
|
-
OutdatedClientVersion,
|
|
352
|
-
ConflictingRequirements,
|
|
353
|
-
RenderingCreditBalanceTooLow,
|
|
354
|
-
NetworkConnectionError,
|
|
355
|
-
ModuleDoesNotExistError,
|
|
356
|
-
UnsupportedResourceType,
|
|
357
|
-
),
|
|
358
|
-
):
|
|
367
|
+
if not isinstance(e, EXPECTED_EXCEPTIONS):
|
|
359
368
|
exc_info = sys.exc_info()
|
|
360
369
|
finally:
|
|
370
|
+
if exc_info:
|
|
371
|
+
dump_crash_logs(args, run_state)
|
|
372
|
+
capture_crash(exc_info, run_state, args)
|
|
361
373
|
print_exit_summary(
|
|
362
374
|
run_state,
|
|
363
375
|
args.filename,
|
|
364
376
|
error_message=error_message,
|
|
365
377
|
)
|
|
366
|
-
if exc_info:
|
|
367
|
-
# Log traceback
|
|
368
|
-
dump_crash_logs(args, run_state)
|
|
369
378
|
|
|
370
379
|
if args.headless and (exc_info is not None or not run_state.render_succeeded):
|
|
371
380
|
sys.exit(1)
|
|
@@ -11,6 +11,7 @@ class RunState:
|
|
|
11
11
|
def __init__(self, spec_filename: str, replay_with: Optional[str] = None):
|
|
12
12
|
self.replay: bool = replay_with is not None
|
|
13
13
|
self.render_succeeded: bool = False
|
|
14
|
+
self.render_cancelled: bool = False
|
|
14
15
|
self.render_generated_code_path: Optional[str] = None
|
|
15
16
|
self.rendered_functionalities: int = 0
|
|
16
17
|
if replay_with:
|
|
@@ -20,9 +21,12 @@ class RunState:
|
|
|
20
21
|
self.spec_filename: str = spec_filename
|
|
21
22
|
self.call_count: int = 0
|
|
22
23
|
self.unittest_batch_id: int = 0
|
|
23
|
-
self.frid_render_anaysis: dict[str, str] = {}
|
|
24
24
|
self.render_time_accumulated: int = 0
|
|
25
25
|
self.last_render_start_timestamp: float = time.monotonic()
|
|
26
|
+
# Mirrored from the render state machine for crash reporting; not authoritative.
|
|
27
|
+
self.current_module: Optional[str] = None
|
|
28
|
+
self.current_frid: Optional[str] = None
|
|
29
|
+
self.current_render_state: Optional[str] = None
|
|
26
30
|
|
|
27
31
|
def increment_call_count(self):
|
|
28
32
|
self.call_count += 1
|
|
@@ -30,12 +34,12 @@ class RunState:
|
|
|
30
34
|
def increment_unittest_batch_id(self):
|
|
31
35
|
self.unittest_batch_id += 1
|
|
32
36
|
|
|
33
|
-
def add_rendering_analysis_for_frid(self, frid, rendering_analysis) -> None:
|
|
34
|
-
self.frid_render_anaysis[frid] = rendering_analysis
|
|
35
|
-
|
|
36
37
|
def set_render_succeeded(self, succeeded: bool):
|
|
37
38
|
self.render_succeeded = succeeded
|
|
38
39
|
|
|
40
|
+
def set_render_cancelled(self):
|
|
41
|
+
self.render_cancelled = True
|
|
42
|
+
|
|
39
43
|
def set_render_generated_code_path(self, generated_code_path: str):
|
|
40
44
|
self.render_generated_code_path = generated_code_path
|
|
41
45
|
|
|
@@ -55,6 +59,3 @@ class RunState:
|
|
|
55
59
|
"replay": self.replay,
|
|
56
60
|
"spec_filename": self.spec_filename,
|
|
57
61
|
}
|
|
58
|
-
|
|
59
|
-
def get_render_func_id(self, frid: str) -> str:
|
|
60
|
-
return f"{self.render_id}-{frid}"
|