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.
Files changed (41) hide show
  1. {codeplain-0.2.4.dist-info → codeplain-0.2.5.dist-info}/METADATA +3 -3
  2. {codeplain-0.2.4.dist-info → codeplain-0.2.5.dist-info}/RECORD +41 -41
  3. codeplain_REST_api.py +57 -37
  4. config/system_config.yaml +1 -16
  5. file_utils.py +5 -4
  6. git_utils.py +42 -7
  7. memory_management.py +1 -1
  8. module_renderer.py +114 -0
  9. plain2code.py +87 -19
  10. plain2code_console.py +3 -5
  11. plain2code_exceptions.py +14 -4
  12. plain2code_logger.py +11 -5
  13. plain2code_utils.py +7 -5
  14. plain_file.py +1 -1
  15. plain_spec.py +22 -2
  16. render_machine/actions/create_dist.py +1 -1
  17. render_machine/actions/exit_with_error.py +1 -1
  18. render_machine/actions/prepare_testing_environment.py +1 -1
  19. render_machine/actions/render_conformance_tests.py +2 -4
  20. render_machine/actions/render_functional_requirement.py +6 -6
  21. render_machine/actions/run_conformance_tests.py +3 -2
  22. render_machine/actions/run_unit_tests.py +1 -1
  23. render_machine/render_context.py +3 -3
  24. render_machine/render_utils.py +14 -6
  25. standard_template_library/golang-console-app-template.plain +2 -2
  26. standard_template_library/python-console-app-template.plain +2 -2
  27. standard_template_library/typescript-react-app-template.plain +2 -2
  28. system_config.py +3 -11
  29. tests/test_imports.py +2 -2
  30. tests/test_plainfile.py +1 -1
  31. tests/test_plainfileparser.py +10 -10
  32. tests/test_plainspec.py +2 -2
  33. tui/components.py +311 -103
  34. tui/plain2code_tui.py +101 -52
  35. tui/state_handlers.py +94 -47
  36. tui/styles.css +240 -52
  37. tui/widget_helpers.py +43 -47
  38. {codeplain-0.2.4.dist-info → codeplain-0.2.5.dist-info}/WHEEL +0 -0
  39. {codeplain-0.2.4.dist-info → codeplain-0.2.5.dist-info}/entry_points.txt +0 -0
  40. {codeplain-0.2.4.dist-info → codeplain-0.2.5.dist-info}/licenses/LICENSE +0 -0
  41. /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 traceback
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 InvalidFridArgument, MissingAPIKey, PlainSyntaxError
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
- traceback.print_exc()
256
- dump_crash_logs(args)
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[b]{header}[/b]", style=style)
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']} [b][#4169E1]({resource_name['target']}, {file_tokens} tokens)[/#4169E1][/b]"
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 NoRenderFound(Exception):
28
+ class UnexpectedState(Exception):
29
29
  pass
30
30
 
31
31
 
32
- class MultipleRendersFound(Exception):
32
+ class MissingAPIKey(Exception):
33
33
  pass
34
34
 
35
35
 
36
- class UnexpectedState(Exception):
36
+ class InvalidAPIKey(Exception):
37
37
  pass
38
38
 
39
39
 
40
- class MissingAPIKey(Exception):
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
- timestamp = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(record.created))
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
- if crash_handler.dump_to_file(log_file_path, formatter):
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"[b]{functional_requirement_text}[/b]"
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"\nGenerating acceptance test #{i}:\n\n{acceptance_test}")
32
+ console.info(f"Generating acceptance test #{i}:\n\n{acceptance_test}")
33
33
  else:
34
- console.info("-------------------------------------")
35
- console.info(f"Skipping rendering iteration: {frid}")
36
- console.info("-------------------------------------")
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. `technical specs`)
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 = "technical specs"
12
- TEST_REQUIREMENTS = "test specs"
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][b]--render-from {render_context.frid_context.frid}[/b][/red] flag."
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"[b]Running testing environment preparation script {render_context.prepare_environment_script} for build folder {render_context.build_folder}.[/b]"
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("[b]Implementing test requirements:[/b]")
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("\n[b]Generating acceptance test:[/b]")
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 = f"Module: {render_context.module_name}\n"
29
- msg += f"Rendering functional requirement {render_context.frid_context.frid}"
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[b]{render_context.frid_context.functional_requirement_text}[/b]"
33
- console.info("-------------------------------------")
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[b]{render_context.frid_context.functional_requirement_text}[/b]\n is too complex to be implemented. Please break down the functional requirement into smaller parts ({str(e)})."
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"\n[b]Running conformance tests script {render_context.conformance_tests_script} "
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
- + ").[/b]"
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"[b]Running unit tests script {render_context.unittests_script}.[/b] (attempt: {render_context.unit_tests_running_context.fix_attempts + 1})"
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,
@@ -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("[b]Refactoring the generated code...[/b]")
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("[b]Implementing conformance tests...[/b]")
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"[b]Running conformance tests attempt {self.conformance_tests_running_context.fix_attempts + 1}.[/b]"
447
+ f"Running conformance tests attempt {self.conformance_tests_running_context.fix_attempts + 1}."
448
448
  )
@@ -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"[b]{script_type} script output stored in: {temp_file_path}[/b]")
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
- console.info(
75
- f"[b]The {script_type} script has failed. Initiating the patching mode to automatically correct the discrepancies.[/b]"
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
- console.info(f"[b]All {script_type} script passed successfully.[/b]")
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
- ***technical specs***
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 specs***
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
- ***technical specs***
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 specs***
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
- ***technical specs***
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 specs***
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 "system_requirements" not in self.config:
16
- raise KeyError("Missing 'system_requirements' section in system_config.yaml")
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.requirements = self.config["system_requirements"]
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: