dcicutils 8.9.0.1b2__py3-none-any.whl → 8.9.0.1b5__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
@@ -4,6 +4,7 @@ This file contains functions that might be generally useful.
4
4
 
5
5
  from collections import namedtuple
6
6
  import appdirs
7
+ from copy import deepcopy
7
8
  import contextlib
8
9
  import datetime
9
10
  import functools
@@ -2199,28 +2200,58 @@ def merge_key_value_dict_lists(x, y):
2199
2200
  return [key_value_dict(k, v) for k, v in merged.items()]
2200
2201
 
2201
2202
 
2202
- def merge_objects(target: Union[dict, List[Any]], source: Union[dict, List[Any]], full: bool = False) -> dict:
2203
+ def merge_objects(target: Union[dict, List[Any]], source: Union[dict, List[Any]],
2204
+ full: bool = False, # deprecated
2205
+ expand_lists: Optional[bool] = None,
2206
+ primitive_lists: bool = False,
2207
+ copy: bool = False, _recursing: bool = False) -> Union[dict, List[Any]]:
2203
2208
  """
2204
- Merges the given source dictionary or list into the target dictionary or list.
2205
- This MAY well change the given target (dictionary or list) IN PLACE.
2206
- The the full argument is True then any target lists longer than the
2207
- source be will be filled out with the last element(s) of the source.
2209
+ Merges the given source dictionary or list into the target dictionary or list and returns the
2210
+ result. This MAY well change the given target (dictionary or list) IN PLACE ... UNLESS the copy
2211
+ argument is True, then the given target will not change as a local copy is made (and returned).
2212
+
2213
+ If the expand_lists argument is True then any target lists longer than the
2214
+ source be will be filled out with the last element(s) of the source; the full
2215
+ argument (is deprecated and) is a synomym for this. The default is False.
2216
+
2217
+ If the primitive_lists argument is True then lists of primitives (i.e. lists in which
2218
+ NONE of its elements are dictionaries, lists, or tuples) will themselves be treated
2219
+ like primitives, meaning the whole of a source list will replace the corresponding
2220
+ target; otherwise they will be merged normally, meaning each element of a source list
2221
+ will be merged, recursively, into the corresponding target list. The default is False.
2208
2222
  """
2223
+ def is_primitive_list(value: Any) -> bool: # noqa
2224
+ if not isinstance(value, list):
2225
+ return False
2226
+ for item in value:
2227
+ if isinstance(item, (dict, list, tuple)):
2228
+ return False
2229
+ return True
2230
+
2209
2231
  if target is None:
2210
2232
  return source
2233
+ if expand_lists not in (True, False):
2234
+ expand_lists = full is True
2235
+ if (copy is True) and (_recursing is not True):
2236
+ target = deepcopy(target)
2211
2237
  if isinstance(target, dict) and isinstance(source, dict) and source:
2212
2238
  for key, value in source.items():
2213
- target[key] = merge_objects(target[key], value, full) if key in target else value
2239
+ if ((primitive_lists is True) and
2240
+ (key in target) and is_primitive_list(target[key]) and is_primitive_list(value)): # noqa
2241
+ target[key] = value
2242
+ else:
2243
+ target[key] = merge_objects(target[key], value,
2244
+ expand_lists=expand_lists, _recursing=True) if key in target else value
2214
2245
  elif isinstance(target, list) and isinstance(source, list) and source:
2215
2246
  for i in range(max(len(source), len(target))):
2216
2247
  if i < len(target):
2217
2248
  if i < len(source):
2218
- target[i] = merge_objects(target[i], source[i], full)
2219
- elif full:
2220
- target[i] = merge_objects(target[i], source[len(source) - 1], full)
2249
+ target[i] = merge_objects(target[i], source[i], expand_lists=expand_lists, _recursing=True)
2250
+ elif expand_lists is True:
2251
+ target[i] = merge_objects(target[i], source[len(source) - 1], expand_lists=expand_lists)
2221
2252
  else:
2222
2253
  target.append(source[i])
2223
- elif source:
2254
+ elif source not in (None, {}, []):
2224
2255
  target = source
2225
2256
  return target
2226
2257
 
@@ -74,7 +74,7 @@ class StructuredDataSet:
74
74
  self._nrows = 0
75
75
  self._autoadd_properties = autoadd if isinstance(autoadd, dict) and autoadd else None
76
76
  self._norefs = True if norefs is True else False
77
- self._merge = True if merge is True else False
77
+ self._merge = True if merge is True else False # New merge functionality (2024-05-25)
78
78
  self._debug_sleep = None
79
79
  if debug_sleep:
80
80
  try:
@@ -347,7 +347,7 @@ class StructuredDataSet:
347
347
  (self._portal.get_schema(schema_name_inferred_from_file_name) is not None)): # noqa
348
348
  # If the JSON file name looks like a schema name then assume it
349
349
  # contains an object or an array of object of that schema type.
350
- if self._merge:
350
+ if self._merge: # New merge functionality (2024-05-25)
351
351
  data = self._merge_with_existing_portal_object(data, schema_name_inferred_from_file_name)
352
352
  self._add(Schema.type_name(file), data)
353
353
  elif isinstance(data, dict):
@@ -356,7 +356,7 @@ class StructuredDataSet:
356
356
  # which (each property) contains a list of object of that schema type.
357
357
  for schema_name in data:
358
358
  item = data[schema_name]
359
- if self._merge:
359
+ if self._merge: # New merge functionality (2024-05-25)
360
360
  item = self._merge_with_existing_portal_object(item, schema_name)
361
361
  self._add(schema_name, item)
362
362
 
@@ -380,8 +380,7 @@ class StructuredDataSet:
380
380
  structured_row_template.set_value(structured_row, column_name, value, reader.file, reader.row_number)
381
381
  if self._autoadd_properties:
382
382
  self._add_properties(structured_row, self._autoadd_properties, schema)
383
- # New merge functionality (2024-05-25).
384
- if self._merge:
383
+ if self._merge: # New merge functionality (2024-05-25)
385
384
  structured_row = self._merge_with_existing_portal_object(structured_row, schema_name)
386
385
  if (prune_error := self._prune_structured_row(structured_row)) is not None:
387
386
  self._note_error({"src": create_dict(type=schema_name, row=reader.row_number),
@@ -437,7 +436,7 @@ class StructuredDataSet:
437
436
  """
438
437
  for identifying_path in self._portal.get_identifying_paths(portal_object, portal_type):
439
438
  if existing_portal_object := self._portal.get_metadata(identifying_path, raw=True, raise_exception=False):
440
- return merge_objects(existing_portal_object, portal_object)
439
+ return merge_objects(existing_portal_object, portal_object, primitive_lists=True)
441
440
  return portal_object
442
441
 
443
442
  def _is_ref_lookup_specified_type(ref_lookup_flags: int) -> bool:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dcicutils
3
- Version: 8.9.0.1b2
3
+ Version: 8.9.0.1b5
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
@@ -44,7 +44,7 @@ dcicutils/license_policies/park-lab-gpl-pipeline.jsonc,sha256=vLZkwm3Js-kjV44nug
44
44
  dcicutils/license_policies/park-lab-pipeline.jsonc,sha256=9qlY0ASy3iUMQlr3gorVcXrSfRHnVGbLhkS427UaRy4,283
45
45
  dcicutils/license_utils.py,sha256=d1cq6iwv5Ju-VjdoINi6q7CPNNL7Oz6rcJdLMY38RX0,46978
46
46
  dcicutils/log_utils.py,sha256=7pWMc6vyrorUZQf-V-M3YC6zrPgNhuV_fzm9xqTPph0,10883
47
- dcicutils/misc_utils.py,sha256=zHwsxxEn24muLBP7mDvMa8I9VdMejwW8HMuCL5xbhhw,107690
47
+ dcicutils/misc_utils.py,sha256=-syqTAj8DESiiP_KHoyBv9VvfboFYB03QbBlmXnBZXw,109423
48
48
  dcicutils/obfuscation_utils.py,sha256=fo2jOmDRC6xWpYX49u80bVNisqRRoPskFNX3ymFAmjw,5963
49
49
  dcicutils/opensearch_utils.py,sha256=V2exmFYW8Xl2_pGFixF4I2Cc549Opwe4PhFi5twC0M8,1017
50
50
  dcicutils/portal_object_utils.py,sha256=Az3n1aL-PQkN5gOFE6ZqC2XkYsqiwKlq7-tZggs1QN4,11062
@@ -64,7 +64,7 @@ dcicutils/secrets_utils.py,sha256=8dppXAsiHhJzI6NmOcvJV5ldvKkQZzh3Fl-cb8Wm7MI,19
64
64
  dcicutils/sheet_utils.py,sha256=VlmzteONW5VF_Q4vo0yA5vesz1ViUah1MZ_yA1rwZ0M,33629
65
65
  dcicutils/snapshot_utils.py,sha256=ymP7PXH6-yEiXAt75w0ldQFciGNqWBClNxC5gfX2FnY,22961
66
66
  dcicutils/ssl_certificate_utils.py,sha256=F0ifz_wnRRN9dfrfsz7aCp4UDLgHEY8LaK7PjnNvrAQ,9707
67
- dcicutils/structured_data.py,sha256=GfG96VyFwQIkmUax-ZdEzbWnfEiJxmeZEaUWz78IQZY,64030
67
+ dcicutils/structured_data.py,sha256=HVe1ruXz0vH4nRBOwq0cNrfR4KtqocC4U940KRXM5zY,64160
68
68
  dcicutils/submitr/progress_constants.py,sha256=5bxyX77ql8qEJearfHEvsvXl7D0GuUODW0T65mbRmnE,2895
69
69
  dcicutils/submitr/ref_lookup_strategy.py,sha256=VJN-Oo0LLna6Vo2cu47eC-eU-yUC9NFlQP29xajejVU,4741
70
70
  dcicutils/task_utils.py,sha256=MF8ujmTD6-O2AC2gRGPHyGdUrVKgtr8epT5XU8WtNjk,8082
@@ -73,8 +73,8 @@ dcicutils/trace_utils.py,sha256=g8kwV4ebEy5kXW6oOrEAUsurBcCROvwtZqz9fczsGRE,1769
73
73
  dcicutils/validation_utils.py,sha256=cMZIU2cY98FYtzK52z5WUYck7urH6JcqOuz9jkXpqzg,14797
74
74
  dcicutils/variant_utils.py,sha256=2H9azNx3xAj-MySg-uZ2SFqbWs4kZvf61JnK6b-h4Qw,4343
75
75
  dcicutils/zip_utils.py,sha256=_Y9EmL3D2dUZhxucxHvrtmmlbZmK4FpSsHEb7rGSJLU,3265
76
- dcicutils-8.9.0.1b2.dist-info/LICENSE.txt,sha256=qnwSmfnEWMl5l78VPDEzAmEbLVrRqQvfUQiHT0ehrOo,1102
77
- dcicutils-8.9.0.1b2.dist-info/METADATA,sha256=Mh-FpaQpv_ipkozMQ_Ul_vezgpxdzX4lvp38jaDD8rc,3439
78
- dcicutils-8.9.0.1b2.dist-info/WHEEL,sha256=7Z8_27uaHI_UZAc4Uox4PpBhQ9Y5_modZXWMxtUi4NU,88
79
- dcicutils-8.9.0.1b2.dist-info/entry_points.txt,sha256=51Q4F_2V10L0282W7HFjP4jdzW4K8lnWDARJQVFy_hw,270
80
- dcicutils-8.9.0.1b2.dist-info/RECORD,,
76
+ dcicutils-8.9.0.1b5.dist-info/LICENSE.txt,sha256=qnwSmfnEWMl5l78VPDEzAmEbLVrRqQvfUQiHT0ehrOo,1102
77
+ dcicutils-8.9.0.1b5.dist-info/METADATA,sha256=YTZR9PRzIbTMoeiRjoZD6mKgIDpZFgQmig1EJXLYoN8,3439
78
+ dcicutils-8.9.0.1b5.dist-info/WHEEL,sha256=7Z8_27uaHI_UZAc4Uox4PpBhQ9Y5_modZXWMxtUi4NU,88
79
+ dcicutils-8.9.0.1b5.dist-info/entry_points.txt,sha256=51Q4F_2V10L0282W7HFjP4jdzW4K8lnWDARJQVFy_hw,270
80
+ dcicutils-8.9.0.1b5.dist-info/RECORD,,