dcicutils 8.7.0.1b35__py3-none-any.whl → 8.7.0.1b37__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/portal_object_utils.py +2 -55
- dcicutils/schema_utils.py +1 -79
- {dcicutils-8.7.0.1b35.dist-info → dcicutils-8.7.0.1b37.dist-info}/METADATA +1 -1
- {dcicutils-8.7.0.1b35.dist-info → dcicutils-8.7.0.1b37.dist-info}/RECORD +7 -7
- {dcicutils-8.7.0.1b35.dist-info → dcicutils-8.7.0.1b37.dist-info}/LICENSE.txt +0 -0
- {dcicutils-8.7.0.1b35.dist-info → dcicutils-8.7.0.1b37.dist-info}/WHEEL +0 -0
- {dcicutils-8.7.0.1b35.dist-info → dcicutils-8.7.0.1b37.dist-info}/entry_points.txt +0 -0
dcicutils/portal_object_utils.py
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
from functools import lru_cache
|
2
|
-
import
|
3
|
-
from typing import Any, Callable, List, Optional, Tuple, Type, Union
|
2
|
+
from typing import List, Optional, Tuple, Type, Union
|
4
3
|
from dcicutils.portal_utils import Portal
|
5
|
-
from dcicutils.schema_utils import Schema
|
6
4
|
|
7
5
|
PortalObject = Type["PortalObject"] # Forward type reference for type hints.
|
8
6
|
|
@@ -73,6 +71,7 @@ class PortalObject:
|
|
73
71
|
for identifying_property in identifying_properties:
|
74
72
|
if (identifying_value := self._data.get(identifying_property)):
|
75
73
|
if identifying_property == "uuid":
|
74
|
+
identifying_paths.append(f"/{self.type}/{identifying_value}")
|
76
75
|
identifying_paths.append(f"/{identifying_value}")
|
77
76
|
# For now at least we include the path both with and without the schema type component,
|
78
77
|
# as for some identifying values, it works (only) with, and some, it works (only) without.
|
@@ -111,55 +110,3 @@ class PortalObject:
|
|
111
110
|
except Exception:
|
112
111
|
pass
|
113
112
|
return None, self.identifying_path
|
114
|
-
|
115
|
-
def compare(self, value: Union[dict, PortalObject], consider_link_to: bool = False) -> dict:
|
116
|
-
"""
|
117
|
-
Compares this Portal object against the given Portal object value; noting differences values of properites
|
118
|
-
which they have in common; and properties which are in this Portal object and not in the given Portal object;
|
119
|
-
we do NOT check the converse, i.e. properties in the given Portal object which are not in this Portal object.
|
120
|
-
Returns a dictionary with a description of the differences.
|
121
|
-
"""
|
122
|
-
def are_properties_equal(property_path: str, property_value_a: Any, property_value_b: Any) -> bool:
|
123
|
-
if property_value_a == property_value_b:
|
124
|
-
return True
|
125
|
-
nonlocal self
|
126
|
-
if (schema := self.schema) and (property_type := Schema.get_property_by_path(schema, property_path)):
|
127
|
-
if link_to := property_type.get("linkTo"):
|
128
|
-
if a := self._portal.get(f"/{link_to}/{property_value_a}", raw=True):
|
129
|
-
if (a.status_code == 200) and (a := a.json()):
|
130
|
-
if b := self._portal.get(f"/{link_to}/{property_value_b}", raw=True):
|
131
|
-
if (b.status_code == 200) and (b := b.json()):
|
132
|
-
return a == b
|
133
|
-
return False
|
134
|
-
return PortalObject._compare(self._data, value.data if isinstance(value, PortalObject) else value,
|
135
|
-
compare=are_properties_equal if consider_link_to else None)
|
136
|
-
|
137
|
-
_ARRAY_KEY_REGULAR_EXPRESSION = re.compile(rf"^({Schema._ARRAY_NAME_SUFFIX_CHAR}\d+)$")
|
138
|
-
|
139
|
-
@staticmethod
|
140
|
-
def _compare(a: dict, b: dict, compare: Optional[Callable] = None, _path: Optional[str] = None) -> dict:
|
141
|
-
def key_to_path(key: str) -> Optional[str]: # noqa
|
142
|
-
nonlocal _path
|
143
|
-
if match := PortalObject._ARRAY_KEY_REGULAR_EXPRESSION.search(key):
|
144
|
-
return f"{_path}{match.group(1)}" if _path else match.group(1)
|
145
|
-
return f"{_path}.{key}" if _path else key
|
146
|
-
def list_to_dictionary(value: list) -> dict: # noqa
|
147
|
-
result = {}
|
148
|
-
for index, item in enumerate(sorted(value)): # ignore array order
|
149
|
-
result[f"#{index}"] = item
|
150
|
-
return result
|
151
|
-
diffs = {}
|
152
|
-
for key in a:
|
153
|
-
path = key_to_path(key)
|
154
|
-
if key not in b:
|
155
|
-
diffs[path] = {"value": a[key], "missing_value": True}
|
156
|
-
else:
|
157
|
-
if isinstance(a[key], dict) and isinstance(b[key], dict):
|
158
|
-
diffs.update(PortalObject._compare(a[key], b[key], compare=compare, _path=path))
|
159
|
-
elif isinstance(a[key], list) and isinstance(b[key], list):
|
160
|
-
diffs.update(PortalObject._compare(list_to_dictionary(a[key]),
|
161
|
-
list_to_dictionary(b[key]), compare=compare, _path=path))
|
162
|
-
elif a[key] != b[key]:
|
163
|
-
if not callable(compare) or not compare(path, a[key], b[key]):
|
164
|
-
diffs[path] = {"value": a[key], "differing_value": b[key]}
|
165
|
-
return diffs
|
dcicutils/schema_utils.py
CHANGED
@@ -1,6 +1,4 @@
|
|
1
|
-
import
|
2
|
-
from typing import Any, Dict, List, Optional, Tuple
|
3
|
-
from dcicutils.misc_utils import to_camel_case
|
1
|
+
from typing import Any, Dict, List
|
4
2
|
|
5
3
|
|
6
4
|
class JsonSchemaConstants:
|
@@ -185,79 +183,3 @@ def get_one_of_formats(schema: Dict[str, Any]) -> List[str]:
|
|
185
183
|
for one_of_schema in get_one_of(schema)
|
186
184
|
if get_format(one_of_schema)
|
187
185
|
]
|
188
|
-
|
189
|
-
|
190
|
-
class Schema:
|
191
|
-
|
192
|
-
def __init__(self, schema: dict, schema_type: Optional[str] = None) -> None:
|
193
|
-
self._data = schema if isinstance(schema, dict) else (schema.data if isinstance(schema, Schema) else {})
|
194
|
-
self._type = (isinstance(schema_type, str) and schema_type) or Schema.type_name(self._data.get("title", ""))
|
195
|
-
|
196
|
-
@property
|
197
|
-
def data(self) -> dict:
|
198
|
-
return self._data
|
199
|
-
|
200
|
-
@property
|
201
|
-
def type(self) -> str:
|
202
|
-
return self._type
|
203
|
-
|
204
|
-
@staticmethod
|
205
|
-
def type_name(value: str) -> Optional[str]: # File or other name.
|
206
|
-
if isinstance(value, str) and (value := os.path.basename(value.replace(" ", ""))):
|
207
|
-
return to_camel_case(value[0:dot] if (dot := value.rfind(".")) >= 0 else value)
|
208
|
-
|
209
|
-
def property_by_path(self, property_path: str) -> Optional[dict]:
|
210
|
-
"""
|
211
|
-
TODO
|
212
|
-
"""
|
213
|
-
return Schema.get_property_by_path(self._data, property_path)
|
214
|
-
|
215
|
-
_ARRAY_NAME_SUFFIX_CHAR = "#"
|
216
|
-
_DOTTED_NAME_DELIMITER_CHAR = "."
|
217
|
-
|
218
|
-
@staticmethod
|
219
|
-
def get_property_by_path(schema: dict, property_path: str) -> Optional[dict]:
|
220
|
-
if not isinstance(schema, dict) or not isinstance(property_path, str):
|
221
|
-
return None
|
222
|
-
elif not (schema_properties := schema.get("properties")):
|
223
|
-
return None
|
224
|
-
property_paths = property_path.split(Schema._DOTTED_NAME_DELIMITER_CHAR)
|
225
|
-
for property_index, property_name in enumerate(property_paths):
|
226
|
-
property_name, array_specifiers = Schema._unarrayize_property_name(property_name)
|
227
|
-
if not (property_value := schema_properties.get(property_name)):
|
228
|
-
return None
|
229
|
-
elif (property_type := property_value.get("type")) == "object":
|
230
|
-
property_paths_tail = Schema._DOTTED_NAME_DELIMITER_CHAR.join(property_paths[property_index + 1:])
|
231
|
-
return Schema.get_property_by_path(property_value, property_paths_tail)
|
232
|
-
elif (property_type := property_value.get("type")) == "array":
|
233
|
-
if not array_specifiers:
|
234
|
-
if property_index == len(property_paths) - 1:
|
235
|
-
return property_value
|
236
|
-
return None
|
237
|
-
for array_index in range(len(array_specifiers)):
|
238
|
-
if property_type != "array":
|
239
|
-
return None
|
240
|
-
elif not (array_items := property_value.get("items")):
|
241
|
-
return None
|
242
|
-
property_type = (property_value := array_items).get("type")
|
243
|
-
if property_type == "object":
|
244
|
-
if property_index == len(property_paths) - 1:
|
245
|
-
return property_value
|
246
|
-
property_paths_tail = Schema._DOTTED_NAME_DELIMITER_CHAR.join(property_paths[property_index + 1:])
|
247
|
-
return Schema.get_property_by_path(property_value, property_paths_tail)
|
248
|
-
return property_value
|
249
|
-
|
250
|
-
@staticmethod
|
251
|
-
def _unarrayize_property_name(property_name: str) -> Tuple[str, Optional[List[int]]]:
|
252
|
-
if len(components := (property_name := property_name.strip()).split(Schema._ARRAY_NAME_SUFFIX_CHAR)) < 2:
|
253
|
-
return property_name, None
|
254
|
-
unarrayized_property_name = components[0].strip()
|
255
|
-
array_specifiers = []
|
256
|
-
for component in components[1:]:
|
257
|
-
if component.isdigit():
|
258
|
-
array_specifiers.append(int(component))
|
259
|
-
elif component == "":
|
260
|
-
array_specifiers.append(0)
|
261
|
-
else:
|
262
|
-
return property_name, None
|
263
|
-
return unarrayized_property_name, array_specifiers
|
@@ -44,7 +44,7 @@ dcicutils/log_utils.py,sha256=7pWMc6vyrorUZQf-V-M3YC6zrPgNhuV_fzm9xqTPph0,10883
|
|
44
44
|
dcicutils/misc_utils.py,sha256=bMRWWxdbhuF3PKdCZEH-H4U1ecgT3Nag3EL92D9XGoY,100973
|
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=
|
47
|
+
dcicutils/portal_object_utils.py,sha256=P8KW0ASWhac8u0Ddzt-uuQ2LFKAEwCrb7ORMsQNJ8HI,5199
|
48
48
|
dcicutils/portal_utils.py,sha256=jKYgZUYVdkg6VOs1hsiX4bSULLguOIBJFFRpvvZEklU,26704
|
49
49
|
dcicutils/project_utils.py,sha256=qPdCaFmWUVBJw4rw342iUytwdQC0P-XKpK4mhyIulMM,31250
|
50
50
|
dcicutils/qa_checkers.py,sha256=cdXjeL0jCDFDLT8VR8Px78aS10hwNISOO5G_Zv2TZ6M,20534
|
@@ -52,7 +52,7 @@ dcicutils/qa_utils.py,sha256=TT0SiJWiuxYvbsIyhK9VO4uV_suxhB6CpuC4qPacCzQ,160208
|
|
52
52
|
dcicutils/redis_tools.py,sha256=qkcSNMtvqkpvts-Cm9gWhneK523Q_oHwhNUud1be1qk,7055
|
53
53
|
dcicutils/redis_utils.py,sha256=VJ-7g8pOZqR1ZCtdcjKz3-6as2DMUcs1b1zG6wSprH4,6462
|
54
54
|
dcicutils/s3_utils.py,sha256=LauLFQGvZLfpBJ81tYMikjLd3SJRz2R_FrL1n4xSlyI,28868
|
55
|
-
dcicutils/schema_utils.py,sha256=
|
55
|
+
dcicutils/schema_utils.py,sha256=3Gd9QboOjQ3FHFawerylvYYU8Lor1Ma2pFv4JmezCdg,5501
|
56
56
|
dcicutils/scripts/publish_to_pypi.py,sha256=LFzNHIQK2EXFr88YcfctyA_WKEBFc1ElnSjWrCXedPM,13889
|
57
57
|
dcicutils/scripts/run_license_checker.py,sha256=z2keYnRDZsHQbTeo1XORAXSXNJK5axVzL5LjiNqZ7jE,4184
|
58
58
|
dcicutils/secrets_utils.py,sha256=8dppXAsiHhJzI6NmOcvJV5ldvKkQZzh3Fl-cb8Wm7MI,19745
|
@@ -66,8 +66,8 @@ 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.0.
|
70
|
-
dcicutils-8.7.0.
|
71
|
-
dcicutils-8.7.0.
|
72
|
-
dcicutils-8.7.0.
|
73
|
-
dcicutils-8.7.0.
|
69
|
+
dcicutils-8.7.0.1b37.dist-info/LICENSE.txt,sha256=qnwSmfnEWMl5l78VPDEzAmEbLVrRqQvfUQiHT0ehrOo,1102
|
70
|
+
dcicutils-8.7.0.1b37.dist-info/METADATA,sha256=p_b5H768OpaJuXp9IfbDntuVg2MIjyAnjvaZB5T7Vv4,3315
|
71
|
+
dcicutils-8.7.0.1b37.dist-info/WHEEL,sha256=7Z8_27uaHI_UZAc4Uox4PpBhQ9Y5_modZXWMxtUi4NU,88
|
72
|
+
dcicutils-8.7.0.1b37.dist-info/entry_points.txt,sha256=8wbw5csMIgBXhkwfgsgJeuFcoUc0WsucUxmOyml2aoA,209
|
73
|
+
dcicutils-8.7.0.1b37.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|