edq-utils 0.1.1__py3-none-any.whl → 0.1.3__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.
Potentially problematic release.
This version of edq-utils might be problematic. Click here for more details.
- edq/__init__.py +1 -1
- edq/testing/cli.py +3 -2
- edq/testing/testdata/cli/tests/help_base.txt +9 -0
- edq/testing/unittest.py +14 -0
- edq/util/hash.py +38 -0
- edq/util/hash_test.py +89 -0
- edq/util/net.py +8 -1
- {edq_utils-0.1.1.dist-info → edq_utils-0.1.3.dist-info}/METADATA +1 -1
- {edq_utils-0.1.1.dist-info → edq_utils-0.1.3.dist-info}/RECORD +12 -9
- {edq_utils-0.1.1.dist-info → edq_utils-0.1.3.dist-info}/WHEEL +0 -0
- {edq_utils-0.1.1.dist-info → edq_utils-0.1.3.dist-info}/licenses/LICENSE +0 -0
- {edq_utils-0.1.1.dist-info → edq_utils-0.1.3.dist-info}/top_level.txt +0 -0
edq/__init__.py
CHANGED
edq/testing/cli.py
CHANGED
|
@@ -35,6 +35,7 @@ import edq.util.json
|
|
|
35
35
|
import edq.util.pyimport
|
|
36
36
|
|
|
37
37
|
TEST_CASE_SEP: str = '---'
|
|
38
|
+
OUTPUT_SEP: str = '+++'
|
|
38
39
|
DATA_DIR_ID: str = '__DATA_DIR__'
|
|
39
40
|
ABS_DATA_DIR_ID: str = '__ABS_DATA_DIR__'
|
|
40
41
|
TEMP_DIR_ID: str = '__TEMP_DIR__'
|
|
@@ -149,7 +150,7 @@ class CLITestInfo:
|
|
|
149
150
|
"""
|
|
150
151
|
Split stdout and stderr into different strings for testing.
|
|
151
152
|
By default, these two will be combined.
|
|
152
|
-
If both are non-empty, then they will be joined like: f"{stdout}\n{
|
|
153
|
+
If both are non-empty, then they will be joined like: f"{stdout}\n{OUTPUT_SEP}\n{stderr}".
|
|
153
154
|
Otherwise, only the non-empty one will be present with no separator.
|
|
154
155
|
Any stdout assertions will be applied to the combined text.
|
|
155
156
|
"""
|
|
@@ -293,7 +294,7 @@ def _get_test_method(test_name: str, path: str, data_dir: str) -> typing.Callabl
|
|
|
293
294
|
|
|
294
295
|
if (not test_info.split_stdout_stderr):
|
|
295
296
|
if ((len(stdout_text) > 0) and (len(stderr_text) > 0)):
|
|
296
|
-
stdout_text = f"{stdout_text}\n{
|
|
297
|
+
stdout_text = f"{stdout_text}\n{OUTPUT_SEP}\n{stderr_text}"
|
|
297
298
|
elif (len(stderr_text) > 0):
|
|
298
299
|
stdout_text = stderr_text
|
|
299
300
|
|
edq/testing/unittest.py
CHANGED
|
@@ -14,6 +14,20 @@ class BaseTest(unittest.TestCase):
|
|
|
14
14
|
maxDiff = None
|
|
15
15
|
""" Don't limit the size of diffs. """
|
|
16
16
|
|
|
17
|
+
def assertJSONEqual(self, a: typing.Any, b: typing.Any, message: typing.Union[str, None] = None) -> None: # pylint: disable=invalid-name
|
|
18
|
+
"""
|
|
19
|
+
Like unittest.TestCase.assertEqual(),
|
|
20
|
+
but uses a default assertion message containing the full JSON representation of the arguments.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
a_json = edq.util.json.dumps(a, indent = 4)
|
|
24
|
+
b_json = edq.util.json.dumps(b, indent = 4)
|
|
25
|
+
|
|
26
|
+
if (message is None):
|
|
27
|
+
message = FORMAT_STR % (a_json, b_json)
|
|
28
|
+
|
|
29
|
+
super().assertEqual(a, b, msg = message)
|
|
30
|
+
|
|
17
31
|
def assertJSONDictEqual(self, a: typing.Any, b: typing.Any, message: typing.Union[str, None] = None) -> None: # pylint: disable=invalid-name
|
|
18
32
|
"""
|
|
19
33
|
Like unittest.TestCase.assertDictEqual(),
|
edq/util/hash.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import hashlib
|
|
2
|
+
import typing
|
|
3
|
+
|
|
4
|
+
import edq.util.dirent
|
|
5
|
+
|
|
6
|
+
DEFAULT_CLIP_HASH_LENGTH: int = 8
|
|
7
|
+
|
|
8
|
+
def sha256_hex(payload: typing.Any, encoding: str = edq.util.dirent.DEFAULT_ENCODING) -> str:
|
|
9
|
+
""" Compute and return the hex string of the SHA3-256 encoding of the payload. """
|
|
10
|
+
|
|
11
|
+
digest = hashlib.new('sha256')
|
|
12
|
+
digest.update(payload.encode(encoding))
|
|
13
|
+
return digest.hexdigest()
|
|
14
|
+
|
|
15
|
+
def clip_text(text: str, max_length: int, hash_length: int = DEFAULT_CLIP_HASH_LENGTH) -> str:
|
|
16
|
+
"""
|
|
17
|
+
Return a clipped version of the input text that is no longer than the specified length.
|
|
18
|
+
If the base text is found to be too long,
|
|
19
|
+
then enough if the tail of the text will be removed to insert a note about the clipping
|
|
20
|
+
and the first |hash_length| characters of the hash from sha256_hex().
|
|
21
|
+
|
|
22
|
+
Note that the max length is actually a soft cap.
|
|
23
|
+
Longer strings can be generated if the original text is shorter than the notification
|
|
24
|
+
that will be inserted into the clipped text.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
if (len(text) <= max_length):
|
|
28
|
+
return text
|
|
29
|
+
|
|
30
|
+
hash_hex = sha256_hex(text)
|
|
31
|
+
notification = f"[text clipped {hash_hex[0:hash_length]}]"
|
|
32
|
+
|
|
33
|
+
# Don't clip the text if the final string would be longer.
|
|
34
|
+
if (len(notification) >= len(text)):
|
|
35
|
+
return text
|
|
36
|
+
|
|
37
|
+
keep_length = max(0, max_length - len(notification))
|
|
38
|
+
return text[0:keep_length] + notification
|
edq/util/hash_test.py
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import edq.testing.unittest
|
|
2
|
+
import edq.util.hash
|
|
3
|
+
|
|
4
|
+
class TestHash(edq.testing.unittest.BaseTest):
|
|
5
|
+
""" Test hash-based operations. """
|
|
6
|
+
|
|
7
|
+
def test_sha256_hex_base(self):
|
|
8
|
+
""" Test the base sha256 hash. """
|
|
9
|
+
|
|
10
|
+
# [(input, expected), ...]
|
|
11
|
+
test_cases = [
|
|
12
|
+
('foo', '2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae'),
|
|
13
|
+
('abcdefghijklmnopqrstuvwxyz1234567890', '77d721c817f9d216c1fb783bcad9cdc20aaa2427402683f1f75dd6dfbe657470'),
|
|
14
|
+
('', 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'),
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
for (i, test_case) in enumerate(test_cases):
|
|
18
|
+
(text, expected) = test_case
|
|
19
|
+
|
|
20
|
+
with self.subTest(msg = f"Case {i} ('{text}'):"):
|
|
21
|
+
actual = edq.util.hash.sha256_hex(text)
|
|
22
|
+
self.assertEqual(expected, actual)
|
|
23
|
+
|
|
24
|
+
def test_clip_text_base(self):
|
|
25
|
+
""" Test the base functionality of clip_text(). """
|
|
26
|
+
|
|
27
|
+
# [(text, max length, kwargs, expected), ...]
|
|
28
|
+
test_cases = [
|
|
29
|
+
# No Clip
|
|
30
|
+
(
|
|
31
|
+
'abcdefghijklmnopqrstuvwxyz1234567890',
|
|
32
|
+
100,
|
|
33
|
+
{},
|
|
34
|
+
'abcdefghijklmnopqrstuvwxyz1234567890',
|
|
35
|
+
),
|
|
36
|
+
|
|
37
|
+
# Base Clip
|
|
38
|
+
(
|
|
39
|
+
'abcdefghijklmnopqrstuvwxyz1234567890',
|
|
40
|
+
30,
|
|
41
|
+
{},
|
|
42
|
+
'abcdefg[text clipped 77d721c8]',
|
|
43
|
+
),
|
|
44
|
+
|
|
45
|
+
# Full Clip
|
|
46
|
+
(
|
|
47
|
+
'abcdefghijklmnopqrstuvwxyz1234567890',
|
|
48
|
+
23,
|
|
49
|
+
{},
|
|
50
|
+
'[text clipped 77d721c8]',
|
|
51
|
+
),
|
|
52
|
+
|
|
53
|
+
# Over Clip
|
|
54
|
+
(
|
|
55
|
+
'abcdefghijklmnopqrstuvwxyz1234567890',
|
|
56
|
+
10,
|
|
57
|
+
{},
|
|
58
|
+
'[text clipped 77d721c8]',
|
|
59
|
+
),
|
|
60
|
+
|
|
61
|
+
# Different Hash Length
|
|
62
|
+
(
|
|
63
|
+
'abcdefghijklmnopqrstuvwxyz1234567890',
|
|
64
|
+
30,
|
|
65
|
+
{'hash_length': 10},
|
|
66
|
+
'abcde[text clipped 77d721c817]',
|
|
67
|
+
),
|
|
68
|
+
|
|
69
|
+
# Notification Longer Than Text
|
|
70
|
+
(
|
|
71
|
+
'abc',
|
|
72
|
+
1,
|
|
73
|
+
{},
|
|
74
|
+
'abc',
|
|
75
|
+
),
|
|
76
|
+
(
|
|
77
|
+
'abcdefghijklmnopqrstuvwxyz1234567890',
|
|
78
|
+
10,
|
|
79
|
+
{'hash_length': 64},
|
|
80
|
+
'abcdefghijklmnopqrstuvwxyz1234567890',
|
|
81
|
+
),
|
|
82
|
+
]
|
|
83
|
+
|
|
84
|
+
for (i, test_case) in enumerate(test_cases):
|
|
85
|
+
(text, max_length, kwargs, expected) = test_case
|
|
86
|
+
|
|
87
|
+
with self.subTest(msg = f"Case {i} ('{text}'):"):
|
|
88
|
+
actual = edq.util.hash.clip_text(text, max_length, **kwargs)
|
|
89
|
+
self.assertEqual(expected, actual)
|
edq/util/net.py
CHANGED
|
@@ -19,6 +19,7 @@ import requests
|
|
|
19
19
|
import requests_toolbelt.multipart.decoder
|
|
20
20
|
|
|
21
21
|
import edq.util.dirent
|
|
22
|
+
import edq.util.hash
|
|
22
23
|
import edq.util.json
|
|
23
24
|
import edq.util.pyimport
|
|
24
25
|
|
|
@@ -30,6 +31,9 @@ DEFAULT_REQUEST_TIMEOUT_SECS: float = 10.0
|
|
|
30
31
|
|
|
31
32
|
DEFAULT_HTTP_EXCHANGE_EXTENSION: str= '.httpex.json'
|
|
32
33
|
|
|
34
|
+
QUERY_CLIP_LENGTH: int = 100
|
|
35
|
+
""" If the query portion of an HTTPExhange being saved is longer than this, then clip the name. """
|
|
36
|
+
|
|
33
37
|
ANCHOR_HEADER_KEY: str = 'edq-anchor'
|
|
34
38
|
"""
|
|
35
39
|
By default, requests made via make_request() will send a header with this key that includes the anchor component of the URL.
|
|
@@ -729,8 +733,11 @@ def make_request(method: str, url: str,
|
|
|
729
733
|
|
|
730
734
|
query = urllib.parse.urlencode(exchange.parameters)
|
|
731
735
|
if (query != ''):
|
|
736
|
+
# The query can get very long, so we may have to clip it.
|
|
737
|
+
query_text = edq.util.hash.clip_text(query, QUERY_CLIP_LENGTH)
|
|
738
|
+
|
|
732
739
|
# Note that the '?' is URL encoded.
|
|
733
|
-
path += f"%3F{
|
|
740
|
+
path += f"%3F{query_text}"
|
|
734
741
|
|
|
735
742
|
path += f"_{method}{http_exchange_extension}"
|
|
736
743
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
edq/__init__.py,sha256
|
|
1
|
+
edq/__init__.py,sha256=aOQ6CQ9r8ar_5BvwewsqizTA8G2aaTUJFFI3ePI5xqs,86
|
|
2
2
|
edq/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
3
|
edq/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
4
|
edq/cli/version.py,sha256=SxarRVD_AVA-nD4pLVMe6ZjSJpMr7h_r3DgYYs42vjE,591
|
|
@@ -21,16 +21,17 @@ edq/procedure/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
21
21
|
edq/procedure/verify_exchanges.py,sha256=Bv-wscTaSx5bbQfD6tZutCtG_pyHuFIBhk_RB5ZMJEQ,2765
|
|
22
22
|
edq/testing/__init__.py,sha256=IKd3fPU_8d_jP19HxG-zKwxFwn7nqFGGtXOY5slY41c,32
|
|
23
23
|
edq/testing/asserts.py,sha256=BxWTH9aQFwmzb0tf5hx3V4aeJzmiBOO-QajaMM6zklI,2523
|
|
24
|
-
edq/testing/cli.py,sha256=
|
|
24
|
+
edq/testing/cli.py,sha256=Pzb1-6nrC6kYRBTI4R-pCfwDQ7reQUzH68xLn2ZSdME,12916
|
|
25
25
|
edq/testing/cli_test.py,sha256=IqzdK1fEit_3HagSU-nNI4EjkoQp6-I88JGZ1_x_uQk,525
|
|
26
26
|
edq/testing/httpserver.py,sha256=r2xSFkHTEzO92vW9XV5g43kC3vOcyQD-RVedXke1IZI,20311
|
|
27
27
|
edq/testing/httpserver_test.py,sha256=tjBgBbKTHeqE1ugHyao4HpW7FNPTkBGpWK3vuqJgNQg,12123
|
|
28
28
|
edq/testing/run.py,sha256=Axv0t6yZRUS39xmf5PWR5b54Rg21Nz0ip8G4UyJm1Ik,4814
|
|
29
|
-
edq/testing/unittest.py,sha256=
|
|
29
|
+
edq/testing/unittest.py,sha256=7-5ScxXpMBvhhyCvRMPiiUbdfx9FcpMgU6M5A3oHV7c,2980
|
|
30
30
|
edq/testing/testdata/cli/data/configs/empty/edq-config.json,sha256=yj0WO6sFU4GCciYUBWjzvvfqrBh869doeOC2Pp5EI1Y,3
|
|
31
31
|
edq/testing/testdata/cli/data/configs/simple-1/edq-config.json,sha256=uQdt7o7VNjOSQk8ni6UrqZ95QkJTUgHcwaL2TvY30bs,42
|
|
32
32
|
edq/testing/testdata/cli/data/configs/simple-2/edq-config.json,sha256=F3vP5hF1G7rPp2PPUgHemvgA-2AuxTC6Pah8Yr-fRmw,37
|
|
33
33
|
edq/testing/testdata/cli/data/configs/value-number/edq-config.json,sha256=UrB_6Pa_JRZN3NzuM0OugxQgPizUC5RMqP5xv06cuvg,20
|
|
34
|
+
edq/testing/testdata/cli/tests/help_base.txt,sha256=7FWdcKFpICb8lfz-Zj_b8izzGK5ri0FqH0FJrTn5jss,122
|
|
34
35
|
edq/testing/testdata/cli/tests/platform_skip.txt,sha256=J6n7v3LhfTyjR3Ejo-uR-g6Xdxx34Fu_cmyGSii_elc,103
|
|
35
36
|
edq/testing/testdata/cli/tests/version_base.txt,sha256=5e9oSQaivA-0Th7wXpNRhACluyDwvFTL_1xfAkS2Pko,69
|
|
36
37
|
edq/testing/testdata/cli/tests/config/list/config_list_base.txt,sha256=jtHqRPxM-uJfMkmnZaSDilsxMF7y6UXw-iEtHe8XCuw,495
|
|
@@ -63,16 +64,18 @@ edq/testing/testdata/http/files/tiny.png,sha256=SvzIuI80iLyJ6ZUAkWu_BsqWwO9BgHet
|
|
|
63
64
|
edq/util/__init__.py,sha256=9EFKQE77S-B6OJJKFaMg8k3WkMMUQYlGjlTv6tQmWVo,29
|
|
64
65
|
edq/util/dirent.py,sha256=gvU7I8TPFIjURAOPalfaOf3GSlAA0mXET_bHKC6_e_A,10369
|
|
65
66
|
edq/util/dirent_test.py,sha256=WUx6Ux-13L9YIg2rDyOROv5Kbvgr4xy693ceG1osAP0,33855
|
|
67
|
+
edq/util/hash.py,sha256=yjXGBCZNvMm49RaPCi9Ygf5FLUpGUrbVU7v8l8hn5Hc,1369
|
|
68
|
+
edq/util/hash_test.py,sha256=GzvCwgPdzsOn5o63lE8cUurAnj-4arHnYCUcctTnWMQ,2714
|
|
66
69
|
edq/util/json.py,sha256=nl_cxrlP97RX1BFtys8IT_3IfO0-XvBDQBe6YdrblB4,5936
|
|
67
70
|
edq/util/json_test.py,sha256=utUVRbw3z42ke4fpRVI294RrFHcMKms8khVYRkISNk4,8009
|
|
68
|
-
edq/util/net.py,sha256=
|
|
71
|
+
edq/util/net.py,sha256=UNaFNEyVFxITATHS4-2IcFPkW29WDiIm22fiN7-wUOs,33403
|
|
69
72
|
edq/util/pyimport.py,sha256=26OIuCXELyqtwlooMqDEs4GJQrkrAgxnXNYTlqqtsBY,2852
|
|
70
73
|
edq/util/pyimport_test.py,sha256=Xno0MIa3yMTfBfoTgjKCIMpr1ZShU6bvo9rBRdecXQU,4202
|
|
71
74
|
edq/util/reflection.py,sha256=jPcW6h0fwSDYh04O5rUxlgoF7HK6fVQ2mq7DD9qPrEg,972
|
|
72
75
|
edq/util/time.py,sha256=anoNM_KniARLombv2BnsoHuCzDqMKiDdIzV7RUe2ZOk,2648
|
|
73
76
|
edq/util/time_test.py,sha256=iQZwzVTVQQ4TdXrLb9MUMCYlKrIe8qyF-hiC9YLTaMo,4610
|
|
74
|
-
edq_utils-0.1.
|
|
75
|
-
edq_utils-0.1.
|
|
76
|
-
edq_utils-0.1.
|
|
77
|
-
edq_utils-0.1.
|
|
78
|
-
edq_utils-0.1.
|
|
77
|
+
edq_utils-0.1.3.dist-info/licenses/LICENSE,sha256=MS4iYEl4rOxMoprZuc86iYVoyk4YgaVoMt7WmGvVF8w,1064
|
|
78
|
+
edq_utils-0.1.3.dist-info/METADATA,sha256=FdzYP_tFRJAJdOBMLfwQPBtAh6jeDhxTQBpSoqjlmek,7535
|
|
79
|
+
edq_utils-0.1.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
80
|
+
edq_utils-0.1.3.dist-info/top_level.txt,sha256=znBHSj6tgXtcMKrUVtovLli5fIEJCb7d-BMxTLRK4zk,4
|
|
81
|
+
edq_utils-0.1.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|