aiverify-moonshot 0.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. aiverify_moonshot-0.4.0.dist-info/METADATA +249 -0
  2. aiverify_moonshot-0.4.0.dist-info/RECORD +163 -0
  3. aiverify_moonshot-0.4.0.dist-info/WHEEL +4 -0
  4. aiverify_moonshot-0.4.0.dist-info/licenses/AUTHORS.md +5 -0
  5. aiverify_moonshot-0.4.0.dist-info/licenses/LICENSE.md +201 -0
  6. aiverify_moonshot-0.4.0.dist-info/licenses/NOTICES.md +3340 -0
  7. moonshot/__init__.py +0 -0
  8. moonshot/__main__.py +198 -0
  9. moonshot/api.py +155 -0
  10. moonshot/integrations/__init__.py +0 -0
  11. moonshot/integrations/cli/__init__.py +0 -0
  12. moonshot/integrations/cli/__main__.py +25 -0
  13. moonshot/integrations/cli/active_session_cfg.py +1 -0
  14. moonshot/integrations/cli/benchmark/__init__.py +0 -0
  15. moonshot/integrations/cli/benchmark/benchmark.py +186 -0
  16. moonshot/integrations/cli/benchmark/cookbook.py +545 -0
  17. moonshot/integrations/cli/benchmark/datasets.py +164 -0
  18. moonshot/integrations/cli/benchmark/metrics.py +141 -0
  19. moonshot/integrations/cli/benchmark/recipe.py +598 -0
  20. moonshot/integrations/cli/benchmark/result.py +216 -0
  21. moonshot/integrations/cli/benchmark/run.py +140 -0
  22. moonshot/integrations/cli/benchmark/runner.py +174 -0
  23. moonshot/integrations/cli/cli.py +64 -0
  24. moonshot/integrations/cli/common/__init__.py +0 -0
  25. moonshot/integrations/cli/common/common.py +72 -0
  26. moonshot/integrations/cli/common/connectors.py +325 -0
  27. moonshot/integrations/cli/common/display_helper.py +42 -0
  28. moonshot/integrations/cli/common/prompt_template.py +94 -0
  29. moonshot/integrations/cli/initialisation/__init__.py +0 -0
  30. moonshot/integrations/cli/initialisation/initialisation.py +14 -0
  31. moonshot/integrations/cli/redteam/__init__.py +0 -0
  32. moonshot/integrations/cli/redteam/attack_module.py +70 -0
  33. moonshot/integrations/cli/redteam/context_strategy.py +147 -0
  34. moonshot/integrations/cli/redteam/prompt_template.py +67 -0
  35. moonshot/integrations/cli/redteam/redteam.py +90 -0
  36. moonshot/integrations/cli/redteam/session.py +467 -0
  37. moonshot/integrations/web_api/.env.dev +7 -0
  38. moonshot/integrations/web_api/__init__.py +0 -0
  39. moonshot/integrations/web_api/__main__.py +56 -0
  40. moonshot/integrations/web_api/app.py +125 -0
  41. moonshot/integrations/web_api/container.py +146 -0
  42. moonshot/integrations/web_api/log/.gitkeep +0 -0
  43. moonshot/integrations/web_api/logging_conf.py +114 -0
  44. moonshot/integrations/web_api/routes/__init__.py +0 -0
  45. moonshot/integrations/web_api/routes/attack_modules.py +66 -0
  46. moonshot/integrations/web_api/routes/benchmark.py +116 -0
  47. moonshot/integrations/web_api/routes/benchmark_result.py +175 -0
  48. moonshot/integrations/web_api/routes/context_strategy.py +129 -0
  49. moonshot/integrations/web_api/routes/cookbook.py +225 -0
  50. moonshot/integrations/web_api/routes/dataset.py +120 -0
  51. moonshot/integrations/web_api/routes/endpoint.py +282 -0
  52. moonshot/integrations/web_api/routes/metric.py +78 -0
  53. moonshot/integrations/web_api/routes/prompt_template.py +128 -0
  54. moonshot/integrations/web_api/routes/recipe.py +219 -0
  55. moonshot/integrations/web_api/routes/redteam.py +609 -0
  56. moonshot/integrations/web_api/routes/runner.py +239 -0
  57. moonshot/integrations/web_api/schemas/__init__.py +0 -0
  58. moonshot/integrations/web_api/schemas/benchmark_runner_dto.py +13 -0
  59. moonshot/integrations/web_api/schemas/cookbook_create_dto.py +19 -0
  60. moonshot/integrations/web_api/schemas/cookbook_response_model.py +9 -0
  61. moonshot/integrations/web_api/schemas/dataset_response_dto.py +9 -0
  62. moonshot/integrations/web_api/schemas/endpoint_create_dto.py +21 -0
  63. moonshot/integrations/web_api/schemas/endpoint_response_model.py +11 -0
  64. moonshot/integrations/web_api/schemas/prompt_response_model.py +14 -0
  65. moonshot/integrations/web_api/schemas/prompt_template_response_model.py +10 -0
  66. moonshot/integrations/web_api/schemas/recipe_create_dto.py +32 -0
  67. moonshot/integrations/web_api/schemas/recipe_response_model.py +7 -0
  68. moonshot/integrations/web_api/schemas/session_create_dto.py +16 -0
  69. moonshot/integrations/web_api/schemas/session_prompt_dto.py +7 -0
  70. moonshot/integrations/web_api/schemas/session_response_model.py +38 -0
  71. moonshot/integrations/web_api/services/__init__.py +0 -0
  72. moonshot/integrations/web_api/services/attack_module_service.py +34 -0
  73. moonshot/integrations/web_api/services/auto_red_team_test_manager.py +86 -0
  74. moonshot/integrations/web_api/services/auto_red_team_test_state.py +57 -0
  75. moonshot/integrations/web_api/services/base_service.py +8 -0
  76. moonshot/integrations/web_api/services/benchmark_result_service.py +25 -0
  77. moonshot/integrations/web_api/services/benchmark_test_manager.py +106 -0
  78. moonshot/integrations/web_api/services/benchmark_test_state.py +56 -0
  79. moonshot/integrations/web_api/services/benchmarking_service.py +31 -0
  80. moonshot/integrations/web_api/services/context_strategy_service.py +22 -0
  81. moonshot/integrations/web_api/services/cookbook_service.py +194 -0
  82. moonshot/integrations/web_api/services/dataset_service.py +20 -0
  83. moonshot/integrations/web_api/services/endpoint_service.py +65 -0
  84. moonshot/integrations/web_api/services/metric_service.py +14 -0
  85. moonshot/integrations/web_api/services/prompt_template_service.py +39 -0
  86. moonshot/integrations/web_api/services/recipe_service.py +155 -0
  87. moonshot/integrations/web_api/services/runner_service.py +147 -0
  88. moonshot/integrations/web_api/services/session_service.py +350 -0
  89. moonshot/integrations/web_api/services/utils/exceptions_handler.py +41 -0
  90. moonshot/integrations/web_api/services/utils/results_formatter.py +47 -0
  91. moonshot/integrations/web_api/status_updater/interface/benchmark_progress_callback.py +14 -0
  92. moonshot/integrations/web_api/status_updater/interface/redteam_progress_callback.py +14 -0
  93. moonshot/integrations/web_api/status_updater/moonshot_ui_webhook.py +72 -0
  94. moonshot/integrations/web_api/types/types.py +99 -0
  95. moonshot/src/__init__.py +0 -0
  96. moonshot/src/api/__init__.py +0 -0
  97. moonshot/src/api/api_connector.py +58 -0
  98. moonshot/src/api/api_connector_endpoint.py +162 -0
  99. moonshot/src/api/api_context_strategy.py +57 -0
  100. moonshot/src/api/api_cookbook.py +160 -0
  101. moonshot/src/api/api_dataset.py +46 -0
  102. moonshot/src/api/api_environment_variables.py +17 -0
  103. moonshot/src/api/api_metrics.py +51 -0
  104. moonshot/src/api/api_prompt_template.py +43 -0
  105. moonshot/src/api/api_recipe.py +182 -0
  106. moonshot/src/api/api_red_teaming.py +59 -0
  107. moonshot/src/api/api_result.py +84 -0
  108. moonshot/src/api/api_run.py +74 -0
  109. moonshot/src/api/api_runner.py +132 -0
  110. moonshot/src/api/api_session.py +290 -0
  111. moonshot/src/configs/__init__.py +0 -0
  112. moonshot/src/configs/env_variables.py +187 -0
  113. moonshot/src/connectors/__init__.py +0 -0
  114. moonshot/src/connectors/connector.py +327 -0
  115. moonshot/src/connectors/connector_prompt_arguments.py +17 -0
  116. moonshot/src/connectors_endpoints/__init__.py +0 -0
  117. moonshot/src/connectors_endpoints/connector_endpoint.py +211 -0
  118. moonshot/src/connectors_endpoints/connector_endpoint_arguments.py +54 -0
  119. moonshot/src/cookbooks/__init__.py +0 -0
  120. moonshot/src/cookbooks/cookbook.py +225 -0
  121. moonshot/src/cookbooks/cookbook_arguments.py +34 -0
  122. moonshot/src/datasets/__init__.py +0 -0
  123. moonshot/src/datasets/dataset.py +255 -0
  124. moonshot/src/datasets/dataset_arguments.py +50 -0
  125. moonshot/src/metrics/__init__.py +0 -0
  126. moonshot/src/metrics/metric.py +192 -0
  127. moonshot/src/metrics/metric_interface.py +95 -0
  128. moonshot/src/prompt_templates/__init__.py +0 -0
  129. moonshot/src/prompt_templates/prompt_template.py +103 -0
  130. moonshot/src/recipes/__init__.py +0 -0
  131. moonshot/src/recipes/recipe.py +340 -0
  132. moonshot/src/recipes/recipe_arguments.py +111 -0
  133. moonshot/src/redteaming/__init__.py +0 -0
  134. moonshot/src/redteaming/attack/__init__.py +0 -0
  135. moonshot/src/redteaming/attack/attack_module.py +618 -0
  136. moonshot/src/redteaming/attack/attack_module_arguments.py +44 -0
  137. moonshot/src/redteaming/attack/context_strategy.py +131 -0
  138. moonshot/src/redteaming/context_strategy/__init__.py +0 -0
  139. moonshot/src/redteaming/context_strategy/context_strategy_interface.py +46 -0
  140. moonshot/src/redteaming/session/__init__.py +0 -0
  141. moonshot/src/redteaming/session/chat.py +209 -0
  142. moonshot/src/redteaming/session/red_teaming_progress.py +128 -0
  143. moonshot/src/redteaming/session/red_teaming_type.py +6 -0
  144. moonshot/src/redteaming/session/session.py +775 -0
  145. moonshot/src/results/__init__.py +0 -0
  146. moonshot/src/results/result.py +119 -0
  147. moonshot/src/results/result_arguments.py +44 -0
  148. moonshot/src/runners/__init__.py +0 -0
  149. moonshot/src/runners/runner.py +476 -0
  150. moonshot/src/runners/runner_arguments.py +46 -0
  151. moonshot/src/runners/runner_type.py +6 -0
  152. moonshot/src/runs/__init__.py +0 -0
  153. moonshot/src/runs/run.py +344 -0
  154. moonshot/src/runs/run_arguments.py +162 -0
  155. moonshot/src/runs/run_progress.py +145 -0
  156. moonshot/src/runs/run_status.py +10 -0
  157. moonshot/src/storage/__init__.py +0 -0
  158. moonshot/src/storage/db_interface.py +128 -0
  159. moonshot/src/storage/io_interface.py +31 -0
  160. moonshot/src/storage/storage.py +525 -0
  161. moonshot/src/utils/__init__.py +0 -0
  162. moonshot/src/utils/import_modules.py +96 -0
  163. moonshot/src/utils/timeit.py +25 -0
@@ -0,0 +1,128 @@
1
+ from __future__ import annotations
2
+
3
+ from abc import abstractmethod
4
+ from typing import Any
5
+
6
+
7
+ class DBInterface:
8
+ @abstractmethod
9
+ def create_connection(self) -> Any | None:
10
+ """
11
+ This method is used to create a connection to the database. The details of the connection
12
+ are implementation specific and should be provided by the concrete class that inherits from this abstract class.
13
+
14
+ Returns:
15
+ Any | None: The connection object if successful, None otherwise.
16
+ """
17
+ pass
18
+
19
+ @abstractmethod
20
+ def close_connection(self) -> None:
21
+ """
22
+ This method is used to close the connection to the database. The details of the operation
23
+ are implementation specific and should be provided by the concrete class that inherits from this abstract class.
24
+ """
25
+ pass
26
+
27
+ @abstractmethod
28
+ def create_table(self, create_table_sql: str) -> None:
29
+ """
30
+ This method is used to create a table in the database. The details of the operation
31
+ are implementation specific and should be provided by the concrete class that inherits from this abstract class.
32
+
33
+ Args:
34
+ create_table_sql (str): The SQL query to create a table.
35
+
36
+ Returns:
37
+ None
38
+ """
39
+ pass
40
+
41
+ @abstractmethod
42
+ def create_record(self, record: tuple, create_record_sql: str) -> None:
43
+ """
44
+ This method is used to create a record in the database. The details of the operation
45
+ are implementation specific and should be provided by the concrete class that inherits from this abstract class.
46
+
47
+ Args:
48
+ record (tuple): The record to be created.
49
+ create_record_sql (str): The SQL query to create a record.
50
+
51
+ Returns:
52
+ None
53
+ """
54
+ pass
55
+
56
+ @abstractmethod
57
+ def read_record(self, record: tuple, read_record_sql: str) -> tuple | None:
58
+ """
59
+ This method is used to read a record from the database. The details of the operation
60
+ are implementation specific and should be provided by the concrete class that inherits from this abstract class.
61
+
62
+ Args:
63
+ record (tuple): The record to be read.
64
+ read_record_sql (str): The SQL query to read a record.
65
+
66
+ Returns:
67
+ tuple | None: The record if found, None otherwise.
68
+ """
69
+ pass
70
+
71
+ @abstractmethod
72
+ def read_records(self, read_records_sql: str) -> list[tuple] | None:
73
+ """
74
+ Executes a SQL query to read data from a table and returns the results.
75
+
76
+ This method attempts to execute a provided SQL query to read data from a table within the SQLite database.
77
+ If the connection to the database is established, it executes the query and returns all fetched rows as a list.
78
+ In case of an error during the execution of the query, it prints an error message detailing the issue.
79
+
80
+ Args:
81
+ read_records_sql (str): The SQL query string used to read data from a table.
82
+
83
+ Returns:
84
+ list | None: A list of tuples representing the rows fetched by the query if successful.
85
+ """
86
+ pass
87
+
88
+ @abstractmethod
89
+ def update_record(self, record: tuple, update_record_sql: str) -> None:
90
+ """
91
+ This method is used to update a record in the database. The details of the operation
92
+ are implementation specific and should be provided by the concrete class that inherits from this abstract class.
93
+
94
+ Args:
95
+ record (tuple): The record to be updated.
96
+ update_record_sql (str): The SQL query to update a record.
97
+
98
+ Returns:
99
+ None
100
+ """
101
+ pass
102
+
103
+ @abstractmethod
104
+ def check_database_table_exists(self, table_name: str) -> bool:
105
+ """
106
+ This method is used to check if a table exists in the database.
107
+
108
+ Args:
109
+ table_name (str): The name of the table to check for existence.
110
+
111
+ Returns:
112
+ bool: True if the table exists, False otherwise.
113
+ """
114
+ pass
115
+
116
+ @abstractmethod
117
+ def delete_database_table(self, delete_table_sql: str) -> None:
118
+ """
119
+ This method is used to delete a table from the database. The details of the operation
120
+ are implementation specific and should be provided by the concrete class that inherits from this abstract class.
121
+
122
+ Args:
123
+ table_name (str): The name of the table to be deleted.
124
+
125
+ Returns:
126
+ None
127
+ """
128
+ pass
@@ -0,0 +1,31 @@
1
+ from abc import abstractmethod
2
+ from typing import Any
3
+
4
+
5
+ class IOInterface:
6
+ @abstractmethod
7
+ def create_file(self, data: dict) -> Any | None:
8
+ """
9
+ Creates a file with the given data.
10
+
11
+ Args:
12
+ data (dict): The data to be written to the file.
13
+
14
+ Returns:
15
+ Any | None: The result of the file creation operation, or None if the operation was unsuccessful.
16
+ """
17
+ pass
18
+
19
+ @abstractmethod
20
+ def read_file(self, filepath: str) -> dict | None:
21
+ """
22
+ Reads the file at the given filepath.
23
+
24
+ Args:
25
+ filepath (str): The path to the file to be read.
26
+
27
+ Returns:
28
+ dict | None: The data from the file if the file was successfully read, or None if the operation was
29
+ unsuccessful.
30
+ """
31
+ pass
@@ -0,0 +1,525 @@
1
+ import datetime
2
+ import glob
3
+ import os
4
+ from itertools import chain
5
+ from pathlib import Path
6
+ from typing import Iterator
7
+
8
+ import xxhash
9
+ from pydantic import validate_call
10
+
11
+ from moonshot.src.configs.env_variables import EnvironmentVars, EnvVariables
12
+ from moonshot.src.storage.db_interface import DBInterface
13
+ from moonshot.src.utils.import_modules import get_instance
14
+
15
+
16
+ class Storage:
17
+ @staticmethod
18
+ @validate_call
19
+ def create_object(
20
+ obj_type: str,
21
+ obj_id: str,
22
+ obj_info: dict,
23
+ obj_extension: str,
24
+ obj_mod_type: str = "jsonio",
25
+ ) -> bool:
26
+ """
27
+ Writes the object information to a file.
28
+
29
+ Args:
30
+ obj_type (str): The type of the object (e.g., 'recipe', 'cookbook').
31
+ obj_id (str): The ID of the object.
32
+ obj_info (dict): A dictionary containing the object information.
33
+ obj_extension (str): The file extension (e.g., 'json', 'py').
34
+ obj_mod_type (str, optional): The module type for object serialization. Defaults to 'jsonio'.
35
+ """
36
+ if not hasattr(EnvironmentVars, obj_type):
37
+ raise RuntimeError(
38
+ f"'{obj_type}' is not a recognized EnvironmentVar value."
39
+ )
40
+
41
+ obj_filepath = Storage.get_filepath(obj_type, obj_id, obj_extension, True)
42
+ if obj_filepath and isinstance(obj_filepath, str):
43
+ obj_mod_instance = get_instance(
44
+ obj_mod_type,
45
+ Storage.get_filepath(EnvVariables.IO_MODULES.name, obj_mod_type, "py"),
46
+ )
47
+ if obj_mod_instance and callable(obj_mod_instance):
48
+ return obj_mod_instance(obj_filepath).create_file(obj_info)
49
+ else:
50
+ raise RuntimeError(
51
+ f"Unable to get defined object module instance - {obj_mod_instance}"
52
+ )
53
+ else:
54
+ raise RuntimeError("Unable to create object.")
55
+
56
+ @staticmethod
57
+ @validate_call
58
+ def read_object_with_iterator(
59
+ obj_type: str,
60
+ obj_id: str,
61
+ obj_extension: str,
62
+ obj_mod_type: str = "jsonio",
63
+ json_keys: list[str] | None = None,
64
+ iterator_keys: list[str] | None = None,
65
+ ) -> dict:
66
+ """
67
+ Retrieves object data from a file, with options to filter by specific keys for efficient data handling.
68
+
69
+ This function reads from a file corresponding to the given object type and ID, deserializing the content
70
+ using the specified module. It can also selectively extract values or create iterators for specified keys,
71
+ which is useful for handling large datasets or deeply nested JSON structures.
72
+
73
+ Args:
74
+ obj_type (str): The category of the object to read (e.g., 'recipe', 'cookbook').
75
+ obj_id (str): The unique identifier for the object.
76
+ obj_extension (str): The file extension indicating the file type (e.g., 'json', 'py').
77
+ obj_mod_type (str, optional): The deserialization module type to use. Defaults to 'jsonio'.
78
+ json_keys (list[str] | None, optional): Keys for which to directly extract values from the file.
79
+ If provided, only these keys will be included in the returned dictionary.
80
+ iterator_keys (list[str] | None, optional): Keys for which to create iterators, allowing for
81
+ streamed access to large or nested structures within the file.
82
+
83
+ Returns:
84
+ dict: A dictionary with the requested object data. If `json_keys` is provided, the dictionary
85
+ will contain only the specified keys and their values. If `iterator_keys` is provided, the dictionary
86
+ will include iterators for the specified keys, enabling streamed data access.
87
+ """
88
+ if not hasattr(EnvironmentVars, obj_type):
89
+ raise RuntimeError(
90
+ f"'{obj_type}' is not a recognized EnvironmentVar value."
91
+ )
92
+
93
+ obj_filepath = Storage.get_filepath(obj_type, obj_id, obj_extension)
94
+ if obj_filepath and isinstance(obj_filepath, str):
95
+ obj_mod_instance = get_instance(
96
+ obj_mod_type,
97
+ Storage.get_filepath(EnvVariables.IO_MODULES.name, obj_mod_type, "py"),
98
+ )
99
+ if obj_mod_instance and callable(obj_mod_instance):
100
+ return obj_mod_instance(obj_filepath).read_file_iterator(
101
+ json_keys, iterator_keys
102
+ )
103
+ else:
104
+ raise RuntimeError(
105
+ f"Unable to get defined object module instance - {obj_mod_instance}"
106
+ )
107
+ else:
108
+ raise RuntimeError(f"No {obj_type.lower()} found with ID: {obj_id}")
109
+
110
+ @staticmethod
111
+ @validate_call
112
+ def read_object(
113
+ obj_type: str, obj_id: str, obj_extension: str, obj_mod_type: str = "jsonio"
114
+ ) -> dict:
115
+ """
116
+ Reads the object information from a file.
117
+
118
+ Args:
119
+ obj_type (str): The type of the object (e.g., 'recipe', 'cookbook').
120
+ obj_id (str): The ID of the object.
121
+ obj_extension (str): The file extension (e.g., 'json', 'py').
122
+ obj_mod_type (str, optional): The module type for object deserialization. Defaults to 'json'.
123
+
124
+ Returns:
125
+ dict: A dictionary containing the object information.
126
+ """
127
+ if not hasattr(EnvironmentVars, obj_type):
128
+ raise RuntimeError(
129
+ f"'{obj_type}' is not a recognized EnvironmentVar value."
130
+ )
131
+
132
+ obj_filepath = Storage.get_filepath(obj_type, obj_id, obj_extension)
133
+ if obj_filepath and isinstance(obj_filepath, str):
134
+ obj_mod_instance = get_instance(
135
+ obj_mod_type,
136
+ Storage.get_filepath(EnvVariables.IO_MODULES.name, obj_mod_type, "py"),
137
+ )
138
+ if obj_mod_instance and callable(obj_mod_instance):
139
+ return obj_mod_instance(obj_filepath).read_file()
140
+ else:
141
+ raise RuntimeError(
142
+ f"Unable to get defined object module instance - {obj_mod_instance}"
143
+ )
144
+ else:
145
+ raise RuntimeError(f"No {obj_type.lower()} found with ID: {obj_id}")
146
+
147
+ @staticmethod
148
+ @validate_call
149
+ def delete_object(obj_type: str, obj_id: str, obj_extension: str) -> bool:
150
+ """
151
+ Deletes the specified object file.
152
+
153
+ Args:
154
+ obj_type (str): The type of the object (e.g., 'recipe', 'cookbook').
155
+ obj_id (str): The ID of the object.
156
+ obj_extension (str): The file extension (e.g., 'json', 'py').
157
+
158
+ Returns:
159
+ bool: True if the file was successfully deleted.
160
+
161
+ Raises:
162
+ RuntimeError: If no file is found with the specified ID and type.
163
+ """
164
+ obj_filepath = Storage.get_filepath(obj_type, obj_id, obj_extension)
165
+ if obj_filepath:
166
+ Path(obj_filepath).unlink()
167
+ return True
168
+ else:
169
+ raise RuntimeError(f"No {obj_type.lower()} found with ID: {obj_id}")
170
+
171
+ @staticmethod
172
+ def count_objects(
173
+ obj_type: str, obj_id: str, obj_extension: str, item_path: str
174
+ ) -> int:
175
+ """
176
+ Counts the number of objects in a dataset without loading them fully into memory.
177
+
178
+ Args:
179
+ obj_type (str): The type of the object (e.g., 'dataset').
180
+ obj_id (str): The ID of the object.
181
+ obj_extension (str): The file extension (e.g., 'json').
182
+ item_path (str): The path to the items in the JSON file.
183
+
184
+ Returns:
185
+ int: The count of the objects.
186
+ """
187
+ count = 0
188
+ generator = Storage.read_object_with_iterator(
189
+ obj_type, obj_id, obj_extension, iterator_keys=[item_path]
190
+ )
191
+ for _ in generator[item_path.split(".")[0]]:
192
+ count += 1
193
+ return count
194
+
195
+ @staticmethod
196
+ @validate_call
197
+ def get_objects(obj_type: str, obj_extension: str) -> Iterator[str]:
198
+ """
199
+ Retrieves all the object files with the specified extension from one or more directories.
200
+
201
+ Args:
202
+ obj_type (str): The type of the object (e.g., 'recipe', 'cookbook').
203
+ obj_extension (str): The file extension (e.g., 'json', 'py').
204
+
205
+ Returns:
206
+ Iterator[str]: An iterator that yields the filepaths of the object files.
207
+ """
208
+ if not hasattr(EnvironmentVars, obj_type):
209
+ raise RuntimeError(
210
+ f"'{obj_type}' is not a recognized EnvironmentVar value."
211
+ )
212
+
213
+ directories = EnvironmentVars.get_file_directory(obj_type)
214
+ return chain.from_iterable(
215
+ glob.iglob(f"{directory}/*.{obj_extension}") for directory in directories
216
+ )
217
+
218
+ @staticmethod
219
+ def get_creation_datetime(
220
+ obj_type: str, obj_id: str, obj_extension: str
221
+ ) -> datetime.datetime:
222
+ """
223
+ Retrieves the creation datetime of an object.
224
+
225
+ Args:
226
+ obj_type (str): The type of the object (e.g., 'recipe', 'cookbook').
227
+ obj_id (str): The ID of the object.
228
+ obj_extension (str): The file extension (e.g., 'json', 'py').
229
+
230
+ Returns:
231
+ datetime: The creation datetime of the object.
232
+ """
233
+ obj_filepath = Storage.get_filepath(obj_type, obj_id, obj_extension)
234
+ if obj_filepath:
235
+ creation_timestamp = os.path.getctime(obj_filepath)
236
+ creation_datetime = datetime.datetime.fromtimestamp(creation_timestamp)
237
+ return creation_datetime
238
+ else:
239
+ raise RuntimeError(f"No {obj_type.lower()} found with ID: {obj_id}")
240
+
241
+ @staticmethod
242
+ def get_file_hash(obj_type: str, obj_id: str, obj_extension: str) -> str:
243
+ """
244
+ Retrieves the hash of the file content for an object using the 'xxhash' library, which provides
245
+ an extremely fast non-cryptographic hash algorithm.
246
+
247
+ Args:
248
+ obj_type (str): The type of the object (e.g., 'recipe', 'cookbook').
249
+ obj_id (str): The ID of the object.
250
+ obj_extension (str): The file extension (e.g., 'json', 'py').
251
+
252
+ Returns:
253
+ str: The hex digest of the xxHash of the file content.
254
+ """
255
+ obj_filepath = Storage.get_filepath(obj_type, obj_id, obj_extension)
256
+ if obj_filepath and Path(obj_filepath).exists():
257
+ with open(obj_filepath, "rb") as file:
258
+ file_content = file.read()
259
+ file_hash = xxhash.xxh64(file_content).hexdigest()
260
+ return file_hash
261
+ else:
262
+ raise RuntimeError(f"No {obj_type.lower()} found with ID: {obj_id}")
263
+
264
+ @staticmethod
265
+ def get_filepath(
266
+ obj_type: str, obj_id: str, obj_extension: str, ignore_existance: bool = False
267
+ ) -> str:
268
+ """
269
+ Retrieves the file path for an object.
270
+
271
+ This method uses the provided object type, object ID, and object extension to construct the file path
272
+ of the object. If the creation flag is set to True, it returns the file path even if the file does not exist.
273
+
274
+ Args:
275
+ obj_type (str): The type of the object (e.g., 'recipe', 'cookbook').
276
+ obj_id (str): The ID of the object.
277
+ obj_extension (str): The file extension (e.g., 'json', 'py').
278
+ ignore_existance (bool, optional): A flag indicating whether to return the file path
279
+ even if the file does not exist. Defaults to False.
280
+
281
+ Returns:
282
+ str: The file path of the object.
283
+ """
284
+ return EnvironmentVars.get_file_path(
285
+ obj_type, f"{obj_id}.{obj_extension}", ignore_existance
286
+ )
287
+
288
+ @staticmethod
289
+ @validate_call
290
+ def is_object_exists(obj_type: str, obj_id: str, obj_extension: str) -> bool:
291
+ """
292
+ Checks if the object exists.
293
+
294
+ Args:
295
+ obj_type (str): The type of the object (e.g., 'runner', 'recipe', 'cookbook').
296
+ obj_id (str): The ID of the object.
297
+ obj_extension (str): The file extension (e.g., 'json', 'py').
298
+
299
+ Returns:
300
+ bool: True if the object exists, False otherwise.
301
+ """
302
+ filepath = Storage.get_filepath(obj_type, obj_id, obj_extension)
303
+ if filepath:
304
+ return Path(filepath).exists()
305
+ else:
306
+ return False
307
+
308
+ @staticmethod
309
+ def create_database_connection(
310
+ obj_type: str, obj_id: str, obj_extension: str, db_mod_type: str = "sqlite"
311
+ ) -> DBInterface:
312
+ """
313
+ Establishes a database connection for a specific object.
314
+
315
+ Args:
316
+ obj_type (str): The type of the object for which the database connection is to be established
317
+ (e.g., 'runner', 'recipe', 'cookbook').
318
+
319
+ obj_id (str): The unique identifier of the object for which the database connection is to be established.
320
+
321
+ obj_extension (str): The file extension of the object for which the database connection is to be established
322
+ (e.g., 'json', 'py').
323
+
324
+ db_mod_type (str): The type of database module to use for establishing the connection (e.g., 'sqlite).
325
+ Defaults to 'sqlite'.
326
+
327
+ Returns:
328
+ DBInterface: An instance of the database accessor, which can be used to interact with the database.
329
+ """
330
+ database_instance = get_instance(
331
+ db_mod_type,
332
+ Storage.get_filepath(
333
+ EnvVariables.DATABASES_MODULES.name, db_mod_type, "py"
334
+ ),
335
+ )
336
+ if database_instance:
337
+ database_instance = database_instance(
338
+ Path(Storage.get_filepath(obj_type, obj_id, obj_extension, True))
339
+ )
340
+ if database_instance.create_connection():
341
+ return database_instance
342
+ else:
343
+ raise RuntimeError(f"Failed to create connection - {db_mod_type}")
344
+ else:
345
+ raise RuntimeError(
346
+ f"Unable to get defined database instance - {db_mod_type}"
347
+ )
348
+
349
+ @staticmethod
350
+ def close_database_connection(database_instance: DBInterface) -> None:
351
+ """
352
+ Closes the database connection.
353
+
354
+ Args:
355
+ database_instance (DBInterface): The instance of the database accessor.
356
+
357
+ Returns:
358
+ None
359
+ """
360
+ if database_instance:
361
+ database_instance.close_connection()
362
+ else:
363
+ raise RuntimeError("Database instance is not initialised.")
364
+
365
+ @staticmethod
366
+ def create_database_table(
367
+ database_instance: DBInterface, sql_create_table: str
368
+ ) -> None:
369
+ """
370
+ Creates a table in the database.
371
+
372
+ This method is used to create a table in the database. If the database instance is not initialised,
373
+ it raises a RuntimeError. Otherwise, it calls the create_table method of the database instance.
374
+
375
+ Args:
376
+ database_instance (DBInterface): The database accessor instance.
377
+ sql_create_table (str): The SQL query to create a table.
378
+
379
+ Returns:
380
+ None
381
+ """
382
+ if database_instance:
383
+ database_instance.create_table(sql_create_table)
384
+ else:
385
+ raise RuntimeError("Database instance is not initialised.")
386
+
387
+ @staticmethod
388
+ def create_database_record(
389
+ database_instance: DBInterface, data: tuple, sql_create_record: str
390
+ ) -> tuple | None:
391
+ """
392
+ Inserts a record into the database.
393
+
394
+ This method is used to insert a record into the database. If the database instance is not initialised,
395
+ it raises a RuntimeError. If the database instance is initialised, it calls the create_record method of the
396
+ database instance with the provided data and SQL query.
397
+
398
+ Args:
399
+ database_instance (DBInterface): The database accessor instance.
400
+ data (tuple): The data to be inserted into the database.
401
+ sql_create_record (str): The SQL query to insert a record.
402
+
403
+ Returns:
404
+ tuple | None: The inserted record if successful, None otherwise.
405
+ """
406
+ if database_instance:
407
+ return database_instance.create_record(data, sql_create_record)
408
+ else:
409
+ raise RuntimeError("Database instance is not initialised.")
410
+
411
+ @staticmethod
412
+ def read_database_record(
413
+ database_instance: DBInterface, data: tuple, sql_read_record: str
414
+ ) -> tuple | None:
415
+ """
416
+ Reads a record from the database.
417
+
418
+ This method is used to retrieve a record from the database. If the database instance is not initialised,
419
+ it raises a RuntimeError. If the database instance is initialised, it calls the read_record method of the
420
+ database instance and returns the record if found.
421
+
422
+ Args:
423
+ database_instance (DBInterface): The database accessor instance.
424
+ data (tuple): The data to be matched for reading the record.
425
+ sql_read_records (str): The SQL query to read a record.
426
+
427
+ Returns:
428
+ tuple | None: The record if found, None otherwise.
429
+ """
430
+ if database_instance:
431
+ return database_instance.read_record(data, sql_read_record)
432
+ else:
433
+ raise RuntimeError("Database instance is not initialised.")
434
+
435
+ @staticmethod
436
+ def read_database_records(
437
+ database_instance: DBInterface, sql_read_records: str
438
+ ) -> list[tuple] | None:
439
+ """
440
+ Reads records from the database.
441
+
442
+ This method is used to retrieve records from the database. If the database instance is not initialised,
443
+ it raises a RuntimeError. If the database instance is initialised, it calls the read_records method of the
444
+ database instance and returns the record if found.
445
+
446
+ Args:
447
+ database_instance (DBInterface): The database accessor instance.
448
+ data (tuple): The data to be matched for reading the record.
449
+ sql_read_records (str): The SQL query to read a record.
450
+
451
+ Returns:
452
+ tuple | None: The record if found, None otherwise.
453
+ """
454
+ if database_instance:
455
+ return database_instance.read_records(sql_read_records)
456
+ else:
457
+ raise RuntimeError("Database instance is not initialised.")
458
+
459
+ @staticmethod
460
+ def update_database_record(
461
+ database_instance: DBInterface, data: tuple, sql_update_record: str
462
+ ) -> None:
463
+ """
464
+ Updates a record in the database.
465
+
466
+ This method is used to update a record in the database. If the database instance is not initialised,
467
+ it raises a RuntimeError. Otherwise, it calls the update_record method of the database instance.
468
+
469
+ Args:
470
+ database_instance (DBInterface): The database accessor instance.
471
+ data (tuple): The data to be updated.
472
+ sql_update_record (str): The SQL query to update a record.
473
+
474
+ Returns:
475
+ None
476
+ """
477
+ if database_instance:
478
+ database_instance.update_record(data, sql_update_record)
479
+ else:
480
+ raise RuntimeError("Database instance is not initialised.")
481
+
482
+ @staticmethod
483
+ def check_database_table_exists(
484
+ database_instance: DBInterface, table_name: str
485
+ ) -> bool | None:
486
+ """
487
+ Checks if a table exists in the database.
488
+
489
+ This method checks if the specified table exists in the database. If the database instance is not initialised,
490
+ it raises a RuntimeError.
491
+
492
+ Args:
493
+ database_instance (DBInterface): The database accessor instance.
494
+ table_name (str): The name of the table to check for existence.
495
+
496
+ Returns:
497
+ bool | None: True if the table exists, False if it does not, None if the database
498
+ instance is not initialised.
499
+ """
500
+ if database_instance:
501
+ return database_instance.check_database_table_exists(table_name)
502
+ else:
503
+ raise RuntimeError("Database instance is not initialised.")
504
+
505
+ @staticmethod
506
+ def delete_database_table(
507
+ database_instance: DBInterface, sql_delete_table: str
508
+ ) -> None:
509
+ """
510
+ Deletes a table from the database.
511
+
512
+ This method is used to delete a table from the database. If the database instance is not initialised,
513
+ it raises a RuntimeError. Otherwise, it calls the delete_database_table method of the database instance.
514
+
515
+ Args:
516
+ database_instance (DBInterface): The database accessor instance.
517
+ sql_delete_table (str): The SQL query to delete a table.
518
+
519
+ Returns:
520
+ None
521
+ """
522
+ if database_instance:
523
+ database_instance.delete_database_table(sql_delete_table)
524
+ else:
525
+ raise RuntimeError("Database instance is not initialised.")
File without changes