codeplain 0.2.4__py3-none-any.whl → 0.2.5__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.
- {codeplain-0.2.4.dist-info → codeplain-0.2.5.dist-info}/METADATA +3 -3
- {codeplain-0.2.4.dist-info → codeplain-0.2.5.dist-info}/RECORD +41 -41
- codeplain_REST_api.py +57 -37
- config/system_config.yaml +1 -16
- file_utils.py +5 -4
- git_utils.py +42 -7
- memory_management.py +1 -1
- module_renderer.py +114 -0
- plain2code.py +87 -19
- plain2code_console.py +3 -5
- plain2code_exceptions.py +14 -4
- plain2code_logger.py +11 -5
- plain2code_utils.py +7 -5
- plain_file.py +1 -1
- plain_spec.py +22 -2
- render_machine/actions/create_dist.py +1 -1
- render_machine/actions/exit_with_error.py +1 -1
- render_machine/actions/prepare_testing_environment.py +1 -1
- render_machine/actions/render_conformance_tests.py +2 -4
- render_machine/actions/render_functional_requirement.py +6 -6
- render_machine/actions/run_conformance_tests.py +3 -2
- render_machine/actions/run_unit_tests.py +1 -1
- render_machine/render_context.py +3 -3
- render_machine/render_utils.py +14 -6
- standard_template_library/golang-console-app-template.plain +2 -2
- standard_template_library/python-console-app-template.plain +2 -2
- standard_template_library/typescript-react-app-template.plain +2 -2
- system_config.py +3 -11
- tests/test_imports.py +2 -2
- tests/test_plainfile.py +1 -1
- tests/test_plainfileparser.py +10 -10
- tests/test_plainspec.py +2 -2
- tui/components.py +311 -103
- tui/plain2code_tui.py +101 -52
- tui/state_handlers.py +94 -47
- tui/styles.css +240 -52
- tui/widget_helpers.py +43 -47
- {codeplain-0.2.4.dist-info → codeplain-0.2.5.dist-info}/WHEEL +0 -0
- {codeplain-0.2.4.dist-info → codeplain-0.2.5.dist-info}/entry_points.txt +0 -0
- {codeplain-0.2.4.dist-info → codeplain-0.2.5.dist-info}/licenses/LICENSE +0 -0
- /spinner.py → /tui/spinner.py +0 -0
plain2code.py
CHANGED
|
@@ -2,7 +2,7 @@ import importlib.resources
|
|
|
2
2
|
import logging
|
|
3
3
|
import logging.config
|
|
4
4
|
import os
|
|
5
|
-
import
|
|
5
|
+
import sys
|
|
6
6
|
from typing import Optional
|
|
7
7
|
|
|
8
8
|
import yaml
|
|
@@ -17,7 +17,20 @@ from event_bus import EventBus
|
|
|
17
17
|
from module_renderer import ModuleRenderer
|
|
18
18
|
from plain2code_arguments import parse_arguments
|
|
19
19
|
from plain2code_console import console
|
|
20
|
-
from plain2code_exceptions import
|
|
20
|
+
from plain2code_exceptions import (
|
|
21
|
+
ConflictingRequirements,
|
|
22
|
+
CreditBalanceTooLow,
|
|
23
|
+
InternalServerError,
|
|
24
|
+
InvalidAPIKey,
|
|
25
|
+
InvalidFridArgument,
|
|
26
|
+
LLMInternalError,
|
|
27
|
+
MissingAPIKey,
|
|
28
|
+
MissingPreviousFunctionalitiesError,
|
|
29
|
+
MissingResource,
|
|
30
|
+
OutdatedClientVersion,
|
|
31
|
+
PlainSyntaxError,
|
|
32
|
+
UnexpectedState,
|
|
33
|
+
)
|
|
21
34
|
from plain2code_logger import (
|
|
22
35
|
CrashLogHandler,
|
|
23
36
|
IndentedFormatter,
|
|
@@ -89,6 +102,7 @@ def setup_logging(
|
|
|
89
102
|
log_to_file: bool,
|
|
90
103
|
log_file_name: str,
|
|
91
104
|
plain_file_path: Optional[str],
|
|
105
|
+
render_id: str,
|
|
92
106
|
):
|
|
93
107
|
# Set default level to INFO for everything not explicitly configured
|
|
94
108
|
logging.getLogger().setLevel(logging.INFO)
|
|
@@ -148,11 +162,29 @@ def setup_logging(
|
|
|
148
162
|
crash_handler.setFormatter(formatter)
|
|
149
163
|
root_logger.addHandler(crash_handler)
|
|
150
164
|
|
|
165
|
+
root_logger.info(f"Render ID: {render_id}") # Ensure render ID is logged in to codeplain.log file
|
|
151
166
|
|
|
152
|
-
def render(args, run_state: RunState, codeplain_api, event_bus: EventBus): # noqa: C901
|
|
153
|
-
# Check system requirements before proceeding
|
|
154
|
-
system_config.verify_requirements()
|
|
155
167
|
|
|
168
|
+
def _check_connection(codeplainAPI):
|
|
169
|
+
"""Check API connectivity and validate API key and client version."""
|
|
170
|
+
response = codeplainAPI.connection_check(system_config.client_version)
|
|
171
|
+
|
|
172
|
+
if not response.get("api_key_valid", False):
|
|
173
|
+
raise InvalidAPIKey(
|
|
174
|
+
"Provided API key is invalid. Please provide a valid API key using the CODEPLAIN_API_KEY environment variable "
|
|
175
|
+
"or the --api-key argument."
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
if not response.get("client_version_valid", False):
|
|
179
|
+
min_version = response.get("min_client_version", "unknown")
|
|
180
|
+
raise OutdatedClientVersion(
|
|
181
|
+
f"Your client version ({system_config.client_version}) is outdated. Minimum required version is {min_version}. "
|
|
182
|
+
"Please update using:"
|
|
183
|
+
" uv tool upgrade codeplain"
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def render(args, run_state: RunState, codeplain_api, event_bus: EventBus): # noqa: C901
|
|
156
188
|
template_dirs = file_utils.get_template_directories(args.filename, args.template_dir, DEFAULT_TEMPLATE_DIRS)
|
|
157
189
|
|
|
158
190
|
console.info(f"Rendering {args.filename} to target code.")
|
|
@@ -173,6 +205,8 @@ def render(args, run_state: RunState, codeplain_api, event_bus: EventBus): # no
|
|
|
173
205
|
assert args.api is not None and args.api != "", "API URL is required"
|
|
174
206
|
codeplainAPI.api_url = args.api
|
|
175
207
|
|
|
208
|
+
_check_connection(codeplainAPI)
|
|
209
|
+
|
|
176
210
|
module_renderer = ModuleRenderer(
|
|
177
211
|
codeplainAPI,
|
|
178
212
|
args.filename,
|
|
@@ -190,6 +224,7 @@ def render(args, run_state: RunState, codeplain_api, event_bus: EventBus): # no
|
|
|
190
224
|
unittests_script=args.unittests_script,
|
|
191
225
|
conformance_tests_script=args.conformance_tests_script,
|
|
192
226
|
prepare_environment_script=args.prepare_environment_script,
|
|
227
|
+
state_machine_version=system_config.client_version,
|
|
193
228
|
css_path="styles.css",
|
|
194
229
|
)
|
|
195
230
|
result = app.run()
|
|
@@ -202,18 +237,19 @@ def render(args, run_state: RunState, codeplain_api, event_bus: EventBus): # no
|
|
|
202
237
|
return
|
|
203
238
|
|
|
204
239
|
|
|
205
|
-
def main():
|
|
240
|
+
def main(): # noqa: C901
|
|
206
241
|
args = parse_arguments()
|
|
207
242
|
|
|
208
243
|
event_bus = EventBus()
|
|
209
244
|
|
|
210
|
-
setup_logging(args, event_bus, args.log_to_file, args.log_file_name, args.filename)
|
|
211
|
-
|
|
212
245
|
if not args.api:
|
|
213
246
|
args.api = "https://api.codeplain.ai"
|
|
214
247
|
|
|
215
248
|
run_state = RunState(spec_filename=args.filename, replay_with=args.replay_with)
|
|
216
249
|
|
|
250
|
+
setup_logging(args, event_bus, args.log_to_file, args.log_file_name, args.filename, run_state.render_id)
|
|
251
|
+
|
|
252
|
+
exc_info = None
|
|
217
253
|
try:
|
|
218
254
|
# Validate API key is present
|
|
219
255
|
if not args.api_key:
|
|
@@ -221,39 +257,71 @@ def main():
|
|
|
221
257
|
"API key is required. Please set the CODEPLAIN_API_KEY environment variable or provide it with the --api-key argument."
|
|
222
258
|
)
|
|
223
259
|
|
|
260
|
+
console.debug(f"Render ID: {run_state.render_id}") # Ensure render ID is logged to the console
|
|
224
261
|
render(args, run_state, codeplain_api, event_bus)
|
|
225
262
|
except InvalidFridArgument as e:
|
|
263
|
+
exc_info = sys.exc_info()
|
|
226
264
|
console.error(f"Invalid FRID argument: {str(e)}.\n")
|
|
227
|
-
# No need to print render ID since this error is going to be thrown at the very start so user will be able to
|
|
228
|
-
# see the render ID that's printed at the very start of the rendering process.
|
|
229
|
-
dump_crash_logs(args)
|
|
230
265
|
except FileNotFoundError as e:
|
|
266
|
+
exc_info = sys.exc_info()
|
|
231
267
|
console.error(f"File not found: {str(e)}\n")
|
|
232
268
|
console.debug(f"Render ID: {run_state.render_id}")
|
|
233
|
-
dump_crash_logs(args)
|
|
234
269
|
except TemplateNotFoundError as e:
|
|
270
|
+
exc_info = sys.exc_info()
|
|
235
271
|
console.error(f"Template not found: {str(e)}\n")
|
|
236
272
|
console.error(system_config.get_error_message("template_not_found"))
|
|
237
|
-
dump_crash_logs(args)
|
|
238
273
|
except PlainSyntaxError as e:
|
|
274
|
+
exc_info = sys.exc_info()
|
|
239
275
|
console.error(f"Plain syntax error: {str(e)}\n")
|
|
240
|
-
dump_crash_logs(args)
|
|
241
276
|
except KeyboardInterrupt:
|
|
277
|
+
exc_info = sys.exc_info()
|
|
242
278
|
console.error("Keyboard interrupt")
|
|
243
|
-
# Don't print the traceback here because it's going to be from keyboard interrupt and we don't really care about that
|
|
244
279
|
console.debug(f"Render ID: {run_state.render_id}")
|
|
245
|
-
dump_crash_logs(args)
|
|
246
280
|
except RequestException as e:
|
|
281
|
+
exc_info = sys.exc_info()
|
|
282
|
+
console.error(f"Error rendering plain code: {str(e)}\n")
|
|
283
|
+
console.debug(f"Render ID: {run_state.render_id}")
|
|
284
|
+
except MissingPreviousFunctionalitiesError as e:
|
|
285
|
+
exc_info = sys.exc_info()
|
|
247
286
|
console.error(f"Error rendering plain code: {str(e)}\n")
|
|
248
287
|
console.debug(f"Render ID: {run_state.render_id}")
|
|
249
|
-
dump_crash_logs(args)
|
|
250
288
|
except MissingAPIKey as e:
|
|
251
289
|
console.error(f"Missing API key: {str(e)}\n")
|
|
290
|
+
except InvalidAPIKey as e:
|
|
291
|
+
console.error(f"Invalid API key: {str(e)}\n")
|
|
292
|
+
except OutdatedClientVersion as e:
|
|
293
|
+
console.error(f"Outdated client version: {str(e)}\n")
|
|
294
|
+
except (InternalServerError, UnexpectedState):
|
|
295
|
+
exc_info = sys.exc_info()
|
|
296
|
+
console.error(
|
|
297
|
+
f"Internal server error.\n\nPlease report the error to support@codeplain.ai with the attached {args.log_file_name} file."
|
|
298
|
+
)
|
|
299
|
+
console.debug(f"Render ID: {run_state.render_id}")
|
|
300
|
+
except ConflictingRequirements as e:
|
|
301
|
+
exc_info = sys.exc_info()
|
|
302
|
+
console.error(f"Conflicting requirements: {str(e)}\n")
|
|
303
|
+
console.debug(f"Render ID: {run_state.render_id}")
|
|
304
|
+
except CreditBalanceTooLow as e:
|
|
305
|
+
exc_info = sys.exc_info()
|
|
306
|
+
console.error(f"Credit balance too low: {str(e)}\n")
|
|
307
|
+
console.debug(f"Render ID: {run_state.render_id}")
|
|
308
|
+
except LLMInternalError as e:
|
|
309
|
+
exc_info = sys.exc_info()
|
|
310
|
+
console.error(f"LLM internal error: {str(e)}\n")
|
|
311
|
+
console.debug(f"Render ID: {run_state.render_id}")
|
|
312
|
+
except MissingResource as e:
|
|
313
|
+
exc_info = sys.exc_info()
|
|
314
|
+
console.error(f"Missing resource: {str(e)}\n")
|
|
315
|
+
console.debug(f"Render ID: {run_state.render_id}")
|
|
252
316
|
except Exception as e:
|
|
317
|
+
exc_info = sys.exc_info()
|
|
253
318
|
console.error(f"Error rendering plain code: {str(e)}\n")
|
|
254
319
|
console.debug(f"Render ID: {run_state.render_id}")
|
|
255
|
-
|
|
256
|
-
|
|
320
|
+
finally:
|
|
321
|
+
if exc_info:
|
|
322
|
+
# Log traceback using the logging system
|
|
323
|
+
logging.error("Render crashed with exception:", exc_info=exc_info)
|
|
324
|
+
dump_crash_logs(args)
|
|
257
325
|
|
|
258
326
|
|
|
259
327
|
if __name__ == "__main__": # noqa: C901
|
plain2code_console.py
CHANGED
|
@@ -52,7 +52,7 @@ class Plain2CodeConsole(Console):
|
|
|
52
52
|
return
|
|
53
53
|
|
|
54
54
|
tree = self._create_tree_from_files(root_folder, files)
|
|
55
|
-
super().print(f"\n
|
|
55
|
+
super().print(f"\n{header}", style=style)
|
|
56
56
|
|
|
57
57
|
super().print(tree, style=style)
|
|
58
58
|
|
|
@@ -87,9 +87,7 @@ class Plain2CodeConsole(Console):
|
|
|
87
87
|
else:
|
|
88
88
|
file_lines = len(content.splitlines())
|
|
89
89
|
file_tokens = len(self.llm_encoding.encode(content))
|
|
90
|
-
current_level = current_level.add(
|
|
91
|
-
f"{part} [b]({file_lines} lines, {file_tokens} tokens)[/b]"
|
|
92
|
-
)
|
|
90
|
+
current_level = current_level.add(f"{part} ({file_lines} lines, {file_tokens} tokens)")
|
|
93
91
|
else:
|
|
94
92
|
current_level = current_level.add(part)
|
|
95
93
|
else:
|
|
@@ -107,7 +105,7 @@ class Plain2CodeConsole(Console):
|
|
|
107
105
|
if resource_name["target"] in linked_resources:
|
|
108
106
|
file_tokens = len(self.llm_encoding.encode(linked_resources[resource_name["target"]]))
|
|
109
107
|
self.input(
|
|
110
|
-
f"- {resource_name['text']} [
|
|
108
|
+
f"- {resource_name['text']} [#4169E1]({resource_name['target']}, {file_tokens} tokens)[/#4169E1]"
|
|
111
109
|
)
|
|
112
110
|
|
|
113
111
|
self.input()
|
plain2code_exceptions.py
CHANGED
|
@@ -25,19 +25,19 @@ class PlainSyntaxError(Exception):
|
|
|
25
25
|
pass
|
|
26
26
|
|
|
27
27
|
|
|
28
|
-
class
|
|
28
|
+
class UnexpectedState(Exception):
|
|
29
29
|
pass
|
|
30
30
|
|
|
31
31
|
|
|
32
|
-
class
|
|
32
|
+
class MissingAPIKey(Exception):
|
|
33
33
|
pass
|
|
34
34
|
|
|
35
35
|
|
|
36
|
-
class
|
|
36
|
+
class InvalidAPIKey(Exception):
|
|
37
37
|
pass
|
|
38
38
|
|
|
39
39
|
|
|
40
|
-
class
|
|
40
|
+
class OutdatedClientVersion(Exception):
|
|
41
41
|
pass
|
|
42
42
|
|
|
43
43
|
|
|
@@ -57,3 +57,13 @@ class InvalidLiquidVariableName(Exception):
|
|
|
57
57
|
|
|
58
58
|
class ModuleDoesNotExistError(Exception):
|
|
59
59
|
pass
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class InternalServerError(Exception):
|
|
63
|
+
pass
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class MissingPreviousFunctionalitiesError(Exception):
|
|
67
|
+
"""Raised when trying to render from a FRID but previous FRID commits are missing."""
|
|
68
|
+
|
|
69
|
+
pass
|
plain2code_logger.py
CHANGED
|
@@ -4,7 +4,6 @@ import time
|
|
|
4
4
|
from typing import Optional
|
|
5
5
|
|
|
6
6
|
from event_bus import EventBus
|
|
7
|
-
from plain2code_console import console
|
|
8
7
|
from plain2code_events import LogMessageEmitted
|
|
9
8
|
|
|
10
9
|
|
|
@@ -42,6 +41,7 @@ class TuiLoggingHandler(logging.Handler):
|
|
|
42
41
|
super().__init__()
|
|
43
42
|
self.event_bus = event_bus
|
|
44
43
|
self._buffer = []
|
|
44
|
+
self.start_time = time.time() # Record start time for offset calculation
|
|
45
45
|
|
|
46
46
|
# Register to be notified when event bus is ready
|
|
47
47
|
self.event_bus.on_ready(self._flush_buffer)
|
|
@@ -49,8 +49,11 @@ class TuiLoggingHandler(logging.Handler):
|
|
|
49
49
|
def emit(self, record):
|
|
50
50
|
try:
|
|
51
51
|
# Extract structured data from the log record
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
offset_seconds = record.created - self.start_time
|
|
53
|
+
minutes = int(offset_seconds // 60)
|
|
54
|
+
seconds = int(offset_seconds % 60)
|
|
55
|
+
milliseconds = int((offset_seconds % 1) * 100)
|
|
56
|
+
timestamp = f"{minutes:02d}:{seconds:02d}:{milliseconds:02d}"
|
|
54
57
|
event = LogMessageEmitted(
|
|
55
58
|
logger_name=record.name,
|
|
56
59
|
level=record.levelname,
|
|
@@ -63,6 +66,10 @@ class TuiLoggingHandler(logging.Handler):
|
|
|
63
66
|
self.event_bus.publish(event)
|
|
64
67
|
else:
|
|
65
68
|
self._buffer.append(event)
|
|
69
|
+
except RuntimeError:
|
|
70
|
+
# We're going to get this crash after the TUI app is closed (forcefully).
|
|
71
|
+
# NOTE: This should be more thought out.
|
|
72
|
+
pass
|
|
66
73
|
except Exception:
|
|
67
74
|
self.handleError(record)
|
|
68
75
|
|
|
@@ -123,5 +130,4 @@ def dump_crash_logs(args, formatter=None):
|
|
|
123
130
|
if crash_handler and args.filename:
|
|
124
131
|
log_file_path = get_log_file_path(args.filename, args.log_file_name)
|
|
125
132
|
|
|
126
|
-
|
|
127
|
-
console.error(f"\nLogs have been dumped to {log_file_path}")
|
|
133
|
+
crash_handler.dump_to_file(log_file_path, formatter)
|
plain2code_utils.py
CHANGED
|
@@ -24,15 +24,17 @@ def print_dry_run_output(plain_source_tree: dict, render_range: Optional[list[st
|
|
|
24
24
|
console.info(
|
|
25
25
|
"-------------------------------------"
|
|
26
26
|
f"Rendering functional requirement {frid}"
|
|
27
|
-
f"
|
|
27
|
+
f"{functional_requirement_text}"
|
|
28
28
|
"-------------------------------------"
|
|
29
29
|
)
|
|
30
30
|
if plain_spec.ACCEPTANCE_TESTS in specifications:
|
|
31
31
|
for i, acceptance_test in enumerate(specifications[plain_spec.ACCEPTANCE_TESTS], 1):
|
|
32
|
-
console.info(f"
|
|
32
|
+
console.info(f"Generating acceptance test #{i}:\n\n{acceptance_test}")
|
|
33
33
|
else:
|
|
34
|
-
console.info(
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
console.info(
|
|
35
|
+
"-------------------------------------\n"
|
|
36
|
+
f"Skipping rendering iteration: {frid}\n"
|
|
37
|
+
"-------------------------------------"
|
|
38
|
+
)
|
|
37
39
|
|
|
38
40
|
frid = plain_spec.get_next_frid(plain_source_tree, frid)
|
plain_file.py
CHANGED
|
@@ -206,7 +206,7 @@ def _process_single_acceptance_test_requirement(functional_requirement: mistleto
|
|
|
206
206
|
# Handle the case when the heading is not valid. This case includes cases such as:
|
|
207
207
|
# - Writing `acceptance test` instead of `acceptance tests` (or any other syntax diffs).
|
|
208
208
|
# - Instead of specifying `acceptance tests` below the functional requirement, creator of the plain file
|
|
209
|
-
# might have specified some other building block (e.g. `
|
|
209
|
+
# might have specified some other building block (e.g. `implementation reqs`)
|
|
210
210
|
raise PlainSyntaxError(acceptance_test_heading_problem)
|
|
211
211
|
|
|
212
212
|
if is_acceptance_test_heading:
|
plain_spec.py
CHANGED
|
@@ -8,8 +8,8 @@ from liquid2.filter import with_context
|
|
|
8
8
|
from plain2code_exceptions import InvalidLiquidVariableName
|
|
9
9
|
|
|
10
10
|
DEFINITIONS = "definitions"
|
|
11
|
-
NON_FUNCTIONAL_REQUIREMENTS = "
|
|
12
|
-
TEST_REQUIREMENTS = "test
|
|
11
|
+
NON_FUNCTIONAL_REQUIREMENTS = "implementation reqs"
|
|
12
|
+
TEST_REQUIREMENTS = "test reqs"
|
|
13
13
|
FUNCTIONAL_REQUIREMENTS = "functional specs"
|
|
14
14
|
ACCEPTANCE_TESTS = "acceptance_tests"
|
|
15
15
|
ACCEPTANCE_TEST_HEADING = "acceptance tests"
|
|
@@ -178,6 +178,26 @@ def get_previous_frid(plain_source_tree, frid):
|
|
|
178
178
|
raise Exception(f"Functional requirement {frid} does not exist.")
|
|
179
179
|
|
|
180
180
|
|
|
181
|
+
def get_frids_before(plain_source_tree, target_frid: str) -> list[str]:
|
|
182
|
+
"""
|
|
183
|
+
Get all FRIDs that appear before the target FRID in the specification.
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
plain_source_tree: The plain source tree
|
|
187
|
+
target_frid: The FRID to find predecessors for
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
List of FRIDs that appear before target_frid, in order
|
|
191
|
+
"""
|
|
192
|
+
previous_frids = []
|
|
193
|
+
for frid in get_frids(plain_source_tree):
|
|
194
|
+
if frid == target_frid:
|
|
195
|
+
break
|
|
196
|
+
previous_frids.append(frid)
|
|
197
|
+
|
|
198
|
+
return previous_frids
|
|
199
|
+
|
|
200
|
+
|
|
181
201
|
def get_specification_item_markdown(specification_item, code_variables, replace_code_variables):
|
|
182
202
|
markdown = specification_item["markdown"]
|
|
183
203
|
if "code_variables" in specification_item:
|
|
@@ -21,6 +21,6 @@ class CreateDist(BaseAction):
|
|
|
21
21
|
render_context.conformance_tests.get_module_conformance_tests_folder(render_context.module_name),
|
|
22
22
|
render_context.conformance_tests_dest,
|
|
23
23
|
)
|
|
24
|
-
console.info(f"Render {render_context.run_state.render_id} completed successfully.")
|
|
24
|
+
console.info(f"[#79FC96]Render {render_context.run_state.render_id} completed successfully.[/#79FC96]")
|
|
25
25
|
|
|
26
26
|
return self.SUCCESSFUL_OUTCOME, None
|
|
@@ -18,7 +18,7 @@ class ExitWithError(BaseAction):
|
|
|
18
18
|
|
|
19
19
|
if render_context.frid_context is not None:
|
|
20
20
|
console.info(
|
|
21
|
-
f"To continue rendering from the last successfully rendered functional requirement, provide the [red]
|
|
21
|
+
f"To continue rendering from the last successfully rendered functional requirement, provide the [red]--render-from {render_context.frid_context.frid}[/red] flag."
|
|
22
22
|
)
|
|
23
23
|
|
|
24
24
|
if render_context.run_state.render_id is not None:
|
|
@@ -14,7 +14,7 @@ class PrepareTestingEnvironment(BaseAction):
|
|
|
14
14
|
def execute(self, render_context: RenderContext, _previous_action_payload: Any | None):
|
|
15
15
|
if render_context.verbose:
|
|
16
16
|
console.info(
|
|
17
|
-
f"
|
|
17
|
+
f"Running testing environment preparation script {render_context.prepare_environment_script} for build folder {render_context.build_folder}."
|
|
18
18
|
)
|
|
19
19
|
exit_code, _, preparation_temp_file_path = render_utils.execute_script(
|
|
20
20
|
render_context.prepare_environment_script,
|
|
@@ -24,7 +24,7 @@ class RenderConformanceTests(BaseAction):
|
|
|
24
24
|
|
|
25
25
|
def _render_conformance_tests(self, render_context: RenderContext):
|
|
26
26
|
if render_context.verbose:
|
|
27
|
-
console.info("
|
|
27
|
+
console.info("Implementing test requirements:")
|
|
28
28
|
console.print_list(
|
|
29
29
|
render_context.conformance_tests_running_context.current_testing_frid_specifications[
|
|
30
30
|
plain_spec.TEST_REQUIREMENTS
|
|
@@ -149,9 +149,7 @@ class RenderConformanceTests(BaseAction):
|
|
|
149
149
|
]
|
|
150
150
|
|
|
151
151
|
if render_context.verbose:
|
|
152
|
-
console.info("
|
|
153
|
-
console.info(f"[b]{acceptance_test}[/b]")
|
|
154
|
-
console.info()
|
|
152
|
+
console.info(f"Generating acceptance test:\n {acceptance_test}")
|
|
155
153
|
|
|
156
154
|
with console.status(
|
|
157
155
|
f"[{console.INFO_STYLE}]Generating acceptance test for functional requirement {render_context.frid_context.frid}...\n"
|
|
@@ -25,14 +25,14 @@ class RenderFunctionalRequirement(BaseAction):
|
|
|
25
25
|
_, memory_files_content = MemoryManager.fetch_memory_files(render_context.memory_manager.memory_folder)
|
|
26
26
|
|
|
27
27
|
if render_context.verbose:
|
|
28
|
-
msg =
|
|
29
|
-
msg += f"
|
|
28
|
+
msg = "-------------------------------------\n"
|
|
29
|
+
msg += f"Module: {render_context.module_name}\n"
|
|
30
|
+
msg += f"Rendering functionality {render_context.frid_context.frid}"
|
|
30
31
|
if render_context.frid_context.functional_requirement_render_attempts > 1:
|
|
31
32
|
msg += f", attempt number {render_context.frid_context.functional_requirement_render_attempts}/{MAX_CODE_GENERATION_RETRIES}."
|
|
32
|
-
msg += f"\n
|
|
33
|
-
|
|
33
|
+
msg += f"\n{render_context.frid_context.functional_requirement_text}\n"
|
|
34
|
+
msg += "-------------------------------------"
|
|
34
35
|
console.info(msg)
|
|
35
|
-
console.info("-------------------------------------")
|
|
36
36
|
|
|
37
37
|
try:
|
|
38
38
|
if render_context.verbose:
|
|
@@ -54,7 +54,7 @@ class RenderFunctionalRequirement(BaseAction):
|
|
|
54
54
|
render_context.run_state,
|
|
55
55
|
)
|
|
56
56
|
except FunctionalRequirementTooComplex as e:
|
|
57
|
-
error_message = f"The functional requirement:\n
|
|
57
|
+
error_message = f"The functional requirement:\n{render_context.frid_context.functional_requirement_text}\n is too complex to be implemented. Please break down the functional requirement into smaller parts ({str(e)})."
|
|
58
58
|
if e.proposed_breakdown:
|
|
59
59
|
error_message += "\nProposed breakdown:"
|
|
60
60
|
for _, part in e.proposed_breakdown["functional_requirements"].items():
|
|
@@ -32,17 +32,18 @@ class RunConformanceTests(BaseAction):
|
|
|
32
32
|
|
|
33
33
|
if render_context.verbose:
|
|
34
34
|
console.info(
|
|
35
|
-
f"
|
|
35
|
+
f"Running conformance tests script {render_context.conformance_tests_script} "
|
|
36
36
|
+ f"for {conformance_tests_folder_name} ("
|
|
37
37
|
+ f"functional requirement {render_context.conformance_tests_running_context.current_testing_frid} "
|
|
38
38
|
+ f"in module {render_context.conformance_tests_running_context.current_testing_module_name}"
|
|
39
|
-
+ ").
|
|
39
|
+
+ ")."
|
|
40
40
|
)
|
|
41
41
|
exit_code, conformance_tests_issue, conformance_tests_temp_file_path = render_utils.execute_script(
|
|
42
42
|
render_context.conformance_tests_script,
|
|
43
43
|
[render_context.build_folder, conformance_tests_folder_name],
|
|
44
44
|
render_context.verbose,
|
|
45
45
|
"Conformance Tests",
|
|
46
|
+
render_context.conformance_tests_running_context.current_testing_frid,
|
|
46
47
|
)
|
|
47
48
|
render_context.script_execution_history.latest_conformance_test_output_path = conformance_tests_temp_file_path
|
|
48
49
|
render_context.script_execution_history.should_update_script_outputs = True
|
|
@@ -17,7 +17,7 @@ class RunUnitTests(BaseAction):
|
|
|
17
17
|
def execute(self, render_context: RenderContext, _previous_action_payload: Any | None):
|
|
18
18
|
if render_context.verbose:
|
|
19
19
|
console.info(
|
|
20
|
-
f"
|
|
20
|
+
f"Running unit tests script {render_context.unittests_script}. (attempt: {render_context.unit_tests_running_context.fix_attempts + 1})"
|
|
21
21
|
)
|
|
22
22
|
exit_code, unittests_issue, unittests_temp_file_path = render_utils.execute_script(
|
|
23
23
|
render_context.unittests_script,
|
render_machine/render_context.py
CHANGED
|
@@ -327,7 +327,7 @@ class RenderContext:
|
|
|
327
327
|
def start_refactoring_code(self):
|
|
328
328
|
|
|
329
329
|
if self.frid_context.refactoring_iteration == 0:
|
|
330
|
-
console.info("
|
|
330
|
+
console.info("Refactoring the generated code...")
|
|
331
331
|
|
|
332
332
|
self.frid_context.refactoring_iteration += 1
|
|
333
333
|
|
|
@@ -349,7 +349,7 @@ class RenderContext:
|
|
|
349
349
|
self.machine.dispatch(triggers.MARK_TESTING_ENVIRONMENT_PREPARED)
|
|
350
350
|
|
|
351
351
|
def start_conformance_tests_processing(self):
|
|
352
|
-
console.info("
|
|
352
|
+
console.info("Implementing conformance tests...")
|
|
353
353
|
self.conformance_tests_running_context = ConformanceTestsRunningContext(
|
|
354
354
|
current_testing_module_name=self.module_name,
|
|
355
355
|
current_testing_frid=None,
|
|
@@ -444,5 +444,5 @@ class RenderContext:
|
|
|
444
444
|
def finish_fixing_conformance_tests(self):
|
|
445
445
|
if self.verbose:
|
|
446
446
|
console.info(
|
|
447
|
-
f"
|
|
447
|
+
f"Running conformance tests attempt {self.conformance_tests_running_context.fix_attempts + 1}."
|
|
448
448
|
)
|
render_machine/render_utils.py
CHANGED
|
@@ -42,7 +42,7 @@ def print_inputs(render_context, existing_files_content, message):
|
|
|
42
42
|
|
|
43
43
|
|
|
44
44
|
def execute_script(
|
|
45
|
-
script: str, scripts_args: list[str], verbose: bool, script_type: str
|
|
45
|
+
script: str, scripts_args: list[str], verbose: bool, script_type: str, frid: Optional[str] = None
|
|
46
46
|
) -> tuple[int, str, Optional[str]]:
|
|
47
47
|
temp_file_path = None
|
|
48
48
|
try:
|
|
@@ -68,14 +68,22 @@ def execute_script(
|
|
|
68
68
|
temp_file.write(f"{script_type} script {script} successfully passed.\n")
|
|
69
69
|
temp_file.write(f"{script_type} script execution time: {elapsed_time:.2f} seconds.\n")
|
|
70
70
|
|
|
71
|
-
console.info(f"[
|
|
71
|
+
console.info(f"[#888888]{script_type} script output stored in: {temp_file_path.strip()}[/#888888]")
|
|
72
72
|
|
|
73
73
|
if result.returncode != 0:
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
74
|
+
if frid is not None:
|
|
75
|
+
console.info(
|
|
76
|
+
f"The {script_type} script for ID {frid} has failed. Initiating the patching mode to automatically correct the discrepancies."
|
|
77
|
+
)
|
|
78
|
+
else:
|
|
79
|
+
console.info(
|
|
80
|
+
f"The {script_type} script has failed. Initiating the patching mode to automatically correct the discrepancies."
|
|
81
|
+
)
|
|
77
82
|
else:
|
|
78
|
-
|
|
83
|
+
if frid is not None:
|
|
84
|
+
console.info(f"[#79FC96]The {script_type} script for ID {frid} has passed successfully.[/#79FC96]")
|
|
85
|
+
else:
|
|
86
|
+
console.info(f"[#79FC96]All {script_type} script passed successfully.[/#79FC96]")
|
|
79
87
|
|
|
80
88
|
return result.returncode, result.stdout, temp_file_path
|
|
81
89
|
except subprocess.TimeoutExpired as e:
|
|
@@ -7,14 +7,14 @@
|
|
|
7
7
|
- :ConformanceTestsExecutableFile: is the main executable code file of :ConformanceTests:.
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
***
|
|
10
|
+
***implementation reqs***
|
|
11
11
|
|
|
12
12
|
- :Implementation: should be in Go lang.
|
|
13
13
|
|
|
14
14
|
- :Implementation: should include unit tests using Go's built-in testing package.
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
***test
|
|
17
|
+
***test reqs***
|
|
18
18
|
|
|
19
19
|
- :ConformanceTests: should be implemented in Go lang.
|
|
20
20
|
|
|
@@ -7,13 +7,13 @@
|
|
|
7
7
|
- :MainExecutableFile: is the main executable code file of :App:.
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
***
|
|
10
|
+
***implementation reqs***
|
|
11
11
|
|
|
12
12
|
- :Implementation: should be in Python.
|
|
13
13
|
|
|
14
14
|
- :Implementation: should include :Unittests: using Unittest framework! If :Unittests: are put in the subfolder, make sure to include __init__.py to make them discoverable.
|
|
15
15
|
|
|
16
|
-
***test
|
|
16
|
+
***test reqs***
|
|
17
17
|
|
|
18
18
|
- :ConformanceTests: of :App: should be implemented in Python using Unittest framework. :ConformanceTests: will be run using "python -m unittest discover" command. Therefore, if :ConformanceTests: are put in the subfolder, make sure to include __init__.py to make them discoverable.
|
|
19
19
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
- :App: is a web application.
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
***
|
|
6
|
+
***implementation reqs***
|
|
7
7
|
|
|
8
8
|
- :App: should be implemented in TypeScript, using React as a web framework.
|
|
9
9
|
|
|
@@ -12,6 +12,6 @@
|
|
|
12
12
|
- :App: should run on port 3000.
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
***test
|
|
15
|
+
***test reqs***
|
|
16
16
|
|
|
17
17
|
- :ConformanceTests: of :App: should be written in TypeScript, using Cypress as the framework for :ConformanceTests:.
|
system_config.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import importlib.resources
|
|
2
|
-
import shutil
|
|
3
2
|
import sys
|
|
4
3
|
|
|
5
4
|
import yaml
|
|
@@ -12,12 +11,12 @@ class SystemConfig:
|
|
|
12
11
|
|
|
13
12
|
def __init__(self):
|
|
14
13
|
self.config = self._load_config()
|
|
15
|
-
if "
|
|
16
|
-
raise KeyError("Missing '
|
|
14
|
+
if "client_version" not in self.config:
|
|
15
|
+
raise KeyError("Missing 'client_version' in system_config.yaml")
|
|
17
16
|
if "error_messages" not in self.config:
|
|
18
17
|
raise KeyError("Missing 'error_messages' section in system_config.yaml")
|
|
19
18
|
|
|
20
|
-
self.
|
|
19
|
+
self.client_version = self.config["client_version"]
|
|
21
20
|
self.error_messages = self.config["error_messages"]
|
|
22
21
|
|
|
23
22
|
def _load_config(self):
|
|
@@ -31,13 +30,6 @@ class SystemConfig:
|
|
|
31
30
|
console.error(f"Failed to load system configuration: {e}")
|
|
32
31
|
sys.exit(69)
|
|
33
32
|
|
|
34
|
-
def verify_requirements(self):
|
|
35
|
-
"""Verify all system requirements are met."""
|
|
36
|
-
for req_data in self.requirements.values():
|
|
37
|
-
if not shutil.which(req_data["command"]):
|
|
38
|
-
console.error(req_data["error_message"])
|
|
39
|
-
sys.exit(69)
|
|
40
|
-
|
|
41
33
|
def get_error_message(self, message_key, **kwargs):
|
|
42
34
|
"""Get a formatted error message by its key."""
|
|
43
35
|
if message_key not in self.error_messages:
|