testit-python-commons 5.2.0__py3-none-any.whl → 5.2.1__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.
@@ -41,6 +41,7 @@ from testit_python_commons.services.retry import (
41
41
  retry,
42
42
  retry_on_connection_error,
43
43
  )
44
+ from testit_python_commons.utils.html_escape_utils import HtmlEscapeUtils
44
45
 
45
46
 
46
47
  class ApiClientWorker:
@@ -469,6 +470,7 @@ class ApiClientWorker:
469
470
  test_result,
470
471
  self.__config.get_project_id())
471
472
 
473
+ HtmlEscapeUtils.escape_html_in_object(model)
472
474
  autotest_response = self.__autotest_api.adapters_auto_tests_post(
473
475
  adapters_auto_tests_post_request=model)
474
476
 
@@ -496,6 +498,7 @@ class ApiClientWorker:
496
498
  model = Converter.prepare_to_update_autotest(test_result, autotest, self.__config.get_project_id())
497
499
 
498
500
  try:
501
+ HtmlEscapeUtils.escape_html_in_object(model)
499
502
  self.__autotest_api.adapters_auto_tests_put(adapters_auto_tests_put_request=model)
500
503
  except Exception as exc:
501
504
  if is_retriable_connection_error(exc):
@@ -562,6 +565,7 @@ class ApiClientWorker:
562
565
  self.__config.get_configuration_id(),
563
566
  self.__status_codes)
564
567
 
568
+ HtmlEscapeUtils.escape_html_in_object(model)
565
569
  response = self.__test_run_api.adapters_test_runs_id_test_results_post(
566
570
  id=self.__config.get_test_run_id(),
567
571
  auto_test_results_for_test_run_model=[model])
@@ -156,7 +156,7 @@ class BulkAutotestHelper:
156
156
  def __create_tests(self, autotests_for_create: List[AutoTestCreateApiModel]):
157
157
  # logging.debug(f'Creating autotests: "{autotests_for_create}')
158
158
 
159
- autotests_for_create = HtmlEscapeUtils.escape_html_in_object(autotests_for_create)
159
+ autotests_for_create = HtmlEscapeUtils.escape_html_in_object_list(autotests_for_create)
160
160
  self.__autotests_api.adapters_auto_tests_bulk_post(auto_test_create_api_model=autotests_for_create)
161
161
 
162
162
  logging.debug(f'Autotests were created')
@@ -165,7 +165,7 @@ class BulkAutotestHelper:
165
165
  def __update_tests(self, autotests_for_update: List[AutoTestUpdateApiModel]):
166
166
  # logging.debug(f'Updating autotests: {autotests_for_update}')
167
167
 
168
- autotests_for_update = HtmlEscapeUtils.escape_html_in_object(autotests_for_update)
168
+ autotests_for_update = HtmlEscapeUtils.escape_html_in_object_list(autotests_for_update)
169
169
  self.__autotests_api.adapters_auto_tests_bulk_put(auto_test_update_api_model=autotests_for_update)
170
170
 
171
171
  logging.debug(f'Autotests were updated')
@@ -174,7 +174,7 @@ class BulkAutotestHelper:
174
174
  def __load_test_results(self, test_results: List[AutoTestResultsForTestRunModel]):
175
175
  # logging.debug(f'Loading test results: {test_results}')
176
176
 
177
- test_results = HtmlEscapeUtils.escape_html_in_object(test_results)
177
+ test_results = HtmlEscapeUtils.escape_html_in_object_list(test_results)
178
178
  self.__test_runs_api.adapters_test_runs_id_test_results_post(
179
179
  id=self.__test_run_id,
180
180
  auto_test_results_for_test_run_model=test_results)
@@ -289,7 +289,7 @@ class TestResult:
289
289
 
290
290
  @adapter_logger
291
291
  def set_external_key(self, external_key: str):
292
- self.__external_key = external_key
292
+ self.__external_key = HtmlEscapeUtils.escape_html_tags(external_key)
293
293
 
294
294
  return self
295
295
 
@@ -80,7 +80,12 @@ class HtmlEscapeUtils:
80
80
  return obj
81
81
 
82
82
  try:
83
- HtmlEscapeUtils._process_object_attributes(obj)
83
+ if isinstance(obj, list):
84
+ HtmlEscapeUtils._process_list(obj)
85
+ elif isinstance(obj, dict):
86
+ HtmlEscapeUtils._process_dict(obj)
87
+ else:
88
+ HtmlEscapeUtils._process_object_attributes(obj)
84
89
  except Exception as e:
85
90
  # Silently ignore reflection errors
86
91
  logging.debug(f"Error processing object attributes: {e}")
@@ -90,7 +95,7 @@ class HtmlEscapeUtils:
90
95
  @staticmethod
91
96
  def escape_html_in_object_list(obj_list: Optional[List[Any]]) -> Optional[List[Any]]:
92
97
  """
93
- Escapes HTML tags in all string attributes of objects in a list using reflection.
98
+ Escapes HTML tags in list items: strings in-place, objects via reflection.
94
99
  Can be disabled by setting NO_ESCAPE_HTML environment variable to "true".
95
100
 
96
101
  Args:
@@ -106,47 +111,68 @@ class HtmlEscapeUtils:
106
111
  no_escape_html = os.environ.get(HtmlEscapeUtils.NO_ESCAPE_HTML_ENV_VAR, "").lower()
107
112
  if no_escape_html == "true":
108
113
  return obj_list
109
-
110
- for obj in obj_list:
111
- HtmlEscapeUtils.escape_html_in_object(obj)
112
-
114
+
115
+ HtmlEscapeUtils._process_list(obj_list)
113
116
  return obj_list
114
117
 
118
+ @staticmethod
119
+ def _process_dict(obj: dict) -> None:
120
+ for key, value in obj.items():
121
+ if isinstance(value, str):
122
+ obj[key] = HtmlEscapeUtils.escape_html_tags(value)
123
+ elif isinstance(value, list):
124
+ HtmlEscapeUtils._process_list(value)
125
+ elif isinstance(value, dict):
126
+ HtmlEscapeUtils._process_dict(value)
127
+ elif value is not None and not HtmlEscapeUtils._is_simple_type(type(value)):
128
+ HtmlEscapeUtils.escape_html_in_object(value)
129
+
130
+ @staticmethod
131
+ def _iter_object_attr_names(obj: Any):
132
+ """
133
+ OpenAPI models store fields in attribute_map/_data_store; they are not listed by dir().
134
+ Prefer those maps, fall back to public dir() attributes for plain objects.
135
+ """
136
+ attribute_map = getattr(obj, 'attribute_map', None)
137
+ if isinstance(attribute_map, dict) and attribute_map:
138
+ return attribute_map.keys()
139
+
140
+ data_store = getattr(obj, '_data_store', None)
141
+ if isinstance(data_store, dict) and data_store:
142
+ return data_store.keys()
143
+
144
+ return (
145
+ attr_name for attr_name in dir(obj)
146
+ if not attr_name.startswith('_') and not callable(getattr(obj, attr_name, None))
147
+ )
148
+
149
+ @staticmethod
150
+ def _should_skip_attr(attr_name: str) -> bool:
151
+ return (
152
+ attr_name.startswith('_')
153
+ or attr_name.startswith("external_id")
154
+ or attr_name.startswith("externalId")
155
+ or attr_name.startswith("auto_test_external_id")
156
+ or attr_name.startswith("autoTestExternalId")
157
+ )
158
+
115
159
  @staticmethod
116
160
  def _process_object_attributes(obj: Any) -> None:
117
161
  """
118
162
  Process all attributes of an object for HTML escaping.
119
163
  """
120
- # Handle dictionary-like objects (common in API models)
121
- if hasattr(obj, '__dict__'):
122
- for attr_name in dir(obj):
123
- # Skip private/protected attributes and methods
124
- if attr_name.startswith('_') or callable(getattr(obj, attr_name, None)):
125
- continue
126
- if (
127
- attr_name.startswith("external_id")
128
- or attr_name.startswith("externalId")
129
- or attr_name.startswith("auto_test_external_id")
130
- or attr_name.startswith("autoTestExternalId")
131
- ):
164
+ for attr_name in HtmlEscapeUtils._iter_object_attr_names(obj):
165
+ if HtmlEscapeUtils._should_skip_attr(attr_name):
166
+ continue
167
+
168
+ try:
169
+ value = getattr(obj, attr_name, None)
170
+ if callable(value):
132
171
  continue
133
-
134
- try:
135
- value = getattr(obj, attr_name)
136
- HtmlEscapeUtils._process_attribute_value(obj, attr_name, value)
137
- except Exception as e:
138
- # Silently ignore attribute errors
139
- logging.debug(f"Error processing attribute {attr_name}: {e}")
140
-
141
- # Handle dictionary objects
142
- elif isinstance(obj, dict):
143
- for key, value in obj.items():
144
- if isinstance(value, str):
145
- obj[key] = HtmlEscapeUtils.escape_html_tags(value)
146
- elif isinstance(value, list):
147
- HtmlEscapeUtils._process_list(value)
148
- elif not HtmlEscapeUtils._is_simple_type(type(value)):
149
- HtmlEscapeUtils.escape_html_in_object(value)
172
+ HtmlEscapeUtils._process_attribute_value(obj, attr_name, value)
173
+ except Exception as e:
174
+ # Silently ignore attribute errors
175
+ logging.debug(f"Error processing attribute {attr_name}: {e}")
150
176
 
151
177
  @staticmethod
152
178
  def _process_attribute_value(obj: Any, attr_name: str, value: Any) -> None:
@@ -162,6 +188,8 @@ class HtmlEscapeUtils:
162
188
  pass
163
189
  elif isinstance(value, list):
164
190
  HtmlEscapeUtils._process_list(value)
191
+ elif isinstance(value, dict):
192
+ HtmlEscapeUtils._process_dict(value)
165
193
  elif value is not None and not HtmlEscapeUtils._is_simple_type(type(value)):
166
194
  # Process nested objects (but not simple types)
167
195
  HtmlEscapeUtils.escape_html_in_object(value)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: testit-python-commons
3
- Version: 5.2.0
3
+ Version: 5.2.1
4
4
  Summary: Python commons for Test IT
5
5
  Home-page: https://github.com/testit-tms/adapters-python/
6
6
  Author: Integration team
@@ -227,11 +227,11 @@ testit_python_commons/decorators.py,sha256=5P0VF1z0rncMiB-G_eeuj536vizYj2UQVDroX
227
227
  testit_python_commons/dynamic_methods.py,sha256=WKHHA6Uj61tWXI3DphALYOi2BV6TnQKvQy0-Vt2JR2I,8816
228
228
  testit_python_commons/step.py,sha256=CYUs_S8opmRg1tsTY1dVJOPzzt-HYR3_uLTBpYtHSfQ,3244
229
229
  testit_python_commons/client/__init__.py,sha256=zSJhxDHzWWTFHJ0s8bqCaBtM5FK2GcPgZxaKdI8zf-o,165
230
- testit_python_commons/client/api_client.py,sha256=SgIPi9AG8o5jAATggZeiUvK7Hffuir_Z7727AJT3OEs,25274
230
+ testit_python_commons/client/api_client.py,sha256=fuQnwyYPktWA-LkDv7FOBe1wXBPa2epR83PLpOcBSkw,25511
231
231
  testit_python_commons/client/client_configuration.py,sha256=1M-OrROU3Xj0YGsUX4venG-l6Ewr43LNvUcyFfY57ys,2776
232
232
  testit_python_commons/client/converter.py,sha256=QJ-zRG7-DuXx6c_ujLIjlOT6kfBRqLh196Xt9_eo8RU,28147
233
233
  testit_python_commons/client/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
234
- testit_python_commons/client/helpers/bulk_autotest_helper.py,sha256=k_5sK3tVz0fCZw_Eh2mk9diHF_bLin0P4016E2gWQQI,10888
234
+ testit_python_commons/client/helpers/bulk_autotest_helper.py,sha256=I8kIetucvjN5Zws8q99GRd8Rjy7I7MrmduBSjG7vTgQ,10903
235
235
  testit_python_commons/client/helpers/test_result_matching.py,sha256=XSGzzESs-vg41MrvcijD91snpczL3fiVTywOe2j2WZg,1373
236
236
  testit_python_commons/client/helpers/threads_manager.py,sha256=PVDQytuNrGs95JfQ03QeUV_447Yn4Oig-ZmQaA6vo6w,3505
237
237
  testit_python_commons/client/models/__init__.py,sha256=4c3yNkadtebFF1eEZw7JmfwcLtOim2zHkwbZIHvCK0A,284
@@ -248,7 +248,7 @@ testit_python_commons/models/outcome_type.py,sha256=WAAuPRTopmYPczd-Gd0IrE7SGDZO
248
248
  testit_python_commons/models/status_type.py,sha256=lm4Hp02Z5Wo1mOaaMDjzBD1W0BE6gtwjBMPdhxuO_5U,152
249
249
  testit_python_commons/models/step_result.py,sha256=T9BbcPB6BCz3ZkrWXy2Z2Guj2wecvKJX1oIrG-yJH4I,2209
250
250
  testit_python_commons/models/test_layers.py,sha256=XACHq-r3bf35ktU9_HUputiBYKXwOXJmeqMJTnrB460,168
251
- testit_python_commons/models/test_result.py,sha256=R2CaQFnHVH_grf0LIvat7Dq1PlPCJm_-kbAkot8ANwg,7781
251
+ testit_python_commons/models/test_result.py,sha256=guf0RD2FjLY5sNFuyucXY07xg01yG_sKKdoASYpJ3rc,7815
252
252
  testit_python_commons/models/test_result_with_all_fixture_step_results_model.py,sha256=aI5nfJ_4EAhzZg7eIV1JhSf9i3EENiRiuQuqJvSUs3M,866
253
253
  testit_python_commons/services/__init__.py,sha256=yVVmiCgMyike_8OoNPiHLNoMqKhCDlWYhvmGW0IRHJM,1197
254
254
  testit_python_commons/services/adapter_manager.py,sha256=UJK0c8aj4X2UI8rFNR2wYtPnHWDOA8c5K0dAQeDOIRU,11849
@@ -265,8 +265,8 @@ testit_python_commons/services/utils.py,sha256=5LBymIxEC1AKtGQz-S1-ipo3OYR-JzRUA
265
265
  testit_python_commons/services/sync_storage/__init__.py,sha256=Qmjs6Xk5l-i5FFItk0-XPrlVK_HkDuX6FD8FID1iKu0,154
266
266
  testit_python_commons/services/sync_storage/sync_storage_runner.py,sha256=DPOHJZ57otKlmAHHD7JV1rfYAgSTm0fYTvlp1QFtR4I,20539
267
267
  testit_python_commons/utils/__init__.py,sha256=CUHziylwvFAuKyUM1tCp13ik5Z2Mw-VK8n7c92aVy-I,78
268
- testit_python_commons/utils/html_escape_utils.py,sha256=wx20lAVrMvhNiQ7bEu_f-SGqDBp85D-EBL2sgTmnJPY,8135
269
- testit_python_commons-5.2.0.dist-info/METADATA,sha256=_bZbHygVpktiVYN0yafMebpPJudr0lBMzx9x1Vdo9ZQ,1766
270
- testit_python_commons-5.2.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
271
- testit_python_commons-5.2.0.dist-info/top_level.txt,sha256=TuyfRneOyFuvNz83bbn-pWaAR1fY7FINrrJRTW9Lyqg,65
272
- testit_python_commons-5.2.0.dist-info/RECORD,,
268
+ testit_python_commons/utils/html_escape_utils.py,sha256=H03t3mYaUMAS-MR9lH00ld2Pe8mW6ebB6OLGIw9x72c,9057
269
+ testit_python_commons-5.2.1.dist-info/METADATA,sha256=4c-5lwTDE6mGM9cgccJw2RDZxvIf5uODJXU_rU5lJwU,1766
270
+ testit_python_commons-5.2.1.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
271
+ testit_python_commons-5.2.1.dist-info/top_level.txt,sha256=TuyfRneOyFuvNz83bbn-pWaAR1fY7FINrrJRTW9Lyqg,65
272
+ testit_python_commons-5.2.1.dist-info/RECORD,,