atomicshop 2.16.15__py3-none-any.whl → 2.16.17__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.16.15'
4
+ __version__ = '2.16.17'
@@ -9,7 +9,6 @@ from typing import Literal
9
9
 
10
10
  from .wrappers import cryptographyw
11
11
  from .wrappers.pywin32w import cert_store
12
- from .print_api import print_api
13
12
 
14
13
 
15
14
  # Valid for 3 years from now
@@ -2,7 +2,6 @@ import datetime
2
2
  import os
3
3
  import multiprocessing
4
4
 
5
- from ..wrappers.loggingw import reading
6
5
  from ..archiver import zips
7
6
  from .. import filesystem
8
7
 
@@ -5,7 +5,7 @@ from typing import Literal
5
5
  from ...print_api import print_api
6
6
  from ...wrappers.loggingw import reading, consts
7
7
  from ...file_io import csvs
8
- from ... import urls
8
+ from ... import urls, filesystem
9
9
 
10
10
 
11
11
  def calculate_moving_average(
@@ -43,7 +43,7 @@ def calculate_moving_average(
43
43
  date_format: str = consts.DEFAULT_ROTATING_SUFFIXES_FROM_WHEN['midnight']
44
44
 
45
45
  # Get all the file paths and their midnight rotations.
46
- logs_paths: list = reading.get_logs_paths(
46
+ logs_paths: list[filesystem.AtomicPath] = reading.get_logs_paths(
47
47
  log_file_path=file_path,
48
48
  date_format=date_format
49
49
  )
@@ -54,14 +54,14 @@ def calculate_moving_average(
54
54
 
55
55
  statistics_content: dict = {}
56
56
  # Read each file to its day.
57
- for log_path_dict in logs_paths:
58
- date_string = log_path_dict['date_string']
57
+ for log_atomic_path in logs_paths:
58
+ date_string = log_atomic_path.datetime_string
59
59
  statistics_content[date_string] = {}
60
60
 
61
- statistics_content[date_string]['file'] = log_path_dict
61
+ statistics_content[date_string]['file'] = log_atomic_path
62
62
 
63
63
  log_file_content, log_file_header = (
64
- csvs.read_csv_to_list_of_dicts_by_header(log_path_dict['file_path'], **(print_kwargs or {})))
64
+ csvs.read_csv_to_list_of_dicts_by_header(log_atomic_path.path, **(print_kwargs or {})))
65
65
  statistics_content[date_string]['content'] = log_file_content
66
66
  statistics_content[date_string]['header'] = log_file_header
67
67
 
@@ -3,7 +3,6 @@ import multiprocessing
3
3
  import time
4
4
  from typing import Literal, Union
5
5
 
6
- from ..print_api import print_api
7
6
  from .tracers import sysmon_etw, event_log
8
7
  from .pollers import psutil_pywin32wmi_dll
9
8
  from ..wrappers.pywin32w.win_event_log.subscribes import process_terminate
@@ -4,7 +4,6 @@ import time
4
4
 
5
5
  from ..wrappers.pywin32w.win_event_log.subscribes import process_create, process_terminate
6
6
  from .. import get_process_list
7
- from ..print_api import print_api
8
7
 
9
8
 
10
9
  WAIT_BEFORE_PROCESS_TERMINATION_CHECK_SECONDS: float = 3
@@ -1,5 +1,4 @@
1
1
  import os
2
- import sys
3
2
  import argparse
4
3
 
5
4
  from .base import msi
@@ -6,10 +6,8 @@ from typing import Union
6
6
  from . import config_basic
7
7
  from ...basics import dicts
8
8
 
9
- ELASTIC_WRAPPER = None
10
9
 
11
-
12
- def get_elastic_wrapper(url: str = None, overwrite: bool = False):
10
+ def get_elastic_wrapper(url: str = config_basic.DEFAULT_ELASTIC_URL, overwrite: bool = False):
13
11
  """
14
12
  The function initializes the Elasticsearch wrapper.
15
13
 
@@ -19,25 +17,9 @@ def get_elastic_wrapper(url: str = None, overwrite: bool = False):
19
17
 
20
18
  Usage:
21
19
  elastic_wrapper = get_elastic_wrapper()
22
- or after you initialize it once, you can use it like:
23
- atomicshop.wrappers.elasticsearchw.elasticsearchw.ELASTIC_WRAPPER
24
20
  """
25
21
 
26
- # If no url is provided, use the default url.
27
- if url is None:
28
- url = config_basic.DEFAULT_ELASTIC_URL
29
-
30
- # Get the global variable.
31
- global ELASTIC_WRAPPER
32
- # If the wrapper is not initialized, initialize it.
33
- if ELASTIC_WRAPPER is None:
34
- ELASTIC_WRAPPER = Elasticsearch([url])
35
- # If the wrapper is already initialized, check if it should be overwritten.
36
- else:
37
- if overwrite:
38
- ELASTIC_WRAPPER = Elasticsearch([url])
39
-
40
- return ELASTIC_WRAPPER
22
+ return Elasticsearch([url])
41
23
 
42
24
 
43
25
  def test_connection(elastic_wrapper: Elasticsearch = None):
@@ -4,7 +4,7 @@ from ...print_api import print_api
4
4
  from ... import process
5
5
  from ...permissions import permissions
6
6
  from .. import ubuntu_terminal
7
- from . import config_basic, infrastructure
7
+ from . import config_basic, elastic_infra
8
8
 
9
9
 
10
10
  def install_elastic_kibana_ubuntu(install_elastic: bool = True, install_kibana: bool = True):
@@ -202,29 +202,29 @@ def install_elastic_kibana_ubuntu(install_elastic: bool = True, install_kibana:
202
202
  sys.exit(1)
203
203
 
204
204
  # Check if the configuration file exists.
205
- infrastructure.is_elastic_config_file_exists(exit_on_error=True, output_message=True)
205
+ elastic_infra.is_elastic_config_file_exists(exit_on_error=True, output_message=True)
206
206
 
207
207
  # Check if the specific setting exists or not and set it to false.
208
- infrastructure.modify_xpack_security_setting(setting=False, output_message=True)
208
+ elastic_infra.modify_xpack_security_setting(setting=False, output_message=True)
209
209
 
210
210
  # Check if the setting was really set to false.
211
- if infrastructure.check_xpack_security_setting() is False:
211
+ if elastic_infra.check_xpack_security_setting() is False:
212
212
  print_api(f"The setting is confirmed to be [{config_basic.XPACK_SECURITY_SETTING_NAME}: false].")
213
213
  else:
214
214
  print_api(f"Failed to set [{config_basic.XPACK_SECURITY_SETTING_NAME}: false].")
215
215
  sys.exit(1)
216
216
 
217
- infrastructure.start_elastic_and_check_service_availability()
217
+ elastic_infra.start_elastic_and_check_service_availability()
218
218
 
219
219
  print_api("Creating custom JVM options file with 4GB memory usage.")
220
- infrastructure.create_jvm_options_custom_4gb_memory_heap_file()
220
+ elastic_infra.create_jvm_options_custom_4gb_memory_heap_file()
221
221
 
222
222
  if install_kibana:
223
223
  # Install Kibana.
224
224
  ubuntu_terminal.install_packages([config_basic.UBUNTU_KIBANA_PACKAGE_NAME])
225
225
 
226
226
  # Start and enable Kibana service.
227
- infrastructure.start_kibana_and_check_service_availability()
227
+ elastic_infra.start_kibana_and_check_service_availability()
228
228
 
229
229
  print_api("Installation completed.", color='green')
230
230
  if install_elastic:
@@ -33,11 +33,18 @@ def get_file_data(directory_path: str, firmwares: list = None):
33
33
  """
34
34
 
35
35
  if not firmwares:
36
- firmwares: list = filesystem.get_file_hashes_from_directory(directory_path, recursive=False, add_binary=True)
36
+ firmwares: list = filesystem.get_paths_from_directory(
37
+ directory_path, get_file=True, recursive=False, add_file_binary=True, add_file_hash=True)
37
38
 
38
39
  # Add UIDs to the list.
40
+ final_firmwares: list = []
39
41
  for firmware in firmwares:
40
- if 'uid' not in firmware:
41
- firmware['uid'] = get_uid_from_file(file_binary=firmware['binary'], sha256_hash=firmware['hash'])
42
-
43
- return firmwares
42
+ uid = get_uid_from_file(file_binary=firmware.binary, sha256_hash=firmware.hash)
43
+ final_firmwares.append({
44
+ 'path': firmware.path,
45
+ 'hash': firmware.hash,
46
+ 'binary': firmware.binary,
47
+ 'uid': uid
48
+ })
49
+
50
+ return final_firmwares
@@ -3,7 +3,6 @@ import fnmatch
3
3
 
4
4
  from .. import web, urls
5
5
  from ..print_api import print_api
6
- from ..basics import strings
7
6
 
8
7
 
9
8
  class GitHubWrapper:
@@ -84,27 +84,42 @@ def add_stream_handler(
84
84
 
85
85
  # Function to start the interval-based rotation check
86
86
  def _start_interval_rotation(handler):
87
+ # def check_rotation():
88
+ # while True:
89
+ # next_rollover = _calculate_next_rollover()
90
+ # while datetime.now() < next_rollover:
91
+ # time.sleep(0.1)
92
+ #
93
+ # # Check if the next_rollover has changed (indicating a rollover by an event)
94
+ # if _calculate_next_rollover() != next_rollover:
95
+ # next_rollover = _calculate_next_rollover()
96
+ # break
97
+ #
98
+ # # Perform manual rollover if needed
99
+ # if datetime.now() >= next_rollover:
100
+ # handler.doRollover()
101
+ #
102
+ # # handler.doRollover()
103
+ #
104
+ # def _calculate_next_rollover():
105
+ # return datetime.fromtimestamp(handler.rolloverAt)
87
106
  def check_rotation():
88
- while True:
89
- next_rollover = _calculate_next_rollover()
90
- while datetime.now() < next_rollover:
91
- time.sleep(0.1)
107
+ last_rollover_at = handler.rolloverAt # Initial rollover time
92
108
 
93
- # Check if the next_rollover has changed (indicating a rollover by an event)
94
- if _calculate_next_rollover() != next_rollover:
95
- next_rollover = _calculate_next_rollover()
96
- break
109
+ while True:
110
+ current_time = datetime.now()
111
+ next_rollover = datetime.fromtimestamp(handler.rolloverAt)
97
112
 
98
- # Perform manual rollover if needed
99
- if datetime.now() >= next_rollover:
100
- _rotate_log()
113
+ # Check if the rollover time has passed and it hasn't been handled yet
114
+ if current_time >= next_rollover and handler.rolloverAt == last_rollover_at:
115
+ # Perform manual rollover
116
+ handler.doRollover()
101
117
 
102
- def _calculate_next_rollover():
103
- return datetime.fromtimestamp(handler.rolloverAt)
118
+ # Update last_rollover_at to the new rolloverAt
119
+ last_rollover_at = handler.rolloverAt
104
120
 
105
- # Function to rotate logs
106
- def _rotate_log():
107
- handler.doRollover()
121
+ # Sleep for a short interval before checking again
122
+ time.sleep(0.1)
108
123
 
109
124
  rotation_thread = threading.Thread(target=check_rotation)
110
125
  rotation_thread.daemon = True
@@ -14,6 +14,17 @@ import logging
14
14
  """
15
15
 
16
16
 
17
+ def is_logger_exists(
18
+ logger_name: str
19
+ ) -> bool:
20
+ """
21
+ Function to check if the logger exists.
22
+ :param logger_name: str, Name of the logger.
23
+ :return: bool, True if the logger exists, False otherwise.
24
+ """
25
+ return logger_name in logging.Logger.manager.loggerDict
26
+
27
+
17
28
  def get_logger(logger_name: str) -> logging.Logger:
18
29
  """
19
30
  Function to get a logger.
@@ -5,8 +5,10 @@ from typing import Literal, Union
5
5
  from . import loggers, handlers
6
6
 
7
7
 
8
+ # noinspection PyPep8Naming
8
9
  def create_logger(
9
10
  logger_name: str,
11
+ get_existing_if_exists: bool = True,
10
12
  file_path: str = None,
11
13
  directory_path: str = None,
12
14
  add_stream: bool = False,
@@ -41,6 +43,8 @@ def create_logger(
41
43
  Function to get a logger and add StreamHandler and TimedRotatingFileHandler to it.
42
44
 
43
45
  :param logger_name: Name of the logger.
46
+ :param get_existing_if_exists: bool, If set to True, the logger will be returned if it already exists.
47
+ If set to False, the new stream/file handler will be added to existing logger again.
44
48
  :param file_path: full path to the log file. If you don't want to use the file, set it to None.
45
49
  You can set the directory_path only and then the 'logger_name' will be used as the file name with the
46
50
  'file_type' as the file extension.
@@ -74,7 +78,9 @@ def create_logger(
74
78
  in the formatter in case you provide 'asctime' element.
75
79
  :param filehandler_rotate_at_rollover_time: bool,
76
80
  If set to True, the log file will be rotated at the rollover time, even if there's nothing to write.
81
+ This behavior overrides the TimedRotatingFileHandler default behavior on doRollover.
77
82
  If set to False, the log file will be rotated after 'when' time, but only when event occurs.
83
+ This is the default doRollover behavior of the TimedRotatingFileHandler.
78
84
  :param filehandler_rotation_date_format: string, Date format to use for the log file rotation.
79
85
  Example for 'when="midnight"': the default date format is '%Y-%m-%d', resulting in filename on rotation like:
80
86
  "test.log.2021-11-25"
@@ -170,8 +176,15 @@ def create_logger(
170
176
 
171
177
  file_path = f"{directory_path}{os.sep}{logger_name}.{file_type}"
172
178
 
179
+ # Check if the logger exists before creating it/getting the existing.
180
+ is_logger_exists = loggers.is_logger_exists(logger_name)
181
+
173
182
  logger = get_logger_with_level(logger_name, logging_level)
174
183
 
184
+ # If the logger already exists, and we don't want to add the handlers again, return the logger.
185
+ if get_existing_if_exists and is_logger_exists:
186
+ return logger
187
+
175
188
  if add_stream:
176
189
  handlers.add_stream_handler(
177
190
  logger=logger, logging_level=logging_level, formatter=formatter_streamhandler,
@@ -14,7 +14,7 @@ def get_logs_paths(
14
14
  latest_only: bool = False,
15
15
  previous_day_only: bool = False,
16
16
  specific_date: str = None
17
- ):
17
+ ) -> list[filesystem.AtomicPath]:
18
18
  """
19
19
  This function gets the logs file paths from the directory. Supports rotating files to get the logs by time.
20
20
 
@@ -61,7 +61,7 @@ def get_logs_paths(
61
61
  log_files_directory_path: str = str(Path(log_file_path).parent)
62
62
 
63
63
  # Get all the log file paths by the file_name_pattern and the date_format string.
64
- logs_files: list = filesystem.get_paths_from_directory(
64
+ logs_files: list[filesystem.AtomicPath] = filesystem.get_paths_from_directory(
65
65
  log_files_directory_path,
66
66
  get_file=True,
67
67
  file_name_check_pattern=file_name_pattern,
@@ -8,7 +8,7 @@ from ... import urls, web
8
8
  from ...permissions import permissions
9
9
  from ...print_api import print_api
10
10
  from .. import msiw
11
- from . import infrastructure
11
+ from . import mongo_infra
12
12
 
13
13
 
14
14
  MONGODB_DOWNLOAD_PAGE_URL: str = 'https://www.mongodb.com/try/download/community'
@@ -111,7 +111,7 @@ def download_install_latest_main(
111
111
  print_api("This function requires administrator privileges.", color='red')
112
112
  return 1
113
113
 
114
- if infrastructure.is_service_running():
114
+ if mongo_infra.is_service_running():
115
115
  print_api("MongoDB service is running - already installed.", color='blue')
116
116
 
117
117
  if not force:
@@ -119,8 +119,8 @@ def download_install_latest_main(
119
119
  else:
120
120
  print_api("MongoDB is service is not running.")
121
121
 
122
- mongo_is_installed: Union[str, None] = infrastructure.is_installed()
123
- if infrastructure.is_installed():
122
+ mongo_is_installed: Union[str, None] = mongo_infra.is_installed()
123
+ if mongo_infra.is_installed():
124
124
  message = f"MongoDB is installed in: {mongo_is_installed}\n" \
125
125
  f"The service is not running. Fix the service or use the 'force' parameter to reinstall."
126
126
  print_api(message, color='yellow')
@@ -151,11 +151,11 @@ def download_install_latest_main(
151
151
 
152
152
  # Check if MongoDB is installed.
153
153
  message: str = ''
154
- mongo_is_installed = infrastructure.is_installed()
154
+ mongo_is_installed = mongo_infra.is_installed()
155
155
  if not mongo_is_installed:
156
156
  message += "MongoDB Executable not found.\n"
157
157
 
158
- if not infrastructure.is_service_running():
158
+ if not mongo_infra.is_service_running():
159
159
  message += "MongoDB service is not running.\n"
160
160
 
161
161
  if message:
@@ -1,9 +1,13 @@
1
+ import os
1
2
  from typing import Union
2
3
 
3
4
  from pymongo import MongoClient
4
5
  import pymongo.errors
5
6
 
6
- from ... import get_process_list, filesystem
7
+ from ... import filesystem
8
+
9
+ if os.name == 'nt':
10
+ from ... import get_process_list
7
11
 
8
12
 
9
13
  WHERE_TO_SEARCH_FOR_MONGODB_EXE: str = 'C:\\Program Files\\MongoDB\\Server\\'
@@ -34,12 +38,16 @@ def is_service_running() -> bool:
34
38
  Check if the MongoDB service is running.
35
39
  :return: bool, True if the MongoDB service is running, False otherwise.
36
40
  """
37
- current_processes: dict = (
38
- get_process_list.GetProcessList(get_method='pywin32', connect_on_init=True).get_processes())
39
41
 
40
- for pid, process_info in current_processes.items():
41
- if MONGODB_EXE_NAME in process_info['name']:
42
- return True
42
+ if os.name == 'nt':
43
+ current_processes: dict = (
44
+ get_process_list.GetProcessList(get_method='pywin32', connect_on_init=True).get_processes())
45
+
46
+ for pid, process_info in current_processes.items():
47
+ if MONGODB_EXE_NAME in process_info['name']:
48
+ return True
49
+ else:
50
+ raise NotImplementedError("This function is not implemented for this OS.")
43
51
 
44
52
  return False
45
53
 
@@ -1,6 +1,11 @@
1
+ from typing import Union
2
+ import datetime
3
+
1
4
  import pymongo
2
5
 
3
- from . import infrastructure
6
+ from ...basics import dicts
7
+
8
+ from . import mongo_infra
4
9
 
5
10
 
6
11
  """
@@ -9,7 +14,7 @@ These are good examples to get you started, but can't really cover all the use c
9
14
  """
10
15
 
11
16
 
12
- def connect(uri: str = infrastructure.MONGODB_DEFAULT_URI):
17
+ def connect(uri: str = mongo_infra.MONGODB_DEFAULT_URI):
13
18
  """
14
19
  Connect to a MongoDB database.
15
20
  :param uri: str, the URI of the MongoDB database.
@@ -18,18 +23,23 @@ def connect(uri: str = infrastructure.MONGODB_DEFAULT_URI):
18
23
  return pymongo.MongoClient(uri)
19
24
 
20
25
 
21
- def add_list_of_dicts_to_mongo(
22
- list_of_dicts: list,
26
+ def index(
27
+ object_instance: Union[list[dict], dict],
23
28
  database_name: str,
24
29
  collection_name: str,
30
+ add_timestamp: bool = False,
31
+ convert_mixed_lists_to_strings: bool = False,
25
32
  mongo_client: pymongo.MongoClient = None,
26
33
  close_client: bool = False
27
34
  ):
28
35
  """
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.
36
+ Add a dictionary or list dictionaries to a MongoDB collection.
37
+ :param object_instance: list of dictionaries or dictionary to add to the collection.
31
38
  :param database_name: str, the name of the database.
32
39
  :param collection_name: str, the name of the collection.
40
+ :param add_timestamp: bool, if True, a current time timestamp will be added to the object.
41
+ :param convert_mixed_lists_to_strings: bool, if True, mixed lists or tuples when entries are strings and integers,
42
+ the integers will be converted to strings.
33
43
  :param mongo_client: pymongo.MongoClient, the connection object.
34
44
  If None, a new connection will be created to default URI.
35
45
  :param close_client: bool, if True, the connection will be closed after the operation.
@@ -37,28 +47,46 @@ def add_list_of_dicts_to_mongo(
37
47
  :return: None
38
48
  """
39
49
 
50
+ _is_object_list_of_dicts_or_dict(object_instance)
51
+
40
52
  if not mongo_client:
41
53
  mongo_client = connect()
54
+ close_client = True
42
55
 
43
56
  db = mongo_client[database_name]
44
57
  collection = db[collection_name]
45
58
 
46
- collection.insert_many(list_of_dicts)
59
+ if convert_mixed_lists_to_strings:
60
+ object_instance = dicts.convert_int_to_str_in_mixed_lists(object_instance)
61
+
62
+ if add_timestamp:
63
+ timestamp = datetime.datetime.now()
64
+ if isinstance(object_instance, dict):
65
+ object_instance['timestamp'] = timestamp
66
+ elif isinstance(object_instance, list):
67
+ for doc in object_instance:
68
+ doc['timestamp'] = timestamp
69
+
70
+ if isinstance(object_instance, dict):
71
+ collection.insert_one(object_instance)
72
+ elif isinstance(object_instance, list):
73
+ collection.insert_many(object_instance)
47
74
 
48
75
  if close_client:
49
76
  mongo_client.close()
50
77
 
51
78
 
52
- def remove_list_of_dicts(
53
- list_of_dicts: list,
79
+ def delete(
80
+ object_instance: Union[list[dict], dict],
54
81
  database_name: str,
55
82
  collection_name: str,
56
83
  mongo_client: pymongo.MongoClient = None,
57
84
  close_client: bool = False
58
85
  ):
59
86
  """
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.
87
+ Remove a list of dictionaries or a dictionary from a MongoDB collection.
88
+
89
+ :param object_instance: list of dictionaries, the list of dictionaries to remove from the collection.
62
90
  :param database_name: str, the name of the database.
63
91
  :param collection_name: str, the name of the collection.
64
92
  :param mongo_client: pymongo.MongoClient, the connection object.
@@ -68,29 +96,74 @@ def remove_list_of_dicts(
68
96
  :return: None
69
97
  """
70
98
 
99
+ _is_object_list_of_dicts_or_dict(object_instance)
100
+
71
101
  if not mongo_client:
72
102
  mongo_client = connect()
103
+ close_client = True
73
104
 
74
105
  db = mongo_client[database_name]
75
106
  collection = db[collection_name]
76
107
 
77
- for doc in list_of_dicts:
78
- collection.delete_one(doc)
108
+ if isinstance(object_instance, dict):
109
+ collection.delete_one(object_instance)
110
+ elif isinstance(object_instance, list):
111
+ for doc in object_instance:
112
+ collection.delete_one(doc)
79
113
 
80
114
  if close_client:
81
115
  mongo_client.close()
82
116
 
83
117
 
84
- def overwrite_list_of_dicts(
85
- list_of_dicts: list,
118
+ def find(
86
119
  database_name: str,
87
120
  collection_name: str,
121
+ query: dict = None,
88
122
  mongo_client: pymongo.MongoClient = None,
89
123
  close_client: bool = False
90
124
  ):
91
125
  """
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.
126
+ Find entries in a MongoDB collection by query.
127
+ :param database_name: str, the name of the database.
128
+ :param collection_name: str, the name of the collection.
129
+ :param query: dict, the query to search for.
130
+ Example, search for all entries with column name 'name' equal to 'John':
131
+ query = {'name': 'John'}
132
+ Example, return all entries from collection:
133
+ query = None
134
+ :param mongo_client: pymongo.MongoClient, the connection object.
135
+ If None, a new connection will be created to default URI.
136
+ :param close_client: bool, if True, the connection will be closed after the operation.
137
+
138
+ :return: list of dictionaries, the list of entries that match the query.
139
+ """
140
+
141
+ if not mongo_client:
142
+ mongo_client = connect()
143
+ close_client = True
144
+
145
+ db = mongo_client[database_name]
146
+ collection = db[collection_name]
147
+
148
+ if query is None:
149
+ entries: list = list(collection.find())
150
+ else:
151
+ entries: list = list(collection.find(query))
152
+
153
+ if close_client:
154
+ mongo_client.close()
155
+
156
+ return entries
157
+
158
+
159
+ def delete_all_entries_from_collection(
160
+ database_name: str,
161
+ collection_name: str,
162
+ mongo_client: pymongo.MongoClient = None,
163
+ close_client: bool = False
164
+ ):
165
+ """
166
+ Remove all entries from a MongoDB collection.
94
167
  :param database_name: str, the name of the database.
95
168
  :param collection_name: str, the name of the collection.
96
169
  :param mongo_client: pymongo.MongoClient, the connection object.
@@ -102,43 +175,120 @@ def overwrite_list_of_dicts(
102
175
 
103
176
  if not mongo_client:
104
177
  mongo_client = connect()
178
+ close_client = True
105
179
 
106
180
  db = mongo_client[database_name]
107
181
  collection = db[collection_name]
108
182
 
109
183
  collection.delete_many({})
110
- collection.insert_many(list_of_dicts)
111
184
 
112
185
  if close_client:
113
186
  mongo_client.close()
114
187
 
115
188
 
116
- def get_all_entries_from_collection(
189
+ def overwrite_collection(
190
+ object_instance: list,
117
191
  database_name: str,
118
192
  collection_name: str,
193
+ add_timestamp: bool = False,
194
+ convert_mixed_lists_to_strings: bool = False,
119
195
  mongo_client: pymongo.MongoClient = None,
120
196
  close_client: bool = False
121
197
  ):
122
198
  """
123
- Get all entries from a MongoDB collection.
199
+ Overwrite a MongoDB collection with list of dicts or a dict.
200
+ :param object_instance: list of dictionaries, the list of dictionaries to overwrite in the collection.
124
201
  :param database_name: str, the name of the database.
125
202
  :param collection_name: str, the name of the collection.
203
+ :param add_timestamp: bool, if True, a current time timestamp will be added to the object.
204
+ :param convert_mixed_lists_to_strings: bool, if True, mixed lists or tuples when entries are strings and integers,
205
+ the integers will be converted to strings.
206
+ :param mongo_client: pymongo.MongoClient, the connection object.
207
+ If None, a new connection will be created to default URI.
208
+ :param close_client: bool, if True, the connection will be closed after the operation.
209
+
210
+ :return: None
211
+ """
212
+
213
+ _is_object_list_of_dicts_or_dict(object_instance)
214
+
215
+ if not mongo_client:
216
+ mongo_client = connect()
217
+ close_client = True
218
+
219
+ delete_all_entries_from_collection(
220
+ database_name=database_name, collection_name=collection_name,
221
+ mongo_client=mongo_client
222
+ )
223
+
224
+ index(
225
+ object_instance=object_instance,
226
+ database_name=database_name, collection_name=collection_name,
227
+ add_timestamp=add_timestamp, convert_mixed_lists_to_strings=convert_mixed_lists_to_strings,
228
+ mongo_client=mongo_client, close_client=close_client)
229
+
230
+
231
+ def _is_object_list_of_dicts_or_dict(
232
+ object_instance: Union[list[dict], dict]
233
+ ):
234
+ if isinstance(object_instance, dict):
235
+ return True
236
+ elif isinstance(object_instance, list):
237
+ if object_instance and isinstance(object_instance[0], dict):
238
+ return True
239
+ else:
240
+ raise ValueError("List must contain dictionaries.")
241
+ else:
242
+ raise ValueError("Object must be a dictionary or a list of dictionaries.")
243
+
244
+
245
+ def get_stats_db(
246
+ database_name: str,
247
+ mongo_client: pymongo.MongoClient = None,
248
+ close_client: bool = False
249
+ ):
250
+ """
251
+ Get the stats of a MongoDB database.
252
+
253
+ :param database_name: str, the name of the database.
126
254
  :param mongo_client: pymongo.MongoClient, the connection object.
127
255
  If None, a new connection will be created to default URI.
128
256
  :param close_client: bool, if True, the connection will be closed after the operation.
129
257
 
130
- :return: list of dictionaries, the list of entries from the collection.
258
+ :return: dict, the stats of the collection.
131
259
  """
132
260
 
133
261
  if not mongo_client:
134
262
  mongo_client = connect()
263
+ close_client = True
135
264
 
136
265
  db = mongo_client[database_name]
137
- collection = db[collection_name]
138
266
 
139
- entries = list(collection.find())
267
+ stats = db.command("dbStats")
140
268
 
141
269
  if close_client:
142
270
  mongo_client.close()
143
271
 
144
- return entries
272
+ return stats
273
+
274
+
275
+ def get_stats_db_size(
276
+ database_name: str,
277
+ mongo_client: pymongo.MongoClient = None,
278
+ close_client: bool = False
279
+ ):
280
+ """
281
+ Get the size of a MongoDB database in bytes.
282
+
283
+ :param database_name: str, the name of the database.
284
+ :param mongo_client: pymongo.MongoClient, the connection object.
285
+ If None, a new connection will be created to default URI.
286
+ :param close_client: bool, if True, the connection will be closed after the operation.
287
+
288
+ :return: int, the size of the database in bytes.
289
+ """
290
+
291
+ stats = get_stats_db(
292
+ database_name=database_name, mongo_client=mongo_client, close_client=close_client)
293
+
294
+ return stats['dataSize']
@@ -54,7 +54,11 @@ def execute_test(config_static):
54
54
  config = config_importer.config['config']
55
55
 
56
56
  # SocketClient is working with 'network' logger by default, so we will initialize it.
57
- loggingw.get_logger_with_stream_handler("network")
57
+ loggingw.create_logger(
58
+ logger_name="network",
59
+ add_stream=True,
60
+ formatter_streamhandler='DEFAULT'
61
+ )
58
62
 
59
63
  # Get all the files in requests folder recursively.
60
64
  request_file_list = filesystem.get_paths_from_directory(config['requests_directory'], get_file=True)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: atomicshop
3
- Version: 2.16.15
3
+ Version: 2.16.17
4
4
  Summary: Atomic functions and classes to make developer life easier
5
5
  Author: Denis Kras
6
6
  License: MIT License
@@ -38,7 +38,6 @@ Requires-Dist: cryptography
38
38
  Requires-Dist: dnslib
39
39
  Requires-Dist: dnspython
40
40
  Requires-Dist: docker
41
- Requires-Dist: elasticsearch
42
41
  Requires-Dist: numpy
43
42
  Requires-Dist: olefile
44
43
  Requires-Dist: openpyxl
@@ -1,9 +1,9 @@
1
- atomicshop/__init__.py,sha256=UzNeZyRkmLnYywv5edPjB0_p7zEgYfoGzcusp1FpodI,124
1
+ atomicshop/__init__.py,sha256=8j7ZCpRQjA2znKZa29KoZQlCV-GjDBzWXCHWB8Kft3c,124
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
5
5
  atomicshop/appointment_management.py,sha256=BsYH_PClTGLVazcuNjt30--hpXKYjSmHp1R1iQbM4Hc,7330
6
- atomicshop/certificates.py,sha256=ssOTD1uN4OaB9TU7mI0Dg_sSGFb6Xxw28A7TtyKmMMY,7637
6
+ atomicshop/certificates.py,sha256=MEGj7t3Nt2CHE7yzXrvFTLCOKZG9tJ6Ok5JC2BsFRis,7603
7
7
  atomicshop/command_line_processing.py,sha256=u5yT9Ger_cu7ni5ID0VFlRbVD46ARHeNC9tRM-_YXrQ,1038
8
8
  atomicshop/config_init.py,sha256=BSxc2FhytQPv06g5z9wbAXuA6oYCAsAJLxu_mTExhwI,2491
9
9
  atomicshop/console_output.py,sha256=AOSJjrRryE97PAGtgDL03IBtWSi02aNol8noDnW3k6M,4667
@@ -129,7 +129,7 @@ atomicshop/mitm/import_config.py,sha256=_nu8mgA-M4s6dZ8_QWx3x0aVb75upvsCuX_PIUg4
129
129
  atomicshop/mitm/initialize_engines.py,sha256=kBG8TBnyFuwlJ1uKaWDzc5AiZNpwdvouq2pr-PYrdEA,8349
130
130
  atomicshop/mitm/message.py,sha256=d_sm3O_aoZf87dDQP44xOMNEG-uZBN1ZecQgMCacbZs,1814
131
131
  atomicshop/mitm/mitm_main.py,sha256=CdCv4nYt_jwd23AI14v6lC2H8SZeIZqsXjFhwq61UtM,21285
132
- atomicshop/mitm/recs_files.py,sha256=btOuYQca4DuBOAKp9OY21HGjeEVOx9r_k-AnZOqs3Dk,3007
132
+ atomicshop/mitm/recs_files.py,sha256=4k6vCemGRmZ605yZ0-yRpF1ny3eAns7f3j61UXTJ99M,2966
133
133
  atomicshop/mitm/shared_functions.py,sha256=hplm98tz8pgJ4WHUVI9sf_oVqUM2KJ1Y2pD6EFSb8P0,1879
134
134
  atomicshop/mitm/statistic_analyzer.py,sha256=AzL9rQhg0tLJj33gZfxdwWghmbXGLh_HyMBDpzuBmsQ,24709
135
135
  atomicshop/mitm/engines/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -145,7 +145,7 @@ atomicshop/mitm/engines/__reference_general/recorder___reference_general.py,sha2
145
145
  atomicshop/mitm/engines/__reference_general/responder___reference_general.py,sha256=1AM49UaFTKA0AHw-k3SV3uH3QbG-o6ux0c-GoWkKNU0,6993
146
146
  atomicshop/mitm/statistic_analyzer_helper/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
147
147
  atomicshop/mitm/statistic_analyzer_helper/analyzer_helper.py,sha256=pk6L1t1ea1kvlBoR9QEJptOmaX-mumhwLsP2GCKukbk,5920
148
- atomicshop/mitm/statistic_analyzer_helper/moving_average_helper.py,sha256=lsG_eSWf6M_3AfWqIYEBNwYAyVHJfBiRVKHhmWIOZ9A,16615
148
+ atomicshop/mitm/statistic_analyzer_helper/moving_average_helper.py,sha256=Wge-OfbbClfjeEWwOyksd-x9C5QZdRD3KRSjtP9VL9Q,16651
149
149
  atomicshop/monitor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
150
150
  atomicshop/monitor/change_monitor.py,sha256=K5NlVp99XIDDPnQQMdru4BDmua_DtcDIhVAzkTOvD5s,7673
151
151
  atomicshop/monitor/checks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -159,8 +159,8 @@ atomicshop/permissions/permissions.py,sha256=CYTDVOI0jh9ks0ZLnnOuPzppgCszFEc9-92
159
159
  atomicshop/permissions/ubuntu_permissions.py,sha256=nMmr8ywwS0or8qJdg4ztMeqB2vppE_aytQTRQrg6lFc,2842
160
160
  atomicshop/permissions/win_permissions.py,sha256=eDQm1jfK9x_hkbLqIJjFTwfqinAWQ0iSr0kW3XrF1BE,1272
161
161
  atomicshop/process_poller/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
162
- atomicshop/process_poller/process_pool.py,sha256=1CaG6Cov-Pt_kZohfFeQFT42YBnEwNBA6ge55dxok_8,9600
163
- atomicshop/process_poller/simple_process_pool.py,sha256=hJkrn-efetLjyC8CevAFcsiKwBBKS8onYbf2ygq2J18,4540
162
+ atomicshop/process_poller/process_pool.py,sha256=4Qs427qd7OcBxu5PMFU5PTmyuxRy0vgj2GLsRt0IoEw,9565
163
+ atomicshop/process_poller/simple_process_pool.py,sha256=zHSxNlY2RTIXdqIyq8pg6kbXrTK7nqrz8iVWvyGWIE8,4505
164
164
  atomicshop/process_poller/tracer_base.py,sha256=IOiHcnmF-MccOSCErixN5mve9RifZ9cPnGVHCIRchrs,1091
165
165
  atomicshop/process_poller/pollers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
166
166
  atomicshop/process_poller/pollers/psutil_pywin32wmi_dll.py,sha256=XRRfOIy62iOYU8IKRcyECWiL0rqQ35DeYbPsv_SHDVM,4510
@@ -180,7 +180,7 @@ atomicshop/wrappers/astw.py,sha256=VkYfkfyc_PJLIOxByT6L7B8uUmKY6-I8XGZl4t_z828,4
180
180
  atomicshop/wrappers/configparserw.py,sha256=JwDTPjZoSrv44YKwIRcjyUnpN-FjgXVfMqMK_tJuSgU,22800
181
181
  atomicshop/wrappers/cryptographyw.py,sha256=LfzTnwvJE03G6WZryOOf43VKhhnyMakzHpn8DPPCoy4,13252
182
182
  atomicshop/wrappers/ffmpegw.py,sha256=wcq0ZnAe0yajBOuTKZCCaKI7CDBjkq7FAgdW5IsKcVE,6031
183
- atomicshop/wrappers/githubw.py,sha256=AQcFuT5mvDUNT_cI31MwkJ7srdhMtttF8FyXS8vs5cU,12270
183
+ atomicshop/wrappers/githubw.py,sha256=gUUVlFnlwKxINXAoK0PTOditEEhe0sWe06aAZ4eQz64,12240
184
184
  atomicshop/wrappers/msiw.py,sha256=GQLqud72nfex3kvO1bJSruNriCYTYX1_G1gSf1MPkIA,6118
185
185
  atomicshop/wrappers/numpyw.py,sha256=sBV4gSKyr23kXTalqAb1oqttzE_2XxBooCui66jbAqc,1025
186
186
  atomicshop/wrappers/olefilew.py,sha256=biD5m58rogifCYmYhJBrAFb9O_Bn_spLek_9HofLeYE,2051
@@ -200,16 +200,16 @@ atomicshop/wrappers/ctyping/etw_winapi/etw_functions.py,sha256=Iwd0wIuoxpjMaaOfZ
200
200
  atomicshop/wrappers/ctyping/msi_windows_installer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
201
201
  atomicshop/wrappers/ctyping/msi_windows_installer/base.py,sha256=Uu9SlWLsQQ6mjE-ek-ptHcmgiI3Ruah9bdZus70EaVY,4884
202
202
  atomicshop/wrappers/ctyping/msi_windows_installer/cabs.py,sha256=htzwb2ROYI8yyc82xApStckPS2yCcoyaw32yC15KROs,3285
203
- atomicshop/wrappers/ctyping/msi_windows_installer/extract_msi_main.py,sha256=Nq-04-PbT_diUOuHLQ6-uBWBCr6UY9GOOQ0Nojs0QhA,5500
203
+ atomicshop/wrappers/ctyping/msi_windows_installer/extract_msi_main.py,sha256=AEkjnqEhfCbCUcYaulv3uba5hZjTB03xm-puAINsZGM,5488
204
204
  atomicshop/wrappers/ctyping/msi_windows_installer/tables.py,sha256=tHsu0YfBgzuIk9L-PyqLgU_IzyVbCfy8L1EqelNnvWk,17674
205
205
  atomicshop/wrappers/dockerw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
206
206
  atomicshop/wrappers/dockerw/dockerw.py,sha256=w8zSJr5C7cbvbuG09ORCpAe0BOcibqqL_Z2EKEBHYK4,6266
207
207
  atomicshop/wrappers/dockerw/install_docker.py,sha256=7NTMxCPBesr0QcqB0RZ5YU0I8FDPtNux_mYAX28wI0I,9982
208
208
  atomicshop/wrappers/elasticsearchw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
209
209
  atomicshop/wrappers/elasticsearchw/config_basic.py,sha256=fDujtrjEjbWiYh_WQ3OcYp_8mXhXPYeKLy4wSPL5qM0,1177
210
- atomicshop/wrappers/elasticsearchw/elasticsearchw.py,sha256=7TqFdEFznO8NlligJhEKk1vm641ALpCYdaRl1uoXdzM,9768
211
- atomicshop/wrappers/elasticsearchw/infrastructure.py,sha256=at0sD-SFtmEvfGyIU_YBEKoU-MNeVtDQSNscPm0JWLc,10368
212
- atomicshop/wrappers/elasticsearchw/install_elastic.py,sha256=mIex4-D7R3qYNfq8f_PrBkYyB1vRF5SLvJ2Mra9hj3Y,8647
210
+ atomicshop/wrappers/elasticsearchw/elastic_infra.py,sha256=at0sD-SFtmEvfGyIU_YBEKoU-MNeVtDQSNscPm0JWLc,10368
211
+ atomicshop/wrappers/elasticsearchw/elasticsearchw.py,sha256=nkjUuW6dhJf65w3JiGIxUFxSO_oNFCFw9fFR0xH_SCU,9152
212
+ atomicshop/wrappers/elasticsearchw/install_elastic.py,sha256=m77uAkfEkjwTf0CfWBHal1C7gcQDwry7LcWN331KZOI,8640
213
213
  atomicshop/wrappers/elasticsearchw/queries/__init__.py,sha256=KBjT-bAt75CJsx1Apko9mpuFU4pfZV8DcGWQvpX65RU,78
214
214
  atomicshop/wrappers/elasticsearchw/queries/aggregation.py,sha256=N9a5yMMnb10sMa_x1qJBFQpgyJ49UWo8_vxuqmUtZ1A,1742
215
215
  atomicshop/wrappers/elasticsearchw/queries/info.py,sha256=P_VhhBo8MvRI4Shi-bh4RsYtlYNRKRBzScXPC64Up_Q,2900
@@ -221,7 +221,7 @@ atomicshop/wrappers/elasticsearchw/queries/sort.py,sha256=-CUhgcs_AVRvncRlLTl5IJ
221
221
  atomicshop/wrappers/factw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
222
222
  atomicshop/wrappers/factw/config_fact.py,sha256=J-K9Zn50WcDC7ubb-boraSZExfBk7a6M52NhRJVlsjk,895
223
223
  atomicshop/wrappers/factw/config_install.py,sha256=N-6Fg4Z_aG33r0WGlYgBQTFnamJceYyW4XjnGyyNAks,555
224
- atomicshop/wrappers/factw/get_file_data.py,sha256=ChKC0OjgjFlNubZQBwcGhRO3L2pccc27RLRlAMIUix4,1641
224
+ atomicshop/wrappers/factw/get_file_data.py,sha256=SeJJgzTQwZ7gyn3hztbbcRaAD2i5uxvT0Kt2tRN18Aw,1852
225
225
  atomicshop/wrappers/factw/fact_extractor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
226
226
  atomicshop/wrappers/factw/fact_extractor/docker_image.py,sha256=2FyYjnw8gxFNwISQ83OwH-iGivkFi6EAluyCZ0loHEQ,2501
227
227
  atomicshop/wrappers/factw/fact_extractor/get_extractor.py,sha256=2mfOAftHIlCcGt1s7MWdq7DsDCuI6wX3MtvcEZ4SK-0,756
@@ -247,14 +247,14 @@ atomicshop/wrappers/fibratusw/install.py,sha256=PLVymDe0HuOvU0r2lje8BkQAgtiOWEeR
247
247
  atomicshop/wrappers/loggingw/consts.py,sha256=JWiUJEydjhwatBxtIJsGTmDUSTLbmIRidtR6qRLMaIY,1608
248
248
  atomicshop/wrappers/loggingw/filters.py,sha256=CMs5PAMb68zxJgBcQobaOFDG5kLJBOVYnoBHjDgksO8,2859
249
249
  atomicshop/wrappers/loggingw/formatters.py,sha256=7XUJvlB0CK4DCkEp8NTL0S0dkyrZD0UTADgEwkStKOY,5483
250
- atomicshop/wrappers/loggingw/handlers.py,sha256=_69ZtmZRUsdgL8Q1rh2X-a0ComDU9gg9v101ngbJcac,17436
251
- atomicshop/wrappers/loggingw/loggers.py,sha256=DHOOTAtqkwn1xgvLHSkOiBm6yFGNuQy1kvbhG-TDog8,2374
252
- atomicshop/wrappers/loggingw/loggingw.py,sha256=BBQe_2i8otcbe0cZcAKd3tZ2D5XL3SPb_IWTXCepSxw,11595
253
- atomicshop/wrappers/loggingw/reading.py,sha256=tplnJlQ7RMxWv2s782tWGOo1C7WNemk2SRTZCCgD9J0,16474
250
+ atomicshop/wrappers/loggingw/handlers.py,sha256=hAPFJQ-wFoNO8QzGrJRSvyuP09Q1F0Dl9_w7zzlgcW0,18155
251
+ atomicshop/wrappers/loggingw/loggers.py,sha256=QH5QainlGLyrDpsDu4T1C8-WQQau3JW2OS5RgC-kXpM,2677
252
+ atomicshop/wrappers/loggingw/loggingw.py,sha256=6HUn2z4ZW8PgakPscosKx23qYwHlBQcLiZGw-VZHi-k,12374
253
+ atomicshop/wrappers/loggingw/reading.py,sha256=SZFE4d6IFoC1GveFf28oAuDQzHG8UnBHx3K3-dE5Mes,16528
254
254
  atomicshop/wrappers/mongodbw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
255
- atomicshop/wrappers/mongodbw/infrastructure.py,sha256=tHqtt__yKGtj24CT5AIk0V0k9t1p_PjezFExXRmmmcA,1517
256
- atomicshop/wrappers/mongodbw/install_mongodb.py,sha256=21Re0NpikgYOKelyPTkosoQQxzu67dy80Kj204TL5eo,6644
257
- atomicshop/wrappers/mongodbw/mongodbw.py,sha256=eDwI3sePLGWhl-neBRQM955KZOl8jaO7U0n_AzXM3es,4568
255
+ atomicshop/wrappers/mongodbw/install_mongodb.py,sha256=3ZPqrXxj3lC-PnAKGXclylLuOqsbyXYeUpb5iGjdeUU,6626
256
+ atomicshop/wrappers/mongodbw/mongo_infra.py,sha256=JO63ShbHYRBmUdFv-GeJxkPQdhRhq59ly6bxnn64fUM,1713
257
+ atomicshop/wrappers/mongodbw/mongodbw.py,sha256=_K1alcxAAIiMhwJYNB82OD-FjQVQijxp2UYnq-g1z98,9860
258
258
  atomicshop/wrappers/nodejsw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
259
259
  atomicshop/wrappers/nodejsw/install_nodejs.py,sha256=QZg-R2iTQt7kFb8wNtnTmwraSGwvUs34JIasdbNa7ZU,5154
260
260
  atomicshop/wrappers/playwrightw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -303,14 +303,14 @@ atomicshop/wrappers/socketw/receiver.py,sha256=G3hDTacm7nwwUNHEbKZpxO0c8rHcl0NeK
303
303
  atomicshop/wrappers/socketw/sender.py,sha256=d7YQFlCBMFTYtkGxbS-8cm5rh5WWFeBVvrEivWHYstI,3666
304
304
  atomicshop/wrappers/socketw/sni.py,sha256=fVwyh3h9IqfLMnf4__bMIzcF4c-Kk9mlbDWMRXKN-ow,17155
305
305
  atomicshop/wrappers/socketw/socket_client.py,sha256=FNmTt94YvjZP0X4RPb7icO3xD_nBHQ_XynnObdWFiAU,19682
306
- atomicshop/wrappers/socketw/socket_server_tester.py,sha256=wAwyst8YdVyVfZfERav1A9_OnMJAiVBy-4uY0RpNqkU,6339
306
+ atomicshop/wrappers/socketw/socket_server_tester.py,sha256=Qobmh4XV8ZxLUaw-eW4ESKAbeSLecCKn2OWFzMhadk0,6420
307
307
  atomicshop/wrappers/socketw/socket_wrapper.py,sha256=DiDzg9OlgahxpqAsnOkCKHNcE-H-YAY3ndVMeU-oCjw,33363
308
308
  atomicshop/wrappers/socketw/ssl_base.py,sha256=kmiif84kMhBr5yjQW17p935sfjR5JKG0LxIwBA4iVvU,2275
309
309
  atomicshop/wrappers/socketw/statistics_csv.py,sha256=V_m1D0KpizQox3IEWp2AUcncwWy5kG25hbFrc-mBSJE,3029
310
310
  atomicshop/wrappers/winregw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
311
311
  atomicshop/wrappers/winregw/winreg_network.py,sha256=bQ8Jql8bVGBJ0dt3VQ56lga_1LBOMLI3Km_otvvbU6c,7138
312
- atomicshop-2.16.15.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
313
- atomicshop-2.16.15.dist-info/METADATA,sha256=i5Xgtqpn2TfPbeSQ_L3nAryipoFyy2Z6whmJsDFQyD4,10503
314
- atomicshop-2.16.15.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
315
- atomicshop-2.16.15.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
316
- atomicshop-2.16.15.dist-info/RECORD,,
312
+ atomicshop-2.16.17.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
313
+ atomicshop-2.16.17.dist-info/METADATA,sha256=hh2ElTNYbFQfLq_0PnD9wptNCmnlx06CLdEMgs-DWDo,10473
314
+ atomicshop-2.16.17.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
315
+ atomicshop-2.16.17.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
316
+ atomicshop-2.16.17.dist-info/RECORD,,