atomicshop 2.6.13__py3-none-any.whl → 2.7.0__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 atomicshop might be problematic. Click here for more details.

atomicshop/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
1
  """Atomic Basic functions and classes to make developer life easier"""
2
2
 
3
3
  __author__ = "Den Kras"
4
- __version__ = '2.6.13'
4
+ __version__ = '2.7.0'
@@ -25,6 +25,10 @@ def is_7z(file_object: Union[str, bytes]) -> bool:
25
25
  return True
26
26
  except py7zr.Bad7zFile:
27
27
  return False
28
+ # For some reason there are files that return this exception instead of Bad7zFile.
29
+ except OSError as e:
30
+ if e.args[0] == 22:
31
+ return False
28
32
 
29
33
 
30
34
  def _is_7z_magic_number(data):
@@ -242,3 +242,45 @@ def convert_object_with_attributes_to_dict(
242
242
  obj_dict[attr_name] = attr_value
243
243
 
244
244
  return obj_dict
245
+
246
+
247
+ def convert_tuples_to_lists(obj):
248
+ """
249
+ Convert all tuples in object to lists. The first input 'obj' can be a dictionary, list or tuple.
250
+ :param obj: dict, list, tuple, the object to convert.
251
+ :return:
252
+ """
253
+ if isinstance(obj, dict):
254
+ return {k: convert_tuples_to_lists(v) for k, v in obj.items()}
255
+ elif isinstance(obj, list):
256
+ return [convert_tuples_to_lists(element) for element in obj]
257
+ elif isinstance(obj, tuple):
258
+ return list(obj)
259
+ return obj
260
+
261
+
262
+ def convert_int_to_str_in_mixed_lists(item):
263
+ """
264
+ Recursively traverse an item (which can be a dictionary, list, tuple, or a basic data type).
265
+ If a list or a tuple contains both integers and strings, convert all integers to strings.
266
+ This is useful when converting indexing a dictionary with mixed lists into Elasticsearch.
267
+ Since Elasticsearch doesn't support mixed lists, we need to convert integers to strings.
268
+ """
269
+
270
+ if isinstance(item, dict):
271
+ # If the item is a dictionary, apply the function to each value.
272
+ return {key: convert_int_to_str_in_mixed_lists(value) for key, value in item.items()}
273
+ elif isinstance(item, (list, tuple)):
274
+ # If the item is a list or a tuple, check if it contains both integers and strings.
275
+ contains_int = any(isinstance(elem, int) for elem in item)
276
+ contains_str = any(isinstance(elem, str) for elem in item)
277
+
278
+ if contains_int and contains_str:
279
+ # If both integers and strings are present, convert integers to strings.
280
+ return type(item)(str(elem) if isinstance(elem, int) else elem for elem in item)
281
+ else:
282
+ # Otherwise, apply the function to each element.
283
+ return type(item)(convert_int_to_str_in_mixed_lists(elem) for elem in item)
284
+ else:
285
+ # If the item is neither a dictionary, list, nor tuple, return it as is.
286
+ return item
@@ -99,3 +99,13 @@ def sort_list_by_another_list(list_of_strings_to_sort, list_of_strings_to_sort_b
99
99
  return len(list_of_strings_to_sort_by)
100
100
 
101
101
  return sorted(list_of_strings_to_sort, key=sort_key)
102
+
103
+
104
+ def get_the_most_frequent_element_from_list(list_instance: list[str]) -> str:
105
+ """
106
+ This function will return the most frequent element from the list.
107
+ :param list_instance: list.
108
+ :return: string.
109
+ """
110
+
111
+ return max(set(list_instance), key=list_instance.count)
@@ -1,5 +1,7 @@
1
+ import sys
1
2
  import contextlib
2
3
  import io
4
+ import logging
3
5
 
4
6
 
5
7
  class TempDisableOutput:
@@ -37,3 +39,118 @@ class TempDisableOutput:
37
39
 
38
40
  def __exit__(self, *args):
39
41
  return self.redirect_stdout_object.__exit__(*args)
42
+
43
+
44
+ def capture_console_output_without_outputting(func, *args, **kwargs):
45
+ """
46
+ Executes a function and captures its console output.
47
+
48
+ Args:
49
+ func: The function to execute.
50
+ *args: Arguments to pass to the function.
51
+ **kwargs: Keyword arguments to pass to the function.
52
+
53
+ Returns:
54
+ A string containing the captured console output.
55
+
56
+ Usage:
57
+ def example_function(name):
58
+ print(f"Hello, {name}!")
59
+
60
+ captured_output = capture_console_output(example_function, "World")
61
+ print("Captured output:", captured_output)
62
+
63
+ """
64
+
65
+ output_capture = io.StringIO()
66
+ with contextlib.redirect_stdout(output_capture):
67
+ func(*args, **kwargs)
68
+ return output_capture.getvalue()
69
+
70
+
71
+ def capture_console_output_of_logging_without_outputting(func, *args, **kwargs):
72
+ """
73
+ Executes a function and captures its logger output.
74
+
75
+ Args:
76
+ func: The function to execute.
77
+ *args: Arguments to pass to the function.
78
+ **kwargs: Keyword arguments to pass to the function.
79
+
80
+ Returns:
81
+ A string containing the captured console output.
82
+
83
+ Usage:
84
+ def example_function():
85
+ logger = logging.getLogger()
86
+ logger.warning("This is a warning!")
87
+
88
+ captured_output = capture_logger_output(example_function)
89
+ print("Captured output:", captured_output)
90
+
91
+ """
92
+
93
+ log_capture_string = io.StringIO()
94
+ ch = logging.StreamHandler(log_capture_string)
95
+ ch.setLevel(logging.DEBUG)
96
+
97
+ logger = logging.getLogger()
98
+ old_handlers = logger.handlers
99
+ logger.handlers = [ch]
100
+
101
+ try:
102
+ func(*args, **kwargs)
103
+ finally:
104
+ logger.handlers = old_handlers
105
+ ch.close()
106
+
107
+ return log_capture_string.getvalue()
108
+
109
+
110
+ def capture_all_output(func, *args, **kwargs):
111
+ """
112
+ Executes a function and captures its console and logger output.
113
+
114
+ Args:
115
+ func: The function to execute.
116
+ *args: Arguments to pass to the function.
117
+ **kwargs: Keyword arguments to pass to the function.
118
+
119
+ Returns:
120
+ A string containing the captured console and logger output.
121
+
122
+ Usage:
123
+ def example_function():
124
+ print("This is a print statement.")
125
+ logging.warning("This is a warning from logging.")
126
+ sys.stderr.write("This is a direct stderr write.\n")
127
+
128
+ captured_output = capture_all_output(example_function)
129
+ print("Captured output:", captured_output)
130
+ """
131
+
132
+ log_capture_string = io.StringIO()
133
+ ch = logging.StreamHandler(log_capture_string)
134
+ ch.setLevel(logging.DEBUG)
135
+
136
+ logger = logging.getLogger()
137
+ old_handlers = logger.handlers
138
+ logger.handlers = [ch]
139
+
140
+ stdout_capture = io.StringIO()
141
+ stderr_capture = io.StringIO()
142
+
143
+ old_stdout = sys.stdout
144
+ old_stderr = sys.stderr
145
+ sys.stdout = stdout_capture
146
+ sys.stderr = stderr_capture
147
+
148
+ try:
149
+ func(*args, **kwargs)
150
+ finally:
151
+ sys.stdout = old_stdout
152
+ sys.stderr = old_stderr
153
+ logger.handlers = old_handlers
154
+ ch.close()
155
+
156
+ return stdout_capture.getvalue() + stderr_capture.getvalue() + log_capture_string.getvalue()
@@ -107,3 +107,27 @@ def convert_json_string_to_dict(json_string: str) -> dict:
107
107
  """
108
108
 
109
109
  return json.loads(json_string)
110
+
111
+
112
+ def is_dict_json_serializable(
113
+ dict_to_check: dict,
114
+ raise_exception: bool = False
115
+ ) -> tuple[bool, Union[str, None]]:
116
+ """
117
+ Check if dictionary is json serializable.
118
+
119
+ :param dict_to_check: dictionary to check.
120
+ :param raise_exception: boolean, if True, raise exception if dictionary is not json serializable.
121
+ :return:
122
+ boolean, True if dictionary is json serializable, False otherwise.
123
+ string, error message if dictionary is not json serializable, None otherwise.
124
+ """
125
+
126
+ try:
127
+ json.dumps(dict_to_check)
128
+ return True, None
129
+ except TypeError as e:
130
+ if raise_exception:
131
+ raise e
132
+ else:
133
+ return False, str(e)
File without changes
@@ -0,0 +1,3 @@
1
+ DEFAULT_PORT: str = '9200'
2
+ DEFAULT_HOST: str = 'localhost'
3
+ DEFAULT_URL: str = f"http://{DEFAULT_HOST}:{DEFAULT_PORT}"
@@ -0,0 +1,101 @@
1
+ from elasticsearch import Elasticsearch
2
+ from datetime import datetime
3
+
4
+ from . import config_basic
5
+ from ...basics import dicts
6
+
7
+
8
+ ELASTIC_WRAPPER = None
9
+
10
+
11
+ def get_elastic_wrapper(url: str = None, overwrite: bool = False):
12
+ """
13
+ The function initializes the Elasticsearch wrapper.
14
+
15
+ :param url: str, the url of the Elasticsearch server. If None, the default url is used: http://localhost:9200
16
+ :param overwrite: bool, if True, the wrapper is reinitialized even if it is already initialized.
17
+ :return: Elasticsearch, the Elasticsearch wrapper.
18
+
19
+ Usage:
20
+ elastic_wrapper = get_elastic_wrapper()
21
+ or after you initialize it once, you can use it like:
22
+ atomicshop.wrappers.elasticsearchw.elasticsearchw.ELASTIC_WRAPPER
23
+ """
24
+
25
+ # If no url is provided, use the default url.
26
+ if url is None:
27
+ url = config_basic.DEFAULT_URL
28
+
29
+ # Get the global variable.
30
+ global ELASTIC_WRAPPER
31
+ # If the wrapper is not initialized, initialize it.
32
+ if ELASTIC_WRAPPER is None:
33
+ ELASTIC_WRAPPER = Elasticsearch([url])
34
+ # If the wrapper is already initialized, check if it should be overwritten.
35
+ else:
36
+ if overwrite:
37
+ ELASTIC_WRAPPER = Elasticsearch([url])
38
+
39
+ return ELASTIC_WRAPPER
40
+
41
+
42
+ def test_connection(elastic_wrapper: Elasticsearch = None):
43
+ """
44
+ The function tests the connection to the Elasticsearch server.
45
+
46
+ :param elastic_wrapper: Elasticsearch, the Elasticsearch wrapper.
47
+ :return: bool, True if the connection is successful, False otherwise.
48
+
49
+ Usage:
50
+ res = test_connection()
51
+ """
52
+
53
+ if elastic_wrapper is None:
54
+ elastic_wrapper = get_elastic_wrapper()
55
+
56
+ if elastic_wrapper.ping():
57
+ return True
58
+ else:
59
+ return False
60
+
61
+
62
+ def index(
63
+ index_name: str,
64
+ doc: dict,
65
+ doc_id: str = None,
66
+ use_current_timestamp: bool = False,
67
+ convert_mixed_lists_to_strings: bool = False,
68
+ elastic_wrapper: Elasticsearch = None):
69
+ """
70
+ The function indexes a document in the Elasticsearch server.
71
+
72
+ :param index_name: str, the name of the index.
73
+ :param doc: dict, the document to be indexed.
74
+ :param doc_id: str, the id of the document. If None, a random id is generated.
75
+ This means that if you index the document with the same id again, the document inside DB will be overwritten.
76
+ :param use_current_timestamp: bool, if True, the current datetime is used as the timestamp of the document.
77
+ :param convert_mixed_lists_to_strings: bool, if True, mixed lists or tuples when entries are strings and integers,
78
+ the integers will be converted to strings.
79
+ :param elastic_wrapper: Elasticsearch, the Elasticsearch wrapper.
80
+ :return: dict, the result of the indexing operation.
81
+
82
+ Usage:
83
+ doc = {
84
+ 'author': 'test_author',
85
+ 'text': 'Some test text',
86
+ }
87
+ res = index(index_name="test_index", doc=doc)
88
+ """
89
+
90
+ if elastic_wrapper is None:
91
+ elastic_wrapper = get_elastic_wrapper()
92
+
93
+ if use_current_timestamp:
94
+ doc['timestamp'] = datetime.now()
95
+
96
+ if convert_mixed_lists_to_strings:
97
+ doc = dicts.convert_int_to_str_in_mixed_lists(doc)
98
+
99
+ res = elastic_wrapper.index(index=index_name, body=doc, id=doc_id)
100
+
101
+ return res
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: atomicshop
3
- Version: 2.6.13
3
+ Version: 2.7.0
4
4
  Summary: Atomic functions and classes to make developer life easier
5
5
  Author: Denis Kras
6
6
  License: MIT License
@@ -37,6 +37,7 @@ Requires-Dist: cryptography
37
37
  Requires-Dist: dnslib
38
38
  Requires-Dist: dnspython
39
39
  Requires-Dist: docker
40
+ Requires-Dist: elasticsearch
40
41
  Requires-Dist: numpy
41
42
  Requires-Dist: openpyxl
42
43
  Requires-Dist: pandas
@@ -1,11 +1,11 @@
1
- atomicshop/__init__.py,sha256=9aDtSeyVZ9KVMcTOD8O_rsV0R5nLiG5uLKyqmUg3EUQ,123
1
+ atomicshop/__init__.py,sha256=pI9HaUaffycFBm-uxjzPkctXsfbBrHNGXNLrGiv1Pv8,122
2
2
  atomicshop/_basics_temp.py,sha256=6cu2dd6r2dLrd1BRNcVDKTHlsHs_26Gpw8QS6v32lQ0,3699
3
3
  atomicshop/_patch_import.py,sha256=ENp55sKVJ0e6-4lBvZnpz9PQCt3Otbur7F6aXDlyje4,6334
4
4
  atomicshop/appointment_management.py,sha256=N3wVGJgrqJfsj_lqiRfaL3FxMEe57by5Stzanh189mk,7263
5
5
  atomicshop/certificates.py,sha256=J-cmd6Rpq3zZyzsOH-GcdqIXdg2UwM8_E9mg7XtUph8,3787
6
6
  atomicshop/command_line_processing.py,sha256=u5yT9Ger_cu7ni5ID0VFlRbVD46ARHeNC9tRM-_YXrQ,1038
7
7
  atomicshop/config_init.py,sha256=z2RXD_mw9nQlAOpuGry1h9QT-2LhNscXgGAktN3dCVQ,2497
8
- atomicshop/console_output.py,sha256=G-6jxnWooT1nJSaPxcCqIuw8S22R_0lOJcfrdovRhwE,1372
8
+ atomicshop/console_output.py,sha256=AOSJjrRryE97PAGtgDL03IBtWSi02aNol8noDnW3k6M,4667
9
9
  atomicshop/console_user_response.py,sha256=31HIy9QGXa7f-GVR8MzJauQ79E_ZqAeagF3Ks4GGdDU,3234
10
10
  atomicshop/datetimes.py,sha256=ICr0_gQqWnIw4BuNtabrHzjSlwnZkBfhyCrOILs5xpU,14623
11
11
  atomicshop/diff_check.py,sha256=RON9cSTgy3jAnwUmAUkOyfF6bgrBKOq9Sbgyl3RYodw,12350
@@ -64,7 +64,7 @@ atomicshop/addons/process_list/compiled/Win10x64/process_list.lib,sha256=n9c2MVP
64
64
  atomicshop/archiver/_search_in_zip.py,sha256=dd8qFSvIhcKmtnPj_uYNJFPmMwZp4tZys0kKgTw_ACw,8385
65
65
  atomicshop/archiver/archiver.py,sha256=BomnK7zT-nQXA1z0i2R2aTv8eu88wPx7tf2HtOdbmEc,1280
66
66
  atomicshop/archiver/search_in_archive.py,sha256=mEngDfULXBd3Oio8a2SUtynfJASVLsH74XIYJOWVkH0,10467
67
- atomicshop/archiver/sevenz.py,sha256=8C90rTS1-JMsuvvuOuKGoNhFeVj3_1mfEKv9UCUf9QE,1201
67
+ atomicshop/archiver/sevenz.py,sha256=5i_C50-deC1Cz_GQdMGofV2jeMPbbGWAvih-QnA72cg,1370
68
68
  atomicshop/archiver/zip.py,sha256=k742K1bEDtc_4N44j_Waebi-uOkxxavqltvV6q-BLW4,14402
69
69
  atomicshop/basics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
70
70
  atomicshop/basics/ansi_escape_codes.py,sha256=WtIkm-BjSZS5J5irDUdAMBNvdX-qXFZcTX98jcBMpJE,3140
@@ -72,7 +72,7 @@ atomicshop/basics/argparse_template.py,sha256=S-BIdKd45bmNdqO3Dj3yOxBgb0O90spZxG
72
72
  atomicshop/basics/booleans.py,sha256=-4JnSQ1pSb6CNY_62wtHBW8NltjPJEKM-gYOxFujunA,1772
73
73
  atomicshop/basics/bytes_arrays.py,sha256=WvSRDhIGt1ywF95t-yNgpxLm1nlZUbM1Dz6QckcyE8Y,5915
74
74
  atomicshop/basics/classes.py,sha256=n5uhPOWIKBAv0xa3--n-oQYIS64s6hb6JGs_1HgW2cs,8558
75
- atomicshop/basics/dicts.py,sha256=sAoHvY-Rtt7QkwHzIKk1j2ZFmteIm9yFCxBGkTrpbdk,9030
75
+ atomicshop/basics/dicts.py,sha256=w9_ZolDNSf2ZyoZ53zIOrFq7VZWGWYAvAvwc4qHORAo,10973
76
76
  atomicshop/basics/dicts_nested.py,sha256=StYxYnYPa0SEJr1lmEwAv5zfERWWqoULeyG8e0zRAwE,4107
77
77
  atomicshop/basics/enumerations.py,sha256=41VVQYh_vnVapggxKg2IRU5e_EiMpZzX1n1mtxvoSzM,1364
78
78
  atomicshop/basics/enums.py,sha256=CeV8MfqWHihK7vvV6Mbzq7rD9JykeQfrJeFdLVzfHI4,3547
@@ -81,7 +81,7 @@ atomicshop/basics/guids.py,sha256=iRx5n18ATZWhpo748BwEjuLWLsu9y3OwF5-Adp-Dtik,40
81
81
  atomicshop/basics/hexs.py,sha256=i8CTG-J0TGGa25yFSbWEvpVyHFnof_qSWUrmXY-ylKM,1054
82
82
  atomicshop/basics/isinstancing.py,sha256=fQ35xfqbguQz2BUn-3a4KVGskhTcIn8JjRtxV2rFcRQ,876
83
83
  atomicshop/basics/list_of_dicts.py,sha256=fu0H6dUD9uUo2kl7FYIrWzOQMwCmg7qRxGnFM5S319E,5716
84
- atomicshop/basics/lists.py,sha256=9H-avAZ0LchdyZyTxmPZQWpvVR0-L4xM3QclI9fED2M,3637
84
+ atomicshop/basics/lists.py,sha256=pLpYPSu0BjJIAe_Ar55XhLsH2YBhftn7b-tTAdkK1sw,3928
85
85
  atomicshop/basics/multiprocesses.py,sha256=AczaI4TmYduNjE6_VEQhxvDQn4VLDdXYRxhPDpjH4T0,17974
86
86
  atomicshop/basics/numbers.py,sha256=II-jP8MG0IYvmjT9_BLEai65xzDJ0LTHK2eFVSrgVqo,527
87
87
  atomicshop/basics/randoms.py,sha256=DmYLtnIhDK29tAQrGP1Nt-A-v8WC7WIEB8Edi-nk3N4,282
@@ -96,7 +96,7 @@ atomicshop/file_io/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuF
96
96
  atomicshop/file_io/csvs.py,sha256=4R4Kij8FmxNwXFjDtlF_A0flAk0Hj5nZKlEnqC5VxgQ,3125
97
97
  atomicshop/file_io/docxs.py,sha256=3IFtaurVhvntcuCL59bpFlzGyKxvDmVUjUQTHWluOMY,4369
98
98
  atomicshop/file_io/file_io.py,sha256=FR84ihjGlr7Eqejo-_js4nBICVst31axD0bwX19S2eM,6385
99
- atomicshop/file_io/jsons.py,sha256=uj-YiK-GxI6qCHgMbvx8t2vzWKvRarrvvqoUq8HvD3A,4416
99
+ atomicshop/file_io/jsons.py,sha256=j-cU1w0iYhiqL5EelMhL3mDMLtaaP73Y97CXqwHcpZU,5154
100
100
  atomicshop/file_io/tomls.py,sha256=oa0Wm8yMkPRXKN9jgBuTnKbioSOee4mABW5IMUFCYyU,3041
101
101
  atomicshop/file_io/xlsxs.py,sha256=v_dyg9GD4LqgWi6wA1QuWRZ8zG4ZwB6Dz52ytdcmmmI,2184
102
102
  atomicshop/file_io/xmls.py,sha256=zh3SuK-dNaFq2NDNhx6ivcf4GYCfGM8M10PcEwDSpxk,2104
@@ -151,6 +151,9 @@ atomicshop/wrappers/ctyping/process_winapi.py,sha256=QcXL-ETtlSSkoT8F7pYle97ubGW
151
151
  atomicshop/wrappers/dockerw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
152
152
  atomicshop/wrappers/dockerw/dockerw.py,sha256=8MZCMek6L-QpawO5hn_gxXczI6ustPGcYBFWNiuQ1cw,949
153
153
  atomicshop/wrappers/dockerw/install_docker.py,sha256=dpSOmD690oLukoLCo0u6Pzh5fRyCWBuSQEtG8VwC3jk,2765
154
+ atomicshop/wrappers/elasticsearchw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
155
+ atomicshop/wrappers/elasticsearchw/config_basic.py,sha256=nMtWYBlkQwzUMTLBaL3US8-f87bNPS_wzVxpLR1D6Hc,121
156
+ atomicshop/wrappers/elasticsearchw/elasticsearchw.py,sha256=utWGAc9vFPhK6typiNat8X1YPHfvwQQyDqjgnvVcInc,3382
154
157
  atomicshop/wrappers/factw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
155
158
  atomicshop/wrappers/factw/config_fact.py,sha256=J-K9Zn50WcDC7ubb-boraSZExfBk7a6M52NhRJVlsjk,895
156
159
  atomicshop/wrappers/factw/config_install.py,sha256=QRygFnZ5r1ybJQl7ftFci3kNRmU-a-w3lMG1joElvcY,417
@@ -209,8 +212,8 @@ atomicshop/wrappers/socketw/socket_server_tester.py,sha256=AhpurHJmP2kgzHaUbq5ey
209
212
  atomicshop/wrappers/socketw/socket_wrapper.py,sha256=aXBwlEIJhFT0-c4i8iNlFx2It9VpCEpsv--5Oqcpxao,11624
210
213
  atomicshop/wrappers/socketw/ssl_base.py,sha256=k4V3gwkbq10MvOH4btU4onLX2GNOsSfUAdcHmL1rpVE,2274
211
214
  atomicshop/wrappers/socketw/statistics_csv.py,sha256=t3dtDEfN47CfYVi0CW6Kc2QHTEeZVyYhc57IYYh5nmA,826
212
- atomicshop-2.6.13.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
213
- atomicshop-2.6.13.dist-info/METADATA,sha256=CkX8ZlZTlnlCQ3q1KMw72rl6cyIgBHjaOj5r75e58MU,10340
214
- atomicshop-2.6.13.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
215
- atomicshop-2.6.13.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
216
- atomicshop-2.6.13.dist-info/RECORD,,
215
+ atomicshop-2.7.0.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
216
+ atomicshop-2.7.0.dist-info/METADATA,sha256=b3_2bHYVrfgK6JoIvpAO-kuFenRKx8bRaE_Fu9je6Wg,10369
217
+ atomicshop-2.7.0.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
218
+ atomicshop-2.7.0.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
219
+ atomicshop-2.7.0.dist-info/RECORD,,