codeplain 0.2.20__py3-none-any.whl → 0.3.0__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. _version.py +1 -1
  2. {codeplain-0.2.20.dist-info → codeplain-0.3.0.dist-info}/METADATA +5 -5
  3. {codeplain-0.2.20.dist-info → codeplain-0.3.0.dist-info}/RECORD +45 -40
  4. codeplain_REST_api.py +30 -10
  5. concept_utils.py +1 -1
  6. file_utils.py +10 -9
  7. git_utils.py +70 -2
  8. module_renderer.py +62 -168
  9. partial_rendering.py +224 -0
  10. plain2code.py +98 -139
  11. plain2code_arguments.py +6 -4
  12. plain2code_events.py +6 -1
  13. plain2code_exceptions.py +4 -0
  14. plain2code_logger.py +8 -4
  15. plain2code_nodes.py +14 -3
  16. plain_file.py +39 -25
  17. plain_modules.py +256 -44
  18. plain_spec.py +53 -1
  19. render_machine/actions/analyze_specification_ambiguity.py +1 -1
  20. render_machine/actions/fix_conformance_test.py +56 -27
  21. render_machine/actions/fix_unit_tests.py +13 -14
  22. render_machine/actions/prepare_repositories.py +14 -3
  23. render_machine/actions/refactor_code.py +8 -10
  24. render_machine/actions/render_conformance_tests.py +76 -59
  25. render_machine/actions/summarize_conformance_tests.py +9 -12
  26. render_machine/code_renderer.py +20 -7
  27. render_machine/conformance_tests.py +1 -1
  28. render_machine/render_context.py +249 -69
  29. render_machine/render_types.py +57 -9
  30. render_machine/render_utils.py +6 -3
  31. tests/test_file_utils.py +41 -0
  32. tests/test_git_utils.py +74 -0
  33. tests/test_partial_rendering.py +316 -0
  34. tests/test_plain_modules.py +294 -0
  35. tests/test_plainfile.py +1 -1
  36. tests/test_plainfileparser.py +2 -2
  37. tui/components.py +73 -11
  38. tui/plain2code_tui.py +21 -30
  39. tui/plain_module_render_choice_tui.py +186 -0
  40. tui/state_handlers.py +9 -4
  41. tui/styles.css +42 -3
  42. tui/widget_helpers.py +24 -1
  43. {codeplain-0.2.20.dist-info → codeplain-0.3.0.dist-info}/WHEEL +0 -0
  44. {codeplain-0.2.20.dist-info → codeplain-0.3.0.dist-info}/entry_points.txt +0 -0
  45. {codeplain-0.2.20.dist-info → codeplain-0.3.0.dist-info}/licenses/LICENSE +0 -0
_version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.2.20"
1
+ __version__ = "0.3.0"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codeplain
3
- Version: 0.2.20
3
+ Version: 0.3.0
4
4
  Summary: Transform plain language specifications into working code
5
5
  License-File: LICENSE
6
6
  Classifier: Environment :: Console
@@ -8,13 +8,13 @@ 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.42
11
+ Requires-Dist: gitpython==3.1.50
12
12
  Requires-Dist: mistletoe==1.3.0
13
13
  Requires-Dist: networkx==3.6.1
14
14
  Requires-Dist: python-frontmatter==1.1.0
15
15
  Requires-Dist: python-liquid2==0.3.0
16
- Requires-Dist: pyyaml==6.0.2
17
- Requires-Dist: requests==2.32.4
16
+ Requires-Dist: pyyaml==6.0.3
17
+ Requires-Dist: requests==2.34.0
18
18
  Requires-Dist: rich==14.2.0
19
19
  Requires-Dist: textual>=7.5.0
20
20
  Requires-Dist: tiktoken==0.12.0
@@ -115,7 +115,7 @@ After completing the installation steps above, you can immediately test the syst
115
115
 
116
116
  ### Documentation
117
117
 
118
- - For more details on the ***plain format, see the [***plain language specification](https://www.plainlang.org/docs/intro/).
118
+ - For more details on the ***plain format, see the [***plain language specification](https://www.plainlang.org/docs/).
119
119
  - 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
120
  - For complete CLI documentation and usage examples, see [plain2code CLI documentation](docs/plain2code_cli.md).
121
121
 
@@ -1,59 +1,60 @@
1
- _version.py,sha256=PP1XN3LVw4efHZSwpFDbQRezvRqPg1JqZ--H2vUombw,23
2
- codeplain_REST_api.py,sha256=s7rgQIgpuwR3_CEpzXJfIHuAokhGDkFgaKMKVVYm-KY,19812
3
- concept_utils.py,sha256=vwSY4-FmxyqaDBxhntYzqG8t9pXvAWwiULP0DYQI32o,7905
1
+ _version.py,sha256=VrXpHDu3erkzwl_WXrqINBm9xWkcyUy53IQOj042dOs,22
2
+ codeplain_REST_api.py,sha256=Z1WV67_Xtw2li1wQyVnhrqr-Ymu_BWp9AdB4YjImIhw,21258
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
- file_utils.py,sha256=YW2UvO6i-Jv5FNyreI6fNfhbAYDdypgLiGJuPPdrEoc,12474
7
- git_utils.py,sha256=3tW4B9Pr-HAILs0SjMaaLic0Mq_G1R4HdvKRnKf6uj4,14349
6
+ file_utils.py,sha256=eO_SHwyjJlkwT3wTlIjIEQ3t8y3k0i-fWKAn5_1SMkY,12597
7
+ git_utils.py,sha256=LQQnL5DAFpH5fDBQgUZX2WfjWY4YC5OEGBPIXryTmnY,16896
8
8
  hash_key.py,sha256=lMKgKpOhPeC4UpFf9e7eCNK20FEaDsXTCJ8j3eCWidE,837
9
9
  memory_management.py,sha256=G42roiUSgX8sqDnkfb7XYtYWZqlOQ3DIpMbi0pcOKZA,6079
10
- module_renderer.py,sha256=AQKFwggggpugd77jQylI5gSJyLAiM5r035NVDEidbvQ,12582
11
- plain2code.py,sha256=NwJ0BnbLipFn-wvxpo3S5m-SLeYqOLiGzIlS1bQhOYw,15306
12
- plain2code_arguments.py,sha256=ILunswU492aDifybU-CDi85vcb01IudupfAX4nwc5EM,14420
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
13
14
  plain2code_console.py,sha256=dSkuwUHS0ufUy24y8sloL3uedDDdXPuFDkBaanp1Cug,4820
14
- plain2code_events.py,sha256=Q6HyXpsARXOOxgcTYw3yQbIu8exHwnLlJTMAf2W_0-o,1395
15
- plain2code_exceptions.py,sha256=t5bq1ZKGeeezjg52NRkna-ohO57L1DXkVnz1PWshgyI,1576
16
- plain2code_logger.py,sha256=Oq0cYSnUz80FgUp5MTWDla7lk2j-6U3p0jVc7eNh7fM,3063
17
- plain2code_nodes.py,sha256=Om624mfb5MB1Z09c0Zqtb9G1gGkUY9x-v2hzAf3A2gw,3818
15
+ plain2code_events.py,sha256=AM8KJS68uiVXRMLt861KaGhs2H4yzujlMB_xiYPfHQc,1478
16
+ plain2code_exceptions.py,sha256=g3wMOnjpQlk2pdKRvVarnuh9BlOsRV2AKm_CoNidmH4,1629
17
+ plain2code_logger.py,sha256=j376mlCTU-EN_mv06rRY2Rp_47CHzEo6b5wvhlmp0fY,3217
18
+ plain2code_nodes.py,sha256=IwjQqJxRSJKgP6eEnkYGy3VQHIyoazgkFxAEkxkjfJ8,4426
18
19
  plain2code_read_config.py,sha256=D5CxnmTRjddupecWvstcUm0kIJWWzq5-Zmk6sU0wbIg,2235
19
20
  plain2code_state.py,sha256=UN3iD3rfJkGK4QQx4RxueQrCy9ngHqExtzxiSEgrW2E,2117
20
21
  plain2code_utils.py,sha256=CLvysMiwChAVaKdqJjkUUlDCQ8dGIFPz1NG7cLbFZuI,2236
21
- plain_file.py,sha256=cZbr8hRRRgLgnB4Vym0-qi1_-vgG6aPKeBgxLOiPXzA,28392
22
- plain_modules.py,sha256=6gteJeiK-WvUZDrIt_kJK0MFiptq63rEVpYvV5yYFXc,4881
23
- plain_spec.py,sha256=BlCKCadkW08mNoWBJm0Ygk22o_bH_qDJVSchiFiUIGw,14051
22
+ plain_file.py,sha256=hiyFdB548sYG9T2NILhGM63dcSgbRzy_y5atX5YabUg,29172
23
+ plain_modules.py,sha256=pC-cFFRmVNfloscqS7uPjaPF0Dw2NFn9fIQNUP2j0ys,14333
24
+ plain_spec.py,sha256=E2Yp2eXMExE7kx7GPQ7v9boHN8XhFUcqprn1DGMaFMs,15669
24
25
  system_config.py,sha256=DMyfiq_uzxWH_uXz5vMWNHkcRFPkIA-M8nr64SeVhPo,1350
25
26
  config/__init__.py,sha256=beYSsJWmBNHDP5rYmVDouqgEeP3t1lkkepbXJ-oq0F8,37
26
27
  config/system_config.yaml,sha256=of5R9vukSohU9AlkGJOcAVQA1UJgxYmDy6islU6iFuE,508
27
28
  docs/generate_cli.py,sha256=0FHVhICbM8g7ahdInTNih3hNKpty1wbV5rdGECMJj48,656
28
29
  examples/example_hello_world_python/harness_tests/hello_world_display/test_hello_world.py,sha256=dwTowrHiVKKbrDv21v8xJC30Q57AXZkQasdGOO5JsBE,470
29
30
  render_machine/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
- render_machine/code_renderer.py,sha256=86Hnq1bUpk24oUWWPkwWyNWxx_FB-FOj79cwSrdmMpc,3357
31
+ render_machine/code_renderer.py,sha256=TEHdlEOB-seMGeEo1rg5kaIL3pkub7CnU2X8eZNc8G4,3762
31
32
  render_machine/conformance_test_helpers.py,sha256=6Ru7Dh24SLNIKWfz_sP5hE_EmWpvk6nfgN1T1yaCZus,2887
32
- render_machine/conformance_tests.py,sha256=SHDGMDw--fO4mglEEG5sGecc-uFbG-XNZl017idi-ms,7222
33
+ render_machine/conformance_tests.py,sha256=y7dhmsFgq7LpaeuLDM5nc1_1HoPo0wkPd3JdaRPxA0Y,7229
33
34
  render_machine/implementation_code_helpers.py,sha256=LdHNLoKgVtrDaGH2APX8C9vjNjLBUhu2V7OAaJvm4rM,2009
34
- render_machine/render_context.py,sha256=hwkXlcfVocVgWIaVtx3v8Zq4X2vRLs8LJAUW2M7-Pw8,21876
35
- render_machine/render_types.py,sha256=9buUCyiM1Y8yth_c5AaalSNHfYOs-sOq0_ctbsJHYuw,7505
36
- render_machine/render_utils.py,sha256=uTZkThRQXHd9zZ3um2LA1bzlNKES0QUC4vDSJCYTIKk,9371
35
+ render_machine/render_context.py,sha256=gu1_TjWjiHVlpx3qlDLg0PYI58rYX8YY7xFCoBahems,29133
36
+ render_machine/render_types.py,sha256=iqRd9MyrRnXasoA8rvssZbCN66BbVCVXH5uFA8QJVXY,9222
37
+ render_machine/render_utils.py,sha256=LIeFJPCj4uPLhyi-LVOr_oNXEKWvE86qpwnPk9u5Vgg,9451
37
38
  render_machine/state_machine_config.py,sha256=1nVddQBnJsbdUYCNK813QnLZH2D4krz0vqHe2K8LDS8,28212
38
39
  render_machine/states.py,sha256=niFXqayyA_rezCrlzo0hYeHAruErcPzQVF-BeIBGnL8,1968
39
40
  render_machine/triggers.py,sha256=o1N5c9xC7fsKY6ZaWcWBtALT8i1acSSharAXKAqBu8E,1285
40
- render_machine/actions/analyze_specification_ambiguity.py,sha256=ND2uwF0yREdzg0OaEQtZIk8hoWOzq8KKS7WIBgEzDfM,2824
41
+ render_machine/actions/analyze_specification_ambiguity.py,sha256=SykZAozGvr1jySxbpKCgt11nMpZhOK0sN-J66M42T5Y,2847
41
42
  render_machine/actions/base_action.py,sha256=vVzMp-LanrZyq-jamHyWaQxpRl-pLqtTdtiEzUBhfyE,534
42
43
  render_machine/actions/commit_conformance_tests_changes.py,sha256=08yxCPCpf5TlrzLPsEh5SJXVVvnLfwdNKSr7BlzlYxo,2589
43
44
  render_machine/actions/commit_implementation_code_changes.py,sha256=yi51oAArLCgWcmFbynes-BK2IGnZwA7wygrTJGoL8dY,826
44
45
  render_machine/actions/create_dist.py,sha256=xMPPyws7swVkaUSQg3dwMGScCi1BuII9y2C_XSP0N80,1067
45
46
  render_machine/actions/exit_with_error.py,sha256=-LkQsFhmV-Nr741APTx38dqu473b6OSiIvKayFFsXxI,1270
46
47
  render_machine/actions/finish_functional_requirement.py,sha256=tj9CzuxuHcbR-2-jMLcAM0gOAcbWpMLg9HIiyngI6gU,739
47
- render_machine/actions/fix_conformance_test.py,sha256=bG4PSvm2Cg9d_8ERMIVtJ7KXrt7EJuoFg7K_x4Cs9Kg,7498
48
- render_machine/actions/fix_unit_tests.py,sha256=8cx7SbxoODNxNcFkD4jci0w-WDBzDviGx91firgRELg,2555
49
- render_machine/actions/prepare_repositories.py,sha256=u6SjvMIb-1G4LBVaDuzHUEF1OJ7GAiHySvrdap7SXCg,3163
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
50
51
  render_machine/actions/prepare_testing_environment.py,sha256=Rvj0LNuQ6anP-3fBr2FmDurt7XrPM-GUREqz3c87jTg,1950
51
- render_machine/actions/refactor_code.py,sha256=0zAmG_UJqU7Ye4y-F4ZU39djq5ujG44a-heM30G2Z08,2161
52
- render_machine/actions/render_conformance_tests.py,sha256=OB-_eqo8w6xzRwvUHQA8uLF4BZs7TqUjip-BIT1re8A,8882
52
+ render_machine/actions/refactor_code.py,sha256=slDnx37RLDP4ACiIv0b3u6JTsYPyfyLY-2oXGUM8ko4,1970
53
+ render_machine/actions/render_conformance_tests.py,sha256=J2p_urgayUNtGlmdZ8NWuE1nvDgOQHpir2ESHcAfD6U,9587
53
54
  render_machine/actions/render_functional_requirement.py,sha256=tr0SqG25v3PwUCVgrx4eB8-Xh6ibab0H5IK3nMmULlg,3955
54
55
  render_machine/actions/run_conformance_tests.py,sha256=WtMSFTYfWFR6bBh3rZ7n5oc7jDDsxnTEdRi-kN1cj4o,4075
55
56
  render_machine/actions/run_unit_tests.py,sha256=zCu3yJbJ0JkiB7lZbgBwsVs_qiFtrlyN2T3xf5A_gPg,2176
56
- render_machine/actions/summarize_conformance_tests.py,sha256=CpXhvtWSUKEj1cjtRycKm6VbXHkkIZRgCzpCge4Fifw,1849
57
+ render_machine/actions/summarize_conformance_tests.py,sha256=Vy1kRK8vyazK_BbsKuz1MLDN_TQWVxxzkwEEm-50byc,1639
57
58
  standard_template_library/__init__.py,sha256=LtsHl0c3VV46DUInIqyOw4KKVoxKEMOx8dAZpMXtAXc,43
58
59
  standard_template_library/golang-console-app-template.plain,sha256=JOusEjCj7jagnXUzaI-SdIr3hj1wTo2uShK017aNtVM,949
59
60
  standard_template_library/python-console-app-template.plain,sha256=HW-REeF8Crk0r_w4-MxnleVHeW6PdOB2C8gO8FDG61g,931
@@ -61,23 +62,27 @@ standard_template_library/typescript-react-app-boilerplate.plain,sha256=6LFxhEOz
61
62
  standard_template_library/typescript-react-app-template.plain,sha256=DN5wEJcT8ZqQTf0vFKi9Z1isi9cin2UVNOZb3r28d3M,715
62
63
  tests/__init__.py,sha256=Wk73Io62J15BtlLVIzxmASDWaaJkQLevS4BLK5LDAQg,16
63
64
  tests/conftest.py,sha256=QZcp08htUlJGgmHDlRWFgsWXZ8o8IBWTD5QqJaUMlU8,790
64
- tests/test_git_utils.py,sha256=MMHEY92LLKkzCldHAqR5A9CyHDCQgl_-IVReLyDg050,12957
65
+ tests/test_file_utils.py,sha256=-O5zImiFVz9JJ4xi8TbsUwNgPFQoQUOl9Frls4BHCBI,1358
66
+ tests/test_git_utils.py,sha256=h3rWhhs4XVRYqzVzdmj51siF_AYD-GkbZiefK2iGfWo,15614
65
67
  tests/test_imports.py,sha256=EWYLUxGunUUdsG9iCiY_tts6T7DIVdhdB_MZlB1LsmU,2081
66
- tests/test_plainfile.py,sha256=fXuZhk2It-EwrWXuBaWuUdH7HZAOfzAhYwmpJ0ErIro,11007
67
- tests/test_plainfileparser.py,sha256=lIdAHCZltFB604ECE7OKvQfd3Hfagzy0KMEipUnnzsY,18654
68
+ tests/test_partial_rendering.py,sha256=4xXZhy9yStn0awSw8B-yrJ3kVYyzJrlR-dKwg9ODXMY,11188
69
+ tests/test_plain_modules.py,sha256=9bWsketDnjpCWkCbMZzYvLqT1ojPhLyxxKljPUq42oY,11393
70
+ tests/test_plainfile.py,sha256=wZ_PN4GULPWIpl_AK_r_6jkgtX0Ch8EHmN5IhX7huOE,11027
71
+ tests/test_plainfileparser.py,sha256=iVTAJWxQZbv96uHXe-qvGSgsC9uJk5Tu10Nod3kqflI,18666
68
72
  tests/test_plainspec.py,sha256=JrzjJ4VFYDmWTZDq-8WFyt2oMx76IEuO1CAUPQeGOsw,2404
69
73
  tests/test_requires.py,sha256=6tnHYaKudxN-RiQjSFoi9nHe_x0ze3QF-f-j67zAE74,1250
70
74
  tests/test_resolve_config_file.py,sha256=b87tDOvyzbsnE-QRWbZ97ou7NuyaAgskv-dbYBum31c,2830
71
75
  tui/__init__.py,sha256=_fy7FowY0RkWdn4kC7XV2jgvQPCj-2gE8fNUb-Mny0A,60
72
- tui/components.py,sha256=Zj7Em_8Z06hjfhSB3RuaQHhILBkekRKyuLQ406PeTkc,23915
76
+ tui/components.py,sha256=G6ktJx211WrYmj-1aZL3mFtnBTOE4WRF3hA1LG8J0jU,25810
73
77
  tui/models.py,sha256=KMjlWubmPD41GA-BjgiqkaV9v7tLJ0fuWKBDPmZj4JA,1493
74
- tui/plain2code_tui.py,sha256=NeiuB4juX2sNKcAuv4JNIbgn8WPLWg9WbLCTX-jacZQ,13496
78
+ tui/plain2code_tui.py,sha256=_c8gaEZeFPvr1-uqpQAsacpHd77Ig16x045Q4ihfwuc,13076
79
+ tui/plain_module_render_choice_tui.py,sha256=ZiB7E2mgFN9v0WHZeDfhLvfYURg1IPcQpC5dvHQwzqA,8499
75
80
  tui/spinner.py,sha256=Ro6Gd9Przf-whuHqPRY6HwI0T57yJjyNPbhDbigZKZE,2471
76
- tui/state_handlers.py,sha256=NCAu-Pv2Bc8HG5GHaV47kwZHtbCOh_mBYQAW7DbN0jg,15927
77
- tui/styles.css,sha256=XrGdtAjyscJL3Rey2gT0rbuRnMnXgePGiAqySKlcezs,6133
78
- tui/widget_helpers.py,sha256=hQKlURgdSC8RT6QgKPzRhSRAIO1bMoLuM3lz_sCc3FU,5819
79
- codeplain-0.2.20.dist-info/METADATA,sha256=iW5pYQEWz1D_i9OSv7g6q8uGIfThMF5kD_ygxFmWzqA,4210
80
- codeplain-0.2.20.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
81
- codeplain-0.2.20.dist-info/entry_points.txt,sha256=oDZkBqu9WhtZApb_K6ia8-fn9aojwmAsgnKELceX5T4,46
82
- codeplain-0.2.20.dist-info/licenses/LICENSE,sha256=pCeKgQ1mXE5OmNUuKOfOZh1T1vqkeCUxghE7N8qgnzc,11345
83
- codeplain-0.2.20.dist-info/RECORD,,
81
+ tui/state_handlers.py,sha256=4r_6m5YMcprQtgiTP0De3XFfGAF8VzgaWXHxPweYPbQ,16325
82
+ tui/styles.css,sha256=9vI6V7n7p76i2Dn62KDyOkRYp__d3v2bCzYxO9PWnm8,6685
83
+ 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,,
codeplain_REST_api.py CHANGED
@@ -2,7 +2,7 @@ import time
2
2
  from typing import Optional
3
3
 
4
4
  import requests
5
- from requests.exceptions import ConnectionError, Timeout
5
+ from requests.exceptions import ConnectionError, RequestException, Timeout
6
6
 
7
7
  import plain2code_exceptions
8
8
  from plain2code_state import RunState
@@ -18,7 +18,7 @@ RETRY_ERROR_CODES = [
18
18
  ERROR_CODE_EXCEPTIONS = {
19
19
  "FunctionalRequirementTooComplex": plain2code_exceptions.FunctionalRequirementTooComplex,
20
20
  "ConflictingRequirements": plain2code_exceptions.ConflictingRequirements,
21
- "CreditBalanceTooLow": plain2code_exceptions.RenderingCreditBalanceTooLow,
21
+ "RenderingCreditBalanceTooLow": plain2code_exceptions.RenderingCreditBalanceTooLow,
22
22
  "LLMInternalError": plain2code_exceptions.LLMInternalError,
23
23
  "MissingResource": plain2code_exceptions.MissingResource,
24
24
  "PlainSyntaxError": plain2code_exceptions.PlainSyntaxError,
@@ -76,9 +76,12 @@ class CodeplainAPI:
76
76
  if not silent:
77
77
  self.console.error(f"Max retries ({num_retries}) exceeded. Last error: {error}")
78
78
  if is_connection_error:
79
- raise plain2code_exceptions.NetworkConnectionError("Failed to connect to API server.")
80
- else:
81
- raise error
79
+ raise plain2code_exceptions.NetworkConnectionError(
80
+ "Connection error: Failed to connect to API server.\n\nPlease check that your internet connection is working."
81
+ )
82
+ if isinstance(error, RequestException):
83
+ raise Exception(f"Error rendering plain code: {error}\n") from error
84
+ raise error
82
85
 
83
86
  def _raise_for_error_code(self, response_json):
84
87
  """Raise appropriate exception based on error code in response."""
@@ -86,13 +89,28 @@ class CodeplainAPI:
86
89
  if error_code not in ERROR_CODE_EXCEPTIONS:
87
90
  return
88
91
 
89
- exception_class = ERROR_CODE_EXCEPTIONS[error_code]
90
92
  message = response_json.get("message", "")
91
93
 
92
- # FunctionalRequirementTooComplex has an extra parameter
93
94
  if error_code == "FunctionalRequirementTooComplex":
94
- raise exception_class(message, response_json.get("proposed_breakdown"))
95
-
95
+ raise plain2code_exceptions.FunctionalRequirementTooComplex(
96
+ message, response_json.get("proposed_breakdown")
97
+ )
98
+ if error_code == "MissingResource":
99
+ raise plain2code_exceptions.MissingResource(f"Missing resource: {message}\n")
100
+ if error_code == "ConflictingRequirements":
101
+ raise plain2code_exceptions.ConflictingRequirements(f"Conflicting requirements: {message}\n")
102
+ if error_code == "RenderingCreditBalanceTooLow":
103
+ raise plain2code_exceptions.RenderingCreditBalanceTooLow(message)
104
+ if error_code == "LLMInternalError":
105
+ raise plain2code_exceptions.LLMInternalError(f"LLM internal error: {message}\n")
106
+ if error_code == "PlainSyntaxError":
107
+ raise plain2code_exceptions.plain_syntax_error(message or "Unknown error")
108
+ if error_code == "InternalServerError":
109
+ raise plain2code_exceptions.InternalServerError(
110
+ "Internal server error.\n\n"
111
+ f"Please report the error to support@codeplain.ai with the attached {self.log_file_name} file."
112
+ )
113
+ exception_class = ERROR_CODE_EXCEPTIONS[error_code]
96
114
  raise exception_class(message)
97
115
 
98
116
  def post_request(
@@ -118,7 +136,7 @@ class CodeplainAPI:
118
136
  response_json = response.json()
119
137
  except requests.exceptions.JSONDecodeError as e:
120
138
  self.console.debug(f"Failed to decode JSON response: {e}. Response text: {response.text}")
121
- raise
139
+ raise Exception(f"Error rendering plain code: Failed to decode API response ({e}).\n") from e
122
140
 
123
141
  if response.status_code == requests.codes.bad_request and "error_code" in response_json:
124
142
  self._raise_for_error_code(response_json)
@@ -350,6 +368,7 @@ class CodeplainAPI:
350
368
  implementation_fix_count,
351
369
  conformance_tests_folder_name,
352
370
  current_testing_frid_high_level_implementation_plan: Optional[str],
371
+ conflicting_requirements_count: int,
353
372
  run_state: RunState,
354
373
  ):
355
374
  endpoint_url = f"{self.api_url}/fix_conformance_tests_issue"
@@ -371,6 +390,7 @@ class CodeplainAPI:
371
390
  "implementation_fix_count": implementation_fix_count,
372
391
  "conformance_tests_folder_name": conformance_tests_folder_name,
373
392
  "current_testing_frid_high_level_implementation_plan": current_testing_frid_high_level_implementation_plan,
393
+ "conflicting_requirements_count": conflicting_requirements_count,
374
394
  }
375
395
 
376
396
  if acceptance_tests is not None:
concept_utils.py CHANGED
@@ -206,7 +206,7 @@ def sort_definitions(definitions: list[dict]) -> list[dict]:
206
206
  msg += "\n".join(cyclic_definitions)
207
207
  msg += "\n"
208
208
 
209
- raise PlainSyntaxError(msg)
209
+ raise PlainSyntaxError(f"Plain syntax error: {msg}")
210
210
 
211
211
  order = list(nx.topological_sort(concept_graph))
212
212
  if len(order) > 0:
file_utils.py CHANGED
@@ -8,6 +8,7 @@ from liquid2.exceptions import UndefinedError
8
8
 
9
9
  import plain_spec
10
10
  from plain2code_console import console
11
+ from plain2code_exceptions import UnsupportedResourceType
11
12
  from plain2code_nodes import Plain2CodeIncludeTag, Plain2CodeLoaderMixin
12
13
  from plain_modules import CODEPLAIN_MEMORY_SUBFOLDER, CODEPLAIN_METADATA_FOLDER
13
14
 
@@ -217,18 +218,12 @@ def open_from(dirs, file_name):
217
218
 
218
219
  with open(full_file_name, "rb") as f:
219
220
  content = f.read()
220
- try:
221
- content_text = content.decode("utf-8")
222
- except UnicodeDecodeError:
223
- console.debug(
224
- f"WARNING! Error loading {file_name} ({file_name}). File is not a text file. Skipping it."
225
- )
226
- return content_text
221
+ return content.decode("utf-8")
227
222
 
228
223
  return None
229
224
 
230
225
 
231
- def load_linked_resources(template_dirs: list[str], resources_list):
226
+ def load_linked_resources(template_dirs: list[str], resources_list, module_name: str):
232
227
  linked_resources = {}
233
228
 
234
229
  for resource in resources_list:
@@ -236,7 +231,13 @@ def load_linked_resources(template_dirs: list[str], resources_list):
236
231
  if file_name in linked_resources:
237
232
  continue
238
233
 
239
- content = open_from(template_dirs, file_name)
234
+ try:
235
+ content = open_from(template_dirs, file_name)
236
+ except UnicodeDecodeError:
237
+ raise UnsupportedResourceType(
238
+ f"Referenced resource '{file_name}' in module '{module_name}' is a binary file. "
239
+ f"Only text files (e.g. .md, .txt, .json, .yaml) can be referenced from a .plain file."
240
+ )
240
241
 
241
242
  if content is None:
242
243
  raise FileNotFoundError(f"""
git_utils.py CHANGED
@@ -1,4 +1,5 @@
1
1
  import os
2
+ import re
2
3
  from configparser import NoOptionError, NoSectionError
3
4
  from typing import Optional, Union
4
5
 
@@ -60,7 +61,10 @@ def _ensure_git_config(repo: Repo) -> None:
60
61
 
61
62
 
62
63
  def init_git_repo(
63
- path_to_repo: Union[str, os.PathLike], module_name: Optional[str] = None, render_id: Optional[str] = None
64
+ path_to_repo: Union[str, os.PathLike],
65
+ module_name: Optional[str] = None,
66
+ render_id: Optional[str] = None,
67
+ initial_files: Optional[dict[str, str]] = None,
64
68
  ) -> Repo:
65
69
  """
66
70
  Initializes a new git repository in the given path.
@@ -74,6 +78,11 @@ def init_git_repo(
74
78
 
75
79
  repo = Repo.init(path_to_repo)
76
80
  _ensure_git_config(repo)
81
+
82
+ if initial_files:
83
+ file_utils.store_response_files(path_to_repo, initial_files, [])
84
+ repo.git.add(".")
85
+
77
86
  repo.git.commit(
78
87
  "--allow-empty", "-m", _get_full_commit_message(INITIAL_COMMIT_MESSAGE, module_name, None, render_id)
79
88
  )
@@ -82,9 +91,18 @@ def init_git_repo(
82
91
 
83
92
 
84
93
  def clone_repo(
85
- source_repo_path: str, new_repo_path: str, module_name: Optional[str] = None, render_id: Optional[str] = None
94
+ source_repo_path: str,
95
+ new_repo_path: str,
96
+ module_name: Optional[str] = None,
97
+ render_id: Optional[str] = None,
98
+ initial_files: Optional[dict[str, str]] = None,
86
99
  ) -> Repo:
87
100
  repo = Repo.clone_from(source_repo_path, new_repo_path)
101
+
102
+ if initial_files:
103
+ file_utils.store_response_files(new_repo_path, initial_files, [])
104
+ repo.git.add(".")
105
+
88
106
  repo.git.commit(
89
107
  "--allow-empty", "-m", _get_full_commit_message(INITIAL_COMMIT_MESSAGE, module_name, None, render_id)
90
108
  )
@@ -415,3 +433,53 @@ def get_repo_info(repo_path: Union[str, os.PathLike]) -> dict:
415
433
  info["remotes"] = remotes
416
434
 
417
435
  return info
436
+
437
+
438
+ def get_last_rendered_functionality(repo_path: Union[str, os.PathLike]) -> tuple[Optional[str], Optional[str]]:
439
+ if not os.path.exists(repo_path):
440
+ return None, None
441
+
442
+ repo = Repo(repo_path)
443
+ grep_pattern = FUNCTIONAL_REQUIREMENT_FINISHED_COMMIT_MESSAGE.format(".*")
444
+ grep_pattern = grep_pattern.replace("[", "\\[").replace("]", "\\]")
445
+ commit_sha = repo.git.rev_list(repo.active_branch.name, "--grep", grep_pattern, "-n", "1")
446
+
447
+ if not commit_sha:
448
+ # Repo was interrupted during the first functionality, fallback to initial commit and provide only module name
449
+ grep_pattern = INITIAL_COMMIT_MESSAGE
450
+ grep_pattern = grep_pattern.replace("[", "\\[").replace("]", "\\]")
451
+ commit_sha = repo.git.rev_list(repo.active_branch.name, "--grep", grep_pattern, "-n", "1")
452
+ if not commit_sha:
453
+ raise InvalidGitRepositoryError("Git repository is in an invalid state. Initial commit could not be found.")
454
+
455
+ commit_message = repo.commit(commit_sha).message
456
+ if isinstance(commit_message, bytes):
457
+ commit_message = commit_message.decode("utf-8")
458
+
459
+ match = re.search(r"Module name:\s*(\S+)\n", commit_message)
460
+ if not match:
461
+ raise InvalidGitRepositoryError(
462
+ "Git repository is in an invalid state. Could not find module name in initial commit."
463
+ )
464
+
465
+ module_name = match.group(1)
466
+ return module_name, None
467
+
468
+ commit_message = repo.commit(commit_sha).message
469
+ if isinstance(commit_message, bytes):
470
+ commit_message = commit_message.decode("utf-8")
471
+
472
+ match = re.search(r"FRID\):(\S+) fully implemented", commit_message)
473
+ if not match:
474
+ raise InvalidGitRepositoryError(
475
+ "Git repository is in an invalid state. Could not find frid in finished commit."
476
+ )
477
+ frid = match.group(1)
478
+
479
+ match = re.search(r"Module name:\s*(\S+)\n", commit_message)
480
+ if not match:
481
+ raise InvalidGitRepositoryError(
482
+ "Git repository is in an invalid state. Could not find module name in finished commit."
483
+ )
484
+ module_name = match.group(1)
485
+ return module_name, frid