dcicutils 8.8.4.1b35__tar.gz → 8.8.4.1b37__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/PKG-INFO +1 -1
  2. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/misc_utils.py +15 -3
  3. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/structured_data.py +35 -5
  4. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/pyproject.toml +1 -1
  5. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/LICENSE.txt +0 -0
  6. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/README.rst +0 -0
  7. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/__init__.py +0 -0
  8. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/base.py +0 -0
  9. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/beanstalk_utils.py +0 -0
  10. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/bundle_utils.py +0 -0
  11. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/captured_output.py +0 -0
  12. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/cloudformation_utils.py +0 -0
  13. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/codebuild_utils.py +0 -0
  14. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/command_utils.py +0 -0
  15. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/common.py +0 -0
  16. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/contribution_scripts.py +0 -0
  17. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/contribution_utils.py +0 -0
  18. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/creds_utils.py +0 -0
  19. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/data_readers.py +0 -0
  20. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/data_utils.py +0 -0
  21. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/datetime_utils.py +0 -0
  22. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/deployment_utils.py +0 -0
  23. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/diff_utils.py +0 -0
  24. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/docker_utils.py +0 -0
  25. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/ecr_scripts.py +0 -0
  26. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/ecr_utils.py +0 -0
  27. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/ecs_utils.py +0 -0
  28. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/env_base.py +0 -0
  29. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/env_manager.py +0 -0
  30. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/env_scripts.py +0 -0
  31. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/env_utils.py +0 -0
  32. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/env_utils_legacy.py +0 -0
  33. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/es_utils.py +0 -0
  34. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/exceptions.py +0 -0
  35. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/ff_mocks.py +0 -0
  36. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/ff_utils.py +0 -0
  37. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/file_utils.py +0 -0
  38. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/function_cache_decorator.py +0 -0
  39. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/glacier_utils.py +0 -0
  40. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/http_utils.py +0 -0
  41. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/jh_utils.py +0 -0
  42. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/kibana/dashboards.json +0 -0
  43. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/kibana/readme.md +0 -0
  44. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/lang_utils.py +0 -0
  45. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/license_policies/c4-infrastructure.jsonc +0 -0
  46. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/license_policies/c4-python-infrastructure.jsonc +0 -0
  47. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/license_policies/park-lab-common-server.jsonc +0 -0
  48. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/license_policies/park-lab-common.jsonc +0 -0
  49. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/license_policies/park-lab-gpl-pipeline.jsonc +0 -0
  50. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/license_policies/park-lab-pipeline.jsonc +0 -0
  51. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/license_utils.py +0 -0
  52. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/log_utils.py +0 -0
  53. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/obfuscation_utils.py +0 -0
  54. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/opensearch_utils.py +0 -0
  55. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/portal_object_utils.py +0 -0
  56. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/portal_utils.py +0 -0
  57. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/progress_bar.py +0 -0
  58. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/project_utils.py +0 -0
  59. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/qa_checkers.py +0 -0
  60. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/qa_utils.py +0 -0
  61. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/redis_tools.py +0 -0
  62. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/redis_utils.py +0 -0
  63. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/s3_utils.py +0 -0
  64. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/schema_utils.py +0 -0
  65. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/scripts/publish_to_pypi.py +0 -0
  66. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/scripts/run_license_checker.py +0 -0
  67. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/scripts/view_portal_object.py +0 -0
  68. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/secrets_utils.py +0 -0
  69. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/sheet_utils.py +0 -0
  70. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/snapshot_utils.py +0 -0
  71. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/ssl_certificate_utils.py +0 -0
  72. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/submitr/progress_constants.py +0 -0
  73. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/submitr/ref_lookup_strategy.py +0 -0
  74. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/task_utils.py +0 -0
  75. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/tmpfile_utils.py +0 -0
  76. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/trace_utils.py +0 -0
  77. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/validation_utils.py +0 -0
  78. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/variant_utils.py +0 -0
  79. {dcicutils-8.8.4.1b35 → dcicutils-8.8.4.1b37}/dcicutils/zip_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dcicutils
3
- Version: 8.8.4.1b35
3
+ Version: 8.8.4.1b37
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
@@ -1155,7 +1155,8 @@ def remove_suffix(suffix: str, text: str, required: bool = False):
1155
1155
 
1156
1156
  def remove_empty_properties(data: Optional[Union[list, dict]],
1157
1157
  isempty: Optional[Callable] = None,
1158
- isempty_array_element: Optional[Callable] = None) -> None:
1158
+ isempty_array_element: Optional[Callable] = None,
1159
+ raise_exception_on_nonempty_array_element_after_empty: bool = False) -> None:
1159
1160
  def _isempty(value: Any) -> bool: # noqa
1160
1161
  return isempty(value) if callable(isempty) else value in [None, "", {}, []]
1161
1162
  if isinstance(data, dict):
@@ -1163,11 +1164,22 @@ def remove_empty_properties(data: Optional[Union[list, dict]],
1163
1164
  if _isempty(value := data[key]):
1164
1165
  del data[key]
1165
1166
  else:
1166
- remove_empty_properties(value, isempty=isempty, isempty_array_element=isempty_array_element)
1167
+ remove_empty_properties(value, isempty=isempty, isempty_array_element=isempty_array_element,
1168
+ raise_exception_on_nonempty_array_element_after_empty= # noqa
1169
+ raise_exception_on_nonempty_array_element_after_empty)
1167
1170
  elif isinstance(data, list):
1168
1171
  for item in data:
1169
- remove_empty_properties(item, isempty=isempty, isempty_array_element=isempty_array_element)
1172
+ remove_empty_properties(item, isempty=isempty, isempty_array_element=isempty_array_element,
1173
+ raise_exception_on_nonempty_array_element_after_empty= # noqa
1174
+ raise_exception_on_nonempty_array_element_after_empty)
1170
1175
  if callable(isempty_array_element):
1176
+ if raise_exception_on_nonempty_array_element_after_empty is True:
1177
+ empty_element_seen = False
1178
+ for item in data:
1179
+ if not empty_element_seen and isempty_array_element(item):
1180
+ empty_element_seen = True
1181
+ elif empty_element_seen and not isempty_array_element(item):
1182
+ raise Exception("Non-empty element found after empty element.")
1171
1183
  data[:] = [item for item in data if not isempty_array_element(item)]
1172
1184
 
1173
1185
 
@@ -53,6 +53,7 @@ class StructuredDataSet:
53
53
  def __init__(self, file: Optional[str] = None, portal: Optional[Union[VirtualApp, TestApp, Portal]] = None,
54
54
  schemas: Optional[List[dict]] = None, autoadd: Optional[dict] = None,
55
55
  order: Optional[List[str]] = None, prune: bool = True,
56
+ remove_empty_objects_from_lists: bool = True,
56
57
  ref_lookup_strategy: Optional[Callable] = None,
57
58
  ref_lookup_nocache: bool = False,
58
59
  norefs: bool = False,
@@ -65,7 +66,8 @@ class StructuredDataSet:
65
66
  ref_lookup_nocache=ref_lookup_nocache) if portal else None
66
67
  self._ref_lookup_strategy = ref_lookup_strategy
67
68
  self._order = order
68
- self._prune = prune
69
+ self._prune = prune is True
70
+ self._remove_empty_objects_from_lists = remove_empty_objects_from_lists is True
69
71
  self._warnings = {}
70
72
  self._errors = {}
71
73
  self._resolved_refs = set()
@@ -93,12 +95,14 @@ class StructuredDataSet:
93
95
  def load(file: str, portal: Optional[Union[VirtualApp, TestApp, Portal]] = None,
94
96
  schemas: Optional[List[dict]] = None, autoadd: Optional[dict] = None,
95
97
  order: Optional[List[str]] = None, prune: bool = True,
98
+ remove_empty_objects_from_lists: bool = True,
96
99
  ref_lookup_strategy: Optional[Callable] = None,
97
100
  ref_lookup_nocache: bool = False,
98
101
  norefs: bool = False,
99
102
  progress: Optional[Callable] = None,
100
103
  debug_sleep: Optional[str] = None) -> StructuredDataSet:
101
104
  return StructuredDataSet(file=file, portal=portal, schemas=schemas, autoadd=autoadd, order=order, prune=prune,
105
+ remove_empty_objects_from_lists=remove_empty_objects_from_lists,
102
106
  ref_lookup_strategy=ref_lookup_strategy, ref_lookup_nocache=ref_lookup_nocache,
103
107
  norefs=norefs, progress=progress, debug_sleep=debug_sleep)
104
108
 
@@ -346,7 +350,18 @@ class StructuredDataSet:
346
350
 
347
351
  def _load_json_file(self, file: str) -> None:
348
352
  with open(file) as f:
349
- self._add(Schema.type_name(file), json.load(f))
353
+ file_json = json.load(f)
354
+ schema_inferred_from_file_name = Schema.type_name(file)
355
+ if self._portal.get_schema(schema_inferred_from_file_name) is not None:
356
+ # If the JSON file name looks like a schema name then assume it
357
+ # contains an object or an array of object of that schema type.
358
+ self._add(Schema.type_name(file), file_json)
359
+ elif isinstance(file_json, dict):
360
+ # Otherwise if the JSON file name does not look like a schema name then
361
+ # assume it a dictionary where each property is the name of a schema, and
362
+ # which (each property) contains a list of object of that schema type.
363
+ for schema_name in file_json:
364
+ self._add(schema_name, file_json[schema_name])
350
365
 
351
366
  def _load_reader(self, reader: RowReader, type_name: str) -> None:
352
367
  schema = None
@@ -368,7 +383,11 @@ class StructuredDataSet:
368
383
  structured_row_template.set_value(structured_row, column_name, value, reader.file, reader.row_number)
369
384
  if self._autoadd_properties:
370
385
  self._add_properties(structured_row, self._autoadd_properties, schema)
371
- self._add(type_name, structured_row)
386
+ if (prune_error := self._prune_structured_row(structured_row)) is not None:
387
+ self._note_error({"src": create_dict(type=schema_name, row=reader.row_number),
388
+ "error": prune_error}, "validation")
389
+ else:
390
+ self._add(type_name, structured_row)
372
391
  if self._progress:
373
392
  self._progress({
374
393
  PROGRESS.LOAD_ITEM: self._nrows,
@@ -385,9 +404,20 @@ class StructuredDataSet:
385
404
  self._note_error(schema._unresolved_refs, "ref")
386
405
  self._resolved_refs.update(schema._resolved_refs)
387
406
 
388
- def _add(self, type_name: str, data: Union[dict, List[dict]]) -> None:
389
- if self._prune:
407
+ def _prune_structured_row(self, data: dict) -> Optional[str]:
408
+ if not self._prune:
409
+ return None
410
+ if not self._remove_empty_objects_from_lists:
390
411
  remove_empty_properties(data)
412
+ return None
413
+ try:
414
+ remove_empty_properties(data, isempty_array_element=lambda element: element == {},
415
+ raise_exception_on_nonempty_array_element_after_empty=True)
416
+ except Exception as e:
417
+ return str(e)
418
+ return None
419
+
420
+ def _add(self, type_name: str, data: Union[dict, List[dict]]) -> None:
391
421
  if type_name in self._data:
392
422
  self._data[type_name].extend([data] if isinstance(data, dict) else data)
393
423
  else:
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "dcicutils"
3
- version = "8.8.4.1b35" # TODO: To become 8.8.5
3
+ version = "8.8.4.1b37" # TODO: To become 8.8.5
4
4
  description = "Utility package for interacting with the 4DN Data Portal and other 4DN resources"
5
5
  authors = ["4DN-DCIC Team <support@4dnucleome.org>"]
6
6
  license = "MIT"