qase-python-commons 4.1.4__tar.gz → 4.1.6__tar.gz

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 (50) hide show
  1. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/PKG-INFO +3 -3
  2. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/pyproject.toml +3 -3
  3. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/src/qase/commons/client/api_v1_client.py +115 -12
  4. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/src/qase/commons/client/api_v2_client.py +10 -8
  5. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/src/qase/commons/client/base_api_client.py +11 -5
  6. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/src/qase/commons/config.py +3 -3
  7. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/src/qase_python_commons.egg-info/PKG-INFO +3 -3
  8. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/src/qase_python_commons.egg-info/requires.txt +2 -2
  9. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/README.md +0 -0
  10. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/setup.cfg +0 -0
  11. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/src/qase/__init__.py +0 -0
  12. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/src/qase/commons/__init__.py +0 -0
  13. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/src/qase/commons/exceptions/reporter.py +0 -0
  14. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/src/qase/commons/loader.py +0 -0
  15. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/src/qase/commons/logger.py +0 -0
  16. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/src/qase/commons/models/__init__.py +0 -0
  17. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/src/qase/commons/models/attachment.py +0 -0
  18. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/src/qase/commons/models/basemodel.py +0 -0
  19. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/src/qase/commons/models/config/api.py +0 -0
  20. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/src/qase/commons/models/config/batch.py +0 -0
  21. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/src/qase/commons/models/config/connection.py +0 -0
  22. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/src/qase/commons/models/config/framework.py +0 -0
  23. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/src/qase/commons/models/config/plan.py +0 -0
  24. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/src/qase/commons/models/config/qaseconfig.py +0 -0
  25. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/src/qase/commons/models/config/report.py +0 -0
  26. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/src/qase/commons/models/config/run.py +0 -0
  27. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/src/qase/commons/models/config/testops.py +0 -0
  28. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/src/qase/commons/models/external_link.py +0 -0
  29. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/src/qase/commons/models/relation.py +0 -0
  30. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/src/qase/commons/models/result.py +0 -0
  31. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/src/qase/commons/models/run.py +0 -0
  32. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/src/qase/commons/models/runtime.py +0 -0
  33. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/src/qase/commons/models/step.py +0 -0
  34. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/src/qase/commons/profilers/__init__.py +0 -0
  35. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/src/qase/commons/profilers/db.py +0 -0
  36. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/src/qase/commons/profilers/network.py +0 -0
  37. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/src/qase/commons/profilers/sleep.py +0 -0
  38. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/src/qase/commons/reporters/__init__.py +0 -0
  39. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/src/qase/commons/reporters/core.py +0 -0
  40. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/src/qase/commons/reporters/report.py +0 -0
  41. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/src/qase/commons/reporters/testops.py +0 -0
  42. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/src/qase/commons/status_mapping/__init__.py +0 -0
  43. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/src/qase/commons/status_mapping/status_mapping.py +0 -0
  44. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/src/qase/commons/util/__init__.py +0 -0
  45. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/src/qase/commons/util/host_data.py +0 -0
  46. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/src/qase/commons/utils.py +0 -0
  47. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/src/qase/commons/validators/base.py +0 -0
  48. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/src/qase_python_commons.egg-info/SOURCES.txt +0 -0
  49. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/src/qase_python_commons.egg-info/dependency_links.txt +0 -0
  50. {qase_python_commons-4.1.4 → qase_python_commons-4.1.6}/src/qase_python_commons.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: qase-python-commons
3
- Version: 4.1.4
3
+ Version: 4.1.6
4
4
  Summary: A library for Qase TestOps and Qase Report
5
5
  Author-email: Qase Team <support@qase.io>
6
6
  Project-URL: Homepage, https://github.com/qase-tms/qase-python/tree/main/qase-python-commons
@@ -21,8 +21,8 @@ Requires-Python: >=3.9
21
21
  Description-Content-Type: text/markdown
22
22
  Requires-Dist: certifi>=2024.2.2
23
23
  Requires-Dist: attrs>=23.2.0
24
- Requires-Dist: qase-api-client~=2.0.1
25
- Requires-Dist: qase-api-v2-client~=2.0.0
24
+ Requires-Dist: qase-api-client~=2.0.2
25
+ Requires-Dist: qase-api-v2-client~=2.0.2
26
26
  Requires-Dist: more_itertools
27
27
  Provides-Extra: testing
28
28
  Requires-Dist: pytest; extra == "testing"
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "qase-python-commons"
7
- version = "4.1.4"
7
+ version = "4.1.6"
8
8
  description = "A library for Qase TestOps and Qase Report"
9
9
  readme = "README.md"
10
10
  authors = [{name = "Qase Team", email = "support@qase.io"}]
@@ -29,8 +29,8 @@ requires-python = ">=3.9"
29
29
  dependencies = [
30
30
  "certifi>=2024.2.2",
31
31
  "attrs>=23.2.0",
32
- "qase-api-client~=2.0.1",
33
- "qase-api-v2-client~=2.0.0",
32
+ "qase-api-client~=2.0.2",
33
+ "qase-api-v2-client~=2.0.2",
34
34
  "more_itertools"
35
35
  ]
36
36
 
@@ -1,9 +1,10 @@
1
1
  from datetime import datetime, timezone
2
- from typing import Union
2
+ from typing import Union, List
3
3
 
4
4
  import certifi
5
5
  from qase.api_client_v1 import ApiClient, ProjectsApi, Project, EnvironmentsApi, RunsApi, AttachmentsApi, \
6
6
  AttachmentGet, RunCreate, ConfigurationsApi, ConfigurationCreate, ConfigurationGroupCreate, RunPublic
7
+ from qase.api_client_v1.models.attachmentupload import Attachmentupload
7
8
  from qase.api_client_v1.configuration import Configuration
8
9
  from .. import Logger
9
10
  from .base_api_client import BaseApiClient
@@ -148,17 +149,119 @@ class ApiV1Client(BaseApiClient):
148
149
  self.logger.log(f"Error at completing run {run_id}: {e}", "error")
149
150
  raise ReporterException(e)
150
151
 
151
- def _upload_attachment(self, project_code: str, attachment: Attachment) -> Union[AttachmentGet, None]:
152
- try:
153
- self.logger.log_debug(f"Uploading attachment {attachment.id} for project {project_code}")
154
- attach_api = AttachmentsApi(self.client)
155
- response = attach_api.upload_attachment(project_code, file=[attachment.get_for_upload()])
156
-
157
- return response.result
158
-
159
- except Exception as e:
160
- self.logger.log(f"Error at uploading attachment: {e}", "error")
161
- return None
152
+ def _upload_attachment(self, project_code: str, attachment: Union[Attachment, List[Attachment]]) -> List[Attachmentupload]:
153
+ """
154
+ Upload one or multiple attachments to Qase TestOps with batching support.
155
+
156
+ The method automatically groups attachments into batches respecting the following limits:
157
+ - Up to 32 MB per file
158
+ - Up to 128 MB per single request
159
+ - Up to 20 files per single request
160
+
161
+ :param project_code: project code
162
+ :param attachment: single attachment or list of attachments
163
+ :return: list of uploaded attachment data
164
+ """
165
+ # Normalize input to list
166
+ attachments = attachment if isinstance(attachment, list) else [attachment]
167
+
168
+ if not attachments:
169
+ return []
170
+
171
+ # Constants for upload limits
172
+ MAX_FILE_SIZE = 32 * 1024 * 1024 # 32 MB in bytes
173
+ MAX_REQUEST_SIZE = 128 * 1024 * 1024 # 128 MB in bytes
174
+ MAX_FILES_PER_REQUEST = 20
175
+
176
+ # Prepare attachments with size information
177
+ attachments_with_size = []
178
+ for att in attachments:
179
+ try:
180
+ # Get file data to check size
181
+ file_tuple = att.get_for_upload()
182
+ file_data = file_tuple[1] # Get file data (second element of tuple)
183
+ file_size = len(file_data)
184
+
185
+ # Check individual file size limit
186
+ if file_size > MAX_FILE_SIZE:
187
+ self.logger.log(
188
+ f"Attachment {att.file_name} ({file_size / 1024 / 1024:.2f} MB) exceeds "
189
+ f"maximum file size limit of 32 MB. Skipping.",
190
+ "error"
191
+ )
192
+ continue
193
+
194
+ attachments_with_size.append((att, file_size))
195
+ except Exception as e:
196
+ self.logger.log(f"Error preparing attachment {att.file_name}: {e}", "error")
197
+ continue
198
+
199
+ if not attachments_with_size:
200
+ return []
201
+
202
+ # Group attachments into batches
203
+ batches = []
204
+ current_batch = []
205
+ current_batch_size = 0
206
+
207
+ for att, file_size in attachments_with_size:
208
+ # Check if adding this file would exceed limits
209
+ would_exceed_size = current_batch_size + file_size > MAX_REQUEST_SIZE
210
+ would_exceed_count = len(current_batch) >= MAX_FILES_PER_REQUEST
211
+
212
+ if would_exceed_size or would_exceed_count:
213
+ # Start a new batch
214
+ if current_batch:
215
+ batches.append(current_batch)
216
+ current_batch = [att]
217
+ current_batch_size = file_size
218
+ else:
219
+ # Add to current batch
220
+ current_batch.append(att)
221
+ current_batch_size += file_size
222
+
223
+ # Add the last batch if it has items
224
+ if current_batch:
225
+ batches.append(current_batch)
226
+
227
+ # Upload batches
228
+ all_uploaded = []
229
+ attach_api = AttachmentsApi(self.client)
230
+
231
+ for batch_idx, batch in enumerate(batches, 1):
232
+ try:
233
+ self.logger.log_debug(
234
+ f"Uploading batch {batch_idx}/{len(batches)} with {len(batch)} file(s) "
235
+ f"for project {project_code}"
236
+ )
237
+
238
+ # Prepare files for upload
239
+ files_for_upload = [att.get_for_upload() for att in batch]
240
+
241
+ # Upload batch
242
+ response = attach_api.upload_attachment(project_code, file=files_for_upload)
243
+
244
+ if response.result:
245
+ all_uploaded.extend(response.result)
246
+ self.logger.log_debug(
247
+ f"Successfully uploaded batch {batch_idx}/{len(batches)}: "
248
+ f"{len(response.result)} file(s)"
249
+ )
250
+ else:
251
+ self.logger.log(
252
+ f"Batch {batch_idx}/{len(batches)} upload returned no results",
253
+ "error"
254
+ )
255
+
256
+ except Exception as e:
257
+ self.logger.log(
258
+ f"Error uploading batch {batch_idx}/{len(batches)}: {e}",
259
+ "error"
260
+ )
261
+ # Continue with next batch even if one fails
262
+ continue
263
+
264
+ return all_uploaded
162
265
 
163
266
  def create_test_run(self, project_code: str, title: str, description: str, plan_id=None,
164
267
  environment_id=None) -> str:
@@ -56,10 +56,13 @@ class ApiV2Client(ApiV1Client):
56
56
  def _prepare_result(self, project_code: str, result: Result) -> ResultCreate:
57
57
  attached = []
58
58
  if result.attachments:
59
- for attachment in result.attachments:
60
- if self.__should_skip_attachment(attachment, result):
61
- continue
62
- attach_id = self._upload_attachment(project_code, attachment)
59
+ # Collect all attachments that should be uploaded
60
+ attachments_to_upload = [
61
+ attachment for attachment in result.attachments
62
+ if not self.__should_skip_attachment(attachment, result)
63
+ ]
64
+ if attachments_to_upload:
65
+ attach_id = self._upload_attachment(project_code, attachments_to_upload)
63
66
  if attach_id:
64
67
  attached.extend(attach_id)
65
68
 
@@ -182,10 +185,9 @@ class ApiV2Client(ApiV1Client):
182
185
 
183
186
  if step.execution.attachments:
184
187
  uploaded_attachments = []
185
- for file in step.execution.attachments:
186
- attach_id = self._upload_attachment(project_code, file)
187
- if attach_id:
188
- uploaded_attachments.extend(attach_id)
188
+ attach_id = self._upload_attachment(project_code, step.execution.attachments)
189
+ if attach_id:
190
+ uploaded_attachments.extend(attach_id)
189
191
 
190
192
  prepared_step['execution']['attachments'] = [attach.hash for attach in uploaded_attachments]
191
193
 
@@ -1,7 +1,8 @@
1
1
  import abc
2
- from typing import Union
2
+ from typing import Union, List
3
3
 
4
4
  from qase.api_client_v1 import Project, AttachmentGet
5
+ from qase.api_client_v1.models.attachmentupload import Attachmentupload
5
6
 
6
7
  from ..models import Attachment
7
8
 
@@ -41,13 +42,18 @@ class BaseApiClient(abc.ABC):
41
42
  pass
42
43
 
43
44
  @abc.abstractmethod
44
- def _upload_attachment(self, project_code: str, attachment: Attachment) -> Union[AttachmentGet, None]:
45
+ def _upload_attachment(self, project_code: str, attachment: Union[Attachment, List[Attachment]]) -> List[Attachmentupload]:
45
46
  """
46
- Upload an attachment to Qase TestOps
47
+ Upload one or multiple attachments to Qase TestOps with batching support.
48
+
49
+ The method automatically groups attachments into batches respecting the following limits:
50
+ - Up to 32 MB per file
51
+ - Up to 128 MB per single request
52
+ - Up to 20 files per single request
47
53
 
48
54
  :param project_code: project code
49
- :param attachment: attachment model
50
- :return: attachment data or None if attachment not uploaded
55
+ :param attachment: single attachment or list of attachments
56
+ :return: list of uploaded attachment data
51
57
  """
52
58
  pass
53
59
 
@@ -276,10 +276,10 @@ class ConfigManager:
276
276
  self.config.testops.set_defect(value)
277
277
 
278
278
  if key == 'QASE_TESTOPS_PLAN_ID':
279
- self.config.testops.plan.set_id(value)
279
+ self.config.testops.plan.set_id(int(value.strip()))
280
280
 
281
281
  if key == 'QASE_TESTOPS_RUN_ID':
282
- self.config.testops.run.set_id(value)
282
+ self.config.testops.run.set_id(int(value.strip()))
283
283
 
284
284
  if key == 'QASE_TESTOPS_RUN_TITLE':
285
285
  self.config.testops.run.set_title(value)
@@ -307,7 +307,7 @@ class ConfigManager:
307
307
  self.config.testops.run.external_link.set_link(value)
308
308
 
309
309
  if key == 'QASE_TESTOPS_BATCH_SIZE':
310
- self.config.testops.batch.set_size(value)
310
+ self.config.testops.batch.set_size(int(value.strip()))
311
311
 
312
312
  if key == 'QASE_TESTOPS_CONFIGURATIONS_VALUES':
313
313
  # Parse configurations from environment variable
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: qase-python-commons
3
- Version: 4.1.4
3
+ Version: 4.1.6
4
4
  Summary: A library for Qase TestOps and Qase Report
5
5
  Author-email: Qase Team <support@qase.io>
6
6
  Project-URL: Homepage, https://github.com/qase-tms/qase-python/tree/main/qase-python-commons
@@ -21,8 +21,8 @@ Requires-Python: >=3.9
21
21
  Description-Content-Type: text/markdown
22
22
  Requires-Dist: certifi>=2024.2.2
23
23
  Requires-Dist: attrs>=23.2.0
24
- Requires-Dist: qase-api-client~=2.0.1
25
- Requires-Dist: qase-api-v2-client~=2.0.0
24
+ Requires-Dist: qase-api-client~=2.0.2
25
+ Requires-Dist: qase-api-v2-client~=2.0.2
26
26
  Requires-Dist: more_itertools
27
27
  Provides-Extra: testing
28
28
  Requires-Dist: pytest; extra == "testing"
@@ -1,7 +1,7 @@
1
1
  certifi>=2024.2.2
2
2
  attrs>=23.2.0
3
- qase-api-client~=2.0.1
4
- qase-api-v2-client~=2.0.0
3
+ qase-api-client~=2.0.2
4
+ qase-api-v2-client~=2.0.2
5
5
  more_itertools
6
6
 
7
7
  [testing]