atomicshop 2.15.11__py3-none-any.whl → 3.10.5__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.
Files changed (221) hide show
  1. atomicshop/__init__.py +1 -1
  2. atomicshop/{addons/mains → a_mains}/FACT/update_extract.py +3 -2
  3. atomicshop/a_mains/dns_gateway_setting.py +11 -0
  4. atomicshop/a_mains/get_local_tcp_ports.py +85 -0
  5. atomicshop/a_mains/github_wrapper.py +11 -0
  6. atomicshop/a_mains/install_ca_certificate.py +172 -0
  7. atomicshop/a_mains/process_from_port.py +119 -0
  8. atomicshop/a_mains/set_default_dns_gateway.py +90 -0
  9. atomicshop/a_mains/update_config_toml.py +38 -0
  10. atomicshop/basics/ansi_escape_codes.py +3 -1
  11. atomicshop/basics/argparse_template.py +2 -0
  12. atomicshop/basics/booleans.py +27 -30
  13. atomicshop/basics/bytes_arrays.py +43 -0
  14. atomicshop/basics/classes.py +149 -1
  15. atomicshop/basics/enums.py +2 -2
  16. atomicshop/basics/exceptions.py +5 -1
  17. atomicshop/basics/list_of_classes.py +29 -0
  18. atomicshop/basics/multiprocesses.py +374 -50
  19. atomicshop/basics/strings.py +72 -3
  20. atomicshop/basics/threads.py +14 -0
  21. atomicshop/basics/tracebacks.py +13 -3
  22. atomicshop/certificates.py +153 -52
  23. atomicshop/config_init.py +11 -6
  24. atomicshop/console_user_response.py +7 -14
  25. atomicshop/consoles.py +9 -0
  26. atomicshop/datetimes.py +1 -1
  27. atomicshop/diff_check.py +3 -3
  28. atomicshop/dns.py +128 -3
  29. atomicshop/etws/_pywintrace_fix.py +17 -0
  30. atomicshop/etws/trace.py +40 -42
  31. atomicshop/etws/traces/trace_dns.py +56 -44
  32. atomicshop/etws/traces/trace_tcp.py +130 -0
  33. atomicshop/file_io/csvs.py +27 -5
  34. atomicshop/file_io/docxs.py +34 -17
  35. atomicshop/file_io/file_io.py +31 -17
  36. atomicshop/file_io/jsons.py +49 -0
  37. atomicshop/file_io/tomls.py +139 -0
  38. atomicshop/filesystem.py +616 -291
  39. atomicshop/get_process_list.py +3 -3
  40. atomicshop/http_parse.py +149 -93
  41. atomicshop/ip_addresses.py +6 -1
  42. atomicshop/mitm/centered_settings.py +132 -0
  43. atomicshop/mitm/config_static.py +207 -0
  44. atomicshop/mitm/config_toml_editor.py +55 -0
  45. atomicshop/mitm/connection_thread_worker.py +875 -357
  46. atomicshop/mitm/engines/__parent/parser___parent.py +4 -17
  47. atomicshop/mitm/engines/__parent/recorder___parent.py +108 -51
  48. atomicshop/mitm/engines/__parent/requester___parent.py +116 -0
  49. atomicshop/mitm/engines/__parent/responder___parent.py +75 -114
  50. atomicshop/mitm/engines/__reference_general/parser___reference_general.py +10 -7
  51. atomicshop/mitm/engines/__reference_general/recorder___reference_general.py +5 -5
  52. atomicshop/mitm/engines/__reference_general/requester___reference_general.py +47 -0
  53. atomicshop/mitm/engines/__reference_general/responder___reference_general.py +95 -13
  54. atomicshop/mitm/engines/create_module_template.py +58 -14
  55. atomicshop/mitm/import_config.py +359 -139
  56. atomicshop/mitm/initialize_engines.py +160 -80
  57. atomicshop/mitm/message.py +64 -23
  58. atomicshop/mitm/mitm_main.py +892 -0
  59. atomicshop/mitm/recs_files.py +183 -0
  60. atomicshop/mitm/shared_functions.py +4 -10
  61. atomicshop/mitm/ssh_tester.py +82 -0
  62. atomicshop/mitm/statistic_analyzer.py +136 -40
  63. atomicshop/mitm/statistic_analyzer_helper/moving_average_helper.py +265 -83
  64. atomicshop/monitor/checks/dns.py +1 -1
  65. atomicshop/networks.py +671 -0
  66. atomicshop/on_exit.py +39 -9
  67. atomicshop/package_mains_processor.py +84 -0
  68. atomicshop/permissions/permissions.py +22 -0
  69. atomicshop/permissions/ubuntu_permissions.py +239 -0
  70. atomicshop/permissions/win_permissions.py +33 -0
  71. atomicshop/print_api.py +24 -42
  72. atomicshop/process.py +24 -6
  73. atomicshop/process_poller/process_pool.py +0 -1
  74. atomicshop/process_poller/simple_process_pool.py +204 -5
  75. atomicshop/python_file_patcher.py +1 -1
  76. atomicshop/python_functions.py +27 -75
  77. atomicshop/speech_recognize.py +8 -0
  78. atomicshop/ssh_remote.py +158 -172
  79. atomicshop/system_resource_monitor.py +61 -47
  80. atomicshop/system_resources.py +8 -8
  81. atomicshop/tempfiles.py +1 -2
  82. atomicshop/urls.py +6 -0
  83. atomicshop/venvs.py +28 -0
  84. atomicshop/versioning.py +27 -0
  85. atomicshop/web.py +98 -27
  86. atomicshop/web_apis/google_custom_search.py +44 -0
  87. atomicshop/web_apis/google_llm.py +188 -0
  88. atomicshop/websocket_parse.py +450 -0
  89. atomicshop/wrappers/certauthw/certauth.py +1 -0
  90. atomicshop/wrappers/cryptographyw.py +29 -8
  91. atomicshop/wrappers/ctyping/etw_winapi/const.py +97 -47
  92. atomicshop/wrappers/ctyping/etw_winapi/etw_functions.py +178 -49
  93. atomicshop/wrappers/ctyping/file_details_winapi.py +67 -0
  94. atomicshop/wrappers/ctyping/msi_windows_installer/cabs.py +2 -1
  95. atomicshop/wrappers/ctyping/msi_windows_installer/extract_msi_main.py +2 -2
  96. atomicshop/wrappers/ctyping/setup_device.py +466 -0
  97. atomicshop/wrappers/ctyping/win_console.py +39 -0
  98. atomicshop/wrappers/dockerw/dockerw.py +113 -2
  99. atomicshop/wrappers/elasticsearchw/config_basic.py +0 -12
  100. atomicshop/wrappers/elasticsearchw/elastic_infra.py +75 -0
  101. atomicshop/wrappers/elasticsearchw/elasticsearchw.py +2 -20
  102. atomicshop/wrappers/factw/get_file_data.py +12 -5
  103. atomicshop/wrappers/factw/install/install_after_restart.py +89 -5
  104. atomicshop/wrappers/factw/install/pre_install_and_install_before_restart.py +20 -14
  105. atomicshop/wrappers/githubw.py +537 -54
  106. atomicshop/wrappers/loggingw/consts.py +1 -1
  107. atomicshop/wrappers/loggingw/filters.py +23 -0
  108. atomicshop/wrappers/loggingw/formatters.py +12 -0
  109. atomicshop/wrappers/loggingw/handlers.py +214 -107
  110. atomicshop/wrappers/loggingw/loggers.py +19 -0
  111. atomicshop/wrappers/loggingw/loggingw.py +860 -22
  112. atomicshop/wrappers/loggingw/reading.py +134 -112
  113. atomicshop/wrappers/mongodbw/mongo_infra.py +31 -0
  114. atomicshop/wrappers/mongodbw/mongodbw.py +1324 -36
  115. atomicshop/wrappers/netshw.py +271 -0
  116. atomicshop/wrappers/playwrightw/engine.py +34 -19
  117. atomicshop/wrappers/playwrightw/infra.py +5 -0
  118. atomicshop/wrappers/playwrightw/javascript.py +7 -3
  119. atomicshop/wrappers/playwrightw/keyboard.py +14 -0
  120. atomicshop/wrappers/playwrightw/scenarios.py +172 -5
  121. atomicshop/wrappers/playwrightw/waits.py +9 -7
  122. atomicshop/wrappers/powershell_networking.py +80 -0
  123. atomicshop/wrappers/psutilw/processes.py +37 -1
  124. atomicshop/wrappers/psutilw/psutil_networks.py +85 -0
  125. atomicshop/wrappers/pyopensslw.py +9 -2
  126. atomicshop/wrappers/pywin32w/cert_store.py +116 -0
  127. atomicshop/wrappers/pywin32w/win_event_log/fetch.py +174 -0
  128. atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_create.py +3 -105
  129. atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_terminate.py +3 -57
  130. atomicshop/wrappers/pywin32w/wmis/msft_netipaddress.py +113 -0
  131. atomicshop/wrappers/pywin32w/wmis/win32_networkadapterconfiguration.py +259 -0
  132. atomicshop/wrappers/pywin32w/wmis/win32networkadapter.py +112 -0
  133. atomicshop/wrappers/pywin32w/wmis/wmi_helpers.py +236 -0
  134. atomicshop/wrappers/socketw/accepter.py +21 -7
  135. atomicshop/wrappers/socketw/certificator.py +216 -150
  136. atomicshop/wrappers/socketw/creator.py +190 -50
  137. atomicshop/wrappers/socketw/dns_server.py +491 -182
  138. atomicshop/wrappers/socketw/exception_wrapper.py +45 -52
  139. atomicshop/wrappers/socketw/process_getter.py +86 -0
  140. atomicshop/wrappers/socketw/receiver.py +144 -102
  141. atomicshop/wrappers/socketw/sender.py +65 -35
  142. atomicshop/wrappers/socketw/sni.py +334 -165
  143. atomicshop/wrappers/socketw/socket_base.py +134 -0
  144. atomicshop/wrappers/socketw/socket_client.py +137 -95
  145. atomicshop/wrappers/socketw/socket_server_tester.py +11 -7
  146. atomicshop/wrappers/socketw/socket_wrapper.py +717 -116
  147. atomicshop/wrappers/socketw/ssl_base.py +15 -14
  148. atomicshop/wrappers/socketw/statistics_csv.py +148 -17
  149. atomicshop/wrappers/sysmonw.py +1 -1
  150. atomicshop/wrappers/ubuntu_terminal.py +65 -26
  151. atomicshop/wrappers/win_auditw.py +189 -0
  152. atomicshop/wrappers/winregw/__init__.py +0 -0
  153. atomicshop/wrappers/winregw/winreg_installed_software.py +58 -0
  154. atomicshop/wrappers/winregw/winreg_network.py +232 -0
  155. {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info}/METADATA +31 -51
  156. atomicshop-3.10.5.dist-info/RECORD +306 -0
  157. {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info}/WHEEL +1 -1
  158. atomicshop/_basics_temp.py +0 -101
  159. atomicshop/a_installs/win/fibratus.py +0 -9
  160. atomicshop/a_installs/win/mongodb.py +0 -9
  161. atomicshop/a_installs/win/pycharm.py +0 -9
  162. atomicshop/addons/a_setup_scripts/install_psycopg2_ubuntu.sh +0 -3
  163. atomicshop/addons/a_setup_scripts/install_pywintrace_0.3.cmd +0 -2
  164. atomicshop/addons/mains/__pycache__/install_fibratus_windows.cpython-312.pyc +0 -0
  165. atomicshop/addons/mains/__pycache__/msi_unpacker.cpython-312.pyc +0 -0
  166. atomicshop/addons/mains/install_docker_rootless_ubuntu.py +0 -11
  167. atomicshop/addons/mains/install_docker_ubuntu_main_sudo.py +0 -11
  168. atomicshop/addons/mains/install_elastic_search_and_kibana_ubuntu.py +0 -10
  169. atomicshop/addons/mains/install_wsl_ubuntu_lts_admin.py +0 -9
  170. atomicshop/addons/package_setup/CreateWheel.cmd +0 -7
  171. atomicshop/addons/package_setup/Setup in Edit mode.cmd +0 -6
  172. atomicshop/addons/package_setup/Setup.cmd +0 -7
  173. atomicshop/archiver/_search_in_zip.py +0 -189
  174. atomicshop/archiver/archiver.py +0 -34
  175. atomicshop/archiver/search_in_archive.py +0 -250
  176. atomicshop/archiver/sevenz_app_w.py +0 -86
  177. atomicshop/archiver/sevenzs.py +0 -44
  178. atomicshop/archiver/zips.py +0 -293
  179. atomicshop/file_types.py +0 -24
  180. atomicshop/mitm/config_editor.py +0 -37
  181. atomicshop/mitm/engines/create_module_template_example.py +0 -13
  182. atomicshop/mitm/initialize_mitm_server.py +0 -268
  183. atomicshop/pbtkmultifile_argparse.py +0 -88
  184. atomicshop/permissions.py +0 -151
  185. atomicshop/script_as_string_processor.py +0 -38
  186. atomicshop/ssh_scripts/process_from_ipv4.py +0 -37
  187. atomicshop/ssh_scripts/process_from_port.py +0 -27
  188. atomicshop/wrappers/_process_wrapper_curl.py +0 -27
  189. atomicshop/wrappers/_process_wrapper_tar.py +0 -21
  190. atomicshop/wrappers/dockerw/install_docker.py +0 -209
  191. atomicshop/wrappers/elasticsearchw/infrastructure.py +0 -265
  192. atomicshop/wrappers/elasticsearchw/install_elastic.py +0 -232
  193. atomicshop/wrappers/ffmpegw.py +0 -125
  194. atomicshop/wrappers/fibratusw/install.py +0 -81
  195. atomicshop/wrappers/mongodbw/infrastructure.py +0 -53
  196. atomicshop/wrappers/mongodbw/install_mongodb.py +0 -190
  197. atomicshop/wrappers/msiw.py +0 -149
  198. atomicshop/wrappers/nodejsw/install_nodejs.py +0 -139
  199. atomicshop/wrappers/process_wrapper_pbtk.py +0 -16
  200. atomicshop/wrappers/psutilw/networks.py +0 -45
  201. atomicshop/wrappers/pycharmw.py +0 -81
  202. atomicshop/wrappers/socketw/base.py +0 -59
  203. atomicshop/wrappers/socketw/get_process.py +0 -107
  204. atomicshop/wrappers/wslw.py +0 -191
  205. atomicshop-2.15.11.dist-info/RECORD +0 -302
  206. /atomicshop/{addons/mains → a_mains}/FACT/factw_fact_extractor_docker_image_main_sudo.py +0 -0
  207. /atomicshop/{addons → a_mains/addons}/PlayWrightCodegen.cmd +0 -0
  208. /atomicshop/{addons → a_mains/addons}/ScriptExecution.cmd +0 -0
  209. /atomicshop/{addons → a_mains/addons}/inits/init_to_import_all_modules.py +0 -0
  210. /atomicshop/{addons → a_mains/addons}/process_list/ReadMe.txt +0 -0
  211. /atomicshop/{addons → a_mains/addons}/process_list/compile.cmd +0 -0
  212. /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.dll +0 -0
  213. /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.exp +0 -0
  214. /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.lib +0 -0
  215. /atomicshop/{addons → a_mains/addons}/process_list/process_list.cpp +0 -0
  216. /atomicshop/{archiver → permissions}/__init__.py +0 -0
  217. /atomicshop/{wrappers/fibratusw → web_apis}/__init__.py +0 -0
  218. /atomicshop/wrappers/{nodejsw → pywin32w/wmis}/__init__.py +0 -0
  219. /atomicshop/wrappers/pywin32w/{wmi_win32process.py → wmis/win32process.py} +0 -0
  220. {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info/licenses}/LICENSE.txt +0 -0
  221. {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info}/top_level.txt +0 -0
@@ -1,3 +1,7 @@
1
+ from typing import Union
2
+ import string
3
+
4
+
1
5
  def get_single_byte_from_byte_string(byte_string, index: int):
2
6
  """
3
7
  Function extracts single byte as byte from byte string object.
@@ -53,6 +57,30 @@ def convert_sequence_of_bytes_to_sequence_of_strings(byte_sequence: bytes) -> li
53
57
  return result
54
58
 
55
59
 
60
+ def convert_sequence_of_bytes_to_exact_string(
61
+ byte_sequence: bytes,
62
+ add_space_between_bytes: bool = False,
63
+ ) -> str:
64
+ """
65
+ Convert sequence of bytes to exact string.
66
+ Example: b'\xc0\x00' -> 'c000'
67
+
68
+ :param byte_sequence: bytes, sequence of bytes.
69
+ :param add_space_between_bytes: bool, add space between bytes.
70
+ Example: b'\xc0\x00' -> 'c0 00'
71
+ :return: string.
72
+ """
73
+
74
+ # Convert to hex string and format
75
+ byte_list: list = []
76
+ for byte in byte_sequence:
77
+ byte_list.append(f'{byte:02x}')
78
+
79
+ result = ''.join(byte_list)
80
+
81
+ return result
82
+
83
+
56
84
  def find_position(target: bytes, file_path: str = None, file_bytes: bytes = None, chunk_size: int = None, starting_position: int = 0) -> int:
57
85
  """
58
86
  Find position of the target bytes string in the file.
@@ -155,3 +183,18 @@ def read_bytes_from_position(
155
183
  # Read the specified number of bytes.
156
184
  data = file.read(num_bytes)
157
185
  return data
186
+
187
+
188
+ def convert_bytes_to_printable_string_only(
189
+ byte_sequence: Union[bytes, bytearray],
190
+ non_printable_character: str = '.'
191
+ ) -> str:
192
+ """
193
+ Convert bytes to printable string. If byte is not printable, replace it with 'non_printable_character'.
194
+ :param byte_sequence: bytes or bytearray, sequence of bytes.
195
+ :param non_printable_character: string, character to replace non-printable characters.
196
+ :return:
197
+ """
198
+
199
+ printable = set(string.printable)
200
+ return ''.join(chr(byte) if chr(byte) in printable else non_printable_character for byte in byte_sequence)
@@ -1,12 +1,160 @@
1
1
  import os
2
2
  import sys
3
3
  import ast
4
- import importlib
5
4
  from pathlib import Path
5
+ import pkgutil
6
+ import importlib
7
+ import inspect
6
8
 
7
9
  from ..file_io.file_io import read_file
8
10
 
9
11
 
12
+ """
13
+ Using logger in the class only once during the import of the module.
14
+
15
+ class ParserParent:
16
+ # Initializing the logger in the "class variable" section will leave the instance of the logger initiated
17
+ # and the rest of the instances of the class will use the same logger.
18
+ # It is not in the "__init__" section, so it's not going to be initiated again.
19
+ # The name of the logger using "__name__" variable, which is the full name of the module package.
20
+ # Example: classes.parsers.parser_1_reference_general
21
+
22
+ # The code outside the functions will be executed during import of the module. When initializing a class
23
+ # in the script these lines will not be called again, only the "init" function.
24
+ logger = create_custom_logger()
25
+
26
+ def __init__(self, class_client_message: ClientMessage):
27
+ self.class_client_message: ClientMessage = class_client_message
28
+
29
+ # Usage: self.logger.info("Message")
30
+ """
31
+
32
+
33
+ """
34
+ Using Base class for easier interfacing on subclasses.
35
+
36
+ recognition/recognition_base.py:
37
+ from abc import abstractmethod
38
+
39
+
40
+ class Recognizer:
41
+ @abstractmethod
42
+ def recognize_vendor(self, file_path: str) -> str:
43
+ pass
44
+
45
+ @abstractmethod
46
+ def recognize_family(self, bytes_list: list[str]]) -> str:
47
+ pass
48
+
49
+
50
+ recognition/super_vendor.py:
51
+ from .recognition_base import Recognizer
52
+
53
+ class SupervendorRecognizer(Recognizer):
54
+ def recognize_vendor(self, file_path: str) -> str:
55
+ classification_string: str = <Some logic to classify the SuperVendor>
56
+ return classification_string
57
+
58
+ def recognize_family(self, bytes_list: list[str]]) -> str:
59
+ family_classification_string: str = <Some logic to classify the family of the SuperVendor>
60
+ return family_classification_string
61
+
62
+
63
+ main_script.py:
64
+ from . import recognition
65
+ from .recognition.recognition_base import Recognizer
66
+
67
+ # Get the list of all the recognizers in the recognition package.
68
+ recognizers_list: list = classes.get_list_of_classes_in_module(
69
+ imported_package=recognition, imported_base_class=Recognizer)
70
+
71
+ # Get the list of all the vendors from the file.
72
+ vendors_list: list = list()
73
+ for recognizer in recognizers_list:
74
+ recognizer_instance = recognizer()
75
+ vendor_name: str = recognizer_instance.recognize_vendor(file_object=file_path)
76
+ if vendor_name:
77
+ vendors_list.append((vendor_name, recognizer_instance))
78
+
79
+ # Get the families of the vendors.
80
+ for vendor_name, recognizer_instance in vendors_list:
81
+ family_name: str = recognizer_instance.recognize_family(bytes_list=file_bytes_list)
82
+ print(f"Vendor: {vendor_name}, Family: {family_name}")
83
+ """
84
+
85
+ def get_list_of_classes_in_module(
86
+ imported_package,
87
+ imported_base_class
88
+ ) -> list:
89
+ """
90
+ Function that returns a list of classes that are subclasses of the imported_base_class from the imported_package.
91
+
92
+ Example:
93
+ # Package structure:
94
+ # unpackers
95
+ # ├── __init__.py
96
+ # ├── unpacker_base.py
97
+ # ├── unpacker_1.py
98
+ # ├── unpacker_2.py
99
+ # ├── unpacker_3.py
100
+ # └── ... (other unpacker modules)
101
+
102
+ # unpacker_base.py:
103
+ from abc import abstractmethod
104
+ class Unpacker:
105
+ @abstractmethod
106
+ def unpack(self, file_path):
107
+ pass
108
+
109
+ # unpacker_1.py:
110
+ from unpackers.unpacker_base import Unpacker
111
+ class Unpacker1(Unpacker):
112
+ def unpack(self, file_path):
113
+ print(f"Unpacking file with Unpacker1: {file_path}")
114
+
115
+ # main_script.py:
116
+ # Import the base class
117
+ from unpackers.unpacker_base import Unpacker
118
+ # Import the package
119
+ import unpackers
120
+ # Get the list of classes
121
+ unpacker_classes = get_list_of_classes_in_module(imported_package=unpackers, imported_base_class=Unpacker)
122
+
123
+ # Initialize the classes
124
+ for unpacker_class in unpacker_classes:
125
+ unpacker_instance = unpacker_class()
126
+ unpacker_instance.unpack("file_path")
127
+ ----------------------------
128
+ # You can also initialize the list of classes dynamically and after that execute methods.
129
+ # Example:
130
+ unpacker_classes = get_list_of_classes_in_module(imported_package=unpackers, imported_base_class=Unpacker)
131
+
132
+ instance_list: list = []
133
+ for unpacker_class in unpacker_classes:
134
+ instance_list.append(unpacker_class())
135
+
136
+ for instance in instance_list:
137
+ instance.unpack("file_path")
138
+
139
+ :param imported_package:
140
+ :param imported_base_class:
141
+ :return:
142
+ """
143
+ unpacker_classes = []
144
+
145
+ # Iterate over all modules in the 'imported_package' package
146
+ for loader, module_name, is_pkg in pkgutil.iter_modules(imported_package.__path__):
147
+ # Import the module
148
+ module = importlib.import_module(f"{imported_package.__name__}.{module_name}")
149
+
150
+ # Inspect module members to find Unpacker subclasses
151
+ for name, obj in inspect.getmembers(module, inspect.isclass):
152
+ if issubclass(obj, imported_base_class) and obj is not imported_base_class:
153
+ unpacker_classes.append(obj)
154
+
155
+ return unpacker_classes
156
+
157
+
10
158
  def create_empty_class():
11
159
  """
12
160
  Function creates empty class, you can add parameters to it dynamically.
@@ -32,10 +32,10 @@ def __comparison_usage_example(
32
32
  main folder.
33
33
 
34
34
  Usage:
35
- from atomicshop.filesystem import get_file_paths_from_directory, ComparisonOperator
35
+ from atomicshop.filesystem import get_paths_from_directory, ComparisonOperator
36
36
 
37
37
  # Get full paths of all the 'engine_config.ini' files.
38
- engine_config_path_list = get_file_paths_from_directory(
38
+ engine_config_path_list = get_paths_from_directory(
39
39
  directory_fullpath=some_directory_path,
40
40
  file_name_check_tuple=(config_file_name, ComparisonOperator.EQ))
41
41
  """
@@ -1,4 +1,3 @@
1
- # v1.0.3 - 28.03.2023 17:20
2
1
  import sys
3
2
 
4
3
  from .threads import current_thread_id
@@ -15,3 +14,8 @@ def print_exception() -> None:
15
14
  exc_type, exc_value, exc_traceback = sys.exc_info()
16
15
 
17
16
  print(f"{error_log_prefix} Thread {thread_id}: * Details: {exc_type}, {exc_value}")
17
+
18
+
19
+ def get_exception_type_string(exception: Exception) -> str:
20
+ """ Get exception type string """
21
+ return type(exception).__name__
@@ -0,0 +1,29 @@
1
+ from operator import attrgetter
2
+
3
+
4
+ def sort_by_attributes(
5
+ list_instance: list,
6
+ attribute_list: list,
7
+ reverse: bool = False,
8
+ case_insensitive: bool = False
9
+ ):
10
+ """
11
+ Sort a list of objects by their attributes.
12
+
13
+ :param list_instance: list of objects.
14
+ :param attribute_list: list of attributes (strings). The sorting will be done by the attributes in the list.
15
+ In the appearing order in the list.
16
+ :param reverse: bool, sort in reverse order.
17
+ :param case_insensitive: bool, sorting will be case-insensitive.
18
+ """
19
+
20
+ for attribute in attribute_list:
21
+ if case_insensitive:
22
+ list_instance = sorted(
23
+ list_instance, key=lambda obj: getattr(obj, attribute).lower(), reverse=reverse
24
+ )
25
+ else:
26
+ list_instance = sorted(
27
+ list_instance, key=attrgetter(attribute), reverse=reverse
28
+ )
29
+ return list_instance