dcicutils 8.7.1.1b5__py3-none-any.whl → 8.7.1.1b6__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.
dcicutils/misc_utils.py CHANGED
@@ -2,6 +2,7 @@
2
2
  This file contains functions that might be generally useful.
3
3
  """
4
4
 
5
+ from collections import namedtuple
5
6
  import contextlib
6
7
  import datetime
7
8
  import functools
@@ -17,6 +18,7 @@ import re
17
18
  import rfc3986.validators
18
19
  import rfc3986.exceptions
19
20
  import time
21
+ import uuid
20
22
  import warnings
21
23
  import webtest # importing the library makes it easier to mock testing
22
24
 
@@ -1525,6 +1527,17 @@ def create_dict(**kwargs) -> dict:
1525
1527
  return result
1526
1528
 
1527
1529
 
1530
+ def create_readonly_object(**kwargs):
1531
+ """
1532
+ Returns a new/unique object instance with readonly properties equal to the give kwargs.
1533
+ """
1534
+ readonly_class_name = "readonlyclass_" + str(uuid.uuid4()).replace("-", "")
1535
+ readonly_class_args = " ".join(kwargs.keys())
1536
+ readonly_class = namedtuple(readonly_class_name, readonly_class_args)
1537
+ readonly_object = readonly_class(**kwargs)
1538
+ return readonly_object
1539
+
1540
+
1528
1541
  def is_c4_arn(arn: str) -> bool:
1529
1542
  """
1530
1543
  Returns True iff the given (presumed) AWS ARN string value looks like it
@@ -3,6 +3,7 @@ from functools import lru_cache
3
3
  import re
4
4
  from typing import Any, List, Optional, Tuple, Type, Union
5
5
  from dcicutils.data_readers import RowReader
6
+ from dcicutils.misc_utils import create_readonly_object
6
7
  from dcicutils.portal_utils import Portal
7
8
  from dcicutils.schema_utils import Schema
8
9
 
@@ -136,21 +137,33 @@ class PortalObject:
136
137
  comparing_data = value
137
138
  else:
138
139
  return {}
139
- return PortalObject._compare(this_data, comparing_data)
140
+ return PortalObject._compare(this_data, comparing_data, self.type)
140
141
 
141
142
  _ARRAY_KEY_REGULAR_EXPRESSION = re.compile(rf"^({Schema._ARRAY_NAME_SUFFIX_CHAR}\d+)$")
142
143
 
143
144
  @staticmethod
144
- def _compare(a: Any, b: Any, _path: Optional[str] = None) -> dict:
145
+ def _compare(a: Any, b: Any, value_type: str, _path: Optional[str] = None) -> dict:
146
+ def diff_creating(value: Any) -> object: # noqa
147
+ nonlocal value_type
148
+ return create_readonly_object(value=value, type=value_type,
149
+ creating_value=True, updating_value=None, deleting_value=False)
150
+ def diff_updating(value: Any, updating_value: Any) -> object: # noqa
151
+ nonlocal value_type
152
+ return create_readonly_object(value=value, type=value_type,
153
+ creating_value=False, updating_value=updating_value, deleting_value=False)
154
+ def diff_deleting(value: Any) -> object: # noqa
155
+ nonlocal value_type
156
+ return create_readonly_object(value=value, type=value_type,
157
+ creating_value=False, updating_value=None, deleting_value=True)
145
158
  diffs = {}
146
159
  if isinstance(a, dict) and isinstance(b, dict):
147
160
  for key in a:
148
161
  path = f"{_path}.{key}" if _path else key
149
162
  if key not in b:
150
163
  if a[key] != PortalObject._PROPERTY_DELETION_SENTINEL:
151
- diffs[path] = {"value": a[key], "creating_value": True}
164
+ diffs[path] = diff_creating(a[key])
152
165
  else:
153
- diffs.update(PortalObject._compare(a[key], b[key], _path=path))
166
+ diffs.update(PortalObject._compare(a[key], b[key], type, _path=path))
154
167
  elif isinstance(a, list) and isinstance(b, list):
155
168
  # Ignore order of array elements; not absolutely technically correct but suits our purpose.
156
169
  for index in range(len(a)):
@@ -159,21 +172,21 @@ class PortalObject:
159
172
  if a[index] not in b:
160
173
  if a[index] != PortalObject._PROPERTY_DELETION_SENTINEL:
161
174
  if index < len(b):
162
- diffs[path] = {"value": a[index], "updating_value": b[index]}
175
+ diffs[path] = diff_updating(a[index], b[index])
163
176
  else:
164
- diffs[path] = {"value": a[index], "creating_value": True}
177
+ diffs[path] = diff_creating(a[index])
165
178
  else:
166
179
  if index < len(b):
167
- diffs[path] = {"value": b[index], "deleting_value": True}
180
+ diffs[path] = diff_deleting(b[index])
168
181
  elif len(b) < index:
169
- diffs.update(PortalObject._compare(a[index], b[index], _path=path))
182
+ diffs.update(PortalObject._compare(a[index], b[index], value_type, _path=path))
170
183
  else:
171
- diffs[path] = {"value": a[index], "creating_value": True}
184
+ diffs[path] = diff_creating(a[index])
172
185
  elif a != b:
173
186
  if a == PortalObject._PROPERTY_DELETION_SENTINEL:
174
- diffs[_path] = {"value": b, "deleting_value": True}
187
+ diffs[_path] = diff_deleting(b)
175
188
  else:
176
- diffs[_path] = {"value": a, "updating_value": b}
189
+ diffs[_path] = diff_updating(a, b)
177
190
  return diffs
178
191
 
179
192
  def normalize_refs(self, refs: List[dict]) -> None:
@@ -183,8 +196,9 @@ class PortalObject:
183
196
  based on the given "refs" list which is assumed to be a list of dictionaries, where each
184
197
  contains a "path" and a "uuid" property; this list is typically (for our first usage of
185
198
  this function) the value of structured_data.StructuredDataSet.resolved_refs_with_uuid.
186
- Change is made to this Portal object in place; use normalized_refs function to make a copy.
187
- If there are no "refs" (None or empty) then the references will be looked up via Portal calls.
199
+ Changes are made to this Portal object in place; use normalized_refs function to make a copy.
200
+ If there are no "refs" (None or empty) or if the speicified reference is not found in this
201
+ list then the references will be looked up via Portal calls (via Portal.get_metadata).
188
202
  """
189
203
  PortalObject._normalize_refs(self.data, refs=refs, schema=self.schema, portal=self.portal)
190
204
 
@@ -369,7 +369,7 @@ class Schema:
369
369
  def validate(self, data: dict) -> List[str]:
370
370
  errors = []
371
371
  for error in SchemaValidator(self.data, format_checker=SchemaValidator.FORMAT_CHECKER).iter_errors(data):
372
- errors.append(error.message)
372
+ errors.append(f"Validation error at '{error.json_path}': {error.message}")
373
373
  return errors
374
374
 
375
375
  def get_typeinfo(self, column_name: str) -> Optional[dict]:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dcicutils
3
- Version: 8.7.1.1b5
3
+ Version: 8.7.1.1b6
4
4
  Summary: Utility package for interacting with the 4DN Data Portal and other 4DN resources
5
5
  Home-page: https://github.com/4dn-dcic/utils
6
6
  License: MIT
@@ -41,10 +41,10 @@ dcicutils/license_policies/park-lab-gpl-pipeline.jsonc,sha256=vLZkwm3Js-kjV44nug
41
41
  dcicutils/license_policies/park-lab-pipeline.jsonc,sha256=9qlY0ASy3iUMQlr3gorVcXrSfRHnVGbLhkS427UaRy4,283
42
42
  dcicutils/license_utils.py,sha256=d1cq6iwv5Ju-VjdoINi6q7CPNNL7Oz6rcJdLMY38RX0,46978
43
43
  dcicutils/log_utils.py,sha256=7pWMc6vyrorUZQf-V-M3YC6zrPgNhuV_fzm9xqTPph0,10883
44
- dcicutils/misc_utils.py,sha256=nBsrE39yRG6yRQO7DcFiBjXoAo_XVvb7st0Ar_D1G_g,101480
44
+ dcicutils/misc_utils.py,sha256=9JqdVjHLkZUDTryngF3Dbu0m7XcbitbR7izWnxUSWc4,101953
45
45
  dcicutils/obfuscation_utils.py,sha256=fo2jOmDRC6xWpYX49u80bVNisqRRoPskFNX3ymFAmjw,5963
46
46
  dcicutils/opensearch_utils.py,sha256=V2exmFYW8Xl2_pGFixF4I2Cc549Opwe4PhFi5twC0M8,1017
47
- dcicutils/portal_object_utils.py,sha256=_nwnNTu2sctziVWt-35jfmh-Dz7-_ImdRNgWeJdrvxM,11545
47
+ dcicutils/portal_object_utils.py,sha256=Aa61u0o9OyWR7M26dGceMW9MSgDEZLqrwAkwu8FD228,12445
48
48
  dcicutils/portal_utils.py,sha256=8XVRCy882RrB8QT2gGvW36c4nT1RJCgUApt_wLP-EG8,26706
49
49
  dcicutils/project_utils.py,sha256=qPdCaFmWUVBJw4rw342iUytwdQC0P-XKpK4mhyIulMM,31250
50
50
  dcicutils/qa_checkers.py,sha256=cdXjeL0jCDFDLT8VR8Px78aS10hwNISOO5G_Zv2TZ6M,20534
@@ -59,15 +59,15 @@ dcicutils/secrets_utils.py,sha256=8dppXAsiHhJzI6NmOcvJV5ldvKkQZzh3Fl-cb8Wm7MI,19
59
59
  dcicutils/sheet_utils.py,sha256=VlmzteONW5VF_Q4vo0yA5vesz1ViUah1MZ_yA1rwZ0M,33629
60
60
  dcicutils/snapshot_utils.py,sha256=ymP7PXH6-yEiXAt75w0ldQFciGNqWBClNxC5gfX2FnY,22961
61
61
  dcicutils/ssl_certificate_utils.py,sha256=F0ifz_wnRRN9dfrfsz7aCp4UDLgHEY8LaK7PjnNvrAQ,9707
62
- dcicutils/structured_data.py,sha256=qm7C3CSwa7ciV9QoxQw9UdyvVa8kwlDnV4YQgkigCnU,35601
62
+ dcicutils/structured_data.py,sha256=voFT8RqVXLX6-sZiEfhsgmwXEiMcJUrHneYUDXNm9wk,35647
63
63
  dcicutils/task_utils.py,sha256=MF8ujmTD6-O2AC2gRGPHyGdUrVKgtr8epT5XU8WtNjk,8082
64
64
  dcicutils/tmpfile_utils.py,sha256=n95XF8dZVbQRSXBZTGToXXfSs3JUVRyN6c3ZZ0nhAWI,1403
65
65
  dcicutils/trace_utils.py,sha256=g8kwV4ebEy5kXW6oOrEAUsurBcCROvwtZqz9fczsGRE,1769
66
66
  dcicutils/validation_utils.py,sha256=cMZIU2cY98FYtzK52z5WUYck7urH6JcqOuz9jkXpqzg,14797
67
67
  dcicutils/variant_utils.py,sha256=2H9azNx3xAj-MySg-uZ2SFqbWs4kZvf61JnK6b-h4Qw,4343
68
68
  dcicutils/zip_utils.py,sha256=rnjNv_k6L9jT2SjDSgVXp4BEJYLtz9XN6Cl2Fy-tqnM,2027
69
- dcicutils-8.7.1.1b5.dist-info/LICENSE.txt,sha256=qnwSmfnEWMl5l78VPDEzAmEbLVrRqQvfUQiHT0ehrOo,1102
70
- dcicutils-8.7.1.1b5.dist-info/METADATA,sha256=RSYaJ9OhvtM1g2FvzvvXYdpK2WsUVLyeJVX4WgSvKsc,3314
71
- dcicutils-8.7.1.1b5.dist-info/WHEEL,sha256=7Z8_27uaHI_UZAc4Uox4PpBhQ9Y5_modZXWMxtUi4NU,88
72
- dcicutils-8.7.1.1b5.dist-info/entry_points.txt,sha256=8wbw5csMIgBXhkwfgsgJeuFcoUc0WsucUxmOyml2aoA,209
73
- dcicutils-8.7.1.1b5.dist-info/RECORD,,
69
+ dcicutils-8.7.1.1b6.dist-info/LICENSE.txt,sha256=qnwSmfnEWMl5l78VPDEzAmEbLVrRqQvfUQiHT0ehrOo,1102
70
+ dcicutils-8.7.1.1b6.dist-info/METADATA,sha256=YLm-JVegaODLOnyLLQgUzFTwUPRlK-9dn0eZP1ANmHY,3314
71
+ dcicutils-8.7.1.1b6.dist-info/WHEEL,sha256=7Z8_27uaHI_UZAc4Uox4PpBhQ9Y5_modZXWMxtUi4NU,88
72
+ dcicutils-8.7.1.1b6.dist-info/entry_points.txt,sha256=8wbw5csMIgBXhkwfgsgJeuFcoUc0WsucUxmOyml2aoA,209
73
+ dcicutils-8.7.1.1b6.dist-info/RECORD,,