ara-cli 0.1.9.70__py3-none-any.whl → 0.1.9.72__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 (35) hide show
  1. ara_cli/ara_command_action.py +10 -10
  2. ara_cli/ara_config.py +3 -3
  3. ara_cli/artefact_autofix.py +2 -2
  4. ara_cli/artefact_creator.py +3 -3
  5. ara_cli/artefact_link_updater.py +4 -4
  6. ara_cli/artefact_models/artefact_model.py +14 -7
  7. ara_cli/artefact_models/artefact_templates.py +1 -1
  8. ara_cli/artefact_models/serialize_helper.py +1 -1
  9. ara_cli/artefact_reader.py +13 -36
  10. ara_cli/artefact_renamer.py +2 -2
  11. ara_cli/artefact_scan.py +1 -1
  12. ara_cli/chat.py +1 -1
  13. ara_cli/file_classifier.py +1 -1
  14. ara_cli/file_lister.py +1 -1
  15. ara_cli/list_filter.py +1 -1
  16. ara_cli/output_suppressor.py +1 -1
  17. ara_cli/prompt_extractor.py +3 -3
  18. ara_cli/prompt_handler.py +27 -1
  19. ara_cli/prompt_rag.py +2 -2
  20. ara_cli/template_manager.py +2 -2
  21. ara_cli/templates/prompt-modules/blueprints/complete_pytest_unittest.blueprint.md +1 -1
  22. ara_cli/update_config_prompt.py +2 -2
  23. ara_cli/version.py +1 -1
  24. {ara_cli-0.1.9.70.dist-info → ara_cli-0.1.9.72.dist-info}/METADATA +1 -1
  25. {ara_cli-0.1.9.70.dist-info → ara_cli-0.1.9.72.dist-info}/RECORD +35 -35
  26. tests/test_ara_command_action.py +7 -7
  27. tests/test_artefact_link_updater.py +3 -3
  28. tests/test_artefact_renamer.py +2 -2
  29. tests/test_artefact_scan.py +2 -2
  30. tests/test_file_lister.py +1 -1
  31. tests/test_list_filter.py +2 -2
  32. tests/test_update_config_prompt.py +2 -2
  33. {ara_cli-0.1.9.70.dist-info → ara_cli-0.1.9.72.dist-info}/WHEEL +0 -0
  34. {ara_cli-0.1.9.70.dist-info → ara_cli-0.1.9.72.dist-info}/entry_points.txt +0 -0
  35. {ara_cli-0.1.9.70.dist-info → ara_cli-0.1.9.72.dist-info}/top_level.txt +0 -0
@@ -33,7 +33,7 @@ def create_action(args):
33
33
  if parent_classifier and parent_name and rule:
34
34
  check_validity(Classifier.is_valid_classifier(parent_classifier), invalid_classifier_message)
35
35
  check_validity(is_valid_filename(parent_name), invalid_name_message)
36
- parent_artefact = ArtefactReader.read_single_artefact(artefact_name=parent_name, classifier=parent_classifier)
36
+ parent_artefact = ArtefactReader.read_artefact(artefact_name=parent_name, classifier=parent_classifier)
37
37
  rule = find_closest_rule(parent_artefact, rule)
38
38
  return parent_classifier, parent_name, rule
39
39
  if parent_classifier and parent_name:
@@ -359,7 +359,7 @@ def reconnect_action(args):
359
359
 
360
360
  feedback_message = f"Updated contribution of {classifier} '{artefact_name}' to {parent_classifier} '{parent_name}'"
361
361
 
362
- content, artefact_info = ArtefactReader.read_artefact(
362
+ content, artefact_info = ArtefactReader.read_artefact_data(
363
363
  artefact_name=artefact_name,
364
364
  classifier=classifier
365
365
  )
@@ -367,7 +367,7 @@ def reconnect_action(args):
367
367
  print(read_error_message)
368
368
  return
369
369
 
370
- parent_content, parent_info = ArtefactReader.read_artefact(
370
+ parent_content, parent_info = ArtefactReader.read_artefact_data(
371
371
  artefact_name=parent_name,
372
372
  classifier=parent_classifier
373
373
  )
@@ -399,7 +399,7 @@ def reconnect_action(args):
399
399
  exit(1)
400
400
 
401
401
  artefact.contribution = contribution
402
- with open(artefact.file_path, 'w') as file:
402
+ with open(artefact.file_path, 'w', encoding='utf-8') as file:
403
403
  artefact_content = artefact.serialize()
404
404
  file.write(artefact_content)
405
405
 
@@ -426,7 +426,7 @@ def read_status_action(args):
426
426
  lambda x: x["title"] == artefact_name, artefact_info_dicts
427
427
  ))
428
428
 
429
- with open(artefact_info["file_path"], 'r') as file:
429
+ with open(artefact_info["file_path"], 'r', encoding='utf-8') as file:
430
430
  content = file.read()
431
431
  artefact = artefact_from_content(content)
432
432
 
@@ -458,7 +458,7 @@ def read_user_action(args):
458
458
  lambda x: x["title"] == artefact_name, artefact_info_dicts
459
459
  ))
460
460
 
461
- with open(artefact_info["file_path"], 'r') as file:
461
+ with open(artefact_info["file_path"], 'r', encoding='utf-8') as file:
462
462
  content = file.read()
463
463
  artefact = artefact_from_content(content)
464
464
 
@@ -500,14 +500,14 @@ def set_status_action(args):
500
500
  lambda x: x["title"] == artefact_name, classified_artefact_dict
501
501
  ))
502
502
 
503
- with open(artefact_info["file_path"], 'r') as file:
503
+ with open(artefact_info["file_path"], 'r', encoding='utf-8') as file:
504
504
  content = file.read()
505
505
  artefact = artefact_from_content(content)
506
506
 
507
507
  artefact.status = new_status
508
508
 
509
509
  serialized_content = artefact.serialize()
510
- with open(f"{artefact_info['file_path']}", 'w') as file:
510
+ with open(f"{artefact_info['file_path']}", 'w', encoding='utf-8') as file:
511
511
  file.write(serialized_content)
512
512
 
513
513
  print(f"Status of task '{artefact_name}' has been updated to '{new_status}'.")
@@ -537,7 +537,7 @@ def set_user_action(args):
537
537
  lambda x: x["title"] == artefact_name, classified_artefact_dict
538
538
  ))
539
539
 
540
- with open(artefact_info["file_path"], 'r') as file:
540
+ with open(artefact_info["file_path"], 'r', encoding='utf-8') as file:
541
541
  content = file.read()
542
542
  artefact = artefact_from_content(content)
543
543
 
@@ -545,7 +545,7 @@ def set_user_action(args):
545
545
 
546
546
  serialized_content = artefact.serialize()
547
547
 
548
- with open(artefact_info["file_path"], 'w') as file:
548
+ with open(artefact_info["file_path"], 'w', encoding='utf-8') as file:
549
549
  file.write(serialized_content)
550
550
 
551
551
  print(f"User of task '{artefact_name}' has been updated to '{new_user}'.")
ara_cli/ara_config.py CHANGED
@@ -100,7 +100,7 @@ def ensure_directory_exists(directory: str):
100
100
 
101
101
 
102
102
  def validate_config_data(filepath: str):
103
- with open(filepath, "r") as file:
103
+ with open(filepath, "r", encoding="utf-8") as file:
104
104
  data = json.load(file)
105
105
  return data
106
106
 
@@ -112,7 +112,7 @@ def read_data(filepath: str) -> ARAconfig:
112
112
  # If file does not exist, create it with default values
113
113
  default_config = ARAconfig()
114
114
 
115
- with open(filepath, "w") as file:
115
+ with open(filepath, "w", encoding="utf-8") as file:
116
116
  json.dump(default_config.model_dump(mode='json'), file, indent=4)
117
117
 
118
118
  print(
@@ -126,7 +126,7 @@ def read_data(filepath: str) -> ARAconfig:
126
126
 
127
127
  # Function to save the modified configuration back to the JSON file
128
128
  def save_data(filepath: str, config: ARAconfig):
129
- with open(filepath, "w") as file:
129
+ with open(filepath, "w", encoding="utf-8") as file:
130
130
  json.dump(config.model_dump(mode='json'), file, indent=4)
131
131
 
132
132
 
@@ -212,7 +212,7 @@ def _update_rule(
212
212
  """Updates the rule in the contribution if a close match is found."""
213
213
  rule = artefact.contribution.rule
214
214
 
215
- content, artefact_data = ArtefactReader.read_artefact(
215
+ content, artefact_data = ArtefactReader.read_artefact_data(
216
216
  artefact_name=name,
217
217
  classifier=classifier,
218
218
  classified_file_info=classified_file_info,
@@ -312,7 +312,7 @@ def set_closest_contribution(
312
312
  if not rule:
313
313
  return artefact, True
314
314
 
315
- content, artefact = ArtefactReader.read_artefact(
315
+ content, artefact = ArtefactReader.read_artefact_data(
316
316
  artefact_name=name,
317
317
  classifier=classifier,
318
318
  classified_file_info=classified_file_info,
@@ -16,7 +16,7 @@ class ArtefactCreator:
16
16
 
17
17
  @lru_cache(maxsize=None)
18
18
  def read_template_content(self, template_file_path):
19
- with open(template_file_path, "r") as template_file:
19
+ with open(template_file_path, "r", encoding="utf-8") as template_file:
20
20
  return template_file.read()
21
21
 
22
22
  def create_artefact_prompt_files(self, dir_path, template_path, classifier):
@@ -118,7 +118,7 @@ class ArtefactCreator:
118
118
  artefact_content = artefact.serialize()
119
119
  rmtree(dir_path, ignore_errors=True)
120
120
  os.makedirs(dir_path, exist_ok=True)
121
- with open(file_path, 'w') as artefact_file:
121
+ with open(file_path, 'w', encoding='utf-8') as artefact_file:
122
122
  artefact_file.write(artefact_content)
123
123
 
124
124
  relative_file_path = os.path.relpath(file_path, original_directory)
@@ -138,7 +138,7 @@ class ArtefactCreator:
138
138
 
139
139
  title = Classifier.get_artefact_title(classifier)
140
140
 
141
- with open(file_path, "r") as file:
141
+ with open(file_path, "r", encoding="utf-8") as file:
142
142
  for line in file:
143
143
  if line.strip().startswith(title):
144
144
  return line.split(':')[1].strip()
@@ -34,7 +34,7 @@ class ArtefactLinkUpdater:
34
34
  # Check if it's a file and not a directory
35
35
  elif self.file_system.path.isfile(item_path) and Classifier.is_valid_classifier(extension):
36
36
  # Read the content of the file
37
- with open(item_path, 'r') as file:
37
+ with open(item_path, 'r', encoding='utf-8') as file:
38
38
  content = file.read()
39
39
 
40
40
  # Replace all occurrences of the old name with the new name using regular expressions
@@ -42,7 +42,7 @@ class ArtefactLinkUpdater:
42
42
  content = pattern.sub(replacement, content)
43
43
 
44
44
  # Write the updated content back to the file
45
- with open(item_path, 'w') as file:
45
+ with open(item_path, 'w', encoding='utf-8') as file:
46
46
  file.write(content)
47
47
 
48
48
  def remove_links_in_related_artefacts(self, artefact_name, dir_path="."):
@@ -65,12 +65,12 @@ class ArtefactLinkUpdater:
65
65
 
66
66
  # Check if it's a file and not a directory, and if extension is a valid artefact classifier
67
67
  elif self.file_system.path.isfile(item_path) and Classifier.is_valid_classifier(extension):
68
- with open(item_path, 'r') as file:
68
+ with open(item_path, 'r', encoding='utf-8') as file:
69
69
  content = file.read()
70
70
 
71
71
  # Remove the artefact name from 'Contributes to' and 'Illustrates' lines
72
72
  content = contribute_pattern.sub("Contributes to", content)
73
73
  content = illustrates_pattern.sub("Illustrates", content)
74
74
 
75
- with open(item_path, 'w') as file:
75
+ with open(item_path, 'w', encoding='utf-8') as file:
76
76
  file.write(content)
@@ -197,6 +197,15 @@ class Artefact(BaseModel, ABC):
197
197
  self._file_path = file_path
198
198
  return self
199
199
 
200
+ @model_validator(mode="after")
201
+ def validate_contribution(self):
202
+ contribution = self.contribution
203
+ classifier = self.artefact_type.value
204
+ name = self.title
205
+ if not contribution:
206
+ warnings.warn(f"Contribution of {classifier} '{name}' is not set and will be empty")
207
+ return self
208
+
200
209
  @field_validator('artefact_type')
201
210
  def validate_artefact_type(cls, v):
202
211
  if not isinstance(v, ArtefactType):
@@ -230,7 +239,11 @@ class Artefact(BaseModel, ABC):
230
239
  def validate_title(cls, v):
231
240
  if not v.strip():
232
241
  raise ValueError("artefact_title must not be empty")
233
- v = replace_space_with_underscore(v)
242
+ v = replace_space_with_underscore(v).strip()
243
+
244
+ whitelisted_placeholder = "<descriptive_title>"
245
+ if v == whitelisted_placeholder:
246
+ return v
234
247
 
235
248
  letters = list(string.ascii_letters)
236
249
  digits = list(string.digits)
@@ -243,12 +256,6 @@ class Artefact(BaseModel, ABC):
243
256
 
244
257
  return v
245
258
 
246
- @field_validator('contribution')
247
- def validate_contribution(cls, v):
248
- if not v:
249
- warnings.warn("Contribution is not set and will be empty")
250
- return v
251
-
252
259
  @classmethod
253
260
  @abstractmethod
254
261
  def _title_prefix(cls) -> str: # pragma: no cover
@@ -207,7 +207,7 @@ def _default_issue(title: str) -> IssueArtefact:
207
207
  )
208
208
 
209
209
 
210
- def template_artefact_of_type(artefact_type: ArtefactType, title: str) -> Artefact:
210
+ def template_artefact_of_type(artefact_type: ArtefactType, title: str = "<descriptive_title>") -> Artefact:
211
211
  default_creation_functions = {
212
212
  ArtefactType.vision: _default_vision,
213
213
  ArtefactType.businessgoal: _default_businessgoal,
@@ -15,4 +15,4 @@ def as_a_serializer(as_a):
15
15
  else:
16
16
  as_a_prefix = "As a"
17
17
 
18
- return f"{as_a_prefix} {role}"
18
+ return f"{as_a_prefix} {role}".strip()
@@ -10,7 +10,7 @@ import re
10
10
 
11
11
  class ArtefactReader:
12
12
  @staticmethod
13
- def read_artefact(artefact_name, classifier, classified_file_info = None) -> tuple[str, dict[str, str]]:
13
+ def read_artefact_data(artefact_name, classifier, classified_file_info = None) -> tuple[str, dict[str, str]]:
14
14
  if not Classifier.is_valid_classifier(classifier):
15
15
  print("Invalid classifier provided. Please provide a valid classifier.")
16
16
  return None, None
@@ -24,7 +24,7 @@ class ArtefactReader:
24
24
  file_path = artefact_info["file_path"]
25
25
  artefact_title = artefact_info["title"]
26
26
  if artefact_title == artefact_name:
27
- with open(file_path, 'r') as file:
27
+ with open(file_path, 'r', encoding='utf-8') as file:
28
28
  content = file.read()
29
29
  return content, artefact_info
30
30
 
@@ -37,25 +37,14 @@ class ArtefactReader:
37
37
  return None, None
38
38
 
39
39
  @staticmethod
40
- def read_single_artefact(artefact_name, classifier, classified_file_info=None) -> Artefact:
41
- if not Classifier.is_valid_classifier(classifier):
42
- print("Invalid classifier provided. Please provide a valid classifier.")
40
+ def read_artefact(artefact_name, classifier, classified_file_info=None) -> Artefact:
41
+ content, artefact_info = ArtefactReader.read_artefact_data(artefact_name, classifier, classified_file_info)
42
+ if not content or not artefact_info:
43
43
  return None
44
- if not classified_file_info:
45
- file_classifier = FileClassifier(os)
46
- classified_file_info = file_classifier.classify_files()
47
- artefact_info_of_classifier = classified_file_info.get(classifier, [])
48
-
49
- for artefact_info in artefact_info_of_classifier:
50
- file_path = artefact_info["file_path"]
51
- artefact_title = artefact_info["title"]
52
- if artefact_title == artefact_name:
53
- with open(file_path, 'r') as file:
54
- content = file.read()
55
- artefact = artefact_from_content(content)
56
- artefact._file_path = file_path
57
- return artefact
58
- return None
44
+ file_path = artefact_info["file_path"]
45
+ artefact = artefact_from_content(content)
46
+ artefact._file_path = file_path
47
+ return artefact
59
48
 
60
49
  @staticmethod
61
50
  def extract_parent_tree(artefact_content):
@@ -95,24 +84,12 @@ class ArtefactReader:
95
84
  for artefact_type in classified_artefacts.keys()}
96
85
  for artefact_type, artefact_info_dicts in classified_artefacts.items():
97
86
  for artefact_info in artefact_info_dicts:
87
+ title = artefact_info["title"]
98
88
  try:
99
- with open(artefact_info["file_path"], 'r') as file:
100
- content = file.read()
101
- artefact = artefact_from_content(content)
102
- if not artefact:
103
- continue
104
- # Store the full file path in the artefact
105
- artefact._file_path = artefact_info["file_path"]
89
+ artefact = ArtefactReader.read_artefact(title, artefact_type, classified_artefacts)
106
90
  artefacts[artefact_type].append(artefact)
107
- # else:
108
- # Include file path if deserialization fails
109
- # FIXME: LOOK INTO IT
110
- # artefacts[artefact_type].append(file_path)
111
- except Exception:
112
- # TODO: catch only specific exceptions
113
- # TODO: implament error message for deserialization or "ara scan" or "ara autofix"
114
- # print(f"Warning: Could not deserialize artefact at {artefact_info}: {e}")
115
- # artefacts[artefact_type].append(file_path)
91
+ except Exception as e:
92
+ print(f"Failed to read {artefact_type} '{title}' with an error: ", e)
116
93
  continue
117
94
  return artefacts
118
95
 
@@ -75,7 +75,7 @@ class ArtefactRenamer:
75
75
  raise ValueError(f"Invalid classifier: {classifier}")
76
76
 
77
77
  # Read the file content
78
- with open(artefact_path, 'r') as file:
78
+ with open(artefact_path, 'r', encoding='utf-8') as file:
79
79
  content = file.read()
80
80
 
81
81
  # Find the old title line
@@ -89,5 +89,5 @@ class ArtefactRenamer:
89
89
  new_content = content.replace(old_title_line, new_title_line, 1)
90
90
 
91
91
  # Write the updated content back to the file
92
- with open(artefact_path, 'w') as file:
92
+ with open(artefact_path, 'w', encoding='utf-8') as file:
93
93
  file.write(new_content)
ara_cli/artefact_scan.py CHANGED
@@ -70,7 +70,7 @@ def find_invalid_files(classified_artefact_info, classifier):
70
70
 
71
71
  def show_results(invalid_artefacts):
72
72
  has_issues = False
73
- with open("incompatible_artefacts_report.md", "w") as report:
73
+ with open("incompatible_artefacts_report.md", "w", encoding="utf-8") as report:
74
74
  report.write("# Artefact Check Report\n\n")
75
75
  for classifier, files in invalid_artefacts.items():
76
76
  if files:
ara_cli/chat.py CHANGED
@@ -154,7 +154,7 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
154
154
  return None
155
155
 
156
156
  def start_non_interactive(self):
157
- with open(self.chat_name, 'r') as file:
157
+ with open(self.chat_name, 'r', encoding='utf-8') as file:
158
158
  content = file.read()
159
159
  print(content)
160
160
 
@@ -68,7 +68,7 @@ class FileClassifier:
68
68
  files_by_classifier = {classifier: [] for classifier in Classifier.ordered_classifiers()}
69
69
 
70
70
  for root, _, files in self.file_system.walk("."):
71
- if root.endswith(".data"):
71
+ if root.endswith(".data") or root.endswith("templates"):
72
72
  continue
73
73
  for file in files:
74
74
  file_path = self.file_system.path.join(root, file)
ara_cli/file_lister.py CHANGED
@@ -28,7 +28,7 @@ def generate_markdown_listing(directories, file_types_to_be_listed, output_file_
28
28
  if any(fnmatch.fnmatch(file, pattern) for pattern in file_types_to_be_listed):
29
29
  markdown_lines.append(f"{indent} - [] {file}")
30
30
 
31
- with open(output_file_path, "w") as md_file:
31
+ with open(output_file_path, "w", encoding="utf-8") as md_file:
32
32
  md_file.write('\n'.join(markdown_lines))
33
33
 
34
34
 
ara_cli/list_filter.py CHANGED
@@ -35,7 +35,7 @@ class ListFilterMonad:
35
35
  def default_content_retrieval(file):
36
36
  # Default strategy assumes file is a path and attempts to read it
37
37
  try:
38
- with open(file, 'r') as f:
38
+ with open(file, 'r', encoding='utf-8') as f:
39
39
  return f.read()
40
40
  except Exception as e:
41
41
  print(f"Error reading file {file}: {e}")
@@ -6,7 +6,7 @@ from contextlib import contextmanager
6
6
  @contextmanager
7
7
  def suppress_stdout(suppress=False):
8
8
  if suppress:
9
- with open(os.devnull, "w") as devnull:
9
+ with open(os.devnull, "w", encoding="utf-8") as devnull:
10
10
  old_stdout = sys.stdout
11
11
  sys.stdout = devnull
12
12
  try:
@@ -21,7 +21,7 @@ def extract_responses(document_path, relative_to_ara_root=False):
21
21
  print(f"Debug: Starting extraction from {document_path}")
22
22
  block_extraction_counter = 0
23
23
 
24
- with open(document_path, 'r') as file:
24
+ with open(document_path, 'r', encoding='utf-8') as file:
25
25
  content = file.read()
26
26
 
27
27
  cwd = os.getcwd()
@@ -81,7 +81,7 @@ def extract_responses(document_path, relative_to_ara_root=False):
81
81
  artefact_path = artefact.file_path
82
82
  directory = os.path.dirname(artefact_path)
83
83
  os.makedirs(directory, exist_ok=True)
84
- with open(artefact_path, 'w') as file:
84
+ with open(artefact_path, 'w', encoding='utf-8') as file:
85
85
  file.write(serialized_artefact)
86
86
 
87
87
  os.chdir(original_directory)
@@ -92,7 +92,7 @@ def extract_responses(document_path, relative_to_ara_root=False):
92
92
 
93
93
  os.chdir(cwd)
94
94
  # Save the updated markdown content
95
- with open(document_path, 'w') as file:
95
+ with open(document_path, 'w', encoding='utf-8') as file:
96
96
  file.write(updated_content)
97
97
 
98
98
  print(f"End of extraction. Found {block_extraction_counter} blocks.")
ara_cli/prompt_handler.py CHANGED
@@ -12,6 +12,7 @@ from re import findall
12
12
  import re
13
13
  import shutil
14
14
  import glob
15
+ import logging
15
16
 
16
17
 
17
18
  class LLMSingleton:
@@ -78,9 +79,25 @@ def send_prompt(prompt):
78
79
  if "provider" in config_parameters:
79
80
  del config_parameters["provider"]
80
81
 
82
+ filtered_prompt = list(filter(
83
+ lambda msg: (
84
+ msg.get('content') and
85
+ isinstance(msg['content'], list) and
86
+ any(
87
+ item.get('type') == 'text' and
88
+ item.get('text', '').strip() != ''
89
+ for item in msg['content']
90
+ )
91
+ ) or (
92
+ isinstance(msg.get('content'), str) and
93
+ msg['content'].strip() != ''
94
+ ),
95
+ prompt
96
+ ))
97
+
81
98
  completion = litellm.completion(
82
99
  **config_parameters,
83
- messages=prompt,
100
+ messages=filtered_prompt,
84
101
  stream=True
85
102
  )
86
103
  for chunk in completion:
@@ -355,10 +372,19 @@ def prepend_system_prompt(message_list):
355
372
 
356
373
 
357
374
  def append_images_to_message(message, image_data_list):
375
+ logger = logging.getLogger(__name__)
376
+
377
+ logger.debug(f"append_images_to_message called with image_data_list length: {len(image_data_list) if image_data_list else 0}")
378
+
358
379
  if not image_data_list:
380
+ logger.debug("No images to append, returning original message")
359
381
  return message
382
+
360
383
  message_content = message["content"]
384
+ logger.debug(f"Original message content: {message_content}")
385
+
361
386
  message["content"] = message_content + image_data_list
387
+ logger.debug(f"Updated message content with {len(image_data_list)} images")
362
388
 
363
389
  return message
364
390
 
ara_cli/prompt_rag.py CHANGED
@@ -16,7 +16,7 @@ def find_files_in_prompt_config_givens(search_file, prompt_givens_file_path):
16
16
  header_stack = []
17
17
  modified_lines = [] # To store the modified file content
18
18
 
19
- with open(prompt_givens_file_path, 'r') as file:
19
+ with open(prompt_givens_file_path, 'r', encoding='utf-8') as file:
20
20
  for line in file:
21
21
  if line.strip().startswith('#'):
22
22
  level = line.count('#')
@@ -37,7 +37,7 @@ def find_files_in_prompt_config_givens(search_file, prompt_givens_file_path):
37
37
 
38
38
  if file_found:
39
39
  # Rewrite the file with the modified content if any line was changed
40
- with open(prompt_givens_file_path, 'w') as file:
40
+ with open(prompt_givens_file_path, 'w', encoding='utf-8') as file:
41
41
  file.writelines(modified_lines)
42
42
 
43
43
  return file_found
@@ -34,7 +34,7 @@ class TemplatePathManager:
34
34
 
35
35
  template_path = (base_path / f"template.{classifier}")
36
36
 
37
- with template_path.open('r') as file:
37
+ with template_path.open('r', encoding='utf-8') as file:
38
38
  content = file.read()
39
39
 
40
40
  return content
@@ -113,7 +113,7 @@ class ArtefactFileManager:
113
113
  def save_behave_steps_to_file(self, artefact_name, behave_steps):
114
114
  self.navigator.navigate_to_target()
115
115
  file_path = f"features/steps/{artefact_name}_steps.py"
116
- with open(file_path, 'w') as file:
116
+ with open(file_path, 'w', encoding='utf-8') as file:
117
117
  file.write(behave_steps)
118
118
 
119
119
 
@@ -24,4 +24,4 @@ Modify and/or create unit tests so this is fully covered:
24
24
  <snippet you want to cover in the next step>
25
25
  ```
26
26
 
27
- Give me only what is relevant to testing this snippet. Use parametrization where applicable. Mock all dependencies of tested code.
27
+ Give me only what is relevant to testing this snippet. Use parametrization where applicable. Split into multiple tests instead of using if-else blocks. Mock all dependencies of tested code.
@@ -5,13 +5,13 @@ from ara_cli.prompt_handler import generate_config_prompt_template_file, generat
5
5
 
6
6
  def read_file(filepath):
7
7
  """Read and return the content of a file."""
8
- with open(filepath, 'r') as file:
8
+ with open(filepath, 'r', encoding='utf-8') as file:
9
9
  return file.read()
10
10
 
11
11
 
12
12
  def write_file(filepath, content):
13
13
  """Write content to a file."""
14
- with open(filepath, 'w') as file:
14
+ with open(filepath, 'w', encoding='utf-8') as file:
15
15
  file.write(content)
16
16
 
17
17
 
ara_cli/version.py CHANGED
@@ -1,2 +1,2 @@
1
1
  # version.py
2
- __version__ = "0.1.9.70" # fith parameter like .0 for local install test purposes only. official numbers should be 4 digit numbers
2
+ __version__ = "0.1.9.72" # fith parameter like .0 for local install test purposes only. official numbers should be 4 digit numbers
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ara_cli
3
- Version: 0.1.9.70
3
+ Version: 0.1.9.72
4
4
  Requires-Dist: litellm
5
5
  Requires-Dist: llama-index
6
6
  Requires-Dist: llama-index-llms-openai
@@ -1,42 +1,42 @@
1
1
  ara_cli/__init__.py,sha256=0zl7IegxTid26EBGLav_fXZ4CCIV3H5TfAoFQiOHjvg,148
2
2
  ara_cli/__main__.py,sha256=Z6XYWRLceIoZPvfC-X9EXouSZdtFOOe84kKVWJGA4r4,1861
3
- ara_cli/ara_command_action.py,sha256=aq074NT63xsuyIJgGnM0htp7kpyolaLwtauXjQEPljA,21852
3
+ ara_cli/ara_command_action.py,sha256=dmv2xAUw3UXB1dMfeJ3ibFYJhGjBDbQdXrHRtx_whaM,21981
4
4
  ara_cli/ara_command_parser.py,sha256=v8LUdkBSI2771gI53PdrxtD8YVjhk-7E8vgTEsTGnRM,17952
5
- ara_cli/ara_config.py,sha256=EBVxVXJauWNmLVSDeTdNvncO0z1avR3hN9mkJhPmaU0,4183
6
- ara_cli/artefact_autofix.py,sha256=F4Vp-SI6YI0rKeVswOlg23OemH7w4CEJ9-ChcqIpn4U,14866
7
- ara_cli/artefact_creator.py,sha256=tUNCNvfFYMheyF_viyrQhm2-43AkbHFoQaHui9ntvws,6002
5
+ ara_cli/ara_config.py,sha256=xynwnCrlpBj20ozL7ra8siniNlfM4eh4OlOHi7fkDY8,4237
6
+ ara_cli/artefact_autofix.py,sha256=_xCAZFNZHJnIS95QCNxGPezrCBb9v8gIMn9_Ixjcj74,14876
7
+ ara_cli/artefact_creator.py,sha256=ysWtlYDbZf_k8ya7ZBXILP_PWZKIssAAJK8kak5mW-E,6056
8
8
  ara_cli/artefact_deleter.py,sha256=Co4wwCH3yW8H9NrOq7_2p5571EeHr0TsfE-H8KqoOfY,1900
9
9
  ara_cli/artefact_fuzzy_search.py,sha256=iBlDqjZf-_D3VUjFf7ZwkiQbpQDcwRndIU7aG_sRTgE,2668
10
- ara_cli/artefact_link_updater.py,sha256=itMS_Z64jE8bBly9WA01z8PqkBeNW6ntTO7ryMeCTRg,3703
10
+ ara_cli/artefact_link_updater.py,sha256=nKdxTpDKqWTOAMD8viKmUaklSFGWzJZ8S8E8xW_ADuM,3775
11
11
  ara_cli/artefact_lister.py,sha256=jhk4n4eqp7hDIq07q43QzS7-36BM3OfZ4EABxCeOGcw,4764
12
- ara_cli/artefact_reader.py,sha256=zVR3DZ4z6vjKPQnFZVbPS75MkluaoLkz0lWmg_hXxTY,8136
13
- ara_cli/artefact_renamer.py,sha256=loIn1DF9kVnjhH7wP1v5qUvt3s0uKeWXuQPrHXenQGE,4025
14
- ara_cli/artefact_scan.py,sha256=ZB5mZkFto3K5xg_nZsQekWz5Ly7X0WyHnBsvPL3sC6I,3599
15
- ara_cli/chat.py,sha256=7xTtPEDk052_wmIzoti7GavEJ1vpRxe5c084WQ1C7dg,28617
12
+ ara_cli/artefact_reader.py,sha256=E6DMBvbOYf1OoLf-OyLaiB6K2-gd7iHmjoQZU9Rsy6g,6965
13
+ ara_cli/artefact_renamer.py,sha256=Hnz_3zD9xxnBa1FHyUE6mIktLk_9ttP2rFRvQIkmz-o,4061
14
+ ara_cli/artefact_scan.py,sha256=yg1ozwavWmqsrO9lx4bY3ggCooiPJ_DYQuZuJlFJdqc,3617
15
+ ara_cli/chat.py,sha256=Vv4tfzP203hCISd2Q0W0ZZ7ua7dk0jJ1qdl9NiHI5gM,28635
16
16
  ara_cli/classifier.py,sha256=zWskj7rBYdqYBGjksBm46iTgVU5IIf2PZsJr4qeiwVU,1878
17
17
  ara_cli/codefusionretriever.py,sha256=fCHgXdIBRzkVAnapX-KI2NQ44XbrrF4tEQmn5J6clUI,1980
18
18
  ara_cli/codehierachieretriever.py,sha256=Xd3EgEWWhkSf1TmTWtf8X5_YvyE_4B66nRrqarwSiTU,1182
19
19
  ara_cli/commandline_completer.py,sha256=b00Dqb5n7SecpxYIDLxAfYhp8X6e3c8a5qYz6ko0i3E,1192
20
20
  ara_cli/directory_navigator.py,sha256=6QbSAjJrJ5a6Lutol9J4HFgVDMiAQ672ny9TATrh04U,3318
21
- ara_cli/file_classifier.py,sha256=26MPVCaZlQkRDa62FoQ6ZqawOiaNgspUvcFOF93bHzo,3971
22
- ara_cli/file_lister.py,sha256=VFpUmHU1d6sQvJWSeuFqkZZ0Ci3ZYCUtAUfvgWypaYU,2314
21
+ ara_cli/file_classifier.py,sha256=A7wilPtIFm81iMgvqD0PjkOVL_QMUc9TB2w2Z9UcPcM,4001
22
+ ara_cli/file_lister.py,sha256=0C-j8IzajXo5qlvnuy5WFfe43ALwJ-0JFh2K6Xx2ccw,2332
23
23
  ara_cli/filename_validator.py,sha256=Aw9PL8d5-Ymhp3EY6lDrUBk3cudaNqo1Uw5RzPpI1jA,118
24
- ara_cli/list_filter.py,sha256=Not17hIngI37gZsLtIKxopB-BmyWoOGlBzSqBwh-Zpc,5273
25
- ara_cli/output_suppressor.py,sha256=ZByUwLH2DxOb-eJ31KQbtIziBKdykoyxvwxZ0tSammA,371
24
+ ara_cli/list_filter.py,sha256=qKGwwQsrWe7L5FbdxEbBYD1bbbi8c-RMypjXqXvLbgs,5291
25
+ ara_cli/output_suppressor.py,sha256=nwiHaQLwabOjMoJOeUESBnZszGMxrQZfJ3N2OvahX7Y,389
26
26
  ara_cli/prompt_chat.py,sha256=kd_OINDQFit6jN04bb7mzgY259JBbRaTaNp9F-webkc,1346
27
- ara_cli/prompt_extractor.py,sha256=a8LwPj6U8sG_v3SqDXQyPvDZQds4kHnYSO8eGissYJA,7503
28
- ara_cli/prompt_handler.py,sha256=ukYWR_AMDd8nfGkeUKyYQ8PO_-PfO8x9tV1kNcNWOT4,17672
29
- ara_cli/prompt_rag.py,sha256=vmlt4-rSboWibwgO_KUF79TK99YXT5KXjmbD9FeWdZY,7449
27
+ ara_cli/prompt_extractor.py,sha256=6l1Or7wuFNUDqbMg-NDXpFX-WMi2XV7YGH6hGDvow2o,7557
28
+ ara_cli/prompt_handler.py,sha256=v0254EUk1A2h0y1NmpkllVH6v6Q6i0Yh9qXkQMBXVY4,18544
29
+ ara_cli/prompt_rag.py,sha256=ydlhe4CUqz0jdzlY7jBbpKaf_5fjMrAZKnriKea3ZAg,7485
30
30
  ara_cli/run_file_lister.py,sha256=XbrrDTJXp1LFGx9Lv91SNsEHZPP-PyEMBF_P4btjbDA,2360
31
31
  ara_cli/tag_extractor.py,sha256=4krQyvmLR2ffhe7N7lWC7QjaxXcb90HaQdmjnBiD8ak,2523
32
- ara_cli/template_manager.py,sha256=YXPj2jGNDb-diIHFEK_vGJ-ZucodnXSGAPofKTnOofI,6633
33
- ara_cli/update_config_prompt.py,sha256=PZgNIN3dTw6p80GyX8Sp5apkAhSoykwnkEbHo3IOkUo,4571
34
- ara_cli/version.py,sha256=_1Hbcx99RFW-ZzwbZEWia-OGS77hOyexYcSKcpQKm_Y,146
32
+ ara_cli/template_manager.py,sha256=flMO3yXMUIgBxCgLdRtIhxezPDs5kfh3IASySSa1IKI,6669
33
+ ara_cli/update_config_prompt.py,sha256=Oy9vNTw6UhDohyTEfSKkqE5ifEMPlmWNYkKHgUrK_pY,4607
34
+ ara_cli/version.py,sha256=EeE9cgmnRIG8uXViIRiujIL8NEX4pIBGBfOof3tGM-c,146
35
35
  ara_cli/artefact_models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
36
36
  ara_cli/artefact_models/artefact_load.py,sha256=dNcwZDW2Dk0bts9YnPZ0ESmWD2NbsLIvl4Z-qQeGmTQ,401
37
37
  ara_cli/artefact_models/artefact_mapping.py,sha256=8aD0spBjkJ8toMAmFawc6UTUxB6-tEEViZXv2I-r88Q,1874
38
- ara_cli/artefact_models/artefact_model.py,sha256=nZZS6WyVuODblLnVTH6VZli--eFrDiwNMtjwHTs2KBI,14703
39
- ara_cli/artefact_models/artefact_templates.py,sha256=Vd7SwoRVKNGKZmxBKS6f9FE1ThUOCqZLScu0ClPfIu8,8321
38
+ ara_cli/artefact_models/artefact_model.py,sha256=FSn_DBaqvcCpybsq0jzaQOinkGPqTVJ6MOSHY6lZtTg,14979
39
+ ara_cli/artefact_models/artefact_templates.py,sha256=kEBmxFfySgO8ISgMRLDKhBl-n8o1mi7p49-Pf686YU0,8345
40
40
  ara_cli/artefact_models/businessgoal_artefact_model.py,sha256=jqYFMXjWle0YW9RvcFLDBAwy61bdT5VuDT_6lTOFzMw,4853
41
41
  ara_cli/artefact_models/capability_artefact_model.py,sha256=SZqHx4O2mj4urn77Stnj4_Jxtlq3-LgBBU9SMkByppI,3079
42
42
  ara_cli/artefact_models/epic_artefact_model.py,sha256=IadQWs6SWNcLgwvtOQWmYDyV9xLr3WwAsx-YMFan5fA,5765
@@ -44,7 +44,7 @@ ara_cli/artefact_models/example_artefact_model.py,sha256=UXrKbaPotg1jwcrVSdCeo-X
44
44
  ara_cli/artefact_models/feature_artefact_model.py,sha256=JvgBKsiI1Y5Cs_0Ygn6lttJIjCpEC9XyZieDExUnsvg,18386
45
45
  ara_cli/artefact_models/issue_artefact_model.py,sha256=v6CpKnkqiUh6Wch2kkEmyyW49c8ysdy1qz8l1Ft9uJA,2552
46
46
  ara_cli/artefact_models/keyfeature_artefact_model.py,sha256=a3MyAiePN9n_GTN6QkTvamdsaorwVUff6w-9CdRZSlo,4243
47
- ara_cli/artefact_models/serialize_helper.py,sha256=0XCruO70-fyfLfTn7pnt8NrSQe79eYNUAjuQaV8K6_8,586
47
+ ara_cli/artefact_models/serialize_helper.py,sha256=Wks30wy-UrwJURetydKykLgJkdGRgXFHkDT24vHe5tU,595
48
48
  ara_cli/artefact_models/task_artefact_model.py,sha256=kHMw_Tr-Ud3EeHWpRWy4jI0xFnPzGZ-FT52c5rSrT1k,3558
49
49
  ara_cli/artefact_models/userstory_artefact_model.py,sha256=u6G8wdeE2EpOsg1OPR-s8uShB4A77GfqN0vkSSuthFI,6582
50
50
  ara_cli/artefact_models/vision_artefact_model.py,sha256=KcNE3QQjyT29ZMMhCQo4pOcXKTkI6pXLvyfqoN2kuUQ,5920
@@ -70,7 +70,7 @@ ara_cli/templates/template.userstory,sha256=x6fouctaYl6I9gAyR8KXLVXec7oUT4uFm4Ec
70
70
  ara_cli/templates/template.userstory.prompt_log.md,sha256=Yp62iF7zDy2XNIwwJN35jKKSmezinK_JKbSvVuagtmA,205
71
71
  ara_cli/templates/template.vision,sha256=FZYcXUZtFOLvk0H1UEMhrJrDWZnS2CSmDv9feMkyYjU,594
72
72
  ara_cli/templates/template.vision.prompt_log.md,sha256=CAzBzj3O23CzrPIUq3xzpXGKn3_nAvyBLRUi-5Bnq_0,196
73
- ara_cli/templates/prompt-modules/blueprints/complete_pytest_unittest.blueprint.md,sha256=LEK9cfOPCxqVNMRJhdyBqSpCsl9mD7H_1QkVyqIKlCY,613
73
+ ara_cli/templates/prompt-modules/blueprints/complete_pytest_unittest.blueprint.md,sha256=DtZsdfVDNy9_cGE_Nn_TE2T3oRwr27kecZchOp5uIG0,672
74
74
  ara_cli/templates/prompt-modules/blueprints/empty.blueprint.md,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
75
75
  ara_cli/templates/prompt-modules/blueprints/task_todo_list_C4_architecture_analysis.blueprint.md,sha256=jEZrZaVK_pkRhLp1SpTX3xR6BGXkox6NafXEjX_GDvM,3099
76
76
  ara_cli/templates/prompt-modules/blueprints/task_todo_list_implement_feature_BDD_way.blueprint.md,sha256=EimfgzjhFIsZnJ0qMNbVlUQEU0vC9Sv1VupesLz7A1E,2088
@@ -132,27 +132,27 @@ ara_cli/templates/specification_breakdown_files/template.step.md,sha256=nzDRl9Xo
132
132
  ara_cli/templates/specification_breakdown_files/template.technology.exploration.md,sha256=zQyiJcmbUfXdte-5uZwZUpT6ey0zwfZ00P4VwI97jQk,2274
133
133
  ara_cli/templates/specification_breakdown_files/template.technology.md,sha256=bySiksz-8xtq0Nnj4svqe2MgUftWrVkbK9AcrDUE3KY,952
134
134
  tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
135
- tests/test_ara_command_action.py,sha256=Y5MrG6VjXgebliKfdFaCaS8i3GoZCGSLpj3AWCbL5Lk,25695
135
+ tests/test_ara_command_action.py,sha256=JTLqXM9BSMlU33OQgrk_sZnoowFJZKZAx8q-st-wa34,25821
136
136
  tests/test_ara_config.py,sha256=1LWby_iSestTIIqK-1clggL8kmbGGbtlYfsxAHaMMF8,2232
137
137
  tests/test_artefact_autofix.py,sha256=0Y6z5EXTEDUbZb-xxWNhaVUKnhxVVS3w2YZWTvyvoZs,12103
138
138
  tests/test_artefact_fuzzy_search.py,sha256=5Sh3_l9QK8-WHn6JpGPU1b6h4QEnl2JoMq1Tdp2cj1U,1261
139
- tests/test_artefact_link_updater.py,sha256=gN5KFF1uY7OoBh8Mr5jWpqXp02YCU5OSIpSU76Rm4Gs,2137
139
+ tests/test_artefact_link_updater.py,sha256=biqbEp2jCOz8giv72hu2P2hDfeJfJ9OrVGdAv5d9cK4,2191
140
140
  tests/test_artefact_lister.py,sha256=VCEOCgDgnAOeUUgIoGAbWgz60hf9UT-tdHg18LGfB34,22656
141
141
  tests/test_artefact_reader.py,sha256=660K-d8ed-j8hulsUB_7baPD2-hhbg9TffUR5yVc4Uo,927
142
- tests/test_artefact_renamer.py,sha256=N9JnnKBEJChTYEdltHoiDnn5UCNQUPj9H9YqOvLzbH0,3458
143
- tests/test_artefact_scan.py,sha256=R-L3JDWV1xH9fneGJoyDEjy2lvKpJLBck5ISTF2MmNs,7476
142
+ tests/test_artefact_renamer.py,sha256=lSnKCCfoFGgKhTdDZrEaeBq1xJAak1QoqH5aSeOe9Ro,3494
143
+ tests/test_artefact_scan.py,sha256=m1dws5d5m-dMnCn43BVhnNHHLU75zgVWaKIEK9y76fk,7512
144
144
  tests/test_chat.py,sha256=-00mni6Kik_RO8BGUpWqaL4S0wt2MbUBi5jD06dSHJM,47538
145
145
  tests/test_classifier.py,sha256=grYGPksydNdPsaEBQxYHZTuTdcJWz7VQtikCKA6BNaQ,1920
146
146
  tests/test_directory_navigator.py,sha256=7G0MVrBbtBvbrFUpL0zb_9EkEWi1dulWuHsrQxMJxDY,140
147
147
  tests/test_file_classifier.py,sha256=kLWPiePu3F5mkVuI_lK_2QlLh2kXD_Mt2K8KZZ1fAnA,10940
148
148
  tests/test_file_creator.py,sha256=D3G7MbgE0m8JmZihxnTryxLco6iZdbV--2CGc0L20FM,2109
149
- tests/test_file_lister.py,sha256=f6B_vIv-wAulKH2ZGgNg4SG79XqGGbfwoIvZlbEnYyM,4306
150
- tests/test_list_filter.py,sha256=gSRKirTtFuhRS3QlFHqWl89WvCvAdVEnFsCWTYmgB2o,7928
149
+ tests/test_file_lister.py,sha256=Q9HwhKKx540EPzTmfzOCnvtAgON0aMmpJE2eOe1J3EA,4324
150
+ tests/test_list_filter.py,sha256=fJA3d_SdaOAUkE7jn68MOVS0THXGghy1fye_64Zvo1U,7964
151
151
  tests/test_tag_extractor.py,sha256=nSiAYlTKZ7TLAOtcJpwK5zTWHhFYU0tI5xKnivLc1dU,2712
152
152
  tests/test_template_manager.py,sha256=q-LMHRG4rHkD6ON6YW4cpZxUx9hul6Or8wVVRC2kb-8,4099
153
- tests/test_update_config_prompt.py,sha256=vSsLvc18HZdVjVM93qXWVbJt752xTLL6VGjSVCrPufk,6729
154
- ara_cli-0.1.9.70.dist-info/METADATA,sha256=im6yqXsHO5Nxn5Jhckz1n16ZUWxDqGiQcDsYLi3xJoA,415
155
- ara_cli-0.1.9.70.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
156
- ara_cli-0.1.9.70.dist-info/entry_points.txt,sha256=v4h7MzysTgSIDYfEo3oj4Kz_8lzsRa3hq-KJHEcLVX8,45
157
- ara_cli-0.1.9.70.dist-info/top_level.txt,sha256=WM4cLHT5DYUaWzLtRj-gu3yVNFpGQ6lLRI3FMmC-38I,14
158
- ara_cli-0.1.9.70.dist-info/RECORD,,
153
+ tests/test_update_config_prompt.py,sha256=xsqj1WTn4BsG5Q2t-sNPfu7EoMURFcS-hfb5VSXUnJc,6765
154
+ ara_cli-0.1.9.72.dist-info/METADATA,sha256=IJcjoYAVthNFzAuL15h343sheK02UrnbDUJVTD3_2Xs,415
155
+ ara_cli-0.1.9.72.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
156
+ ara_cli-0.1.9.72.dist-info/entry_points.txt,sha256=v4h7MzysTgSIDYfEo3oj4Kz_8lzsRa3hq-KJHEcLVX8,45
157
+ ara_cli-0.1.9.72.dist-info/top_level.txt,sha256=WM4cLHT5DYUaWzLtRj-gu3yVNFpGQ6lLRI3FMmC-38I,14
158
+ ara_cli-0.1.9.72.dist-info/RECORD,,
@@ -439,7 +439,7 @@ def read_user_action(args):
439
439
  lambda x: x["title"] == artefact_name, artefact_info_dicts
440
440
  ))
441
441
 
442
- with open(artefact_info["file_path"], 'r') as file:
442
+ with open(artefact_info["file_path"], 'r', encoding='utf-8') as file:
443
443
  content = file.read()
444
444
  artefact = artefact_from_content(content)
445
445
 
@@ -514,8 +514,8 @@ def test_set_status_action(classifier, artefact_name, artefact_names, new_status
514
514
 
515
515
  # Verify the file was opened for reading and writing
516
516
  expected_file_path = next(info["file_path"] for info in artefact_info_dicts if info["title"] == artefact_name)
517
- mock_open.assert_any_call(expected_file_path, 'r')
518
- mock_open.assert_any_call(expected_file_path, 'w')
517
+ mock_open.assert_any_call(expected_file_path, 'r', encoding='utf-8')
518
+ mock_open.assert_any_call(expected_file_path, 'w', encoding='utf-8')
519
519
 
520
520
  # Verify the serialized content was written
521
521
  mock_file_handle.__enter__.return_value.write.assert_called_once_with("serialized_content")
@@ -593,14 +593,14 @@ def test_set_user_action(
593
593
  else:
594
594
  # Should open the file and read content
595
595
  expected_file_path = next(info["file_path"] for info in artefact_info_dicts if info["title"] == artefact_name)
596
- mock_open.assert_any_call(expected_file_path, 'r')
596
+ mock_open.assert_any_call(expected_file_path, 'r', encoding='utf-8')
597
597
  mock_artefact_from_content.assert_called_once_with(mock_file_content)
598
598
 
599
599
  # Should set the users attribute on the artefact
600
600
  assert mock_artefact.users == [new_user.lstrip('@') if new_user.startswith('@') else new_user]
601
601
 
602
602
  # Should write the serialized content back to the file
603
- mock_open.assert_any_call(expected_file_path, 'w')
603
+ mock_open.assert_any_call(expected_file_path, 'w', encoding='utf-8')
604
604
  mock_file_handle.__enter__.return_value.write.assert_called_once_with("serialized_content")
605
605
 
606
606
  # Should print a success message
@@ -655,7 +655,7 @@ def test_scan_action_with_issues(capsys):
655
655
  "\t\treason1\n"
656
656
  )
657
657
  assert captured.out == expected_output
658
- m.assert_called_once_with("incompatible_artefacts_report.md", "w")
658
+ m.assert_called_once_with("incompatible_artefacts_report.md", "w", encoding="utf-8")
659
659
  handle = m()
660
660
  expected_writes = [
661
661
  call("# Artefact Check Report\n\n"),
@@ -684,7 +684,7 @@ def test_scan_action_all_good(capsys):
684
684
 
685
685
  captured = capsys.readouterr()
686
686
  assert captured.out == "All files are good!\n"
687
- m.assert_called_once_with("incompatible_artefacts_report.md", "w")
687
+ m.assert_called_once_with("incompatible_artefacts_report.md", "w", encoding="utf-8")
688
688
  handle = m()
689
689
  handle.write.assert_has_calls([
690
690
  call("# Artefact Check Report\n\n"),
@@ -25,7 +25,7 @@ def test_update_links_in_related_artefacts(mock_isfile, mock_listdir):
25
25
  mock_file_handles = {filename: mock_open(read_data=content).return_value
26
26
  for filename, content in file_contents.items()}
27
27
  mock_open_function = mock_open()
28
- mock_open_function.side_effect = lambda file_path, mode='r': mock_file_handles[os.path.basename(file_path)]
28
+ mock_open_function.side_effect = lambda file_path, mode='r', encoding='utf-8': mock_file_handles[os.path.basename(file_path)]
29
29
 
30
30
  with patch("builtins.open", mock_open_function):
31
31
  # Instantiate the ArtefactLinkUpdater with the mocked file system
@@ -38,8 +38,8 @@ def test_update_links_in_related_artefacts(mock_isfile, mock_listdir):
38
38
  # Verify that the open function was called correctly for each file
39
39
  for filename in file_contents.keys():
40
40
  expected_file_path = os.path.join(dir_path, filename)
41
- mock_open_function.assert_any_call(expected_file_path, 'r')
42
- mock_open_function.assert_any_call(expected_file_path, 'w')
41
+ mock_open_function.assert_any_call(expected_file_path, 'r', encoding='utf-8')
42
+ mock_open_function.assert_any_call(expected_file_path, 'w', encoding='utf-8')
43
43
 
44
44
  # Verify the content of the files was updated correctly
45
45
  for filename, content in file_contents.items():
@@ -53,9 +53,9 @@ def test_update_title_in_artefact(mock_file, classifier, artefact_name, read_dat
53
53
  ar._update_title_in_artefact(artefact_path, new_title, classifier)
54
54
 
55
55
  # Check that the file was opened for reading
56
- mock_file.assert_any_call(artefact_path, 'r')
56
+ mock_file.assert_any_call(artefact_path, 'r', encoding='utf-8')
57
57
  # Check that the file was opened for writing
58
- mock_file.assert_any_call(artefact_path, 'w')
58
+ mock_file.assert_any_call(artefact_path, 'w', encoding='utf-8')
59
59
  # Check that the file write was called with the correct new content
60
60
  expected_content = read_data.replace(f"{read_data_prefix}{old_title}", f"{read_data_prefix}{new_title}")
61
61
  mock_file().write.assert_called_with(expected_content)
@@ -120,7 +120,7 @@ def test_show_results_no_issues(capsys):
120
120
  show_results(invalid_artefacts)
121
121
  captured = capsys.readouterr()
122
122
  assert captured.out == "All files are good!\n"
123
- m.assert_called_once_with("incompatible_artefacts_report.md", "w")
123
+ m.assert_called_once_with("incompatible_artefacts_report.md", "w", encoding="utf-8")
124
124
  handle = m()
125
125
  handle.write.assert_has_calls([
126
126
  call("# Artefact Check Report\n\n"),
@@ -147,7 +147,7 @@ def test_show_results_with_issues(capsys):
147
147
  "\t\treason3\n"
148
148
  )
149
149
  assert captured.out == expected_output
150
- m.assert_called_once_with("incompatible_artefacts_report.md", "w")
150
+ m.assert_called_once_with("incompatible_artefacts_report.md", "w", encoding="utf-8")
151
151
  handle = m()
152
152
  expected_writes = [
153
153
  call("# Artefact Check Report\n\n"),
tests/test_file_lister.py CHANGED
@@ -55,7 +55,7 @@ def test_generate_markdown_listing_multiple_directories(setup_test_environment):
55
55
 
56
56
  generate_markdown_listing([temp_dir, another_temp_dir], ['*.py'], output_file_path)
57
57
 
58
- with open(output_file_path, 'r') as f:
58
+ with open(output_file_path, 'r', encoding='utf-8') as f:
59
59
  output_content = f.read().splitlines()
60
60
 
61
61
  assert output_content == expected_content
tests/test_list_filter.py CHANGED
@@ -65,7 +65,7 @@ def test_default_content_retrieval():
65
65
  with patch("builtins.open", mock_open(read_data=mock_data)) as mocked_file:
66
66
  content = ListFilterMonad.default_content_retrieval("dummy_path")
67
67
  assert content == mock_data
68
- mocked_file.assert_called_once_with("dummy_path", 'r')
68
+ mocked_file.assert_called_once_with("dummy_path", 'r', encoding='utf-8')
69
69
 
70
70
 
71
71
  def test_default_tag_retrieval():
@@ -77,7 +77,7 @@ def test_default_content_retrieval_exception():
77
77
  with patch("builtins.open", side_effect=Exception("Mocked exception")) as mocked_file:
78
78
  content = ListFilterMonad.default_content_retrieval("dummy_path")
79
79
  assert content == "" # Expect empty string on exception
80
- mocked_file.assert_called_once_with("dummy_path", 'r')
80
+ mocked_file.assert_called_once_with("dummy_path", 'r', encoding='utf-8')
81
81
 
82
82
 
83
83
  @pytest.mark.parametrize("include, exclude, expected", [
@@ -34,13 +34,13 @@ mock_content_updated = """
34
34
  def test_read_file():
35
35
  with patch('builtins.open', mock_open(read_data="data")) as mocked_file:
36
36
  result = read_file('dummy_path')
37
- mocked_file.assert_called_once_with('dummy_path', 'r')
37
+ mocked_file.assert_called_once_with('dummy_path', 'r', encoding='utf-8')
38
38
  assert result == "data"
39
39
 
40
40
  def test_write_file():
41
41
  with patch('builtins.open', mock_open()) as mocked_file:
42
42
  write_file('dummy_path', 'data')
43
- mocked_file.assert_called_once_with('dummy_path', 'w')
43
+ mocked_file.assert_called_once_with('dummy_path', 'w', encoding='utf-8')
44
44
  mocked_file().write.assert_called_once_with('data')
45
45
 
46
46
  def test_find_checked_items():