codeplain 0.3.6__py3-none-any.whl → 0.3.8__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 (45) hide show
  1. cli_output/render_summary.py +2 -2
  2. cli_output/status.py +2 -2
  3. {codeplain-0.3.6.dist-info → codeplain-0.3.8.dist-info}/METADATA +7 -7
  4. {codeplain-0.3.6.dist-info → codeplain-0.3.8.dist-info}/RECORD +44 -32
  5. codeplain_REST_api.py +6 -2
  6. file_utils.py +1 -1
  7. module_renderer.py +3 -2
  8. path_resolution.py +64 -0
  9. plain2code.py +6 -10
  10. plain2code_arguments.py +200 -99
  11. plain2code_console.py +34 -22
  12. plain2code_events.py +1 -0
  13. plain2code_logger.py +13 -22
  14. plain2code_state.py +17 -1
  15. plain2code_utils.py +17 -13
  16. plain_file.py +39 -28
  17. render_machine/actions/create_dist.py +2 -2
  18. render_machine/actions/exit_with_error.py +2 -1
  19. render_machine/actions/fix_conformance_test.py +4 -2
  20. render_machine/actions/render_functional_requirement.py +4 -3
  21. render_machine/render_context.py +5 -3
  22. render_machine/render_utils.py +14 -8
  23. system_config.py +35 -1
  24. tests/test_arg_provenance.py +89 -0
  25. tests/test_cli_output.py +2 -2
  26. tests/test_console_log_styles.py +112 -0
  27. tests/test_folder_path_resolution.py +165 -0
  28. tests/test_log_file_name_resolution.py +89 -0
  29. tests/test_logging_config_path_resolution.py +67 -0
  30. tests/test_path_resolution.py +65 -0
  31. tests/test_plain2code_state.py +36 -0
  32. tests/test_plain2code_utils.py +36 -0
  33. tests/test_plainfileparser.py +2 -2
  34. tests/test_script_path_resolution.py +107 -0
  35. tests/test_tui_usage.py +209 -0
  36. tests/test_usage_summary.py +28 -0
  37. tui/components.py +29 -20
  38. tui/plain2code_tui.py +71 -2
  39. tui/styles.css +4 -0
  40. tui/widget_helpers.py +20 -1
  41. usage_summary.py +30 -0
  42. _version.py +0 -1
  43. {codeplain-0.3.6.dist-info → codeplain-0.3.8.dist-info}/WHEEL +0 -0
  44. {codeplain-0.3.6.dist-info → codeplain-0.3.8.dist-info}/entry_points.txt +0 -0
  45. {codeplain-0.3.6.dist-info → codeplain-0.3.8.dist-info}/licenses/LICENSE +0 -0
@@ -4,7 +4,7 @@ from typing import Optional
4
4
 
5
5
  from plain2code_console import console
6
6
  from plain2code_state import RunState
7
- from plain2code_utils import format_duration_hms
7
+ from usage_summary import format_usage_summary
8
8
 
9
9
 
10
10
  def print_exit_summary(
@@ -24,7 +24,7 @@ def print_exit_summary(
24
24
  msg += f" [#8E8F91]render id:\t\t\t[#FFFFFF]{run_state.render_id}\n"
25
25
  msg += f" [#8E8F91]input file:\t\t\t[#FFFFFF]{spec_filename}\n"
26
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"
27
+ msg += format_usage_summary(run_state.rendered_functionalities, run_state.render_time_accumulated) + "\n"
28
28
  console.print(msg)
29
29
 
30
30
  if not run_state.render_succeeded and error_message:
cli_output/status.py CHANGED
@@ -32,7 +32,7 @@ def _display_credit_line(plan_credits: dict) -> None:
32
32
  # If naive datetime, assume UTC
33
33
  if dt.tzinfo is None:
34
34
  dt = dt.replace(tzinfo=timezone.utc)
35
- formatted_date = dt.strftime("%b %-d, %Y")
35
+ formatted_date = f"{dt.strftime('%b')} {dt.day}, {dt.year}"
36
36
 
37
37
  # Check if expired
38
38
  now = datetime.now(timezone.utc)
@@ -62,7 +62,7 @@ def _display_bucket_credit_line(bucket: dict, label: str) -> None:
62
62
  # If naive datetime, assume UTC
63
63
  if dt.tzinfo is None:
64
64
  dt = dt.replace(tzinfo=timezone.utc)
65
- formatted_date = dt.strftime("%b %-d, %Y")
65
+ formatted_date = f"{dt.strftime('%b')} {dt.day}, {dt.year}"
66
66
 
67
67
  # Check if expired
68
68
  now = datetime.now(timezone.utc)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codeplain
3
- Version: 0.3.6
3
+ Version: 0.3.8
4
4
  Summary: Transform plain language specifications into working code
5
5
  License-File: LICENSE
6
6
  Classifier: Environment :: Console
@@ -8,23 +8,23 @@ Classifier: Intended Audience :: Developers
8
8
  Classifier: Operating System :: OS Independent
9
9
  Classifier: Topic :: Software Development :: Code Generators
10
10
  Requires-Python: ==3.11.*
11
- Requires-Dist: gitpython==3.1.50
12
- Requires-Dist: mistletoe==1.3.0
11
+ Requires-Dist: gitpython==3.1.52
12
+ Requires-Dist: mistletoe==1.5.1
13
13
  Requires-Dist: networkx==3.6.1
14
14
  Requires-Dist: python-frontmatter==1.3.0
15
15
  Requires-Dist: python-liquid2==0.4.0
16
16
  Requires-Dist: pyyaml==6.0.3
17
17
  Requires-Dist: requests==2.34.2
18
18
  Requires-Dist: rich==15.0.0
19
- Requires-Dist: sentry-sdk==2.63.0
19
+ Requires-Dist: sentry-sdk==2.66.0
20
20
  Requires-Dist: textual>=7.5.0
21
21
  Requires-Dist: tiktoken==0.13.0
22
22
  Requires-Dist: transitions==0.9.3
23
23
  Provides-Extra: dev
24
- Requires-Dist: black==26.3.1; extra == 'dev'
24
+ Requires-Dist: black==26.5.1; extra == 'dev'
25
25
  Requires-Dist: flake8==7.3.0; extra == 'dev'
26
- Requires-Dist: isort==5.13.2; extra == 'dev'
27
- Requires-Dist: mypy==2.1.0; extra == 'dev'
26
+ Requires-Dist: isort==8.0.1; extra == 'dev'
27
+ Requires-Dist: mypy==2.3.0; extra == 'dev'
28
28
  Requires-Dist: pytest==9.1.1; extra == 'dev'
29
29
  Description-Content-Type: text/markdown
30
30
 
@@ -1,33 +1,34 @@
1
- _version.py,sha256=W_9dCm49nLvZulVAvvsafxLJjVBSKDBHz9K7szFZllo,22
2
1
  change_detection.py,sha256=CazORZrezeZSw2I6VBvieN-c2SEjieZPo6pNrKwmMVA,6915
3
- codeplain_REST_api.py,sha256=h_XoNws8fvf7OTtoVXhHh6OfLGg77UNFuggMBwaqBwE,21533
2
+ codeplain_REST_api.py,sha256=x-sCbbBSswID4eKXDMvv84nzWynNSbBPoyAtq__6tas,21644
4
3
  concept_utils.py,sha256=D4qVHFBRvfJZwiIq1AD_SV0Ev-RnOMg2yGWo6Cal7PM,7913
5
4
  diff_utils.py,sha256=AjiQlqo5pRos_8hVXZo5yBurl5BzSrTMGrQv4dCtRCg,1198
6
5
  event_bus.py,sha256=g5XLwRfjrvTIcB6urjREuCJzZj5TA93hy8lKXLGXuCE,650
7
- file_utils.py,sha256=q-ApwiLLJCgmmoy6-nf1QM9DaFXwzCJek6B-jQTI9ak,11665
6
+ file_utils.py,sha256=MNZ5li1ofIMOYB6FCrMa_UkX6nkGCgSZuaqzhqVc1Ok,11682
8
7
  git_utils.py,sha256=LQQnL5DAFpH5fDBQgUZX2WfjWY4YC5OEGBPIXryTmnY,16896
9
8
  memory_management.py,sha256=nHNudgaOqp5UGkIMi62TRCc2nUeFOM0w-I1FfxBFaQE,6120
10
- module_renderer.py,sha256=pXMXKKGMhUwLLlSxppE_hpZlwpJ3WSHSfR9FQxt6rSI,6860
9
+ module_renderer.py,sha256=DZCIo_QzvhFw8EqQTKeF3HUbvfumrV2O1sz-gUhWpIA,6752
11
10
  partial_rendering.py,sha256=0ChOaGytogBUEGISpbWkbjT9Hqy8pnOkJdJXcitJDgk,12219
12
- plain2code.py,sha256=7VUE_q06VQinFHNfyh1I2u2Nt3EOa6WPkiPfqEF6x58,15452
13
- plain2code_arguments.py,sha256=J05NwaY-O04rIWVF2yIXysNB_ofZS0wJFn0u1WaCCSA,15399
14
- plain2code_console.py,sha256=YdE-Kz9qRAzIIAg7aYA1bnXRcwy_aTpbFtkR2zHbd-I,4918
15
- plain2code_events.py,sha256=AM8KJS68uiVXRMLt861KaGhs2H4yzujlMB_xiYPfHQc,1478
11
+ path_resolution.py,sha256=0tMqU0a1g4DYhTOp7QjLVSEkv_cgasb4NI1mK809dUM,2233
12
+ plain2code.py,sha256=YLwwzAvE0EWz51V9p8b_kbTltkPvhA2Q8J4JrYyMrAM,15327
13
+ plain2code_arguments.py,sha256=3tAA9Haw-j97zztjXdhi6LxFtURP0Tfg9J_rvJDoXmw,17685
14
+ plain2code_console.py,sha256=Ifs8tmfNX31UujQ5_0sk9AJcajMG_6qhF-6QZcfbcEE,5773
15
+ plain2code_events.py,sha256=rgDF5PDtNr_lEPvJmd1tIQXZrzwgzZW5b-_pykugqe4,1587
16
16
  plain2code_exceptions.py,sha256=wkkEXw2k4LCd09PIH3_Hu0HyZvtXYjFaA653Sfey4FA,2287
17
- plain2code_logger.py,sha256=LdCaS29gWA7e1VhQijyXagsrFAnEi7VKU62b1dnlKnc,5114
17
+ plain2code_logger.py,sha256=NRPLVIMnCHNVl6dII9gFoIzMdr8xa7icnL0T_Fx2XLw,4839
18
18
  plain2code_nodes.py,sha256=IwjQqJxRSJKgP6eEnkYGy3VQHIyoazgkFxAEkxkjfJ8,4426
19
19
  plain2code_read_config.py,sha256=D5CxnmTRjddupecWvstcUm0kIJWWzq5-Zmk6sU0wbIg,2235
20
- plain2code_state.py,sha256=1VwyxgKdA13fAJNKSiPBfBb3A20TKfGCNnOm06YdcS4,2233
20
+ plain2code_state.py,sha256=uePQlC29Pbhm1yXfdCFebONYdy6jo13gTTmXSwC11cQ,3168
21
21
  plain2code_telemetry.py,sha256=7yuTH7mQE_wGVW2H1oiVZ3UJg98ArB5HcUUm9isnv9g,4354
22
- plain2code_utils.py,sha256=id04t9Iez5pYygrE3fKEyrcfWDcniKFT2iQoF2CzTJs,1409
23
- plain_file.py,sha256=xdaDm5BL2dIhwMQeyq9nTXsugS4PDnR25CyrKBtHlLU,34386
22
+ plain2code_utils.py,sha256=unOxuqSQRpwG8YF6uoQhnZ3ZSTJRIUCPLdJ-1lRZR5M,1534
23
+ plain_file.py,sha256=k2rcq53q0kOf9_8egrZILpkrVkZdfJt7HU0gHBFskU4,35116
24
24
  plain_modules.py,sha256=uvRPfxBJwstaWi5lgYKgtQdhhf0Naiy2mS41tLw_TjY,16186
25
25
  plain_spec.py,sha256=7nb5lLJKHZ3uu9THkSXyJ5brIKkbGW0ooVfRclLdlzA,16062
26
- system_config.py,sha256=trgytfXKXoCn0Pgy9u00x2D0ZRHlh_tADlbkwZnF8QE,1038
26
+ system_config.py,sha256=VyE6MhXfi6agZvE02p_G4K8yCHkLh3eu890cBzPyDvs,2196
27
+ usage_summary.py,sha256=MA4WcVaAwz6IWCN4k_rgxVcwO4I157wN7BZlFi3h0R4,1282
27
28
  cli_output/__init__.py,sha256=MBKl0YbVpWE-tBa3V5_aVbB7vbaqlIK2NsHTjRUZqis,284
28
29
  cli_output/dry_run.py,sha256=6NKhNWPtD0CBK_QHSsSqIKLTzibTwxS9d8ltP9d1bgU,1473
29
- cli_output/render_summary.py,sha256=QrW3Iq15xUoWXuVXpqClAKncAvHdQR0lWG1_OMUBsWk,1307
30
- cli_output/status.py,sha256=PFhHqFaC0nt2zsPR0VbD-JBPB3sEVPfhopJmOylFxbU,6160
30
+ cli_output/render_summary.py,sha256=-yRoI7ZZTS9IHyp1vd-sFleGXTVbxlrCkvuM5TX0Mp4,1168
31
+ cli_output/status.py,sha256=MvdocFXL1aA73NMp92gQb0Nd-f7-iw66XVUiHK33sEM,6194
31
32
  config/__init__.py,sha256=beYSsJWmBNHDP5rYmVDouqgEeP3t1lkkepbXJ-oq0F8,37
32
33
  config/system_config.yaml,sha256=of5R9vukSohU9AlkGJOcAVQA1UJgxYmDy6islU6iFuE,508
33
34
  docs/generate_cli.py,sha256=0FHVhICbM8g7ahdInTNih3hNKpty1wbV5rdGECMJj48,656
@@ -36,9 +37,9 @@ render_machine/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
36
37
  render_machine/code_renderer.py,sha256=-vp_ltU7e3ssX9hbS8IY4ETSp-39JAuoJ3ZltKpdt1M,3959
37
38
  render_machine/conformance_tests.py,sha256=MeEveEGJTClLGxWw04P4TR6U9kX9Qgz9qtxNrB9a8QI,7085
38
39
  render_machine/implementation_code_helpers.py,sha256=LdHNLoKgVtrDaGH2APX8C9vjNjLBUhu2V7OAaJvm4rM,2009
39
- render_machine/render_context.py,sha256=fjU_S5W5HoAX3-WlEM8hp4DKMX_NDgVlfwQUAp0yaf8,25633
40
+ render_machine/render_context.py,sha256=eoaO807LntIRYjLSpc3x7bvwBnFfzLlHmKkyKlIRrxs,25697
40
41
  render_machine/render_types.py,sha256=cT3D0lmxQ3enIo0jGp3LEGCY2t3X1Q1_tqwcadzSAv0,8761
41
- render_machine/render_utils.py,sha256=ao0e64VSOGYfb8hwJXIZlbqer5HjNMYJWHlLDoHnJyc,9123
42
+ render_machine/render_utils.py,sha256=Jb9NJ73yV1qCfGy9S5A9zcnz0FGVt3MHG8Ofk-9A-aI,9355
42
43
  render_machine/state_machine_config.py,sha256=NnX5sBGkc6cNG8OOQ4PrJeJBCADRGZKJYQXV7ndR4bs,26789
43
44
  render_machine/states.py,sha256=niFXqayyA_rezCrlzo0hYeHAruErcPzQVF-BeIBGnL8,1968
44
45
  render_machine/triggers.py,sha256=VekNhHGR-u5JXIboqc_Eyqf73LNH7wezLK7r3WWtodk,1269
@@ -46,16 +47,16 @@ render_machine/actions/analyze_specification_ambiguity.py,sha256=SykZAozGvr1jySx
46
47
  render_machine/actions/base_action.py,sha256=vVzMp-LanrZyq-jamHyWaQxpRl-pLqtTdtiEzUBhfyE,534
47
48
  render_machine/actions/commit_conformance_tests_changes.py,sha256=08yxCPCpf5TlrzLPsEh5SJXVVvnLfwdNKSr7BlzlYxo,2589
48
49
  render_machine/actions/commit_implementation_code_changes.py,sha256=yi51oAArLCgWcmFbynes-BK2IGnZwA7wygrTJGoL8dY,826
49
- render_machine/actions/create_dist.py,sha256=xMPPyws7swVkaUSQg3dwMGScCi1BuII9y2C_XSP0N80,1067
50
- render_machine/actions/exit_with_error.py,sha256=-LkQsFhmV-Nr741APTx38dqu473b6OSiIvKayFFsXxI,1270
50
+ render_machine/actions/create_dist.py,sha256=ykER7i2OvWAXjrlAZnkQHpiNl00vSMF8WDxNhlWKfsA,1088
51
+ render_machine/actions/exit_with_error.py,sha256=W6TMULHJcHEaXE4WUnlELEu0_l7IZy9HWaPkXyqg7ck,1279
51
52
  render_machine/actions/finish_functional_requirement.py,sha256=usQgPSg6PHpBOR1RhK7lPz9zjY-2VJJDqi3T0nlqdLw,841
52
- render_machine/actions/fix_conformance_test.py,sha256=jtVeL11J8K-a5YvoR3uW6TVytQZkiBgXlDyFR3wXYEs,10022
53
+ render_machine/actions/fix_conformance_test.py,sha256=EZLVjF4A8zSTmyqdgcLoz-2pLpeieq4M_Rb71pY3IKA,10095
53
54
  render_machine/actions/fix_unit_tests.py,sha256=hvwvCJDk_-8fHMpkNFyeBSG1IshIcufDpymF00ulPXY,2301
54
55
  render_machine/actions/prepare_repositories.py,sha256=eE-uU6H_K5IQMraVWSRi4TKk_XPrnA878T_JwtI9cWo,3410
55
56
  render_machine/actions/prepare_testing_environment.py,sha256=kIsXgV6-jOlqp-04cimPgTQoHPrVIu0zW-H_Jj-jowE,2106
56
57
  render_machine/actions/refactor_code.py,sha256=gyPsnqgHseL2bkENu4WeGCRyV0R18_GH3JOrPdoxKvg,2609
57
58
  render_machine/actions/render_conformance_tests.py,sha256=5gOQn-G1T9UWEI8sDfALcVdArk0ln9DgUprOPjccS-U,9268
58
- render_machine/actions/render_functional_requirement.py,sha256=uiEwrMdArRdPX7eWysYMTI5y2pXacUySTpIgL2O-lOI,4602
59
+ render_machine/actions/render_functional_requirement.py,sha256=53KzMVlcefoag1x73roCEQvQ7FbmH8FzhXMQau-s8tg,4655
59
60
  render_machine/actions/run_conformance_tests.py,sha256=4sJTORl6YLFXQIdLMBQHDeJd2hztGyt3tsVvrCviBko,4008
60
61
  render_machine/actions/run_unit_tests.py,sha256=A4WXtSzpCLDHYR8fQAbfxUf_E19GYflZSHZaSreoFPk,2101
61
62
  render_machine/actions/summarize_conformance_tests.py,sha256=Vy1kRK8vyazK_BbsKuz1MLDN_TQWVxxzkwEEm-50byc,1639
@@ -65,34 +66,45 @@ standard_template_library/typescript-react-app-boilerplate.plain,sha256=6LFxhEOz
65
66
  standard_template_library/typescript-react-app-template.plain,sha256=DN5wEJcT8ZqQTf0vFKi9Z1isi9cin2UVNOZb3r28d3M,715
66
67
  tests/__init__.py,sha256=Wk73Io62J15BtlLVIzxmASDWaaJkQLevS4BLK5LDAQg,16
67
68
  tests/conftest.py,sha256=QZcp08htUlJGgmHDlRWFgsWXZ8o8IBWTD5QqJaUMlU8,790
69
+ tests/test_arg_provenance.py,sha256=Vp-ZtNbTlSNOE8iiHOin3YI9sobbN0tRPUIfrYjxttQ,3459
68
70
  tests/test_change_detection.py,sha256=jH8SEhndX400DNqIKmcYLzxHqq9WFdgmuvY3XPq-ZFc,13534
69
- tests/test_cli_output.py,sha256=owDP9pLHdErtPAr1paNKp1o044kuH-HxInHLIBLZQFI,18420
71
+ tests/test_cli_output.py,sha256=iKS1Li4yhoIL0ezJ58o4oEf9V8TiKcQkvQLVtJpdM4c,18420
72
+ tests/test_console_log_styles.py,sha256=63DQWiwWNhe_rMP70bW9DAB6-EPaqU3Wpwyh8eR09pc,4080
70
73
  tests/test_file_utils.py,sha256=R_ZIhrTtwlhHxgmbBtA8PFEU5-QB4Fi5skJ7PS2KzdU,3614
74
+ tests/test_folder_path_resolution.py,sha256=_ZvVFu5m2-AH8iQNn0fCYkA0flVxVwdh1oQiWcPNIxw,6611
71
75
  tests/test_git_utils.py,sha256=h3rWhhs4XVRYqzVzdmj51siF_AYD-GkbZiefK2iGfWo,15614
72
76
  tests/test_imports.py,sha256=1ncbqQhtQW237kIjyV-Ufl7-R-PuduvoYFj2iS-DXW4,2608
77
+ tests/test_log_file_name_resolution.py,sha256=99g9NgPF-BZ98CvNyqDcltBAx4sLBE8gpQPAAieKiPc,3619
78
+ tests/test_logging_config_path_resolution.py,sha256=avz_ehRd18INwIXvAy1AwVzNNUz0vS8eu1nRe0bra8g,2650
73
79
  tests/test_partial_rendering.py,sha256=r0RUUd1pI4-2ZkRrM1j8_kUqv-70hrOGjuKBhZoRmH0,22758
80
+ tests/test_path_resolution.py,sha256=yQpd-Unq1lwNv0wkRPwVs5pDT8JmyyDgbpiTQsyt--Y,2615
74
81
  tests/test_plain2code.py,sha256=DQYFc0JVSkMI8LZCNaQ__2Nph-DcZZW17rprJA54eyw,3851
82
+ tests/test_plain2code_state.py,sha256=cowPZS9oRbv9J49CE4P3Rc47Q5oQOby4bTf2OlnRlBI,1217
83
+ tests/test_plain2code_utils.py,sha256=-W8a7FquMJQ_pTdBoBF1XprzNL2G555QhdtXCy6BDyQ,1237
75
84
  tests/test_plain_modules.py,sha256=S7sd4ZYNotXOgCyZzmBd714QFLih69nlZ3tV8WNtVPo,13766
76
85
  tests/test_plainfile.py,sha256=wZ_PN4GULPWIpl_AK_r_6jkgtX0Ch8EHmN5IhX7huOE,11027
77
- tests/test_plainfileparser.py,sha256=kXWwaGKtYnTDn5yDoYTqumlwW0iSu441wxj3WZVQOP0,22807
86
+ tests/test_plainfileparser.py,sha256=xd9P0KqZJlJPn3RKT510IQbfBotk6nQ9O8p9iLwJiQk,22815
78
87
  tests/test_plainspec.py,sha256=ng55E8ajrH2-gPC1p_QDIe_zXplPvoqHJ-d57UWKFTw,3577
79
88
  tests/test_requires.py,sha256=6tnHYaKudxN-RiQjSFoi9nHe_x0ze3QF-f-j67zAE74,1250
80
89
  tests/test_resolve_config_file.py,sha256=b87tDOvyzbsnE-QRWbZ97ou7NuyaAgskv-dbYBum31c,2830
90
+ tests/test_script_path_resolution.py,sha256=tiEqMevQRXr9YpfHN0xldOOQpLAUgnSqTzeZWkKHaUY,4156
81
91
  tests/test_telemetry.py,sha256=hjm81aRQQZcZ0RB6aVbS3WO46_nIAii_csmbbtnUh74,7110
92
+ tests/test_tui_usage.py,sha256=sfiAGTpRrT3MRr0-8DH3qjuECSxjODVgwzrK_K3iPXs,7959
93
+ tests/test_usage_summary.py,sha256=Ps0kVzvmdZYeXrBR-cqkn3szTV5neYKs1PmmURngeIc,1079
82
94
  tests/e2e/conftest.py,sha256=5Dm67UTOE-M6hMb-mqZvwa6Ek_wmZgsR7fUv9ZEqCwY,4164
83
95
  tests/e2e/test_hello_world_python.py,sha256=PQy4elzB7WvpiWLKa6_HFO0K8HA6_6hGLaAqRi0J3OU,1627
84
96
  tests/e2e/test_hello_world_python_windows.py,sha256=kJQZIuzj8OEEtgBVxnzFvyQJ_wvP2oBu9L-E9geg8sE,2010
85
97
  tui/__init__.py,sha256=_fy7FowY0RkWdn4kC7XV2jgvQPCj-2gE8fNUb-Mny0A,60
86
- tui/components.py,sha256=DoMDhwNVVpv--OY6o8YQYPzOQPG9Dyh9kgMOqUYtNiE,25412
98
+ tui/components.py,sha256=XkC4Aq6VyTnFnBQs8PEHwVdAOczhm0ebKN92kiYMbq0,25791
87
99
  tui/models.py,sha256=KMjlWubmPD41GA-BjgiqkaV9v7tLJ0fuWKBDPmZj4JA,1493
88
- tui/plain2code_tui.py,sha256=t-0DonjrK6_x6jPUMYW0w6tvjZZlk89E-DEor7GzjG4,13785
100
+ tui/plain2code_tui.py,sha256=8F7ZYRpmTiWRdTeQ63LPf2LGrreYjIYXvf_9XNSPyHs,17104
89
101
  tui/plain_module_render_choice_tui.py,sha256=DRl_xhUbUJsyOaCYt_uNDSLUAA3Ty9NYI9zFrVDwp9Y,11270
90
102
  tui/spinner.py,sha256=Ro6Gd9Przf-whuHqPRY6HwI0T57yJjyNPbhDbigZKZE,2471
91
103
  tui/state_handlers.py,sha256=4r_6m5YMcprQtgiTP0De3XFfGAF8VzgaWXHxPweYPbQ,16325
92
- tui/styles.css,sha256=c13WY1nOgmtkT_FH9Ad2PjXrfi8LYwr6zzD6rBOh9tU,6685
93
- tui/widget_helpers.py,sha256=mWDPrALsxLOzJg8KD9MXXafJKRiqG-EIp797jbOT77o,5972
94
- codeplain-0.3.6.dist-info/METADATA,sha256=tX12onr0DJU5GgIbceMo3m8Cys7eptunE_ISO681FqA,6352
95
- codeplain-0.3.6.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
96
- codeplain-0.3.6.dist-info/entry_points.txt,sha256=oDZkBqu9WhtZApb_K6ia8-fn9aojwmAsgnKELceX5T4,46
97
- codeplain-0.3.6.dist-info/licenses/LICENSE,sha256=pCeKgQ1mXE5OmNUuKOfOZh1T1vqkeCUxghE7N8qgnzc,11345
98
- codeplain-0.3.6.dist-info/RECORD,,
104
+ tui/styles.css,sha256=kiC7Og_2G1E7owoiZgV95-3_dSeQnsNj3qzaY04xkDg,6727
105
+ tui/widget_helpers.py,sha256=ToO9MV2bF2Rp-XMNcjIYclnB_qmgO5sLw4UtQ2CyTMg,6652
106
+ codeplain-0.3.8.dist-info/METADATA,sha256=nfj-9PV0EN0MS1W9ILf1GyOnSfcB-YSyEXW04UY9vHQ,6351
107
+ codeplain-0.3.8.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
108
+ codeplain-0.3.8.dist-info/entry_points.txt,sha256=oDZkBqu9WhtZApb_K6ia8-fn9aojwmAsgnKELceX5T4,46
109
+ codeplain-0.3.8.dist-info/licenses/LICENSE,sha256=pCeKgQ1mXE5OmNUuKOfOZh1T1vqkeCUxghE7N8qgnzc,11345
110
+ codeplain-0.3.8.dist-info/RECORD,,
codeplain_REST_api.py CHANGED
@@ -5,6 +5,7 @@ import requests
5
5
  from requests.exceptions import ConnectionError, RequestException, Timeout
6
6
 
7
7
  import plain2code_exceptions
8
+ from plain2code_console import RETRY_COLOR
8
9
  from plain2code_state import RunState
9
10
 
10
11
  MAX_RETRIES = 4
@@ -67,8 +68,11 @@ class CodeplainAPI:
67
68
  connection_error_type = "Network error" if is_connection_error else "Error"
68
69
  if attempt < num_retries:
69
70
  if not silent:
70
- self.console.debug(f"{connection_error_type} on attempt {attempt + 1}/{num_retries + 1}: {error}")
71
- self.console.debug(f"Retrying in {retry_delay} seconds...")
71
+ self.console.debug(
72
+ f"↻ {connection_error_type} on attempt {attempt + 1}/{num_retries + 1}: {error}. "
73
+ f"Retrying in {retry_delay} seconds...",
74
+ color=RETRY_COLOR,
75
+ )
72
76
  time.sleep(retry_delay)
73
77
  # Exponential backoff
74
78
  return retry_delay * 2
file_utils.py CHANGED
@@ -293,7 +293,7 @@ def get_template_directories(plain_file_path, custom_template_dir=None, default_
293
293
  3. Default template directory - for standard/fallback templates
294
294
  """
295
295
  template_dirs = [
296
- os.path.dirname(plain_file_path), # Highest precedence - directory containing plain file
296
+ os.path.dirname(os.path.abspath(plain_file_path)), # Highest precedence - directory containing plain file
297
297
  ]
298
298
 
299
299
  if custom_template_dir:
module_renderer.py CHANGED
@@ -158,13 +158,14 @@ class ModuleRenderer:
158
158
  module.wipe_module()
159
159
 
160
160
  self.loaded_modules = list[PlainModule]()
161
+
161
162
  _, rendering_failed = self._render_module(self.plain_module, self.render_range, True)
163
+
162
164
  if not rendering_failed:
163
- # Get the last module that completed rendering
164
165
  if self.args.copy_build:
165
166
  rendered_code_path = f"{self.args.build_dest}/"
166
167
  else:
167
- rendered_code_path = f"{os.path.join(self.args.build_folder, self.plain_module.module_name)}/"
168
+ rendered_code_path = self.args.build_folder
168
169
 
169
170
  self.run_state.set_render_generated_code_path(rendered_code_path)
170
171
  self.event_bus.publish(RenderCompleted(rendered_code_path=rendered_code_path))
path_resolution.py ADDED
@@ -0,0 +1,64 @@
1
+ """Path resolution for CLI / config / default path arguments.
2
+
3
+ The rule, in one sentence: the resolution base for a relative path is determined
4
+ by *where the value was written*, not by which option key it sets.
5
+
6
+ - Values supplied on the command line resolve against the current working
7
+ directory (so they match what shell tab completion just produced).
8
+ - Values read from ``config.yaml`` resolve against the directory containing
9
+ that config file.
10
+ - Values left at their default (not supplied anywhere) resolve against the
11
+ directory containing the spec file.
12
+
13
+ Paths are expanded for ``~`` and returned as absolute paths. Canonicalization
14
+ (symlink resolution) is left to the caller, to be done just before I/O that
15
+ needs it (writing output, executing scripts) so we don't accidentally
16
+ dereference links the user wanted preserved.
17
+ """
18
+
19
+ import os
20
+ from typing import Literal, Optional
21
+
22
+ PathSource = Literal["cli", "config", "default"]
23
+
24
+
25
+ def resolve_path(
26
+ value: str,
27
+ source: PathSource,
28
+ *,
29
+ cwd: str,
30
+ config_dir: Optional[str] = None,
31
+ spec_dir: str,
32
+ ) -> str:
33
+ """Resolve *value* to an absolute path using the base anchor for *source*.
34
+
35
+ Args:
36
+ value: The path string as written by the user. May be absolute, relative,
37
+ or contain a leading ``~``.
38
+ source: Where the value came from -- ``"cli"``, ``"config"``, or
39
+ ``"default"``.
40
+ cwd: Base directory for ``"cli"`` values.
41
+ config_dir: Base directory for ``"config"`` values. Must be provided
42
+ whenever ``source == "config"``.
43
+ spec_dir: Base directory for ``"default"`` values.
44
+
45
+ Returns:
46
+ Absolute path with ``~`` expanded. Symlinks are not resolved.
47
+ """
48
+ expanded = os.path.expanduser(value)
49
+
50
+ if os.path.isabs(expanded):
51
+ return os.path.normpath(expanded)
52
+
53
+ if source == "cli":
54
+ base = cwd
55
+ elif source == "config":
56
+ if config_dir is None:
57
+ raise ValueError("config_dir must be provided when source == 'config'")
58
+ base = config_dir
59
+ elif source == "default":
60
+ base = spec_dir
61
+ else:
62
+ raise ValueError(f"Unknown path source: {source!r}")
63
+
64
+ return os.path.normpath(os.path.join(base, expanded))
plain2code.py CHANGED
@@ -48,7 +48,6 @@ from plain2code_logger import (
48
48
  IndentedFormatter,
49
49
  LoggingHandler,
50
50
  dump_crash_logs,
51
- get_log_file_path,
52
51
  )
53
52
  from plain2code_state import RunState
54
53
  from plain2code_telemetry import capture_crash, initialize_telemetry
@@ -89,8 +88,7 @@ def setup_logging(
89
88
  event_bus: EventBus,
90
89
  run_state: RunState,
91
90
  log_to_file: bool,
92
- log_file_name: str,
93
- plain_file_path: Optional[str],
91
+ log_file_path: str,
94
92
  headless: bool = False,
95
93
  ):
96
94
  default_level = logging.DEBUG if args.verbose else logging.INFO
@@ -101,10 +99,9 @@ def setup_logging(
101
99
  logging.getLogger("transitions").setLevel(logging.ERROR)
102
100
  logging.getLogger("transitions.extensions.diagrams").setLevel(logging.ERROR)
103
101
 
104
- log_file_path = get_log_file_path(plain_file_path, log_file_name)
105
-
106
102
  # Try to load logging configuration from YAML file (takes precedence over --verbose)
107
103
  config_loaded = False
104
+
108
105
  if args.logging_config_path and os.path.exists(args.logging_config_path):
109
106
  try:
110
107
  with open(args.logging_config_path, "r") as f:
@@ -127,7 +124,7 @@ def setup_logging(
127
124
  handler.setFormatter(formatter)
128
125
  root_logger.addHandler(handler)
129
126
 
130
- if log_to_file and log_file_path:
127
+ if log_to_file:
131
128
  try:
132
129
  file_handler = logging.FileHandler(log_file_path, mode="w", encoding="utf-8")
133
130
  file_handler.setFormatter(file_formatter)
@@ -289,6 +286,7 @@ def render( # noqa: C901
289
286
  render_thread = threading.Thread(target=run_render, daemon=True)
290
287
  app = Plain2CodeTUI(
291
288
  event_bus=event_bus,
289
+ run_state=run_state,
292
290
  on_ready=render_thread.start,
293
291
  render_id=run_state.render_id,
294
292
  unittests_script=args.unittests_script,
@@ -356,7 +354,7 @@ def main(): # noqa: C901
356
354
  # Parse the plain file (and its required modules) once; reused by dry-run and rendering.
357
355
  try:
358
356
  plain_module = plain_modules.PlainModule(
359
- args.filename,
357
+ os.path.basename(args.filename),
360
358
  args.build_folder,
361
359
  args.conformance_tests_folder,
362
360
  template_dirs,
@@ -383,9 +381,7 @@ def main(): # noqa: C901
383
381
  # Suppress Rich console output.
384
382
  console.quiet = True
385
383
 
386
- default_log_level = setup_logging(
387
- args, event_bus, run_state, args.log_to_file, args.log_file_name, args.filename, args.headless
388
- )
384
+ default_log_level = setup_logging(args, event_bus, run_state, args.log_to_file, args.log_file_name, args.headless)
389
385
 
390
386
  initialize_telemetry()
391
387