codeplain 0.3.0__py3-none-any.whl → 0.3.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. _version.py +1 -1
  2. cli_output/__init__.py +7 -0
  3. cli_output/dry_run.py +35 -0
  4. cli_output/render_summary.py +32 -0
  5. cli_output/status.py +160 -0
  6. {codeplain-0.3.0.dist-info → codeplain-0.3.2.dist-info}/METADATA +45 -24
  7. codeplain-0.3.2.dist-info/RECORD +94 -0
  8. {codeplain-0.3.0.dist-info → codeplain-0.3.2.dist-info}/WHEEL +1 -1
  9. codeplain_REST_api.py +9 -1
  10. concept_utils.py +0 -1
  11. file_utils.py +18 -62
  12. memory_management.py +2 -2
  13. module_renderer.py +2 -5
  14. partial_rendering.py +10 -6
  15. plain2code.py +63 -44
  16. plain2code_arguments.py +32 -2
  17. plain2code_console.py +2 -0
  18. plain2code_exceptions.py +6 -0
  19. plain2code_logger.py +37 -3
  20. plain2code_state.py +4 -7
  21. plain2code_utils.py +0 -34
  22. plain_file.py +6 -0
  23. plain_modules.py +11 -8
  24. render_machine/actions/fix_conformance_test.py +27 -29
  25. render_machine/actions/fix_unit_tests.py +2 -6
  26. render_machine/actions/prepare_repositories.py +3 -6
  27. render_machine/actions/prepare_testing_environment.py +3 -5
  28. render_machine/actions/refactor_code.py +11 -15
  29. render_machine/actions/render_conformance_tests.py +34 -39
  30. render_machine/actions/render_functional_requirement.py +15 -20
  31. render_machine/actions/run_conformance_tests.py +11 -11
  32. render_machine/actions/run_unit_tests.py +5 -7
  33. render_machine/code_renderer.py +1 -1
  34. render_machine/conformance_tests.py +9 -13
  35. render_machine/render_context.py +12 -15
  36. render_machine/render_types.py +0 -10
  37. render_machine/render_utils.py +42 -47
  38. render_machine/state_machine_config.py +39 -33
  39. system_config.py +0 -6
  40. tests/e2e/conftest.py +145 -0
  41. tests/e2e/test_hello_world_python.py +37 -0
  42. tests/e2e/test_hello_world_python_windows.py +64 -0
  43. tests/test_cli_output.py +407 -0
  44. tests/test_plainfileparser.py +4 -4
  45. tui/components.py +0 -6
  46. tui/plain2code_tui.py +15 -0
  47. tui/plain_module_render_choice_tui.py +36 -9
  48. tui/styles.css +1 -1
  49. tui/widget_helpers.py +0 -18
  50. codeplain-0.3.0.dist-info/RECORD +0 -88
  51. hash_key.py +0 -29
  52. render_machine/conformance_test_helpers.py +0 -68
  53. {codeplain-0.3.0.dist-info → codeplain-0.3.2.dist-info}/entry_points.txt +0 -0
  54. {codeplain-0.3.0.dist-info → codeplain-0.3.2.dist-info}/licenses/LICENSE +0 -0
_version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.3.0"
1
+ __version__ = "0.3.2"
cli_output/__init__.py ADDED
@@ -0,0 +1,7 @@
1
+ """CLI output formatting for non-interactive display."""
2
+
3
+ from cli_output.dry_run import print_dry_run_output
4
+ from cli_output.render_summary import print_exit_summary
5
+ from cli_output.status import print_status
6
+
7
+ __all__ = ["print_dry_run_output", "print_exit_summary", "print_status"]
cli_output/dry_run.py ADDED
@@ -0,0 +1,35 @@
1
+ """Dry run output display for --dry-run flag."""
2
+
3
+ from typing import Optional
4
+
5
+ import plain_spec
6
+ from plain2code_console import console
7
+
8
+
9
+ def print_dry_run_output(plain_source_tree: dict, render_range: Optional[list[str]]):
10
+ """Print dry run output showing what would be rendered."""
11
+ frid = plain_spec.get_first_frid(plain_source_tree)
12
+
13
+ while frid is not None:
14
+ is_inside_range = render_range is None or frid in render_range
15
+
16
+ if is_inside_range:
17
+ specifications, _ = plain_spec.get_specifications_for_frid(plain_source_tree, frid)
18
+ functional_requirement_text = specifications[plain_spec.FUNCTIONAL_REQUIREMENTS][-1]
19
+ console.info(
20
+ "-------------------------------------\n"
21
+ f"Rendering functionality {frid}:\n"
22
+ f"{functional_requirement_text}\n"
23
+ "-------------------------------------\n"
24
+ )
25
+ if plain_spec.ACCEPTANCE_TESTS in specifications:
26
+ for i, acceptance_test in enumerate(specifications[plain_spec.ACCEPTANCE_TESTS], 1):
27
+ console.info(f"Generating acceptance test #{i}:\n\n{acceptance_test}\n")
28
+ else:
29
+ console.info(
30
+ "-------------------------------------\n"
31
+ f"Skipping rendering iteration: {frid}\n"
32
+ "-------------------------------------"
33
+ )
34
+
35
+ frid = plain_spec.get_next_frid(plain_source_tree, frid)
@@ -0,0 +1,32 @@
1
+ """Render completion summary display."""
2
+
3
+ from typing import Optional
4
+
5
+ from plain2code_console import console
6
+ from plain2code_state import RunState
7
+ from plain2code_utils import format_duration_hms
8
+
9
+
10
+ def print_exit_summary(
11
+ run_state: RunState,
12
+ spec_filename: str,
13
+ error_message: Optional[str] = None,
14
+ ) -> None:
15
+ """Print render outcome after the TUI exits (terminal restored)."""
16
+ console.quiet = False
17
+
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"
24
+ msg += f" [#8E8F91]render id:\t\t\t[#FFFFFF]{run_state.render_id}\n"
25
+ msg += f" [#8E8F91]input file:\t\t\t[#FFFFFF]{spec_filename}\n"
26
+ msg += f" [#8E8F91]generated code folder:\t[#FFFFFF]{run_state.render_generated_code_path or '-'}\n\n"
27
+ msg += f"[#8E8F91]functionalities [#FFFFFF]{run_state.rendered_functionalities} [#8E8F91]used credits [#FFFFFF]{run_state.rendered_functionalities} [#8E8F91]render time [#FFFFFF]{format_duration_hms(run_state.render_time_accumulated)}\n"
28
+ console.print(msg)
29
+
30
+ if not run_state.render_succeeded and error_message:
31
+ console.error(error_message)
32
+ console.quiet = True
cli_output/status.py ADDED
@@ -0,0 +1,160 @@
1
+ """Status display for --status flag."""
2
+
3
+ from datetime import datetime, timezone
4
+ from typing import Optional
5
+
6
+ import codeplain_REST_api as codeplain_api
7
+ from plain2code_console import console
8
+
9
+
10
+ def _create_progress_bar(remaining: int, total: int, width: int = 30) -> str:
11
+ """Create a Unicode progress bar."""
12
+ if total == 0:
13
+ filled = 0
14
+ else:
15
+ filled = int((remaining / total) * width)
16
+
17
+ empty = width - filled
18
+ return "█" * filled + "░" * empty
19
+
20
+
21
+ def _display_credit_line(plan_credits: dict) -> None:
22
+ """Display a plan credit line with progress bar."""
23
+ plan_type = plan_credits["type"]
24
+ remaining = plan_credits["remaining"]
25
+ total = plan_credits["total"]
26
+ period_end = plan_credits["period_end"]
27
+
28
+ # Parse ISO-8601 timestamp and format as "Jun 1, 2026"
29
+ # Handle both with and without timezone info
30
+ dt_str = period_end.replace("Z", "+00:00")
31
+ dt = datetime.fromisoformat(dt_str)
32
+ # If naive datetime, assume UTC
33
+ if dt.tzinfo is None:
34
+ dt = dt.replace(tzinfo=timezone.utc)
35
+ formatted_date = dt.strftime("%b %-d, %Y")
36
+
37
+ # Check if expired
38
+ now = datetime.now(timezone.utc)
39
+ is_expired = dt < now
40
+
41
+ # Create progress bar (30 chars wide)
42
+ bar = _create_progress_bar(remaining, total, width=30)
43
+
44
+ # Format plan type label
45
+ plan_label = "Free trial" if plan_type == "free" else plan_type.upper() + " plan"
46
+
47
+ if is_expired:
48
+ console.print(f" {plan_label:10} {bar} expired {formatted_date}")
49
+ else:
50
+ console.print(f" {plan_label:10} {bar} {remaining:2} of {total} remaining expires {formatted_date}")
51
+
52
+
53
+ def _display_purchased_credit_line(bucket: dict) -> None:
54
+ """Display a purchased credit line with progress bar."""
55
+ remaining = bucket["remaining"]
56
+ total = bucket["total"]
57
+ expiry_date = bucket["expiry_date"]
58
+
59
+ # Parse ISO-8601 timestamp and handle both with and without timezone info
60
+ dt_str = expiry_date.replace("Z", "+00:00")
61
+ dt = datetime.fromisoformat(dt_str)
62
+ # If naive datetime, assume UTC
63
+ if dt.tzinfo is None:
64
+ dt = dt.replace(tzinfo=timezone.utc)
65
+ formatted_date = dt.strftime("%b %-d, %Y")
66
+
67
+ # Check if expired
68
+ now = datetime.now(timezone.utc)
69
+ is_expired = dt < now
70
+
71
+ bar = _create_progress_bar(remaining, total, width=30)
72
+
73
+ if is_expired:
74
+ console.print(f" Purchased {bar} expired {formatted_date}")
75
+ else:
76
+ console.print(f" Purchased {bar} {remaining:2} of {total} remaining expires {formatted_date}")
77
+
78
+
79
+ def _display_status_message(plan_credits: Optional[dict], purchased_credits: list) -> None:
80
+ """Display appropriate status message based on credit state."""
81
+ has_remaining = False
82
+
83
+ # Check if plan credits have remaining balance and are not expired
84
+ if plan_credits:
85
+ remaining = plan_credits["remaining"]
86
+ dt_str = plan_credits["period_end"].replace("Z", "+00:00")
87
+ period_end = datetime.fromisoformat(dt_str)
88
+ # If naive datetime, assume UTC
89
+ if period_end.tzinfo is None:
90
+ period_end = period_end.replace(tzinfo=timezone.utc)
91
+ now = datetime.now(timezone.utc)
92
+
93
+ if remaining > 0 and period_end > now:
94
+ has_remaining = True
95
+
96
+ # Check if any purchased credits have remaining balance and not expired
97
+ for bucket in purchased_credits:
98
+ dt_str = bucket["expiry_date"].replace("Z", "+00:00")
99
+ expiry_date = datetime.fromisoformat(dt_str)
100
+ # If naive datetime, assume UTC
101
+ if expiry_date.tzinfo is None:
102
+ expiry_date = expiry_date.replace(tzinfo=timezone.utc)
103
+ now = datetime.now(timezone.utc)
104
+
105
+ if bucket["remaining"] > 0 and expiry_date > now:
106
+ has_remaining = True
107
+ break
108
+
109
+ if not has_remaining:
110
+ console.print("\nNo rendering credits remaining. Upgrade to continue rendering.")
111
+
112
+
113
+ def print_status(api_key: str, api_url: str, client_version: str) -> None:
114
+ """Display account status including user info and rendering credits."""
115
+ codeplainAPI = codeplain_api.CodeplainAPI(api_key, console)
116
+ codeplainAPI.api_url = api_url
117
+
118
+ # First check client version
119
+ version_check = codeplainAPI.connection_check(client_version)
120
+ client_version_valid = version_check.get("client_version_valid", False)
121
+ min_version = version_check.get("min_client_version", "unknown")
122
+
123
+ response = codeplainAPI.status()
124
+
125
+ user = response["user"]
126
+ api_key_label = response["api_key_label"]
127
+ org_owner = response.get("organization_owner_email")
128
+ plan_credits = response.get("plan_credits")
129
+ purchased_credits = response.get("purchased_credits", [])
130
+
131
+ # Display header information
132
+ if client_version_valid:
133
+ console.print(f"Version: {client_version}")
134
+ else:
135
+ from plain2code_console import Plain2CodeConsole
136
+
137
+ console.print(
138
+ f"Version: {client_version} (outdated — minimum required: {min_version})",
139
+ style=Plain2CodeConsole.ERROR_STYLE,
140
+ )
141
+ console.print("To update, run: uv tool upgrade codeplain\n", style=Plain2CodeConsole.ERROR_STYLE)
142
+ console.print(f"Name: {user['first_name']} {user['last_name']}")
143
+ console.print(f"User email: {user['email']}")
144
+ console.print(f"Organization owner: {org_owner or 'N/A'}")
145
+ console.print(f"API key label: {api_key_label}\n")
146
+
147
+ # Display rendering credits section
148
+ console.print("Rendering credits:")
149
+
150
+ # Display plan credits (free trial or subscription)
151
+ if plan_credits:
152
+ _display_credit_line(plan_credits)
153
+
154
+ # Display purchased credits
155
+ for bucket in purchased_credits:
156
+ _display_purchased_credit_line(bucket)
157
+
158
+ # Display status messages and management link
159
+ _display_status_message(plan_credits, purchased_credits)
160
+ console.print("\nTo manage your plan navigate to https://platform.codeplain.ai/plans")
@@ -1,27 +1,27 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codeplain
3
- Version: 0.3.0
3
+ Version: 0.3.2
4
4
  Summary: Transform plain language specifications into working code
5
5
  License-File: LICENSE
6
6
  Classifier: Environment :: Console
7
7
  Classifier: Intended Audience :: Developers
8
8
  Classifier: Operating System :: OS Independent
9
9
  Classifier: Topic :: Software Development :: Code Generators
10
- Requires-Python: ==3.11
10
+ Requires-Python: >=3.11
11
11
  Requires-Dist: gitpython==3.1.50
12
12
  Requires-Dist: mistletoe==1.3.0
13
13
  Requires-Dist: networkx==3.6.1
14
- Requires-Dist: python-frontmatter==1.1.0
14
+ Requires-Dist: python-frontmatter==1.3.0
15
15
  Requires-Dist: python-liquid2==0.3.0
16
16
  Requires-Dist: pyyaml==6.0.3
17
17
  Requires-Dist: requests==2.34.0
18
- Requires-Dist: rich==14.2.0
18
+ Requires-Dist: rich==15.0.0
19
19
  Requires-Dist: textual>=7.5.0
20
20
  Requires-Dist: tiktoken==0.12.0
21
21
  Requires-Dist: transitions==0.9.3
22
22
  Provides-Extra: dev
23
23
  Requires-Dist: black==26.3.1; extra == 'dev'
24
- Requires-Dist: flake8==7.0.0; extra == 'dev'
24
+ Requires-Dist: flake8==7.3.0; extra == 'dev'
25
25
  Requires-Dist: isort==5.13.2; extra == 'dev'
26
26
  Requires-Dist: mypy==1.11.2; extra == 'dev'
27
27
  Requires-Dist: pytest==8.3.5; extra == 'dev'
@@ -29,31 +29,28 @@ Description-Content-Type: text/markdown
29
29
 
30
30
  # Codeplain plain2code renderer
31
31
 
32
- Render ***plain source to software code using the Codeplain API.
32
+ Render \*\*\*plain source to software code using the Codeplain API.
33
33
 
34
34
  ## Codeplain.ai - Code Generation as a Service
35
35
 
36
- Codeplain is a platform that generates software code using large language models based on requirements you specify in ***plain specification language.
36
+ Codeplain is a platform that generates software code using large language models based on requirements you specify in \*\*\*plain specification language.
37
37
 
38
38
  Schematic overview of the Codeplain's code generation service
39
39
 
40
40
  <img src="https://raw.githubusercontent.com/Codeplain-ai/plain2code_client/main/resources/codeplain_overview.png">
41
41
 
42
- ### Abstracting Away Code Generation Complexity with ***plain
42
+ ### Abstracting Away Code Generation Complexity with \*\*\*plain
43
43
 
44
+ \*\*\*plain is a novel specification language that helps abstracting away complexity of using large language models for code generation.
44
45
 
45
- ***plain is a novel specification language that helps abstracting away complexity of using large language models for code generation.
46
-
47
- An example application in ***plain
46
+ An example application in \*\*\*plain
48
47
 
49
48
  <img src="resources/plain_example.png" width="70%" height="70%">
50
49
 
51
-
52
50
  ## Getting started
53
51
 
54
52
  ### Prerequisites
55
53
 
56
-
57
54
  #### System requirements
58
55
 
59
56
  To run the plain2code client, you need Python 3.11 or a later version.
@@ -92,31 +89,55 @@ export CODEPLAIN_API_KEY="your_actual_api_key_here"
92
89
  After completing the installation steps above, you can immediately test the system with a simple "Hello World" example:
93
90
 
94
91
  - 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
92
 
100
- *Note: Rendering will take a few minutes to complete.*
93
+ ```
94
+ cd examples/example_hello_world_python
95
+ python ../../plain2code.py hello_world_python.plain
96
+ ```
97
+
98
+ _Note: Rendering will take a few minutes to complete._
101
99
 
102
100
  - The system will generate a Python application in the `build` directory. You can run it with:
101
+ ```
102
+ cd build
103
+ python hello_world.py
104
+ ```
105
+
106
+ ## Releasing
107
+
108
+ Releases are built and published with [uv](https://docs.astral.sh/uv/). The version is read from `_version.py`.
109
+
110
+ 1. Bump the version in `_version.py` and commit/tag (`git tag v<version>`).
111
+ 2. Build the distributions:
112
+ ```bash
113
+ uv build --python 3.11
114
+ ```
115
+ This produces `dist/codeplain-<version>-py3-none-any.whl` and `dist/codeplain-<version>.tar.gz`.
116
+ 3. (Optional) Smoke-test the wheel:
117
+ ```bash
118
+ uv run --with dist/codeplain-<version>-py3-none-any.whl --no-project -- codeplain --status
119
+ ```
120
+ 4. (Optional) Publish to TestPyPI first:
121
+ ```bash
122
+ uv publish --publish-url https://test.pypi.org/legacy/ dist/codeplain-<version>*
103
123
  ```
104
- cd build
105
- python hello_world.py
124
+ 5. Publish to PyPI:
125
+ ```bash
126
+ uv publish dist/codeplain-<version>*
106
127
  ```
128
+ Authenticate with `UV_PUBLISH_TOKEN=pypi-...` (recommended), `--token pypi-...`, or `--username __token__ --password pypi-...`.
129
+ 6. Push the tag: `git push origin v<version>`.
107
130
 
108
131
  ## Additional Resources
109
132
 
110
133
  ### Examples and Sample Projects
111
134
 
112
135
  - See the [examples](examples) folder for sample projects in Golang, Python, and React.
113
- - For example application how to implement task manager in ***plain see [example-task-manager](https://github.com/Codeplain-ai/example-task-manager) repository.
114
- - For example application how to implement SaaS connectors in ***plain see [example-saas-connectors](https://github.com/Codeplain-ai/example-saas-connectors) repository.
136
+ - For example application how to implement task manager in \*\*\*plain see [example-task-manager](https://github.com/Codeplain-ai/example-task-manager) repository.
137
+ - For example application how to implement SaaS connectors in \*\*\*plain see [example-saas-connectors](https://github.com/Codeplain-ai/example-saas-connectors) repository.
115
138
 
116
139
  ### Documentation
117
140
 
118
141
  - For more details on the ***plain format, see the [***plain language specification](https://www.plainlang.org/docs/).
119
142
  - 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).
120
143
  - For complete CLI documentation and usage examples, see [plain2code CLI documentation](docs/plain2code_cli.md).
121
-
122
-
@@ -0,0 +1,94 @@
1
+ _version.py,sha256=vNiWJ14r_cw5t_7UDqDQIVZvladKFGyHH2avsLpN7Vg,22
2
+ codeplain_REST_api.py,sha256=h_XoNws8fvf7OTtoVXhHh6OfLGg77UNFuggMBwaqBwE,21533
3
+ concept_utils.py,sha256=D4qVHFBRvfJZwiIq1AD_SV0Ev-RnOMg2yGWo6Cal7PM,7913
4
+ diff_utils.py,sha256=AjiQlqo5pRos_8hVXZo5yBurl5BzSrTMGrQv4dCtRCg,1198
5
+ event_bus.py,sha256=g5XLwRfjrvTIcB6urjREuCJzZj5TA93hy8lKXLGXuCE,650
6
+ file_utils.py,sha256=grPx2Rl6-q2ijSRoaAOZcsrpOjTqb4i_mtF-jzwhKcQ,10953
7
+ git_utils.py,sha256=LQQnL5DAFpH5fDBQgUZX2WfjWY4YC5OEGBPIXryTmnY,16896
8
+ memory_management.py,sha256=nHNudgaOqp5UGkIMi62TRCc2nUeFOM0w-I1FfxBFaQE,6120
9
+ module_renderer.py,sha256=pXMXKKGMhUwLLlSxppE_hpZlwpJ3WSHSfR9FQxt6rSI,6860
10
+ partial_rendering.py,sha256=prWEJo9m1TbF7SKzpNInbcefyTF0o3oXE7Qf1l4SmBs,8409
11
+ plain2code.py,sha256=gyPeZtLe7VkGChWwGVNNZRXvkqpH8uC27szHbrZgmeY,14010
12
+ plain2code_arguments.py,sha256=J05NwaY-O04rIWVF2yIXysNB_ofZS0wJFn0u1WaCCSA,15399
13
+ plain2code_console.py,sha256=YdE-Kz9qRAzIIAg7aYA1bnXRcwy_aTpbFtkR2zHbd-I,4918
14
+ plain2code_events.py,sha256=AM8KJS68uiVXRMLt861KaGhs2H4yzujlMB_xiYPfHQc,1478
15
+ plain2code_exceptions.py,sha256=Fj1pwCffn06aivEplsTJJGdg6lAOR8gmWd4ctLl9cj4,1745
16
+ plain2code_logger.py,sha256=kNKIfkS6Ydtut7Ptw5jmv5bFPgXlxB-_Ds5nd-j4d9o,4609
17
+ plain2code_nodes.py,sha256=IwjQqJxRSJKgP6eEnkYGy3VQHIyoazgkFxAEkxkjfJ8,4426
18
+ plain2code_read_config.py,sha256=D5CxnmTRjddupecWvstcUm0kIJWWzq5-Zmk6sU0wbIg,2235
19
+ plain2code_state.py,sha256=gmqbGfM2dx6ieiqRQRlLPD49JsMAyb3lOWspljcTj9g,1944
20
+ plain2code_utils.py,sha256=HYDJ4lTC3RwBgcJadPvcoU3-Y6krNiuDP4_t2xYKA2Q,874
21
+ plain_file.py,sha256=vJKCkIA12o7LHAGugIsTbzcpoQrTQX2uqlXEC_EqfPo,29470
22
+ plain_modules.py,sha256=NXUtxW8fEpJAuEIeVE_iwRBCbHFh2ji-p9xdkgojLqU,14423
23
+ plain_spec.py,sha256=E2Yp2eXMExE7kx7GPQ7v9boHN8XhFUcqprn1DGMaFMs,15669
24
+ system_config.py,sha256=trgytfXKXoCn0Pgy9u00x2D0ZRHlh_tADlbkwZnF8QE,1038
25
+ cli_output/__init__.py,sha256=MBKl0YbVpWE-tBa3V5_aVbB7vbaqlIK2NsHTjRUZqis,284
26
+ cli_output/dry_run.py,sha256=6NKhNWPtD0CBK_QHSsSqIKLTzibTwxS9d8ltP9d1bgU,1473
27
+ cli_output/render_summary.py,sha256=QrW3Iq15xUoWXuVXpqClAKncAvHdQR0lWG1_OMUBsWk,1307
28
+ cli_output/status.py,sha256=5jr9XFU1MWD3pN404qw7SNYCGdvqfA9j4xv9z1tZG4s,5890
29
+ config/__init__.py,sha256=beYSsJWmBNHDP5rYmVDouqgEeP3t1lkkepbXJ-oq0F8,37
30
+ config/system_config.yaml,sha256=of5R9vukSohU9AlkGJOcAVQA1UJgxYmDy6islU6iFuE,508
31
+ docs/generate_cli.py,sha256=0FHVhICbM8g7ahdInTNih3hNKpty1wbV5rdGECMJj48,656
32
+ examples/example_hello_world_python/harness_tests/hello_world_display/test_hello_world.py,sha256=dwTowrHiVKKbrDv21v8xJC30Q57AXZkQasdGOO5JsBE,470
33
+ render_machine/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
+ render_machine/code_renderer.py,sha256=bLdoZcWaLGvuR0GygnWY9y9kycGySD-IEWY5DFGcGsg,3781
35
+ render_machine/conformance_tests.py,sha256=MeEveEGJTClLGxWw04P4TR6U9kX9Qgz9qtxNrB9a8QI,7085
36
+ render_machine/implementation_code_helpers.py,sha256=LdHNLoKgVtrDaGH2APX8C9vjNjLBUhu2V7OAaJvm4rM,2009
37
+ render_machine/render_context.py,sha256=NHDtJkhQP8tW0iyRH6PR7oSk-PO_pEGMKkX38x52YMc,29128
38
+ render_machine/render_types.py,sha256=cT3D0lmxQ3enIo0jGp3LEGCY2t3X1Q1_tqwcadzSAv0,8761
39
+ render_machine/render_utils.py,sha256=ao0e64VSOGYfb8hwJXIZlbqer5HjNMYJWHlLDoHnJyc,9123
40
+ render_machine/state_machine_config.py,sha256=uqrU9Pu4cAKxu_-e1eiuUQK-tZWxZko0U0Bfvl67v_c,28813
41
+ render_machine/states.py,sha256=niFXqayyA_rezCrlzo0hYeHAruErcPzQVF-BeIBGnL8,1968
42
+ render_machine/triggers.py,sha256=o1N5c9xC7fsKY6ZaWcWBtALT8i1acSSharAXKAqBu8E,1285
43
+ render_machine/actions/analyze_specification_ambiguity.py,sha256=SykZAozGvr1jySxbpKCgt11nMpZhOK0sN-J66M42T5Y,2847
44
+ render_machine/actions/base_action.py,sha256=vVzMp-LanrZyq-jamHyWaQxpRl-pLqtTdtiEzUBhfyE,534
45
+ render_machine/actions/commit_conformance_tests_changes.py,sha256=08yxCPCpf5TlrzLPsEh5SJXVVvnLfwdNKSr7BlzlYxo,2589
46
+ render_machine/actions/commit_implementation_code_changes.py,sha256=yi51oAArLCgWcmFbynes-BK2IGnZwA7wygrTJGoL8dY,826
47
+ render_machine/actions/create_dist.py,sha256=xMPPyws7swVkaUSQg3dwMGScCi1BuII9y2C_XSP0N80,1067
48
+ render_machine/actions/exit_with_error.py,sha256=-LkQsFhmV-Nr741APTx38dqu473b6OSiIvKayFFsXxI,1270
49
+ render_machine/actions/finish_functional_requirement.py,sha256=tj9CzuxuHcbR-2-jMLcAM0gOAcbWpMLg9HIiyngI6gU,739
50
+ render_machine/actions/fix_conformance_test.py,sha256=ePfxz9Xm0IXht0ch7H4tLPdRzoKnVdlbcDC9EgeDAAQ,8786
51
+ render_machine/actions/fix_unit_tests.py,sha256=hvwvCJDk_-8fHMpkNFyeBSG1IshIcufDpymF00ulPXY,2301
52
+ render_machine/actions/prepare_repositories.py,sha256=eE-uU6H_K5IQMraVWSRi4TKk_XPrnA878T_JwtI9cWo,3410
53
+ render_machine/actions/prepare_testing_environment.py,sha256=GQNVzc_Ap5n74OAmeQuAhXXHK2FVVY374Z0lutrN2UQ,1867
54
+ render_machine/actions/refactor_code.py,sha256=_1C16bQbpxdzJkEwl8jDhEKVyXcyGTd-5YYDNTq2vA8,1782
55
+ render_machine/actions/render_conformance_tests.py,sha256=5gOQn-G1T9UWEI8sDfALcVdArk0ln9DgUprOPjccS-U,9268
56
+ render_machine/actions/render_functional_requirement.py,sha256=2SbPGsLbrrQx3kasXpSpphzaMsq9GD8ASnSzvIdodT4,3748
57
+ render_machine/actions/run_conformance_tests.py,sha256=4sJTORl6YLFXQIdLMBQHDeJd2hztGyt3tsVvrCviBko,4008
58
+ render_machine/actions/run_unit_tests.py,sha256=A4WXtSzpCLDHYR8fQAbfxUf_E19GYflZSHZaSreoFPk,2101
59
+ render_machine/actions/summarize_conformance_tests.py,sha256=Vy1kRK8vyazK_BbsKuz1MLDN_TQWVxxzkwEEm-50byc,1639
60
+ standard_template_library/__init__.py,sha256=LtsHl0c3VV46DUInIqyOw4KKVoxKEMOx8dAZpMXtAXc,43
61
+ standard_template_library/golang-console-app-template.plain,sha256=JOusEjCj7jagnXUzaI-SdIr3hj1wTo2uShK017aNtVM,949
62
+ standard_template_library/python-console-app-template.plain,sha256=HW-REeF8Crk0r_w4-MxnleVHeW6PdOB2C8gO8FDG61g,931
63
+ standard_template_library/typescript-react-app-boilerplate.plain,sha256=6LFxhEOzWnDHdVpFj-XBTq6s4_2dy7LKV3lVkCzv8i4,114
64
+ standard_template_library/typescript-react-app-template.plain,sha256=DN5wEJcT8ZqQTf0vFKi9Z1isi9cin2UVNOZb3r28d3M,715
65
+ tests/__init__.py,sha256=Wk73Io62J15BtlLVIzxmASDWaaJkQLevS4BLK5LDAQg,16
66
+ tests/conftest.py,sha256=QZcp08htUlJGgmHDlRWFgsWXZ8o8IBWTD5QqJaUMlU8,790
67
+ tests/test_cli_output.py,sha256=YdIQtplncTeMf82E0nWNrGYoYAFI8Man4zneYkWmbxk,14425
68
+ tests/test_file_utils.py,sha256=-O5zImiFVz9JJ4xi8TbsUwNgPFQoQUOl9Frls4BHCBI,1358
69
+ tests/test_git_utils.py,sha256=h3rWhhs4XVRYqzVzdmj51siF_AYD-GkbZiefK2iGfWo,15614
70
+ tests/test_imports.py,sha256=EWYLUxGunUUdsG9iCiY_tts6T7DIVdhdB_MZlB1LsmU,2081
71
+ tests/test_partial_rendering.py,sha256=4xXZhy9yStn0awSw8B-yrJ3kVYyzJrlR-dKwg9ODXMY,11188
72
+ tests/test_plain_modules.py,sha256=9bWsketDnjpCWkCbMZzYvLqT1ojPhLyxxKljPUq42oY,11393
73
+ tests/test_plainfile.py,sha256=wZ_PN4GULPWIpl_AK_r_6jkgtX0Ch8EHmN5IhX7huOE,11027
74
+ tests/test_plainfileparser.py,sha256=7AassxH7AKX4lfW6E_58FYxRe-IzIiA16BMVWPjWqCM,18750
75
+ tests/test_plainspec.py,sha256=JrzjJ4VFYDmWTZDq-8WFyt2oMx76IEuO1CAUPQeGOsw,2404
76
+ tests/test_requires.py,sha256=6tnHYaKudxN-RiQjSFoi9nHe_x0ze3QF-f-j67zAE74,1250
77
+ tests/test_resolve_config_file.py,sha256=b87tDOvyzbsnE-QRWbZ97ou7NuyaAgskv-dbYBum31c,2830
78
+ tests/e2e/conftest.py,sha256=5Dm67UTOE-M6hMb-mqZvwa6Ek_wmZgsR7fUv9ZEqCwY,4164
79
+ tests/e2e/test_hello_world_python.py,sha256=PQy4elzB7WvpiWLKa6_HFO0K8HA6_6hGLaAqRi0J3OU,1627
80
+ tests/e2e/test_hello_world_python_windows.py,sha256=kJQZIuzj8OEEtgBVxnzFvyQJ_wvP2oBu9L-E9geg8sE,2010
81
+ tui/__init__.py,sha256=_fy7FowY0RkWdn4kC7XV2jgvQPCj-2gE8fNUb-Mny0A,60
82
+ tui/components.py,sha256=DoMDhwNVVpv--OY6o8YQYPzOQPG9Dyh9kgMOqUYtNiE,25412
83
+ tui/models.py,sha256=KMjlWubmPD41GA-BjgiqkaV9v7tLJ0fuWKBDPmZj4JA,1493
84
+ tui/plain2code_tui.py,sha256=t-0DonjrK6_x6jPUMYW0w6tvjZZlk89E-DEor7GzjG4,13785
85
+ tui/plain_module_render_choice_tui.py,sha256=MLc-Ps3O2DMBKRCPJAxUY059SdDIYq2ha7-sWxuguj0,9611
86
+ tui/spinner.py,sha256=Ro6Gd9Przf-whuHqPRY6HwI0T57yJjyNPbhDbigZKZE,2471
87
+ tui/state_handlers.py,sha256=4r_6m5YMcprQtgiTP0De3XFfGAF8VzgaWXHxPweYPbQ,16325
88
+ tui/styles.css,sha256=c13WY1nOgmtkT_FH9Ad2PjXrfi8LYwr6zzD6rBOh9tU,6685
89
+ tui/widget_helpers.py,sha256=mWDPrALsxLOzJg8KD9MXXafJKRiqG-EIp797jbOT77o,5972
90
+ codeplain-0.3.2.dist-info/METADATA,sha256=_m-JTZCg09Ur5aGhureD24GwhQpQravmQnsFxfT37bc,5134
91
+ codeplain-0.3.2.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
92
+ codeplain-0.3.2.dist-info/entry_points.txt,sha256=oDZkBqu9WhtZApb_K6ia8-fn9aojwmAsgnKELceX5T4,46
93
+ codeplain-0.3.2.dist-info/licenses/LICENSE,sha256=pCeKgQ1mXE5OmNUuKOfOZh1T1vqkeCUxghE7N8qgnzc,11345
94
+ codeplain-0.3.2.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.29.0
2
+ Generator: hatchling 1.30.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
codeplain_REST_api.py CHANGED
@@ -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
- f"Please report the error to support@codeplain.ai with the attached {self.log_file_name} file."
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)
@@ -161,6 +161,14 @@ class CodeplainAPI:
161
161
  }
162
162
  return self.post_request(endpoint_url, headers, payload, None, num_retries=0, silent=True)
163
163
 
164
+ def status(self):
165
+ endpoint_url = f"{self.api_url}/status"
166
+ headers = {"Content-Type": "application/json"}
167
+ payload = {
168
+ "api_key": self.api_key,
169
+ }
170
+ return self.post_request(endpoint_url, headers, payload, None, num_retries=0, silent=True)
171
+
164
172
  def render_functional_requirement(
165
173
  self,
166
174
  frid: str,
concept_utils.py CHANGED
@@ -2,7 +2,6 @@ import re
2
2
  from copy import deepcopy
3
3
  from typing import Optional
4
4
 
5
- import mistletoe
6
5
  import mistletoe.block_token
7
6
  import networkx as nx
8
7
 
file_utils.py CHANGED
@@ -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
- # delete a folder and all its subfolders and files
103
- def delete_folder(folder_name):
104
- if os.path.exists(folder_name):
105
- # Use writable-aware deletion so read-only files (e.g. .git objects on Windows) don't cause PermissionError
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 _make_writable(path: str) -> None:
112
- """On Windows, clear read-only so deletion can succeed."""
113
- # TODO - Check if this can be done in a cleaner way.
114
- is_windows = os.name == "nt"
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
- total_files_deleted = 0
125
- total_folders_deleted = 0
126
-
127
- # Walk the directory in reverse order (bottom-up)
128
- for root, dirs, files in os.walk(directory, topdown=False):
129
- # Delete files
130
- for file in files:
131
- file_path = os.path.join(root, file)
132
- _make_writable(file_path)
133
- os.remove(file_path)
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):
memory_management.py CHANGED
@@ -24,9 +24,9 @@ class MemoryManager:
24
24
  console.debug(f"Loaded {len(memory_files_content)} memory files.")
25
25
  return memory_files, memory_files_content
26
26
 
27
- def __init__(self, codeplain_api, module_build_folder: str):
27
+ def __init__(self, codeplain_api, module_name: str, conformance_tests_folder: str):
28
28
  self.codeplain_api = codeplain_api
29
- self.memory_folder = os.path.join(module_build_folder, CODEPLAIN_MEMORY_SUBFOLDER)
29
+ self.memory_folder = os.path.join(conformance_tests_folder, module_name, CODEPLAIN_MEMORY_SUBFOLDER)
30
30
 
31
31
  def create_conformance_tests_memory(
32
32
  self, render_context: RenderContext, exit_code: int, conformance_tests_issue: str
module_renderer.py CHANGED
@@ -60,7 +60,6 @@ class ModuleRenderer:
60
60
  render_range=render_range,
61
61
  render_conformance_tests=self.args.render_conformance_tests,
62
62
  base_folder=self.args.base_folder,
63
- verbose=self.args.verbose,
64
63
  run_state=self.run_state,
65
64
  event_bus=self.event_bus,
66
65
  test_script_timeout=self.args.test_script_timeout,
@@ -116,10 +115,8 @@ class ModuleRenderer:
116
115
 
117
116
  memory_manager = MemoryManager(
118
117
  self.codeplainAPI,
119
- os.path.join(
120
- self.args.build_folder,
121
- plain_module.module_name,
122
- ),
118
+ plain_module.module_name,
119
+ self.args.conformance_tests_folder,
123
120
  )
124
121
  render_context = self._build_render_context_for_module(
125
122
  plain_module,