dcicutils 7.11.0__tar.gz → 7.11.0.1b9__tar.gz
Sign up to get free protection for your applications and to get access to all the features.
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/PKG-INFO +3 -1
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/misc_utils.py +95 -1
- dcicutils-7.11.0.1b9/dcicutils/sheet_utils.py +1131 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/pyproject.toml +9 -6
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/LICENSE.txt +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/README.rst +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/__init__.py +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/base.py +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/beanstalk_utils.py +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/cloudformation_utils.py +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/codebuild_utils.py +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/command_utils.py +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/common.py +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/contribution_scripts.py +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/contribution_utils.py +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/creds_utils.py +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/data_utils.py +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/deployment_utils.py +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/diff_utils.py +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/docker_utils.py +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/ecr_scripts.py +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/ecr_utils.py +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/ecs_utils.py +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/env_base.py +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/env_manager.py +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/env_scripts.py +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/env_utils.py +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/env_utils_legacy.py +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/es_utils.py +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/exceptions.py +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/ff_mocks.py +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/ff_utils.py +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/function_cache_decorator.py +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/glacier_utils.py +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/jh_utils.py +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/kibana/dashboards.json +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/kibana/readme.md +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/lang_utils.py +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/license_utils.py +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/log_utils.py +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/obfuscation_utils.py +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/opensearch_utils.py +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/project_utils.py +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/qa_checkers.py +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/qa_utils.py +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/redis_tools.py +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/redis_utils.py +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/s3_utils.py +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/scripts/publish_to_pypi.py +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/secrets_utils.py +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/snapshot_utils.py +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/ssl_certificate_utils.py +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/task_utils.py +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/trace_utils.py +0 -0
- {dcicutils-7.11.0 → dcicutils-7.11.0.1b9}/dcicutils/variant_utils.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: dcicutils
|
3
|
-
Version: 7.11.0
|
3
|
+
Version: 7.11.0.1b9
|
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
|
@@ -25,9 +25,11 @@ Requires-Dist: PyYAML (>=5.1,<5.5)
|
|
25
25
|
Requires-Dist: aws-requests-auth (>=0.4.2,<1)
|
26
26
|
Requires-Dist: boto3 (>=1.17.39,<2.0.0)
|
27
27
|
Requires-Dist: botocore (>=1.20.39,<2.0.0)
|
28
|
+
Requires-Dist: chardet (>=5.2.0,<6.0.0)
|
28
29
|
Requires-Dist: docker (>=4.4.4,<5.0.0)
|
29
30
|
Requires-Dist: elasticsearch (==7.13.4)
|
30
31
|
Requires-Dist: gitpython (>=3.1.2,<4.0.0)
|
32
|
+
Requires-Dist: openpyxl (>=3.1.2,<4.0.0)
|
31
33
|
Requires-Dist: opensearch-py (>=2.0.1,<3.0.0)
|
32
34
|
Requires-Dist: pyOpenSSL (>=23.1.1,<24.0.0)
|
33
35
|
Requires-Dist: pytz (>=2020.4)
|
@@ -9,6 +9,7 @@ import hashlib
|
|
9
9
|
import inspect
|
10
10
|
import math
|
11
11
|
import io
|
12
|
+
import json
|
12
13
|
import os
|
13
14
|
import logging
|
14
15
|
import pytz
|
@@ -191,7 +192,11 @@ class _VirtualAppHelper(webtest.TestApp):
|
|
191
192
|
pass
|
192
193
|
|
193
194
|
|
194
|
-
class
|
195
|
+
class AbstractVirtualApp:
|
196
|
+
pass
|
197
|
+
|
198
|
+
|
199
|
+
class VirtualApp(AbstractVirtualApp):
|
195
200
|
"""
|
196
201
|
Wrapper class for TestApp, to allow custom control over submitting Encoded requests,
|
197
202
|
simulating a number of conditions, including permissions.
|
@@ -1352,6 +1357,25 @@ def capitalize1(s):
|
|
1352
1357
|
return s[:1].upper() + s[1:]
|
1353
1358
|
|
1354
1359
|
|
1360
|
+
"""
|
1361
|
+
Python's UUID ignores all dashes, whereas Postgres is more strict
|
1362
|
+
http://www.postgresql.org/docs/9.2/static/datatype-uuid.html
|
1363
|
+
See also http://www.postgresql.org/docs/9.2/static/datatype-uuid.html
|
1364
|
+
And, anyway, this pattern is what our portals have been doing
|
1365
|
+
for quite a while, so it's the most stable choice for us now.
|
1366
|
+
"""
|
1367
|
+
|
1368
|
+
uuid_re = re.compile(r'(?i)[{]?(?:[0-9a-f]{4}-?){8}[}]?')
|
1369
|
+
|
1370
|
+
|
1371
|
+
def is_uuid(instance):
|
1372
|
+
"""
|
1373
|
+
Predicate returns true for any group of 32 hex characters with optional hyphens every four characters.
|
1374
|
+
We insist on lowercase to make matching faster. See other notes on this design choice above.
|
1375
|
+
"""
|
1376
|
+
return bool(uuid_re.match(instance))
|
1377
|
+
|
1378
|
+
|
1355
1379
|
def string_list(s):
|
1356
1380
|
"""
|
1357
1381
|
Turns a comma-separated list into an actual list, trimming whitespace and ignoring nulls.
|
@@ -2313,3 +2337,73 @@ def parse_in_radix(text: str, *, radix: int):
|
|
2313
2337
|
except Exception:
|
2314
2338
|
pass
|
2315
2339
|
raise ValueError(f"Unable to parse: {text!r}")
|
2340
|
+
|
2341
|
+
|
2342
|
+
def pad_to(target_size: int, data: list, *, padding=None):
|
2343
|
+
"""
|
2344
|
+
This will pad to a given target size, a list of a potentially different actual size, using given padding.
|
2345
|
+
e.g., pad_to(3, [1, 2]) will return [1, 2, None]
|
2346
|
+
"""
|
2347
|
+
actual_size = len(data)
|
2348
|
+
if actual_size < target_size:
|
2349
|
+
data = data + [padding] * (target_size - actual_size)
|
2350
|
+
return data
|
2351
|
+
|
2352
|
+
|
2353
|
+
class JsonLinesReader:
|
2354
|
+
|
2355
|
+
def __init__(self, fp, padded=False, padding=None):
|
2356
|
+
"""
|
2357
|
+
Given an fp (the conventional name for a "file pointer", the thing a call to io.open returns,
|
2358
|
+
this creates an object that can be used to iterate across the lines in the JSON lines file
|
2359
|
+
that the fp is reading from.
|
2360
|
+
|
2361
|
+
There are two possible formats that this will return.
|
2362
|
+
|
2363
|
+
For files that contain a series of dictionaries, such as:
|
2364
|
+
{"something": 1, "else": "a"}
|
2365
|
+
{"something": 2, "else": "b"}
|
2366
|
+
...etc
|
2367
|
+
this will just return thos those dictionaries one-by-one when iterated over.
|
2368
|
+
|
2369
|
+
The same set of dictionaries will also be yielded by a file containing:
|
2370
|
+
["something", "else"]
|
2371
|
+
[1, "a"]
|
2372
|
+
[2, "b"]
|
2373
|
+
...etc
|
2374
|
+
this will just return thos those dictionaries one-by-one when iterated over.
|
2375
|
+
|
2376
|
+
NOTES:
|
2377
|
+
|
2378
|
+
* In the second case, shorter lists on subsequent lines return only partial dictionaries.
|
2379
|
+
* In the second case, longer lists on subsequent lines will quietly drop any extra elements.
|
2380
|
+
"""
|
2381
|
+
|
2382
|
+
self.fp = fp
|
2383
|
+
self.padded: bool = padded
|
2384
|
+
self.padding = padding
|
2385
|
+
self.headers = None # Might change after we see first line
|
2386
|
+
|
2387
|
+
def __iter__(self):
|
2388
|
+
first_line = True
|
2389
|
+
n_headers = 0
|
2390
|
+
for raw_line in self.fp:
|
2391
|
+
line = json.loads(raw_line)
|
2392
|
+
if first_line:
|
2393
|
+
first_line = False
|
2394
|
+
if isinstance(line, list):
|
2395
|
+
self.headers = line
|
2396
|
+
n_headers = len(line)
|
2397
|
+
continue
|
2398
|
+
# If length of line is more than we expect, ignore it. Let user put comments beyond our table
|
2399
|
+
# But if length of line is less than we expect, extend the line with None
|
2400
|
+
if self.headers:
|
2401
|
+
if not isinstance(line, list):
|
2402
|
+
raise Exception("If the first line is a list, all lines must be.")
|
2403
|
+
if self.padded and len(line) < n_headers:
|
2404
|
+
line = pad_to(n_headers, line, padding=self.padding)
|
2405
|
+
yield dict(zip(self.headers, line))
|
2406
|
+
elif isinstance(line, dict):
|
2407
|
+
yield line
|
2408
|
+
else:
|
2409
|
+
raise Exception(f"If the first line is not a list, all lines must be dictionaries: {line!r}")
|