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.
- _version.py +1 -1
- {codeplain-0.2.20.dist-info → codeplain-0.3.0.dist-info}/METADATA +5 -5
- {codeplain-0.2.20.dist-info → codeplain-0.3.0.dist-info}/RECORD +45 -40
- codeplain_REST_api.py +30 -10
- concept_utils.py +1 -1
- file_utils.py +10 -9
- git_utils.py +70 -2
- module_renderer.py +62 -168
- partial_rendering.py +224 -0
- plain2code.py +98 -139
- plain2code_arguments.py +6 -4
- plain2code_events.py +6 -1
- plain2code_exceptions.py +4 -0
- plain2code_logger.py +8 -4
- plain2code_nodes.py +14 -3
- plain_file.py +39 -25
- plain_modules.py +256 -44
- plain_spec.py +53 -1
- render_machine/actions/analyze_specification_ambiguity.py +1 -1
- render_machine/actions/fix_conformance_test.py +56 -27
- render_machine/actions/fix_unit_tests.py +13 -14
- render_machine/actions/prepare_repositories.py +14 -3
- render_machine/actions/refactor_code.py +8 -10
- render_machine/actions/render_conformance_tests.py +76 -59
- render_machine/actions/summarize_conformance_tests.py +9 -12
- render_machine/code_renderer.py +20 -7
- render_machine/conformance_tests.py +1 -1
- render_machine/render_context.py +249 -69
- render_machine/render_types.py +57 -9
- render_machine/render_utils.py +6 -3
- tests/test_file_utils.py +41 -0
- tests/test_git_utils.py +74 -0
- tests/test_partial_rendering.py +316 -0
- tests/test_plain_modules.py +294 -0
- tests/test_plainfile.py +1 -1
- tests/test_plainfileparser.py +2 -2
- tui/components.py +73 -11
- tui/plain2code_tui.py +21 -30
- tui/plain_module_render_choice_tui.py +186 -0
- tui/state_handlers.py +9 -4
- tui/styles.css +42 -3
- tui/widget_helpers.py +24 -1
- {codeplain-0.2.20.dist-info → codeplain-0.3.0.dist-info}/WHEEL +0 -0
- {codeplain-0.2.20.dist-info → codeplain-0.3.0.dist-info}/entry_points.txt +0 -0
- {codeplain-0.2.20.dist-info → codeplain-0.3.0.dist-info}/licenses/LICENSE +0 -0
_version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.
|
|
1
|
+
__version__ = "0.3.0"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codeplain
|
|
3
|
-
Version: 0.
|
|
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.
|
|
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.
|
|
17
|
-
Requires-Dist: requests==2.
|
|
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/
|
|
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=
|
|
2
|
-
codeplain_REST_api.py,sha256=
|
|
3
|
-
concept_utils.py,sha256=
|
|
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=
|
|
7
|
-
git_utils.py,sha256=
|
|
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=
|
|
11
|
-
|
|
12
|
-
|
|
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=
|
|
15
|
-
plain2code_exceptions.py,sha256=
|
|
16
|
-
plain2code_logger.py,sha256=
|
|
17
|
-
plain2code_nodes.py,sha256=
|
|
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=
|
|
22
|
-
plain_modules.py,sha256=
|
|
23
|
-
plain_spec.py,sha256=
|
|
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=
|
|
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=
|
|
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=
|
|
35
|
-
render_machine/render_types.py,sha256=
|
|
36
|
-
render_machine/render_utils.py,sha256=
|
|
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=
|
|
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=
|
|
48
|
-
render_machine/actions/fix_unit_tests.py,sha256=
|
|
49
|
-
render_machine/actions/prepare_repositories.py,sha256=
|
|
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=
|
|
52
|
-
render_machine/actions/render_conformance_tests.py,sha256=
|
|
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=
|
|
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/
|
|
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/
|
|
67
|
-
tests/
|
|
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=
|
|
76
|
+
tui/components.py,sha256=G6ktJx211WrYmj-1aZL3mFtnBTOE4WRF3hA1LG8J0jU,25810
|
|
73
77
|
tui/models.py,sha256=KMjlWubmPD41GA-BjgiqkaV9v7tLJ0fuWKBDPmZj4JA,1493
|
|
74
|
-
tui/plain2code_tui.py,sha256=
|
|
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=
|
|
77
|
-
tui/styles.css,sha256=
|
|
78
|
-
tui/widget_helpers.py,sha256=
|
|
79
|
-
codeplain-0.
|
|
80
|
-
codeplain-0.
|
|
81
|
-
codeplain-0.
|
|
82
|
-
codeplain-0.
|
|
83
|
-
codeplain-0.
|
|
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
|
-
"
|
|
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(
|
|
80
|
-
|
|
81
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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],
|
|
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,
|
|
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
|