codeplain 0.3.0__py3-none-any.whl → 0.3.1__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 (39) 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 +27 -0
  5. cli_output/status.py +160 -0
  6. {codeplain-0.3.0.dist-info → codeplain-0.3.1.dist-info}/METADATA +29 -4
  7. {codeplain-0.3.0.dist-info → codeplain-0.3.1.dist-info}/RECORD +39 -31
  8. codeplain_REST_api.py +8 -0
  9. memory_management.py +2 -2
  10. module_renderer.py +2 -5
  11. partial_rendering.py +10 -6
  12. plain2code.py +56 -41
  13. plain2code_arguments.py +32 -2
  14. plain2code_console.py +2 -0
  15. plain2code_logger.py +37 -3
  16. plain_modules.py +5 -6
  17. render_machine/actions/fix_conformance_test.py +27 -29
  18. render_machine/actions/fix_unit_tests.py +2 -6
  19. render_machine/actions/prepare_repositories.py +3 -6
  20. render_machine/actions/prepare_testing_environment.py +3 -5
  21. render_machine/actions/refactor_code.py +11 -15
  22. render_machine/actions/render_conformance_tests.py +34 -39
  23. render_machine/actions/render_functional_requirement.py +15 -20
  24. render_machine/actions/run_conformance_tests.py +7 -9
  25. render_machine/actions/run_unit_tests.py +3 -5
  26. render_machine/code_renderer.py +1 -1
  27. render_machine/conformance_tests.py +9 -13
  28. render_machine/render_context.py +5 -13
  29. render_machine/render_utils.py +42 -47
  30. render_machine/state_machine_config.py +39 -33
  31. tests/e2e/conftest.py +145 -0
  32. tests/e2e/test_hello_world_python.py +37 -0
  33. tests/e2e/test_hello_world_python_windows.py +64 -0
  34. tests/test_cli_output.py +411 -0
  35. tui/plain2code_tui.py +11 -0
  36. tui/plain_module_render_choice_tui.py +27 -9
  37. {codeplain-0.3.0.dist-info → codeplain-0.3.1.dist-info}/WHEEL +0 -0
  38. {codeplain-0.3.0.dist-info → codeplain-0.3.1.dist-info}/entry_points.txt +0 -0
  39. {codeplain-0.3.0.dist-info → codeplain-0.3.1.dist-info}/licenses/LICENSE +0 -0
_version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.3.0"
1
+ __version__ = "0.3.1"
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,27 @@
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
+ msg = "\n[#79FC96]✓ rendering completed\n\n" if run_state.render_succeeded else "[#FF6B6B]✗ rendering failed\n\n"
19
+ msg += f" [#8E8F91]render id:\t\t\t[#FFFFFF]{run_state.render_id}\n"
20
+ msg += f" [#8E8F91]input file:\t\t\t[#FFFFFF]{spec_filename}\n"
21
+ msg += f" [#8E8F91]generated code folder:\t[#FFFFFF]{run_state.render_generated_code_path or '-'}\n\n"
22
+ 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"
23
+ console.print(msg)
24
+
25
+ if not run_state.render_succeeded and error_message:
26
+ console.error(error_message)
27
+ 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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codeplain
3
- Version: 0.3.0
3
+ Version: 0.3.1
4
4
  Summary: Transform plain language specifications into working code
5
5
  License-File: LICENSE
6
6
  Classifier: Environment :: Console
@@ -11,17 +11,17 @@ 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'
@@ -105,6 +105,31 @@ After completing the installation steps above, you can immediately test the syst
105
105
  python hello_world.py
106
106
  ```
107
107
 
108
+ ## Releasing
109
+
110
+ Releases are built and published with [uv](https://docs.astral.sh/uv/). The version is read from `_version.py`.
111
+
112
+ 1. Bump the version in `_version.py` and commit/tag (`git tag v<version>`).
113
+ 2. Build the distributions:
114
+ ```bash
115
+ uv build
116
+ ```
117
+ This produces `dist/codeplain-<version>-py3-none-any.whl` and `dist/codeplain-<version>.tar.gz`.
118
+ 3. (Optional) Smoke-test the wheel:
119
+ ```bash
120
+ uv run --with dist/codeplain-<version>-py3-none-any.whl --no-project -- codeplain --status
121
+ ```
122
+ 4. (Optional) Publish to TestPyPI first:
123
+ ```bash
124
+ uv publish --publish-url https://test.pypi.org/legacy/ dist/codeplain-<version>*
125
+ ```
126
+ 5. Publish to PyPI:
127
+ ```bash
128
+ uv publish dist/codeplain-<version>*
129
+ ```
130
+ Authenticate with `UV_PUBLISH_TOKEN=pypi-...` (recommended), `--token pypi-...`, or `--username __token__ --password pypi-...`.
131
+ 6. Push the tag: `git push origin v<version>`.
132
+
108
133
  ## Additional Resources
109
134
 
110
135
  ### Examples and Sample Projects
@@ -1,41 +1,45 @@
1
- _version.py,sha256=VrXpHDu3erkzwl_WXrqINBm9xWkcyUy53IQOj042dOs,22
2
- codeplain_REST_api.py,sha256=Z1WV67_Xtw2li1wQyVnhrqr-Ymu_BWp9AdB4YjImIhw,21258
1
+ _version.py,sha256=r4xAFihOf72W9TD-lpMi6ntWSTKTP2SlzKP1ytkjRbI,22
2
+ codeplain_REST_api.py,sha256=5O7-PgdxDZZZ95tq4tDyaYeouQdwedU3P6KBeJ0krwo,21550
3
3
  concept_utils.py,sha256=VLHur80-XSJTveClLiHb-CMuqsXd8sIQAr0Ehe1Hid0,7930
4
4
  diff_utils.py,sha256=AjiQlqo5pRos_8hVXZo5yBurl5BzSrTMGrQv4dCtRCg,1198
5
5
  event_bus.py,sha256=g5XLwRfjrvTIcB6urjREuCJzZj5TA93hy8lKXLGXuCE,650
6
6
  file_utils.py,sha256=eO_SHwyjJlkwT3wTlIjIEQ3t8y3k0i-fWKAn5_1SMkY,12597
7
7
  git_utils.py,sha256=LQQnL5DAFpH5fDBQgUZX2WfjWY4YC5OEGBPIXryTmnY,16896
8
8
  hash_key.py,sha256=lMKgKpOhPeC4UpFf9e7eCNK20FEaDsXTCJ8j3eCWidE,837
9
- memory_management.py,sha256=G42roiUSgX8sqDnkfb7XYtYWZqlOQ3DIpMbi0pcOKZA,6079
10
- module_renderer.py,sha256=HfxqzwK5nj0EXC2hx5y6IYY3kVeCMJnPRFoqfFLvaF4,6936
11
- partial_rendering.py,sha256=kZP23kzR__J4ajpo6pmklFpVOoJcaQkgqC5KuLjS--A,7951
12
- plain2code.py,sha256=BvFbgFihS6pW6T5Hferu5HpyC5SFlEe__iAu1KJy0NY,13368
13
- plain2code_arguments.py,sha256=l2K7LVjGe230xwk8K0Q9jkbohBu-4hDJYhueFLPsXMc,14502
14
- plain2code_console.py,sha256=dSkuwUHS0ufUy24y8sloL3uedDDdXPuFDkBaanp1Cug,4820
9
+ memory_management.py,sha256=nHNudgaOqp5UGkIMi62TRCc2nUeFOM0w-I1FfxBFaQE,6120
10
+ module_renderer.py,sha256=pXMXKKGMhUwLLlSxppE_hpZlwpJ3WSHSfR9FQxt6rSI,6860
11
+ partial_rendering.py,sha256=prWEJo9m1TbF7SKzpNInbcefyTF0o3oXE7Qf1l4SmBs,8409
12
+ plain2code.py,sha256=eYmXKAsdvladjoG6ujLWpZCXIOq5FsIabBkCwZd2tDs,13767
13
+ plain2code_arguments.py,sha256=J05NwaY-O04rIWVF2yIXysNB_ofZS0wJFn0u1WaCCSA,15399
14
+ plain2code_console.py,sha256=YdE-Kz9qRAzIIAg7aYA1bnXRcwy_aTpbFtkR2zHbd-I,4918
15
15
  plain2code_events.py,sha256=AM8KJS68uiVXRMLt861KaGhs2H4yzujlMB_xiYPfHQc,1478
16
16
  plain2code_exceptions.py,sha256=g3wMOnjpQlk2pdKRvVarnuh9BlOsRV2AKm_CoNidmH4,1629
17
- plain2code_logger.py,sha256=j376mlCTU-EN_mv06rRY2Rp_47CHzEo6b5wvhlmp0fY,3217
17
+ plain2code_logger.py,sha256=kNKIfkS6Ydtut7Ptw5jmv5bFPgXlxB-_Ds5nd-j4d9o,4609
18
18
  plain2code_nodes.py,sha256=IwjQqJxRSJKgP6eEnkYGy3VQHIyoazgkFxAEkxkjfJ8,4426
19
19
  plain2code_read_config.py,sha256=D5CxnmTRjddupecWvstcUm0kIJWWzq5-Zmk6sU0wbIg,2235
20
20
  plain2code_state.py,sha256=UN3iD3rfJkGK4QQx4RxueQrCy9ngHqExtzxiSEgrW2E,2117
21
21
  plain2code_utils.py,sha256=CLvysMiwChAVaKdqJjkUUlDCQ8dGIFPz1NG7cLbFZuI,2236
22
22
  plain_file.py,sha256=hiyFdB548sYG9T2NILhGM63dcSgbRzy_y5atX5YabUg,29172
23
- plain_modules.py,sha256=pC-cFFRmVNfloscqS7uPjaPF0Dw2NFn9fIQNUP2j0ys,14333
23
+ plain_modules.py,sha256=QwSjX2SC62bQBR-UZM8NSzlepwEs3uSPXgiCqdjoWy4,14281
24
24
  plain_spec.py,sha256=E2Yp2eXMExE7kx7GPQ7v9boHN8XhFUcqprn1DGMaFMs,15669
25
25
  system_config.py,sha256=DMyfiq_uzxWH_uXz5vMWNHkcRFPkIA-M8nr64SeVhPo,1350
26
+ cli_output/__init__.py,sha256=MBKl0YbVpWE-tBa3V5_aVbB7vbaqlIK2NsHTjRUZqis,284
27
+ cli_output/dry_run.py,sha256=6NKhNWPtD0CBK_QHSsSqIKLTzibTwxS9d8ltP9d1bgU,1473
28
+ cli_output/render_summary.py,sha256=X9wHvyaQa8niO87GR2gnzI0rwF6XU55OktwzgmMpJaQ,1186
29
+ cli_output/status.py,sha256=5jr9XFU1MWD3pN404qw7SNYCGdvqfA9j4xv9z1tZG4s,5890
26
30
  config/__init__.py,sha256=beYSsJWmBNHDP5rYmVDouqgEeP3t1lkkepbXJ-oq0F8,37
27
31
  config/system_config.yaml,sha256=of5R9vukSohU9AlkGJOcAVQA1UJgxYmDy6islU6iFuE,508
28
32
  docs/generate_cli.py,sha256=0FHVhICbM8g7ahdInTNih3hNKpty1wbV5rdGECMJj48,656
29
33
  examples/example_hello_world_python/harness_tests/hello_world_display/test_hello_world.py,sha256=dwTowrHiVKKbrDv21v8xJC30Q57AXZkQasdGOO5JsBE,470
30
34
  render_machine/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
- render_machine/code_renderer.py,sha256=TEHdlEOB-seMGeEo1rg5kaIL3pkub7CnU2X8eZNc8G4,3762
35
+ render_machine/code_renderer.py,sha256=bLdoZcWaLGvuR0GygnWY9y9kycGySD-IEWY5DFGcGsg,3781
32
36
  render_machine/conformance_test_helpers.py,sha256=6Ru7Dh24SLNIKWfz_sP5hE_EmWpvk6nfgN1T1yaCZus,2887
33
- render_machine/conformance_tests.py,sha256=y7dhmsFgq7LpaeuLDM5nc1_1HoPo0wkPd3JdaRPxA0Y,7229
37
+ render_machine/conformance_tests.py,sha256=MeEveEGJTClLGxWw04P4TR6U9kX9Qgz9qtxNrB9a8QI,7085
34
38
  render_machine/implementation_code_helpers.py,sha256=LdHNLoKgVtrDaGH2APX8C9vjNjLBUhu2V7OAaJvm4rM,2009
35
- render_machine/render_context.py,sha256=gu1_TjWjiHVlpx3qlDLg0PYI58rYX8YY7xFCoBahems,29133
39
+ render_machine/render_context.py,sha256=ke2HWKNTh3QoPLv_T3b-8ohrM2mzqsCtlP3lsk011uA,28921
36
40
  render_machine/render_types.py,sha256=iqRd9MyrRnXasoA8rvssZbCN66BbVCVXH5uFA8QJVXY,9222
37
- render_machine/render_utils.py,sha256=LIeFJPCj4uPLhyi-LVOr_oNXEKWvE86qpwnPk9u5Vgg,9451
38
- render_machine/state_machine_config.py,sha256=1nVddQBnJsbdUYCNK813QnLZH2D4krz0vqHe2K8LDS8,28212
41
+ render_machine/render_utils.py,sha256=ao0e64VSOGYfb8hwJXIZlbqer5HjNMYJWHlLDoHnJyc,9123
42
+ render_machine/state_machine_config.py,sha256=uqrU9Pu4cAKxu_-e1eiuUQK-tZWxZko0U0Bfvl67v_c,28813
39
43
  render_machine/states.py,sha256=niFXqayyA_rezCrlzo0hYeHAruErcPzQVF-BeIBGnL8,1968
40
44
  render_machine/triggers.py,sha256=o1N5c9xC7fsKY6ZaWcWBtALT8i1acSSharAXKAqBu8E,1285
41
45
  render_machine/actions/analyze_specification_ambiguity.py,sha256=SykZAozGvr1jySxbpKCgt11nMpZhOK0sN-J66M42T5Y,2847
@@ -45,15 +49,15 @@ render_machine/actions/commit_implementation_code_changes.py,sha256=yi51oAArLCgW
45
49
  render_machine/actions/create_dist.py,sha256=xMPPyws7swVkaUSQg3dwMGScCi1BuII9y2C_XSP0N80,1067
46
50
  render_machine/actions/exit_with_error.py,sha256=-LkQsFhmV-Nr741APTx38dqu473b6OSiIvKayFFsXxI,1270
47
51
  render_machine/actions/finish_functional_requirement.py,sha256=tj9CzuxuHcbR-2-jMLcAM0gOAcbWpMLg9HIiyngI6gU,739
48
- render_machine/actions/fix_conformance_test.py,sha256=aYf3j-q7HnZGB9M1qEMpYqLyCUMqWJR9T6VZqn-oWrA,8972
49
- render_machine/actions/fix_unit_tests.py,sha256=AWrzaLpd-PuIBt_udzaQCRhlGyPIIgMrlzIDum9Xl8E,2409
50
- render_machine/actions/prepare_repositories.py,sha256=jaRrOGnTR54e8UdaeaX7T73h2rdPJ3IDAQ5mrKHdFgw,3547
51
- render_machine/actions/prepare_testing_environment.py,sha256=Rvj0LNuQ6anP-3fBr2FmDurt7XrPM-GUREqz3c87jTg,1950
52
- render_machine/actions/refactor_code.py,sha256=slDnx37RLDP4ACiIv0b3u6JTsYPyfyLY-2oXGUM8ko4,1970
53
- render_machine/actions/render_conformance_tests.py,sha256=J2p_urgayUNtGlmdZ8NWuE1nvDgOQHpir2ESHcAfD6U,9587
54
- render_machine/actions/render_functional_requirement.py,sha256=tr0SqG25v3PwUCVgrx4eB8-Xh6ibab0H5IK3nMmULlg,3955
55
- render_machine/actions/run_conformance_tests.py,sha256=WtMSFTYfWFR6bBh3rZ7n5oc7jDDsxnTEdRi-kN1cj4o,4075
56
- render_machine/actions/run_unit_tests.py,sha256=zCu3yJbJ0JkiB7lZbgBwsVs_qiFtrlyN2T3xf5A_gPg,2176
52
+ render_machine/actions/fix_conformance_test.py,sha256=ePfxz9Xm0IXht0ch7H4tLPdRzoKnVdlbcDC9EgeDAAQ,8786
53
+ render_machine/actions/fix_unit_tests.py,sha256=hvwvCJDk_-8fHMpkNFyeBSG1IshIcufDpymF00ulPXY,2301
54
+ render_machine/actions/prepare_repositories.py,sha256=eE-uU6H_K5IQMraVWSRi4TKk_XPrnA878T_JwtI9cWo,3410
55
+ render_machine/actions/prepare_testing_environment.py,sha256=GQNVzc_Ap5n74OAmeQuAhXXHK2FVVY374Z0lutrN2UQ,1867
56
+ render_machine/actions/refactor_code.py,sha256=_1C16bQbpxdzJkEwl8jDhEKVyXcyGTd-5YYDNTq2vA8,1782
57
+ render_machine/actions/render_conformance_tests.py,sha256=5gOQn-G1T9UWEI8sDfALcVdArk0ln9DgUprOPjccS-U,9268
58
+ render_machine/actions/render_functional_requirement.py,sha256=2SbPGsLbrrQx3kasXpSpphzaMsq9GD8ASnSzvIdodT4,3748
59
+ render_machine/actions/run_conformance_tests.py,sha256=jmpB-yklUeUjywSpOfbOM0bFGOAiXBQkZYh13prwL6Q,3976
60
+ render_machine/actions/run_unit_tests.py,sha256=-X7IOrZ7d9fuy_pL7CiON5wU1mI4ccSLuMyq-768aD8,2093
57
61
  render_machine/actions/summarize_conformance_tests.py,sha256=Vy1kRK8vyazK_BbsKuz1MLDN_TQWVxxzkwEEm-50byc,1639
58
62
  standard_template_library/__init__.py,sha256=LtsHl0c3VV46DUInIqyOw4KKVoxKEMOx8dAZpMXtAXc,43
59
63
  standard_template_library/golang-console-app-template.plain,sha256=JOusEjCj7jagnXUzaI-SdIr3hj1wTo2uShK017aNtVM,949
@@ -62,6 +66,7 @@ standard_template_library/typescript-react-app-boilerplate.plain,sha256=6LFxhEOz
62
66
  standard_template_library/typescript-react-app-template.plain,sha256=DN5wEJcT8ZqQTf0vFKi9Z1isi9cin2UVNOZb3r28d3M,715
63
67
  tests/__init__.py,sha256=Wk73Io62J15BtlLVIzxmASDWaaJkQLevS4BLK5LDAQg,16
64
68
  tests/conftest.py,sha256=QZcp08htUlJGgmHDlRWFgsWXZ8o8IBWTD5QqJaUMlU8,790
69
+ tests/test_cli_output.py,sha256=Zh4-zWXnW5Znjoj7L33CUYUjNqYzXyFScvWhKPIRn4A,14504
65
70
  tests/test_file_utils.py,sha256=-O5zImiFVz9JJ4xi8TbsUwNgPFQoQUOl9Frls4BHCBI,1358
66
71
  tests/test_git_utils.py,sha256=h3rWhhs4XVRYqzVzdmj51siF_AYD-GkbZiefK2iGfWo,15614
67
72
  tests/test_imports.py,sha256=EWYLUxGunUUdsG9iCiY_tts6T7DIVdhdB_MZlB1LsmU,2081
@@ -72,17 +77,20 @@ tests/test_plainfileparser.py,sha256=iVTAJWxQZbv96uHXe-qvGSgsC9uJk5Tu10Nod3kqflI
72
77
  tests/test_plainspec.py,sha256=JrzjJ4VFYDmWTZDq-8WFyt2oMx76IEuO1CAUPQeGOsw,2404
73
78
  tests/test_requires.py,sha256=6tnHYaKudxN-RiQjSFoi9nHe_x0ze3QF-f-j67zAE74,1250
74
79
  tests/test_resolve_config_file.py,sha256=b87tDOvyzbsnE-QRWbZ97ou7NuyaAgskv-dbYBum31c,2830
80
+ tests/e2e/conftest.py,sha256=5Dm67UTOE-M6hMb-mqZvwa6Ek_wmZgsR7fUv9ZEqCwY,4164
81
+ tests/e2e/test_hello_world_python.py,sha256=PQy4elzB7WvpiWLKa6_HFO0K8HA6_6hGLaAqRi0J3OU,1627
82
+ tests/e2e/test_hello_world_python_windows.py,sha256=kJQZIuzj8OEEtgBVxnzFvyQJ_wvP2oBu9L-E9geg8sE,2010
75
83
  tui/__init__.py,sha256=_fy7FowY0RkWdn4kC7XV2jgvQPCj-2gE8fNUb-Mny0A,60
76
84
  tui/components.py,sha256=G6ktJx211WrYmj-1aZL3mFtnBTOE4WRF3hA1LG8J0jU,25810
77
85
  tui/models.py,sha256=KMjlWubmPD41GA-BjgiqkaV9v7tLJ0fuWKBDPmZj4JA,1493
78
- tui/plain2code_tui.py,sha256=_c8gaEZeFPvr1-uqpQAsacpHd77Ig16x045Q4ihfwuc,13076
79
- tui/plain_module_render_choice_tui.py,sha256=ZiB7E2mgFN9v0WHZeDfhLvfYURg1IPcQpC5dvHQwzqA,8499
86
+ tui/plain2code_tui.py,sha256=Migz-ZdylT8I5ilgvPXxIsTvF30EMC18jyMBGMdattI,13608
87
+ tui/plain_module_render_choice_tui.py,sha256=8aQBwjFmKoKOo-s_sCWuEs4ax1Ww0GKadbMiZAdpKSM,9366
80
88
  tui/spinner.py,sha256=Ro6Gd9Przf-whuHqPRY6HwI0T57yJjyNPbhDbigZKZE,2471
81
89
  tui/state_handlers.py,sha256=4r_6m5YMcprQtgiTP0De3XFfGAF8VzgaWXHxPweYPbQ,16325
82
90
  tui/styles.css,sha256=9vI6V7n7p76i2Dn62KDyOkRYp__d3v2bCzYxO9PWnm8,6685
83
91
  tui/widget_helpers.py,sha256=DdXCAJEvFVMgLM2GNBqE-yMtgoNnk7sHG6ehaEV3zdk,6577
84
- codeplain-0.3.0.dist-info/METADATA,sha256=uosx3aB3OZ_wcwt-YeAFzzVHKHyyMf78k_rqFEp6iSs,4203
85
- codeplain-0.3.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
86
- codeplain-0.3.0.dist-info/entry_points.txt,sha256=oDZkBqu9WhtZApb_K6ia8-fn9aojwmAsgnKELceX5T4,46
87
- codeplain-0.3.0.dist-info/licenses/LICENSE,sha256=pCeKgQ1mXE5OmNUuKOfOZh1T1vqkeCUxghE7N8qgnzc,11345
88
- codeplain-0.3.0.dist-info/RECORD,,
92
+ codeplain-0.3.1.dist-info/METADATA,sha256=Rg-b_OM3ViTIsGXtcIk55V_ZXLoKw3lKhA1r3BaAFLw,5112
93
+ codeplain-0.3.1.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
94
+ codeplain-0.3.1.dist-info/entry_points.txt,sha256=oDZkBqu9WhtZApb_K6ia8-fn9aojwmAsgnKELceX5T4,46
95
+ codeplain-0.3.1.dist-info/licenses/LICENSE,sha256=pCeKgQ1mXE5OmNUuKOfOZh1T1vqkeCUxghE7N8qgnzc,11345
96
+ codeplain-0.3.1.dist-info/RECORD,,
codeplain_REST_api.py CHANGED
@@ -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,
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,
partial_rendering.py CHANGED
@@ -60,7 +60,6 @@ def module_comes_before_or_equal(
60
60
  module1: PlainModule,
61
61
  module2: PlainModule,
62
62
  ) -> bool:
63
-
64
63
  for module in all_required_modules:
65
64
  if module.module_name == module1.module_name:
66
65
  return True
@@ -120,7 +119,7 @@ def get_all_affected_modules_from_change(
120
119
  plain_module: PlainModule,
121
120
  plain_module_render_state: PlainModuleRenderState,
122
121
  ) -> list[PlainModule]:
123
- all_affected_modules = list[PlainModule]()
122
+ all_affected_modules = dict[str, PlainModule]()
124
123
 
125
124
  if plain_module_render_state.change_type == "spec_change":
126
125
  start_module = plain_module_render_state.change
@@ -138,10 +137,10 @@ def get_all_affected_modules_from_change(
138
137
  if module.module_name == start_module.module_name:
139
138
  affected_module = True
140
139
 
141
- if affected_module:
142
- all_affected_modules.append(module)
140
+ if affected_module and module.module_name not in all_affected_modules:
141
+ all_affected_modules[module.module_name] = module
143
142
 
144
- return all_affected_modules
143
+ return list(all_affected_modules.values())
145
144
 
146
145
 
147
146
  def get_render_choices(
@@ -151,6 +150,7 @@ def get_render_choices(
151
150
  ) -> dict[str, RenderChoice]:
152
151
  choices = dict[str, RenderChoice]()
153
152
  choice_idx = 1
153
+ module_start_points = list[str]()
154
154
 
155
155
  if plain_module_render_state.last_render_module.is_initial_module():
156
156
  choices[str(choice_idx)] = RenderChoice(
@@ -190,12 +190,13 @@ def get_render_choices(
190
190
  is_destructive=plain_module_render_state.last_render_module.module_name == plain_module.module_name,
191
191
  choice_type="module_start",
192
192
  )
193
+ module_start_points.append(next_module.module_name)
193
194
  choice_idx += 1
194
195
 
195
196
  if plain_module_render_state.change:
196
197
  all_affected_modules = get_all_affected_modules_from_change(plain_module, plain_module_render_state)
197
198
 
198
- if len(all_affected_modules) > 0:
199
+ if len(all_affected_modules) > 0 and all_affected_modules[0].module_name not in module_start_points:
199
200
  choices[str(choice_idx)] = RenderChoice(
200
201
  module=all_affected_modules[0],
201
202
  render_range=None,
@@ -203,6 +204,7 @@ def get_render_choices(
203
204
  wipe_later_modules=True,
204
205
  is_destructive=True,
205
206
  )
207
+ module_start_points.append(all_affected_modules[0].module_name)
206
208
  choice_idx += 1
207
209
 
208
210
  if len(plain_module.all_required_modules) > 0:
@@ -210,6 +212,7 @@ def get_render_choices(
210
212
  if first_module.module_name != plain_module_render_state.last_render_module.module_name and (
211
213
  plain_module_render_state.change is not None
212
214
  and first_module.module_name != plain_module_render_state.change.module_name
215
+ and first_module.module_name not in module_start_points
213
216
  ):
214
217
  choices[str(choice_idx)] = RenderChoice(
215
218
  module=first_module,
@@ -218,6 +221,7 @@ def get_render_choices(
218
221
  wipe_later_modules=True,
219
222
  is_destructive=True,
220
223
  )
224
+ module_start_points.append(first_module.module_name)
221
225
  choice_idx += 1
222
226
 
223
227
  choices[str(choice_idx)] = RenderChoice(module=None, render_range=None, choice_type="quit")