dcicutils 8.7.1.1b9__py3-none-any.whl → 8.7.1.1b12__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- dcicutils/portal_object_utils.py +32 -27
- dcicutils/portal_utils.py +1 -1
- dcicutils/schema_utils.py +2 -2
- dcicutils/structured_data.py +12 -10
- {dcicutils-8.7.1.1b9.dist-info → dcicutils-8.7.1.1b12.dist-info}/METADATA +1 -1
- {dcicutils-8.7.1.1b9.dist-info → dcicutils-8.7.1.1b12.dist-info}/RECORD +9 -9
- {dcicutils-8.7.1.1b9.dist-info → dcicutils-8.7.1.1b12.dist-info}/LICENSE.txt +0 -0
- {dcicutils-8.7.1.1b9.dist-info → dcicutils-8.7.1.1b12.dist-info}/WHEEL +0 -0
- {dcicutils-8.7.1.1b9.dist-info → dcicutils-8.7.1.1b12.dist-info}/entry_points.txt +0 -0
dcicutils/portal_object_utils.py
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
from copy import deepcopy
|
2
2
|
from functools import lru_cache
|
3
|
-
import re
|
4
3
|
from typing import Any, List, Optional, Tuple, Type, Union
|
5
4
|
from dcicutils.data_readers import RowReader
|
6
5
|
from dcicutils.misc_utils import create_readonly_object
|
@@ -14,28 +13,28 @@ class PortalObject:
|
|
14
13
|
|
15
14
|
_PROPERTY_DELETION_SENTINEL = RowReader.CELL_DELETION_SENTINEL
|
16
15
|
|
17
|
-
def __init__(self,
|
18
|
-
self._portal = portal
|
16
|
+
def __init__(self, portal_object: dict, portal: Portal = None, type: Optional[str] = None) -> None:
|
19
17
|
self._data = portal_object
|
20
|
-
self.
|
18
|
+
self._portal = portal
|
19
|
+
self._type = type if isinstance(type, str) and type else None
|
21
20
|
|
22
21
|
@property
|
23
|
-
def data(self):
|
22
|
+
def data(self) -> dict:
|
24
23
|
return self._data
|
25
24
|
|
26
25
|
@property
|
27
|
-
def portal(self):
|
26
|
+
def portal(self) -> Optional[Portal]:
|
28
27
|
return self._portal
|
29
28
|
|
30
29
|
@property
|
31
30
|
@lru_cache(maxsize=1)
|
32
|
-
def type(self):
|
31
|
+
def type(self) -> Optional[str]:
|
33
32
|
return self._type or Portal.get_schema_type(self._data)
|
34
33
|
|
35
34
|
@property
|
36
35
|
@lru_cache(maxsize=1)
|
37
|
-
def types(self):
|
38
|
-
return self._type
|
36
|
+
def types(self) -> Optional[List[str]]:
|
37
|
+
return [self._type] if self._type else Portal.get_schema_types(self._data)
|
39
38
|
|
40
39
|
@property
|
41
40
|
@lru_cache(maxsize=1)
|
@@ -44,22 +43,22 @@ class PortalObject:
|
|
44
43
|
|
45
44
|
@property
|
46
45
|
@lru_cache(maxsize=1)
|
47
|
-
def schema(self):
|
48
|
-
return self._portal.get_schema(self.type)
|
46
|
+
def schema(self) -> Optional[dict]:
|
47
|
+
return self._portal.get_schema(self.type) if self._portal else None
|
49
48
|
|
50
49
|
def copy(self) -> PortalObject:
|
51
|
-
return PortalObject(
|
50
|
+
return PortalObject(deepcopy(self.data), portal=self.portal, type=self.type)
|
52
51
|
|
53
52
|
@property
|
54
53
|
@lru_cache(maxsize=1)
|
55
|
-
def identifying_properties(self) -> List[str]:
|
54
|
+
def identifying_properties(self) -> Optional[List[str]]:
|
56
55
|
"""
|
57
56
|
Returns the list of all identifying property names of this Portal object which actually have values.
|
58
57
|
Implicitly include "uuid" and "identifier" properties as identifying properties if they are actually
|
59
58
|
properties in the object schema, and favor these (first); defavor "aliases"; no other ordering defined.
|
60
59
|
"""
|
61
60
|
if not (schema := self.schema) or not (schema_identifying_properties := schema.get("identifyingProperties")):
|
62
|
-
return
|
61
|
+
return None
|
63
62
|
identifying_properties = []
|
64
63
|
for identifying_property in schema_identifying_properties:
|
65
64
|
if identifying_property not in ["uuid", "identifier", "aliases"]:
|
@@ -71,17 +70,21 @@ class PortalObject:
|
|
71
70
|
identifying_properties.insert(0, "uuid")
|
72
71
|
if "aliases" in schema_identifying_properties and self._data.get("aliases"):
|
73
72
|
identifying_properties.append("aliases")
|
74
|
-
return identifying_properties
|
73
|
+
return identifying_properties or None
|
75
74
|
|
76
75
|
@property
|
77
76
|
@lru_cache(maxsize=1)
|
78
|
-
def identifying_paths(self) -> List[str]:
|
77
|
+
def identifying_paths(self) -> Optional[List[str]]:
|
79
78
|
"""
|
80
79
|
Returns a list of the possible Portal URL paths identifying this Portal object.
|
81
80
|
"""
|
82
|
-
if not (identifying_properties := self.identifying_properties):
|
83
|
-
return []
|
84
81
|
identifying_paths = []
|
82
|
+
if not (identifying_properties := self.identifying_properties):
|
83
|
+
if self.uuid:
|
84
|
+
if self.type:
|
85
|
+
identifying_paths.append(f"/{self.type}/{self.uuid}")
|
86
|
+
identifying_paths.append(f"/{self.uuid}")
|
87
|
+
return identifying_paths
|
85
88
|
for identifying_property in identifying_properties:
|
86
89
|
if (identifying_value := self._data.get(identifying_property)):
|
87
90
|
if identifying_property == "uuid":
|
@@ -96,12 +99,14 @@ class PortalObject:
|
|
96
99
|
# not work but /FileSet/UW_FILE-SET_COLO-829BL_HI-C_1 does work.
|
97
100
|
elif isinstance(identifying_value, list):
|
98
101
|
for identifying_value_item in identifying_value:
|
99
|
-
|
102
|
+
if self.type:
|
103
|
+
identifying_paths.append(f"/{self.type}/{identifying_value_item}")
|
100
104
|
identifying_paths.append(f"/{identifying_value_item}")
|
101
105
|
else:
|
102
|
-
|
106
|
+
if self.type:
|
107
|
+
identifying_paths.append(f"/{self.type}/{identifying_value}")
|
103
108
|
identifying_paths.append(f"/{identifying_value}")
|
104
|
-
return identifying_paths
|
109
|
+
return identifying_paths or None
|
105
110
|
|
106
111
|
@property
|
107
112
|
@lru_cache(maxsize=1)
|
@@ -118,9 +123,11 @@ class PortalObject:
|
|
118
123
|
|
119
124
|
def _lookup(self, raw: bool = False) -> Tuple[Optional[PortalObject], Optional[str]]:
|
120
125
|
try:
|
121
|
-
|
122
|
-
|
123
|
-
|
126
|
+
if identifying_paths := self.identifying_paths:
|
127
|
+
for identifying_path in identifying_paths:
|
128
|
+
if (value := self._portal.get(identifying_path, raw=raw)) and (value.status_code == 200):
|
129
|
+
return PortalObject(value.json(),
|
130
|
+
portal=self._portal, type=self.type if raw else None), identifying_path
|
124
131
|
except Exception:
|
125
132
|
pass
|
126
133
|
return None, self.identifying_path
|
@@ -139,8 +146,6 @@ class PortalObject:
|
|
139
146
|
return {}
|
140
147
|
return PortalObject._compare(this_data, comparing_data)
|
141
148
|
|
142
|
-
_ARRAY_KEY_REGULAR_EXPRESSION = re.compile(rf"^({Schema._ARRAY_NAME_SUFFIX_CHAR}\d+)$")
|
143
|
-
|
144
149
|
@staticmethod
|
145
150
|
def _compare(a: Any, b: Any, _path: Optional[str] = None) -> dict:
|
146
151
|
def diff_creating(value: Any) -> object: # noqa
|
@@ -175,7 +180,7 @@ class PortalObject:
|
|
175
180
|
else:
|
176
181
|
if index < len(b):
|
177
182
|
diffs[path] = diff_deleting(b[index])
|
178
|
-
elif len(b)
|
183
|
+
elif index < len(b):
|
179
184
|
diffs.update(PortalObject._compare(a[index], b[index], _path=path))
|
180
185
|
else:
|
181
186
|
diffs[path] = diff_creating(a[index])
|
dcicutils/portal_utils.py
CHANGED
@@ -28,7 +28,7 @@ class Portal:
|
|
28
28
|
"""
|
29
29
|
This is meant to be an Über wrapper for Portal access. It can be created in a variety of ways:
|
30
30
|
1. From a (Portal) .ini file (e.g. development.ini).
|
31
|
-
2. From a key dictionary, containing "key" and "secret" property values.
|
31
|
+
2. From a key dictionary, containing "key" and "secret" and (optional) "server" property values.
|
32
32
|
3. From a key pair tuple, containing (in order) a key and secret values.
|
33
33
|
4. From a keys .json file residing in ~/.{app}-keys.json where the given "app" value is either "smaht", "cgap",
|
34
34
|
or "fourfront"; where is assumed to contain a dictionary with a key for the given "env" value, e.g. smaht-local;
|
dcicutils/schema_utils.py
CHANGED
@@ -189,9 +189,9 @@ def get_one_of_formats(schema: Dict[str, Any]) -> List[str]:
|
|
189
189
|
|
190
190
|
class Schema:
|
191
191
|
|
192
|
-
def __init__(self, schema: dict,
|
192
|
+
def __init__(self, schema: dict, type: Optional[str] = None) -> None:
|
193
193
|
self._data = schema if isinstance(schema, dict) else (schema.data if isinstance(schema, Schema) else {})
|
194
|
-
self._type = (isinstance(
|
194
|
+
self._type = (type if isinstance(type, str) else "") or Schema.type_name(self._data.get("title", ""))
|
195
195
|
|
196
196
|
@property
|
197
197
|
def data(self) -> dict:
|
dcicutils/structured_data.py
CHANGED
@@ -16,6 +16,7 @@ from dcicutils.misc_utils import (create_dict, create_readonly_object, load_json
|
|
16
16
|
split_string, to_boolean, to_enum, to_float, to_integer, VirtualApp)
|
17
17
|
from dcicutils.portal_object_utils import PortalObject
|
18
18
|
from dcicutils.portal_utils import Portal as PortalBase
|
19
|
+
from dcicutils.schema_utils import Schema as SchemaBase
|
19
20
|
from dcicutils.zip_utils import unpack_gz_file_to_temporary_file, unpack_files
|
20
21
|
|
21
22
|
|
@@ -154,7 +155,7 @@ class StructuredDataSet:
|
|
154
155
|
if not diffs.get(object_type):
|
155
156
|
diffs[object_type] = []
|
156
157
|
for portal_object in self.data[object_type]:
|
157
|
-
portal_object = PortalObject(self.portal,
|
158
|
+
portal_object = PortalObject(portal_object, portal=self.portal, type=object_type)
|
158
159
|
existing_object, identifying_path = portal_object.lookup(include_identifying_path=True, raw=True)
|
159
160
|
if existing_object:
|
160
161
|
object_diffs = portal_object.compare(existing_object, consider_refs=True, resolved_refs=refs)
|
@@ -363,11 +364,12 @@ class _StructuredRowTemplate:
|
|
363
364
|
return structured_row_template
|
364
365
|
|
365
366
|
|
366
|
-
class Schema:
|
367
|
+
class Schema(SchemaBase):
|
367
368
|
|
368
369
|
def __init__(self, schema_json: dict, portal: Optional[Portal] = None) -> None:
|
369
|
-
|
370
|
-
|
370
|
+
super().__init__(schema_json)
|
371
|
+
# self._data = schema_json if isinstance(schema_json, dict) else {}
|
372
|
+
# self._type = Schema.type_name(schema_json.get("title", ""))
|
371
373
|
self._portal = portal # Needed only to resolve linkTo references.
|
372
374
|
self._map_value_functions = {
|
373
375
|
"boolean": self._map_function_boolean,
|
@@ -380,13 +382,13 @@ class Schema:
|
|
380
382
|
self._unresolved_refs = []
|
381
383
|
self._typeinfo = self._create_typeinfo(schema_json)
|
382
384
|
|
383
|
-
|
384
|
-
|
385
|
-
|
385
|
+
# @property
|
386
|
+
# def data(self) -> dict:
|
387
|
+
# return self._data
|
386
388
|
|
387
|
-
|
388
|
-
|
389
|
-
|
389
|
+
# @property
|
390
|
+
# def type(self) -> str:
|
391
|
+
# return self._type
|
390
392
|
|
391
393
|
@staticmethod
|
392
394
|
def load_by_name(name: str, portal: Portal) -> Optional[dict]:
|
@@ -44,30 +44,30 @@ dcicutils/log_utils.py,sha256=7pWMc6vyrorUZQf-V-M3YC6zrPgNhuV_fzm9xqTPph0,10883
|
|
44
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=
|
48
|
-
dcicutils/portal_utils.py,sha256=
|
47
|
+
dcicutils/portal_object_utils.py,sha256=cez8sLu9dyk5f4TrR2QoOPPjCK0m_Sns1FeW5gbdbVw,12686
|
48
|
+
dcicutils/portal_utils.py,sha256=5SfmAs_XEIeS26c6vWsujQgbyK7bACJVzqKgcTrE2SQ,27089
|
49
49
|
dcicutils/project_utils.py,sha256=qPdCaFmWUVBJw4rw342iUytwdQC0P-XKpK4mhyIulMM,31250
|
50
50
|
dcicutils/qa_checkers.py,sha256=cdXjeL0jCDFDLT8VR8Px78aS10hwNISOO5G_Zv2TZ6M,20534
|
51
51
|
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=Ky1KCrHYbDR4qd1prHBKJvO8Z_1x1xVUup1SsQsVP24,10002
|
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
|
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=
|
62
|
+
dcicutils/structured_data.py,sha256=OzT6BZoh7LDw3Tw7mjPJ-SscCyCDQjJ_-yFOWV3jOf8,37319
|
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.
|
70
|
-
dcicutils-8.7.1.
|
71
|
-
dcicutils-8.7.1.
|
72
|
-
dcicutils-8.7.1.
|
73
|
-
dcicutils-8.7.1.
|
69
|
+
dcicutils-8.7.1.1b12.dist-info/LICENSE.txt,sha256=qnwSmfnEWMl5l78VPDEzAmEbLVrRqQvfUQiHT0ehrOo,1102
|
70
|
+
dcicutils-8.7.1.1b12.dist-info/METADATA,sha256=TX64_N_gAD4az9l4pcyZDOHOT8ahGQqp9eTA-LQXDCM,3315
|
71
|
+
dcicutils-8.7.1.1b12.dist-info/WHEEL,sha256=7Z8_27uaHI_UZAc4Uox4PpBhQ9Y5_modZXWMxtUi4NU,88
|
72
|
+
dcicutils-8.7.1.1b12.dist-info/entry_points.txt,sha256=8wbw5csMIgBXhkwfgsgJeuFcoUc0WsucUxmOyml2aoA,209
|
73
|
+
dcicutils-8.7.1.1b12.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|