atomicshop 2.15.1__py3-none-any.whl → 2.15.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 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.15.1'
4
+ __version__ = '2.15.3'
@@ -1,12 +1,12 @@
1
1
  import os
2
2
  import datetime
3
3
  import json
4
- from typing import Union
4
+ from typing import Union, Literal
5
5
 
6
6
  from .statistic_analyzer_helper import analyzer_helper, moving_average_helper
7
7
  from .. import filesystem, domains, datetimes, urls
8
8
  from ..basics import dicts
9
- from ..file_io import tomls, xlsxs, jsons
9
+ from ..file_io import tomls, xlsxs, jsons, csvs
10
10
  from ..wrappers.loggingw import reading
11
11
  from ..print_api import print_api
12
12
 
@@ -350,7 +350,8 @@ def deviation_calculator_by_moving_average_main(
350
350
  top_bottom_deviation_percentage: float,
351
351
  get_deviation_for_last_day_only: bool = False,
352
352
  summary: bool = False,
353
- output_json_file_path: str = None
353
+ output_file_path: str = None,
354
+ output_file_type: Literal['json', 'csv'] = 'json'
354
355
  ) -> Union[list, None]:
355
356
  """
356
357
  This function is the main function for the moving average calculator.
@@ -375,7 +376,8 @@ def deviation_calculator_by_moving_average_main(
375
376
  to 2021-01-05.
376
377
  :param summary: bool, if True, Only the summary will be generated without all the numbers that were used
377
378
  to calculate the averages and the moving average data.
378
- :param output_json_file_path: string, if None, no json file will be written.
379
+ :param output_file_path: string, if None, no file will be written.
380
+ :param output_file_type: string, the type of the output file. 'json' or 'csv'.
379
381
  -----------------------------
380
382
  :return: the deviation list of dicts.
381
383
 
@@ -397,6 +399,9 @@ def deviation_calculator_by_moving_average_main(
397
399
  sys.exit(main())
398
400
  """
399
401
 
402
+ if output_file_type not in ['json', 'csv']:
403
+ raise ValueError(f'output_file_type must be "json" or "csv", not [{output_file_type}]')
404
+
400
405
  statistics_file_path: str = f'{statistics_file_directory}{os.sep}{STATISTICS_FILE_NAME}'
401
406
 
402
407
  def convert_data_value_to_string(value_key: str, list_index: int) -> None:
@@ -414,29 +419,41 @@ def deviation_calculator_by_moving_average_main(
414
419
  )
415
420
 
416
421
  if deviation_list:
417
- if output_json_file_path:
418
- for deviation_list_index, deviation in enumerate(deviation_list):
419
- convert_data_value_to_string('request_sizes', deviation_list_index)
420
- convert_data_value_to_string('response_sizes', deviation_list_index)
421
- convert_value_to_string('ma_data', deviation_list_index)
422
-
423
- print_api(f'Deviation Found, saving to file: {output_json_file_path}', color='blue')
424
- jsons.write_json_file(deviation_list, output_json_file_path, use_default_indent=True)
425
-
426
422
  if summary:
427
423
  summary_deviation_list: list = []
428
424
  for deviation in deviation_list:
425
+ value = deviation.get('value', None)
426
+ ma_value = deviation.get('ma_value', None)
427
+ if not value or not ma_value:
428
+ total_entries_averaged = None
429
+ else:
430
+ total_entries_averaged = deviation['data']['count']
431
+
429
432
  summary_deviation_list.append({
430
433
  'day': deviation['day'],
431
434
  'host': deviation['host'],
432
435
  'message': deviation['message'],
433
- 'value': deviation['value'],
434
- 'ma_value': deviation['ma_value'],
435
- 'total_entries_averaged': deviation['data']['count']
436
+ 'value': deviation.get('value', None),
437
+ 'ma_value': deviation.get('ma_value', None),
438
+ 'total_entries_averaged': total_entries_averaged
436
439
  })
437
440
 
438
441
  deviation_list = summary_deviation_list
439
442
 
443
+ if output_file_path:
444
+ if not summary:
445
+ for deviation_list_index, deviation in enumerate(deviation_list):
446
+ convert_data_value_to_string('request_sizes', deviation_list_index)
447
+ convert_data_value_to_string('response_sizes', deviation_list_index)
448
+ convert_value_to_string('ma_data', deviation_list_index)
449
+
450
+ print_api(f'Deviation Found, saving to file: {output_file_path}', color='blue')
451
+
452
+ if output_file_type == 'csv':
453
+ csvs.write_list_to_csv(output_file_path, deviation_list)
454
+ elif output_file_type == 'json':
455
+ jsons.write_json_file(deviation_list, output_file_path, use_default_indent=True)
456
+
440
457
  return deviation_list
441
458
 
442
459
  return None
@@ -7,6 +7,10 @@ from ... import filesystem, datetimes
7
7
  from ...file_io import csvs
8
8
 
9
9
 
10
+ class LogReaderTimeCouldntBeFoundInFileNameError(Exception):
11
+ pass
12
+
13
+
10
14
  def get_logs_paths(
11
15
  log_files_directory_path: str = None,
12
16
  log_file_path: str = None,
@@ -95,6 +99,13 @@ def get_logs_paths(
95
99
  if timestamp_float and timestamp_float > latest_timestamp:
96
100
  latest_timestamp = timestamp_float
97
101
 
102
+ # Check timestamps, if more than 1 file is None, then the function that gets the date from the file name
103
+ # didn't work properly, probably because of the string datetime format or the filenames.
104
+ none_timestamps = [single_file['last_modified'] for single_file in logs_files].count(None)
105
+ if none_timestamps > 1:
106
+ raise LogReaderTimeCouldntBeFoundInFileNameError(
107
+ 'The date pattern could not be found in the file name. Check the date pattern and the file names.')
108
+
98
109
  # Now, there should be a file that doesn't have the string date pattern in the file name.
99
110
  # We will add one day to the latest date that we found and assign to that file path.
100
111
  for file_index, single_file in enumerate(logs_files):
@@ -0,0 +1,53 @@
1
+ from typing import Union
2
+
3
+ from pymongo import MongoClient
4
+ import pymongo.errors
5
+
6
+ from ... import get_process_list, filesystem
7
+
8
+
9
+ WHERE_TO_SEARCH_FOR_MONGODB_EXE: str = 'C:\\Program Files\\MongoDB\\Server\\'
10
+ MONGODB_EXE_NAME: str = 'mongod.exe'
11
+ MONGODB_DEFAULT_URI: str = 'mongodb://localhost:27017/'
12
+
13
+
14
+ class MongoDBNoConnectionError(Exception):
15
+ pass
16
+
17
+
18
+ def test_connection(
19
+ uri: str = MONGODB_DEFAULT_URI,
20
+ raise_exception: bool = False
21
+ ) -> bool:
22
+ try:
23
+ client = MongoClient(uri)
24
+ client.admin.command('ping')
25
+ return True
26
+ except pymongo.errors.ServerSelectionTimeoutError:
27
+ if raise_exception:
28
+ raise MongoDBNoConnectionError(f"Could not connect to the MongoDB server on: ")
29
+ return False
30
+
31
+
32
+ def is_service_running() -> bool:
33
+ """
34
+ Check if the MongoDB service is running.
35
+ :return: bool, True if the MongoDB service is running, False otherwise.
36
+ """
37
+ current_processes: dict = (
38
+ get_process_list.GetProcessList(get_method='pywin32', connect_on_init=True).get_processes())
39
+
40
+ for pid, process_info in current_processes.items():
41
+ if MONGODB_EXE_NAME in process_info['name']:
42
+ return True
43
+
44
+ return False
45
+
46
+
47
+ def is_installed() -> Union[str, None]:
48
+ """
49
+ Check if MongoDB is installed.
50
+ :return: string if MongoDB executable is found, None otherwise.
51
+ """
52
+
53
+ return filesystem.find_file(MONGODB_EXE_NAME, WHERE_TO_SEARCH_FOR_MONGODB_EXE)
@@ -1,15 +1,18 @@
1
1
  import os
2
2
  import requests
3
3
  from typing import Union
4
+ import argparse
5
+ import subprocess
4
6
 
5
- from ... import urls, web, permissions, get_process_list, filesystem
7
+ from ... import urls, web, permissions
6
8
  from ...print_api import print_api
7
9
  from .. import msiw
10
+ from . import infrastructure
8
11
 
9
12
 
10
13
  MONGODB_DOWNLOAD_PAGE_URL: str = 'https://www.mongodb.com/try/download/community'
11
- WHERE_TO_SEARCH_FOR_MONGODB_EXE: str = 'C:\\Program Files\\MongoDB\\Server\\'
12
- MONGODB_EXE_NAME: str = 'mongod.exe'
14
+ COMPASS_INSTALLATION_SCRIPT_URL: str = \
15
+ 'https://raw.githubusercontent.com/mongodb/mongo/master/src/mongo/installer/compass/Install-Compass.ps1'
13
16
 
14
17
 
15
18
  class MongoDBWebPageNoSuccessCodeError(Exception):
@@ -62,48 +65,52 @@ def get_latest_mongodb_download_url(
62
65
  return windows_urls[0]
63
66
 
64
67
 
65
- def is_service_running() -> bool:
66
- """
67
- Check if the MongoDB service is running.
68
- :return: bool, True if the MongoDB service is running, False otherwise.
69
- """
70
- current_processes: dict = (
71
- get_process_list.GetProcessList(get_method='pywin32', connect_on_init=True).get_processes())
72
-
73
- for pid, process_info in current_processes.items():
74
- if MONGODB_EXE_NAME in process_info['name']:
75
- return True
76
-
77
- return False
78
-
79
-
80
- def is_installed() -> Union[str, None]:
81
- """
82
- Check if MongoDB is installed.
83
- :return: string if MongoDB executable is found, None otherwise.
84
- """
68
+ def parse_args():
69
+ parser = argparse.ArgumentParser(description='Install MongoDB Community Server.')
70
+ parser.add_argument(
71
+ '-nr', '--no-rc', action='store_true', help='Do not download release candidate versions.')
72
+ parser.add_argument(
73
+ '-m', '--major', type=int, help='Download the latest version of the specified major version.')
74
+ parser.add_argument(
75
+ '-c', '--compass', action='store_true', help='Install MongoDB Compass.')
76
+ parser.add_argument(
77
+ '-f', '--force', action='store_true', help='Force the installation even if MongoDB is already installed.')
85
78
 
86
- return filesystem.find_file(MONGODB_EXE_NAME, WHERE_TO_SEARCH_FOR_MONGODB_EXE)
79
+ return parser.parse_args()
87
80
 
88
81
 
89
82
  def download_install_latest_main(
90
- no_rc_version: bool = True,
83
+ no_rc_version: bool = False,
91
84
  major_specific: int = None,
85
+ compass: bool = False,
92
86
  force: bool = False
93
87
  ):
94
88
  """
95
89
  Download and install the latest version of MongoDB Community Server.
96
90
  :param no_rc_version: bool, if True, the latest non-RC version will be downloaded.
97
91
  :param major_specific: int, if set, the latest version of the specified major version will be downloaded.
92
+ :param compass: bool, if True, MongoDB Compass will be installed.
98
93
  :param force: bool, if True, MongoDB will be installed even if it is already installed.
99
94
  :return:
100
95
  """
101
96
 
97
+ args = parse_args()
98
+
99
+ # Set the args only if they were used.
100
+ if args.no_rc:
101
+ no_rc_version = args.no_rc
102
+ if args.major:
103
+ major_specific = args.major
104
+ if args.compass:
105
+ compass = args.compass
106
+ if args.force:
107
+ force = args.force
108
+
102
109
  if not permissions.is_admin():
103
110
  print_api("This function requires administrator privileges.", color='red')
104
111
  return 1
105
112
 
106
- if is_service_running():
113
+ if infrastructure.is_service_running():
107
114
  print_api("MongoDB service is running - already installed.", color='blue')
108
115
 
109
116
  if not force:
@@ -111,8 +118,8 @@ def download_install_latest_main(
111
118
  else:
112
119
  print_api("MongoDB is service is not running.")
113
120
 
114
- mongo_is_installed: Union[str, None] = is_installed()
115
- if is_installed():
121
+ mongo_is_installed: Union[str, None] = infrastructure.is_installed()
122
+ if infrastructure.is_installed():
116
123
  message = f"MongoDB is installed in: {mongo_is_installed}\n" \
117
124
  f"The service is not running. Fix the service or use the 'force' parameter to reinstall."
118
125
  print_api(message, color='yellow')
@@ -143,11 +150,11 @@ def download_install_latest_main(
143
150
 
144
151
  # Check if MongoDB is installed.
145
152
  message: str = ''
146
- mongo_is_installed = is_installed()
153
+ mongo_is_installed = infrastructure.is_installed()
147
154
  if not mongo_is_installed:
148
155
  message += "MongoDB Executable not found.\n"
149
156
 
150
- if not is_service_running():
157
+ if not infrastructure.is_service_running():
151
158
  message += "MongoDB service is not running.\n"
152
159
 
153
160
  if message:
@@ -164,4 +171,20 @@ def download_install_latest_main(
164
171
  os.remove(installer_file_path)
165
172
  print_api("Cleaned up the installer file.")
166
173
 
174
+ if not compass:
175
+ return 0
176
+
177
+ # It doesn't matter what you do with the MSI it will not install Compass, only if you run it manually.
178
+ # So we will use installation script from their GitHub.
179
+ print_api("Downloading MongoDB Compass installation script...")
180
+ compass_script_path: str = web.download(COMPASS_INSTALLATION_SCRIPT_URL)
181
+
182
+ print_api("Installing MongoDB Compass from script...")
183
+ subprocess.run(["powershell", "-ExecutionPolicy", "Bypass", "-File", compass_script_path])
184
+
185
+ # Clean up the installer file
186
+ if os.path.exists(compass_script_path):
187
+ os.remove(compass_script_path)
188
+ print_api("Cleaned up the Compass installer file.")
189
+
167
190
  return 0
@@ -0,0 +1,144 @@
1
+ import pymongo
2
+
3
+ from . import infrastructure
4
+
5
+
6
+ """
7
+ DISCLAIMER: you should use the pymongo library directly instead of using the atomicshop/wrappers/mongodbw/mongodbw.py.
8
+ These are good examples to get you started, but can't really cover all the use cases you might have.
9
+ """
10
+
11
+
12
+ def connect(uri: str = infrastructure.MONGODB_DEFAULT_URI):
13
+ """
14
+ Connect to a MongoDB database.
15
+ :param uri: str, the URI of the MongoDB database.
16
+ :return: pymongo.MongoClient, the client object.
17
+ """
18
+ return pymongo.MongoClient(uri)
19
+
20
+
21
+ def add_list_of_dicts_to_mongo(
22
+ list_of_dicts: list,
23
+ database_name: str,
24
+ collection_name: str,
25
+ mongo_client: pymongo.MongoClient = None,
26
+ close_client: bool = False
27
+ ):
28
+ """
29
+ Add a list of dictionaries to a MongoDB collection.
30
+ :param list_of_dicts: list of dictionaries, the list of dictionaries to add to the collection.
31
+ :param database_name: str, the name of the database.
32
+ :param collection_name: str, the name of the collection.
33
+ :param mongo_client: pymongo.MongoClient, the connection object.
34
+ If None, a new connection will be created to default URI.
35
+ :param close_client: bool, if True, the connection will be closed after the operation.
36
+
37
+ :return: None
38
+ """
39
+
40
+ if not mongo_client:
41
+ mongo_client = connect()
42
+
43
+ db = mongo_client[database_name]
44
+ collection = db[collection_name]
45
+
46
+ collection.insert_many(list_of_dicts)
47
+
48
+ if close_client:
49
+ mongo_client.close()
50
+
51
+
52
+ def remove_list_of_dicts(
53
+ list_of_dicts: list,
54
+ database_name: str,
55
+ collection_name: str,
56
+ mongo_client: pymongo.MongoClient = None,
57
+ close_client: bool = False
58
+ ):
59
+ """
60
+ Remove a list of dictionaries from a MongoDB collection.
61
+ :param list_of_dicts: list of dictionaries, the list of dictionaries to remove from the collection.
62
+ :param database_name: str, the name of the database.
63
+ :param collection_name: str, the name of the collection.
64
+ :param mongo_client: pymongo.MongoClient, the connection object.
65
+ If None, a new connection will be created to default URI.
66
+ :param close_client: bool, if True, the connection will be closed after the operation.
67
+
68
+ :return: None
69
+ """
70
+
71
+ if not mongo_client:
72
+ mongo_client = connect()
73
+
74
+ db = mongo_client[database_name]
75
+ collection = db[collection_name]
76
+
77
+ for doc in list_of_dicts:
78
+ collection.delete_one(doc)
79
+
80
+ if close_client:
81
+ mongo_client.close()
82
+
83
+
84
+ def overwrite_list_of_dicts(
85
+ list_of_dicts: list,
86
+ database_name: str,
87
+ collection_name: str,
88
+ mongo_client: pymongo.MongoClient = None,
89
+ close_client: bool = False
90
+ ):
91
+ """
92
+ Overwrite a list of dictionaries in a MongoDB collection.
93
+ :param list_of_dicts: list of dictionaries, the list of dictionaries to overwrite in the collection.
94
+ :param database_name: str, the name of the database.
95
+ :param collection_name: str, the name of the collection.
96
+ :param mongo_client: pymongo.MongoClient, the connection object.
97
+ If None, a new connection will be created to default URI.
98
+ :param close_client: bool, if True, the connection will be closed after the operation.
99
+
100
+ :return: None
101
+ """
102
+
103
+ if not mongo_client:
104
+ mongo_client = connect()
105
+
106
+ db = mongo_client[database_name]
107
+ collection = db[collection_name]
108
+
109
+ collection.delete_many({})
110
+ collection.insert_many(list_of_dicts)
111
+
112
+ if close_client:
113
+ mongo_client.close()
114
+
115
+
116
+ def get_all_entries_from_collection(
117
+ database_name: str,
118
+ collection_name: str,
119
+ mongo_client: pymongo.MongoClient = None,
120
+ close_client: bool = False
121
+ ):
122
+ """
123
+ Get all entries from a MongoDB collection.
124
+ :param database_name: str, the name of the database.
125
+ :param collection_name: str, the name of the collection.
126
+ :param mongo_client: pymongo.MongoClient, the connection object.
127
+ If None, a new connection will be created to default URI.
128
+ :param close_client: bool, if True, the connection will be closed after the operation.
129
+
130
+ :return: list of dictionaries, the list of entries from the collection.
131
+ """
132
+
133
+ if not mongo_client:
134
+ mongo_client = connect()
135
+
136
+ db = mongo_client[database_name]
137
+ collection = db[collection_name]
138
+
139
+ entries = list(collection.find())
140
+
141
+ if close_client:
142
+ mongo_client.close()
143
+
144
+ return entries
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: atomicshop
3
- Version: 2.15.1
3
+ Version: 2.15.3
4
4
  Summary: Atomic functions and classes to make developer life easier
5
5
  Author: Denis Kras
6
6
  License: MIT License
@@ -1,4 +1,4 @@
1
- atomicshop/__init__.py,sha256=d6t5Ql0zc3dSUJNAyTjHj6blBObI-unNn2kZw5eOlhs,123
1
+ atomicshop/__init__.py,sha256=aCesTJoNLZUFzz58NTSDXvO_WoEVSNiCYUamRk5wgJs,123
2
2
  atomicshop/_basics_temp.py,sha256=6cu2dd6r2dLrd1BRNcVDKTHlsHs_26Gpw8QS6v32lQ0,3699
3
3
  atomicshop/_create_pdf_demo.py,sha256=Yi-PGZuMg0RKvQmLqVeLIZYadqEZwUm-4A9JxBl_vYA,3713
4
4
  atomicshop/_patch_import.py,sha256=ENp55sKVJ0e6-4lBvZnpz9PQCt3Otbur7F6aXDlyje4,6334
@@ -128,7 +128,7 @@ atomicshop/mitm/initialize_engines.py,sha256=YnXPK1UKrmULnfL4zLo2LOpKWq-aGKzc9p3
128
128
  atomicshop/mitm/initialize_mitm_server.py,sha256=j1yMUbHsnFh9l5rFiUgBQA0mRZqREOKviP0frRzYikM,14611
129
129
  atomicshop/mitm/message.py,sha256=u2U2f2SOHdBNU-6r1Ik2W14ai2EOwxUV4wVfGZA098k,1732
130
130
  atomicshop/mitm/shared_functions.py,sha256=PaK_sbnEA5zo9k2ktEOKLmvo-6wRUunxzSNRr41uXIQ,1924
131
- atomicshop/mitm/statistic_analyzer.py,sha256=FdUmKVmDZp0-2ohBVwubG9v0wz8Wb-NIi50Qk8BR85E,22913
131
+ atomicshop/mitm/statistic_analyzer.py,sha256=w3mqvnfh68zW78bcIyw1Au4DimSu9dbNlqoG7jP--fM,23750
132
132
  atomicshop/mitm/engines/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
133
133
  atomicshop/mitm/engines/create_module_template.py,sha256=tRjVSm1sD6FzML71Qbuwvita0qsusdFGm8NZLsZ-XMs,4853
134
134
  atomicshop/mitm/engines/create_module_template_example.py,sha256=X5xhvbV6-g9jU_bQVhf_crZmaH50LRWz3bS-faQ18ds,489
@@ -244,9 +244,11 @@ atomicshop/wrappers/loggingw/formatters.py,sha256=7XUJvlB0CK4DCkEp8NTL0S0dkyrZD0
244
244
  atomicshop/wrappers/loggingw/handlers.py,sha256=yFYBeTkxnpmtlauoH3ZEFEHUYQYu9YL-ycd9sYTvOl4,16928
245
245
  atomicshop/wrappers/loggingw/loggers.py,sha256=DHOOTAtqkwn1xgvLHSkOiBm6yFGNuQy1kvbhG-TDog8,2374
246
246
  atomicshop/wrappers/loggingw/loggingw.py,sha256=lo4OZPXCbYZi3GqpaaJSs9SOGFfqD2EgHzzTK7f5IR4,11275
247
- atomicshop/wrappers/loggingw/reading.py,sha256=b4-ibM5WwjEOanvHY3hIsu9-4b2RAdPYiCxvl7745fk,17521
247
+ atomicshop/wrappers/loggingw/reading.py,sha256=ZgFbmQdStLg2nlgzJEznsvEP0r63ki_2RRwsNnggLDI,18148
248
248
  atomicshop/wrappers/mongodbw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
249
- atomicshop/wrappers/mongodbw/install_mongodb.py,sha256=cuisakPkuIsMfleyARyuOTgbcXWvfSWKqoYaMdOnPuw,5456
249
+ atomicshop/wrappers/mongodbw/infrastructure.py,sha256=tHqtt__yKGtj24CT5AIk0V0k9t1p_PjezFExXRmmmcA,1517
250
+ atomicshop/wrappers/mongodbw/install_mongodb.py,sha256=dik6tpXws4FaQN_-u8s-yn5Z1InHROV7k1WhFA8eu4M,6617
251
+ atomicshop/wrappers/mongodbw/mongodbw.py,sha256=eDwI3sePLGWhl-neBRQM955KZOl8jaO7U0n_AzXM3es,4568
250
252
  atomicshop/wrappers/nodejsw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
251
253
  atomicshop/wrappers/nodejsw/install_nodejs.py,sha256=QZg-R2iTQt7kFb8wNtnTmwraSGwvUs34JIasdbNa7ZU,5154
252
254
  atomicshop/wrappers/playwrightw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -292,8 +294,8 @@ atomicshop/wrappers/socketw/socket_server_tester.py,sha256=AhpurHJmP2kgzHaUbq5ey
292
294
  atomicshop/wrappers/socketw/socket_wrapper.py,sha256=aXBwlEIJhFT0-c4i8iNlFx2It9VpCEpsv--5Oqcpxao,11624
293
295
  atomicshop/wrappers/socketw/ssl_base.py,sha256=k4V3gwkbq10MvOH4btU4onLX2GNOsSfUAdcHmL1rpVE,2274
294
296
  atomicshop/wrappers/socketw/statistics_csv.py,sha256=t3dtDEfN47CfYVi0CW6Kc2QHTEeZVyYhc57IYYh5nmA,826
295
- atomicshop-2.15.1.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
296
- atomicshop-2.15.1.dist-info/METADATA,sha256=uIXlm-6-iPV6AdPBYI4b8Y9LnSZmj2qU6yKOWK1V13E,10502
297
- atomicshop-2.15.1.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
298
- atomicshop-2.15.1.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
299
- atomicshop-2.15.1.dist-info/RECORD,,
297
+ atomicshop-2.15.3.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
298
+ atomicshop-2.15.3.dist-info/METADATA,sha256=0Fx6JFpY55KI4OpSnfP220ErQEvXv5RX4kcJKAvmCpI,10502
299
+ atomicshop-2.15.3.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
300
+ atomicshop-2.15.3.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
301
+ atomicshop-2.15.3.dist-info/RECORD,,