atomicshop 2.11.47__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.
- atomicshop/__init__.py +1 -1
- atomicshop/{addons/mains → a_mains}/FACT/update_extract.py +3 -2
- atomicshop/a_mains/addons/process_list/compile.cmd +7 -0
- atomicshop/a_mains/addons/process_list/compiled/Win10x64/process_list.dll +0 -0
- atomicshop/a_mains/addons/process_list/compiled/Win10x64/process_list.exp +0 -0
- atomicshop/a_mains/addons/process_list/compiled/Win10x64/process_list.lib +0 -0
- atomicshop/{addons → a_mains/addons}/process_list/process_list.cpp +8 -1
- atomicshop/a_mains/dns_gateway_setting.py +11 -0
- atomicshop/a_mains/get_local_tcp_ports.py +85 -0
- atomicshop/a_mains/github_wrapper.py +11 -0
- atomicshop/a_mains/install_ca_certificate.py +172 -0
- atomicshop/{addons/mains → a_mains}/msi_unpacker.py +3 -1
- atomicshop/a_mains/process_from_port.py +119 -0
- atomicshop/a_mains/set_default_dns_gateway.py +90 -0
- atomicshop/a_mains/update_config_toml.py +38 -0
- atomicshop/appointment_management.py +5 -3
- atomicshop/basics/ansi_escape_codes.py +3 -1
- atomicshop/basics/argparse_template.py +2 -0
- atomicshop/basics/booleans.py +27 -30
- atomicshop/basics/bytes_arrays.py +43 -0
- atomicshop/basics/classes.py +149 -1
- atomicshop/basics/dicts.py +12 -0
- atomicshop/basics/enums.py +2 -2
- atomicshop/basics/exceptions.py +5 -1
- atomicshop/basics/list_of_classes.py +29 -0
- atomicshop/basics/list_of_dicts.py +69 -5
- atomicshop/basics/lists.py +14 -0
- atomicshop/basics/multiprocesses.py +374 -50
- atomicshop/basics/package_module.py +10 -0
- atomicshop/basics/strings.py +160 -7
- atomicshop/basics/threads.py +14 -0
- atomicshop/basics/tracebacks.py +13 -4
- atomicshop/certificates.py +153 -52
- atomicshop/config_init.py +12 -7
- atomicshop/console_user_response.py +7 -14
- atomicshop/consoles.py +9 -0
- atomicshop/datetimes.py +98 -0
- atomicshop/diff_check.py +340 -40
- atomicshop/dns.py +128 -12
- atomicshop/etws/_pywintrace_fix.py +17 -0
- atomicshop/etws/const.py +38 -0
- atomicshop/etws/providers.py +21 -0
- atomicshop/etws/sessions.py +43 -0
- atomicshop/etws/trace.py +168 -0
- atomicshop/etws/traces/trace_dns.py +162 -0
- atomicshop/etws/traces/trace_sysmon_process_creation.py +126 -0
- atomicshop/etws/traces/trace_tcp.py +130 -0
- atomicshop/file_io/csvs.py +222 -24
- atomicshop/file_io/docxs.py +35 -18
- atomicshop/file_io/file_io.py +35 -19
- atomicshop/file_io/jsons.py +49 -0
- atomicshop/file_io/tomls.py +139 -0
- atomicshop/filesystem.py +864 -293
- atomicshop/get_process_list.py +133 -0
- atomicshop/{process_name_cmd.py → get_process_name_cmd_dll.py} +52 -19
- atomicshop/http_parse.py +149 -93
- atomicshop/ip_addresses.py +6 -1
- atomicshop/mitm/centered_settings.py +132 -0
- atomicshop/mitm/config_static.py +207 -0
- atomicshop/mitm/config_toml_editor.py +55 -0
- atomicshop/mitm/connection_thread_worker.py +875 -357
- atomicshop/mitm/engines/__parent/parser___parent.py +4 -17
- atomicshop/mitm/engines/__parent/recorder___parent.py +108 -51
- atomicshop/mitm/engines/__parent/requester___parent.py +116 -0
- atomicshop/mitm/engines/__parent/responder___parent.py +75 -114
- atomicshop/mitm/engines/__reference_general/parser___reference_general.py +10 -7
- atomicshop/mitm/engines/__reference_general/recorder___reference_general.py +5 -5
- atomicshop/mitm/engines/__reference_general/requester___reference_general.py +47 -0
- atomicshop/mitm/engines/__reference_general/responder___reference_general.py +95 -13
- atomicshop/mitm/engines/create_module_template.py +58 -14
- atomicshop/mitm/import_config.py +359 -139
- atomicshop/mitm/initialize_engines.py +160 -74
- atomicshop/mitm/message.py +64 -23
- atomicshop/mitm/mitm_main.py +892 -0
- atomicshop/mitm/recs_files.py +183 -0
- atomicshop/mitm/shared_functions.py +4 -10
- atomicshop/mitm/ssh_tester.py +82 -0
- atomicshop/mitm/statistic_analyzer.py +257 -166
- atomicshop/mitm/statistic_analyzer_helper/analyzer_helper.py +136 -0
- atomicshop/mitm/statistic_analyzer_helper/moving_average_helper.py +525 -0
- atomicshop/monitor/change_monitor.py +96 -120
- atomicshop/monitor/checks/dns.py +139 -70
- atomicshop/monitor/checks/file.py +77 -0
- atomicshop/monitor/checks/network.py +81 -77
- atomicshop/monitor/checks/process_running.py +33 -34
- atomicshop/monitor/checks/url.py +94 -0
- atomicshop/networks.py +671 -0
- atomicshop/on_exit.py +205 -0
- atomicshop/package_mains_processor.py +84 -0
- atomicshop/permissions/permissions.py +22 -0
- atomicshop/permissions/ubuntu_permissions.py +239 -0
- atomicshop/permissions/win_permissions.py +33 -0
- atomicshop/print_api.py +24 -41
- atomicshop/process.py +63 -17
- atomicshop/process_poller/__init__.py +0 -0
- atomicshop/process_poller/pollers/__init__.py +0 -0
- atomicshop/process_poller/pollers/psutil_pywin32wmi_dll.py +95 -0
- atomicshop/process_poller/process_pool.py +207 -0
- atomicshop/process_poller/simple_process_pool.py +311 -0
- atomicshop/process_poller/tracer_base.py +45 -0
- atomicshop/process_poller/tracers/__init__.py +0 -0
- atomicshop/process_poller/tracers/event_log.py +46 -0
- atomicshop/process_poller/tracers/sysmon_etw.py +68 -0
- atomicshop/python_file_patcher.py +1 -1
- atomicshop/python_functions.py +27 -75
- atomicshop/question_answer_engine.py +2 -2
- atomicshop/scheduling.py +24 -5
- atomicshop/sound.py +4 -2
- atomicshop/speech_recognize.py +8 -0
- atomicshop/ssh_remote.py +158 -172
- atomicshop/startup/__init__.py +0 -0
- atomicshop/startup/win/__init__.py +0 -0
- atomicshop/startup/win/startup_folder.py +53 -0
- atomicshop/startup/win/task_scheduler.py +119 -0
- atomicshop/system_resource_monitor.py +61 -46
- atomicshop/system_resources.py +8 -8
- atomicshop/tempfiles.py +1 -2
- atomicshop/timer.py +30 -11
- atomicshop/urls.py +41 -0
- atomicshop/venvs.py +28 -0
- atomicshop/versioning.py +27 -0
- atomicshop/web.py +110 -25
- atomicshop/web_apis/__init__.py +0 -0
- atomicshop/web_apis/google_custom_search.py +44 -0
- atomicshop/web_apis/google_llm.py +188 -0
- atomicshop/websocket_parse.py +450 -0
- atomicshop/wrappers/certauthw/certauth.py +1 -0
- atomicshop/wrappers/cryptographyw.py +29 -8
- atomicshop/wrappers/ctyping/etw_winapi/__init__.py +0 -0
- atomicshop/wrappers/ctyping/etw_winapi/const.py +335 -0
- atomicshop/wrappers/ctyping/etw_winapi/etw_functions.py +393 -0
- atomicshop/wrappers/ctyping/file_details_winapi.py +67 -0
- atomicshop/wrappers/ctyping/msi_windows_installer/cabs.py +2 -1
- atomicshop/wrappers/ctyping/msi_windows_installer/extract_msi_main.py +13 -9
- atomicshop/wrappers/ctyping/msi_windows_installer/tables.py +35 -0
- atomicshop/wrappers/ctyping/setup_device.py +466 -0
- atomicshop/wrappers/ctyping/win_console.py +39 -0
- atomicshop/wrappers/dockerw/dockerw.py +113 -2
- atomicshop/wrappers/elasticsearchw/config_basic.py +0 -12
- atomicshop/wrappers/elasticsearchw/elastic_infra.py +75 -0
- atomicshop/wrappers/elasticsearchw/elasticsearchw.py +2 -20
- atomicshop/wrappers/factw/get_file_data.py +12 -5
- atomicshop/wrappers/factw/install/install_after_restart.py +89 -5
- atomicshop/wrappers/factw/install/pre_install_and_install_before_restart.py +20 -14
- atomicshop/wrappers/factw/postgresql/firmware.py +4 -6
- atomicshop/wrappers/githubw.py +583 -51
- atomicshop/wrappers/loggingw/consts.py +49 -0
- atomicshop/wrappers/loggingw/filters.py +102 -0
- atomicshop/wrappers/loggingw/formatters.py +58 -71
- atomicshop/wrappers/loggingw/handlers.py +459 -40
- atomicshop/wrappers/loggingw/loggers.py +19 -0
- atomicshop/wrappers/loggingw/loggingw.py +1010 -178
- atomicshop/wrappers/loggingw/reading.py +344 -19
- atomicshop/wrappers/mongodbw/__init__.py +0 -0
- atomicshop/wrappers/mongodbw/mongo_infra.py +31 -0
- atomicshop/wrappers/mongodbw/mongodbw.py +1432 -0
- atomicshop/wrappers/netshw.py +271 -0
- atomicshop/wrappers/playwrightw/engine.py +34 -19
- atomicshop/wrappers/playwrightw/infra.py +5 -0
- atomicshop/wrappers/playwrightw/javascript.py +7 -3
- atomicshop/wrappers/playwrightw/keyboard.py +14 -0
- atomicshop/wrappers/playwrightw/scenarios.py +172 -5
- atomicshop/wrappers/playwrightw/waits.py +9 -7
- atomicshop/wrappers/powershell_networking.py +80 -0
- atomicshop/wrappers/psutilw/processes.py +81 -0
- atomicshop/wrappers/psutilw/psutil_networks.py +85 -0
- atomicshop/wrappers/psutilw/psutilw.py +9 -0
- atomicshop/wrappers/pyopensslw.py +9 -2
- atomicshop/wrappers/pywin32w/__init__.py +0 -0
- atomicshop/wrappers/pywin32w/cert_store.py +116 -0
- atomicshop/wrappers/pywin32w/console.py +34 -0
- atomicshop/wrappers/pywin32w/win_event_log/__init__.py +0 -0
- atomicshop/wrappers/pywin32w/win_event_log/fetch.py +174 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribe.py +212 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/__init__.py +0 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_create.py +57 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_terminate.py +49 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/schannel_logging.py +97 -0
- atomicshop/wrappers/pywin32w/winshell.py +19 -0
- atomicshop/wrappers/pywin32w/wmis/__init__.py +0 -0
- atomicshop/wrappers/pywin32w/wmis/msft_netipaddress.py +113 -0
- atomicshop/wrappers/pywin32w/wmis/win32_networkadapterconfiguration.py +259 -0
- atomicshop/wrappers/pywin32w/wmis/win32networkadapter.py +112 -0
- atomicshop/wrappers/pywin32w/wmis/wmi_helpers.py +236 -0
- atomicshop/wrappers/socketw/accepter.py +21 -7
- atomicshop/wrappers/socketw/certificator.py +216 -150
- atomicshop/wrappers/socketw/creator.py +190 -50
- atomicshop/wrappers/socketw/dns_server.py +500 -173
- atomicshop/wrappers/socketw/exception_wrapper.py +45 -52
- atomicshop/wrappers/socketw/process_getter.py +86 -0
- atomicshop/wrappers/socketw/receiver.py +144 -102
- atomicshop/wrappers/socketw/sender.py +65 -35
- atomicshop/wrappers/socketw/sni.py +334 -165
- atomicshop/wrappers/socketw/socket_base.py +134 -0
- atomicshop/wrappers/socketw/socket_client.py +137 -95
- atomicshop/wrappers/socketw/socket_server_tester.py +14 -9
- atomicshop/wrappers/socketw/socket_wrapper.py +717 -116
- atomicshop/wrappers/socketw/ssl_base.py +15 -14
- atomicshop/wrappers/socketw/statistics_csv.py +148 -17
- atomicshop/wrappers/sysmonw.py +157 -0
- atomicshop/wrappers/ubuntu_terminal.py +65 -26
- atomicshop/wrappers/win_auditw.py +189 -0
- atomicshop/wrappers/winregw/__init__.py +0 -0
- atomicshop/wrappers/winregw/winreg_installed_software.py +58 -0
- atomicshop/wrappers/winregw/winreg_network.py +232 -0
- {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info}/METADATA +31 -49
- atomicshop-3.10.5.dist-info/RECORD +306 -0
- {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info}/WHEEL +1 -1
- atomicshop/_basics_temp.py +0 -101
- atomicshop/addons/a_setup_scripts/install_psycopg2_ubuntu.sh +0 -3
- atomicshop/addons/a_setup_scripts/install_pywintrace_0.3.cmd +0 -2
- atomicshop/addons/mains/install_docker_rootless_ubuntu.py +0 -11
- atomicshop/addons/mains/install_docker_ubuntu_main_sudo.py +0 -11
- atomicshop/addons/mains/install_elastic_search_and_kibana_ubuntu.py +0 -10
- atomicshop/addons/mains/install_wsl_ubuntu_lts_admin.py +0 -9
- atomicshop/addons/package_setup/CreateWheel.cmd +0 -7
- atomicshop/addons/package_setup/Setup in Edit mode.cmd +0 -6
- atomicshop/addons/package_setup/Setup.cmd +0 -7
- atomicshop/addons/process_list/compile.cmd +0 -2
- atomicshop/addons/process_list/compiled/Win10x64/process_list.dll +0 -0
- atomicshop/addons/process_list/compiled/Win10x64/process_list.exp +0 -0
- atomicshop/addons/process_list/compiled/Win10x64/process_list.lib +0 -0
- atomicshop/archiver/_search_in_zip.py +0 -189
- atomicshop/archiver/archiver.py +0 -34
- atomicshop/archiver/search_in_archive.py +0 -250
- atomicshop/archiver/sevenz_app_w.py +0 -86
- atomicshop/archiver/sevenzs.py +0 -44
- atomicshop/archiver/zips.py +0 -293
- atomicshop/etw/dns_trace.py +0 -118
- atomicshop/etw/etw.py +0 -61
- atomicshop/file_types.py +0 -24
- atomicshop/mitm/engines/create_module_template_example.py +0 -13
- atomicshop/mitm/initialize_mitm_server.py +0 -240
- atomicshop/monitor/checks/hash.py +0 -44
- atomicshop/monitor/checks/hash_checks/file.py +0 -55
- atomicshop/monitor/checks/hash_checks/url.py +0 -62
- atomicshop/pbtkmultifile_argparse.py +0 -88
- atomicshop/permissions.py +0 -110
- atomicshop/process_poller.py +0 -237
- atomicshop/script_as_string_processor.py +0 -38
- atomicshop/ssh_scripts/process_from_ipv4.py +0 -37
- atomicshop/ssh_scripts/process_from_port.py +0 -27
- atomicshop/wrappers/_process_wrapper_curl.py +0 -27
- atomicshop/wrappers/_process_wrapper_tar.py +0 -21
- atomicshop/wrappers/dockerw/install_docker.py +0 -209
- atomicshop/wrappers/elasticsearchw/infrastructure.py +0 -265
- atomicshop/wrappers/elasticsearchw/install_elastic.py +0 -232
- atomicshop/wrappers/ffmpegw.py +0 -125
- atomicshop/wrappers/loggingw/checks.py +0 -20
- atomicshop/wrappers/nodejsw/install_nodejs.py +0 -139
- atomicshop/wrappers/process_wrapper_pbtk.py +0 -16
- atomicshop/wrappers/socketw/base.py +0 -59
- atomicshop/wrappers/socketw/get_process.py +0 -107
- atomicshop/wrappers/wslw.py +0 -191
- atomicshop-2.11.47.dist-info/RECORD +0 -251
- /atomicshop/{addons/mains → a_mains}/FACT/factw_fact_extractor_docker_image_main_sudo.py +0 -0
- /atomicshop/{addons → a_mains/addons}/PlayWrightCodegen.cmd +0 -0
- /atomicshop/{addons → a_mains/addons}/ScriptExecution.cmd +0 -0
- /atomicshop/{addons/mains → a_mains/addons}/inits/init_to_import_all_modules.py +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/ReadMe.txt +0 -0
- /atomicshop/{addons/mains → a_mains}/search_for_hyperlinks_in_docx.py +0 -0
- /atomicshop/{archiver → etws}/__init__.py +0 -0
- /atomicshop/{etw → etws/traces}/__init__.py +0 -0
- /atomicshop/{monitor/checks/hash_checks → mitm/statistic_analyzer_helper}/__init__.py +0 -0
- /atomicshop/{wrappers/nodejsw → permissions}/__init__.py +0 -0
- /atomicshop/wrappers/pywin32w/{wmi_win32process.py → wmis/win32process.py} +0 -0
- {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info/licenses}/LICENSE.txt +0 -0
- {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import datetime
|
|
3
|
+
|
|
4
|
+
import win32com.client
|
|
5
|
+
import pythoncom
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
TRIGGER_ONE_TIME: int = 1
|
|
9
|
+
TRIGGER_DAILY: int = 2
|
|
10
|
+
TRIGGER_AT_SYSTEM_STARTUP: int = 8
|
|
11
|
+
TRIGGER_ON_LOGON: int = 9
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def create_on_logon_task_with_system_privileges(exe_file_path: str, task_name: str, user_id: str = None):
|
|
15
|
+
"""
|
|
16
|
+
This function will add a task to the Windows Task Scheduler with system privileges.
|
|
17
|
+
|
|
18
|
+
:param exe_file_path: The path to your executable file.
|
|
19
|
+
:param task_name: The name of the task to create in the Task Scheduler.
|
|
20
|
+
:param user_id: The user ID to run the task as.
|
|
21
|
+
None: the task will run for every user that logs on.
|
|
22
|
+
"SYSTEM": is a common user ID to run tasks with system privileges.
|
|
23
|
+
:return: True if the task was added successfully, False otherwise.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
scheduler = win32com.client.Dispatch('Schedule.Service')
|
|
27
|
+
scheduler.Connect()
|
|
28
|
+
|
|
29
|
+
root_folder = scheduler.GetFolder('\\')
|
|
30
|
+
task_def = scheduler.NewTask(0)
|
|
31
|
+
|
|
32
|
+
# Set up registration information for the task
|
|
33
|
+
reg_info = task_def.RegistrationInfo
|
|
34
|
+
reg_info.Description = f'Task to run {os.path.basename(exe_file_path)} at logon'
|
|
35
|
+
reg_info.Author = 'Your Name'
|
|
36
|
+
|
|
37
|
+
# Set up the principal for the task
|
|
38
|
+
principal = task_def.Principal
|
|
39
|
+
if user_id is not None:
|
|
40
|
+
principal.UserId = user_id
|
|
41
|
+
# principal.LogonType = 3 # TaskLogonTypeInteractiveToken, Only run when the user is logged on.
|
|
42
|
+
principal.LogonType = 1 # 1 is for password not required
|
|
43
|
+
principal.RunLevel = 1 # TaskRunLevelHighest
|
|
44
|
+
|
|
45
|
+
# Create the logon trigger
|
|
46
|
+
trigger = task_def.Triggers.Create(TRIGGER_ON_LOGON)
|
|
47
|
+
if user_id:
|
|
48
|
+
trigger.UserId = user_id
|
|
49
|
+
trigger.Id = "LogonTriggerId"
|
|
50
|
+
trigger.Enabled = True
|
|
51
|
+
trigger.StartBoundary = datetime.datetime.now().isoformat() # Set start boundary to current time in ISO format
|
|
52
|
+
|
|
53
|
+
# Create the action to run the executable
|
|
54
|
+
action = task_def.Actions.Create(0) # 0 stands for TASK_ACTION_EXEC
|
|
55
|
+
action.Path = exe_file_path
|
|
56
|
+
action.WorkingDirectory = os.path.dirname(exe_file_path)
|
|
57
|
+
action.Arguments = ''
|
|
58
|
+
|
|
59
|
+
# Set task settings
|
|
60
|
+
settings = task_def.Settings
|
|
61
|
+
settings.Enabled = True
|
|
62
|
+
settings.StartWhenAvailable = True
|
|
63
|
+
settings.Hidden = False
|
|
64
|
+
settings.StopIfGoingOnBatteries = False
|
|
65
|
+
settings.DisallowStartIfOnBatteries = False
|
|
66
|
+
# Sets the limit to zero, which means the task will run indefinitely. The default is 3 days.
|
|
67
|
+
settings.ExecutionTimeLimit = 'PT0S'
|
|
68
|
+
|
|
69
|
+
# Register the task
|
|
70
|
+
root_folder.RegisterTaskDefinition(
|
|
71
|
+
task_name, task_def, 6, # 6 is for CREATE_OR_UPDATE
|
|
72
|
+
None, # No user (runs in system context)
|
|
73
|
+
None, # No password
|
|
74
|
+
3
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def is_task_in_scheduler(task_name: str, scheduler_instance=None) -> bool:
|
|
79
|
+
"""
|
|
80
|
+
This function will check if the task is in the Windows Task Scheduler.
|
|
81
|
+
|
|
82
|
+
:param task_name: The name of the task to check in the Task Scheduler.
|
|
83
|
+
:param scheduler_instance: The instance of the Task Scheduler to use.
|
|
84
|
+
If None, a new instance will be created.
|
|
85
|
+
:return: True if the task is in the Task Scheduler, False otherwise.
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
if scheduler_instance is None:
|
|
89
|
+
scheduler_instance = win32com.client.Dispatch('Schedule.Service')
|
|
90
|
+
scheduler_instance.Connect()
|
|
91
|
+
|
|
92
|
+
try:
|
|
93
|
+
root_folder = scheduler_instance.GetFolder('\\')
|
|
94
|
+
root_folder.GetTask(task_name)
|
|
95
|
+
return True
|
|
96
|
+
except pythoncom.com_error as e:
|
|
97
|
+
if e.hresult == -2147352567 and e.excepinfo[5] == -2147024894: # HRESULT code for "Task does not exist"
|
|
98
|
+
return False
|
|
99
|
+
else:
|
|
100
|
+
raise
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def remove_task_from_scheduler(task_name: str) -> bool:
|
|
104
|
+
"""
|
|
105
|
+
This function will remove the task from the Windows Task Scheduler.
|
|
106
|
+
|
|
107
|
+
:param task_name: The name of the task to remove from the Task Scheduler.
|
|
108
|
+
:return: True if the task was removed successfully, False otherwise.
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
scheduler_instance = win32com.client.Dispatch('Schedule.Service')
|
|
112
|
+
scheduler_instance.Connect()
|
|
113
|
+
|
|
114
|
+
if not is_task_in_scheduler(task_name, scheduler_instance=scheduler_instance):
|
|
115
|
+
return False
|
|
116
|
+
else:
|
|
117
|
+
root_folder = scheduler_instance.GetFolder('\\')
|
|
118
|
+
root_folder.GetTask(task_name)
|
|
119
|
+
return True
|
|
@@ -2,8 +2,7 @@ from typing import Union
|
|
|
2
2
|
import threading
|
|
3
3
|
import multiprocessing.managers
|
|
4
4
|
|
|
5
|
-
from .
|
|
6
|
-
from . import system_resources
|
|
5
|
+
from . import system_resources, print_api
|
|
7
6
|
|
|
8
7
|
|
|
9
8
|
class SystemResourceMonitor:
|
|
@@ -109,66 +108,77 @@ class SystemResourceMonitor:
|
|
|
109
108
|
self.thread: Union[threading.Thread, None] = None
|
|
110
109
|
# Sets the running state of the monitoring process. Needed to stop the monitoring and queue threads.
|
|
111
110
|
self.running: bool = False
|
|
112
|
-
# The shared results dictionary.
|
|
111
|
+
# The shared results' dictionary.
|
|
113
112
|
self.results: dict = {}
|
|
114
113
|
|
|
115
|
-
def start(
|
|
114
|
+
def start(
|
|
115
|
+
self,
|
|
116
|
+
print_kwargs: dict = None,
|
|
117
|
+
thread_as_daemon: bool = True
|
|
118
|
+
):
|
|
116
119
|
"""
|
|
117
120
|
Start the monitoring process.
|
|
118
121
|
:param print_kwargs:
|
|
122
|
+
:param thread_as_daemon: bool, set the thread as daemon. If you're running the monitoring process in the main
|
|
123
|
+
process, set it to True. If you're running the monitoring process in a separate process, set it to False.
|
|
124
|
+
In child processes created by multiprocessing.Process, the thread works differently. You might not
|
|
125
|
+
get the desired result.
|
|
119
126
|
:return:
|
|
120
127
|
"""
|
|
121
128
|
|
|
122
|
-
def run_check_system_resources(
|
|
123
|
-
interval, get_cpu, get_memory, get_disk_io_bytes, get_disk_files_count, get_disk_busy_time,
|
|
124
|
-
get_disk_used_percent, calculate_maximum_changed_disk_io, maximum_disk_io, queue_list, manager_dict):
|
|
125
|
-
"""
|
|
126
|
-
Continuously update the system resources in the shared results dictionary.
|
|
127
|
-
This function runs in a separate process.
|
|
128
|
-
"""
|
|
129
|
-
|
|
130
|
-
while self.running:
|
|
131
|
-
# Get the results of the system resources check function and store them in temporary results dictionary.
|
|
132
|
-
results = system_resources.check_system_resources(
|
|
133
|
-
interval=interval, get_cpu=get_cpu, get_memory=get_memory,
|
|
134
|
-
get_disk_io_bytes=get_disk_io_bytes, get_disk_files_count=get_disk_files_count,
|
|
135
|
-
get_disk_busy_time=get_disk_busy_time, get_disk_used_percent=get_disk_used_percent)
|
|
136
|
-
|
|
137
|
-
if calculate_maximum_changed_disk_io:
|
|
138
|
-
if results['disk_io_read'] > maximum_disk_io['read_bytes_per_sec']:
|
|
139
|
-
maximum_disk_io['read_bytes_per_sec'] = results['disk_io_read']
|
|
140
|
-
if results['disk_io_write'] > maximum_disk_io['write_bytes_per_sec']:
|
|
141
|
-
maximum_disk_io['write_bytes_per_sec'] = results['disk_io_write']
|
|
142
|
-
if results['disk_files_count_read'] > maximum_disk_io['read_files_count_per_sec']:
|
|
143
|
-
maximum_disk_io['read_files_count_per_sec'] = results['disk_files_count_read']
|
|
144
|
-
if results['disk_files_count_write'] > maximum_disk_io['write_files_count_per_sec']:
|
|
145
|
-
maximum_disk_io['write_files_count_per_sec'] = results['disk_files_count_write']
|
|
146
|
-
results['maximum_disk_io'] = maximum_disk_io
|
|
147
|
-
|
|
148
|
-
if queue_list is not None:
|
|
149
|
-
for queue in queue_list:
|
|
150
|
-
queue.put(results)
|
|
151
|
-
|
|
152
|
-
# Update the shared results dictionary with the temporary results dictionary.
|
|
153
|
-
# This is done in separate steps to avoid overwriting the special 'multiprocessing.Manager.dict' object.
|
|
154
|
-
# So we update the shared results dictionary with the temporary results dictionary.
|
|
155
|
-
if manager_dict is not None:
|
|
156
|
-
manager_dict.update(results)
|
|
157
|
-
|
|
158
|
-
self.results = results
|
|
159
|
-
|
|
160
129
|
if print_kwargs is None:
|
|
161
130
|
print_kwargs = {}
|
|
162
131
|
|
|
163
132
|
if self.thread is None:
|
|
164
133
|
self.running = True
|
|
165
|
-
self.thread = threading.Thread(target=run_check_system_resources, args=(
|
|
134
|
+
self.thread = threading.Thread(target=self.run_check_system_resources, args=(
|
|
166
135
|
self.interval, self.get_cpu, self.get_memory, self.get_disk_io_bytes, self.get_disk_files_count,
|
|
167
136
|
self.get_disk_busy_time, self.get_disk_used_percent, self.calculate_maximum_changed_disk_io,
|
|
168
137
|
self.maximum_disk_io, self.queue_list, self.manager_dict))
|
|
138
|
+
self.thread.daemon = thread_as_daemon
|
|
169
139
|
self.thread.start()
|
|
170
140
|
else:
|
|
171
|
-
print_api("Monitoring is already running.", color='yellow', **print_kwargs)
|
|
141
|
+
print_api.print_api("Monitoring is already running.", color='yellow', **print_kwargs)
|
|
142
|
+
|
|
143
|
+
def run_check_system_resources(
|
|
144
|
+
self,
|
|
145
|
+
interval, get_cpu, get_memory, get_disk_io_bytes, get_disk_files_count, get_disk_busy_time,
|
|
146
|
+
get_disk_used_percent, calculate_maximum_changed_disk_io, maximum_disk_io, queue_list, manager_dict):
|
|
147
|
+
"""
|
|
148
|
+
Continuously update the system resources in the shared results dictionary.
|
|
149
|
+
This function runs in a separate process.
|
|
150
|
+
"""
|
|
151
|
+
|
|
152
|
+
while self.running:
|
|
153
|
+
# Get the results of the system resources check function and store them in
|
|
154
|
+
# temporary results' dictionary.
|
|
155
|
+
results = system_resources.check_system_resources(
|
|
156
|
+
interval=interval, get_cpu=get_cpu, get_memory=get_memory,
|
|
157
|
+
get_disk_io_bytes=get_disk_io_bytes, get_disk_files_count=get_disk_files_count,
|
|
158
|
+
get_disk_busy_time=get_disk_busy_time, get_disk_used_percent=get_disk_used_percent)
|
|
159
|
+
|
|
160
|
+
if calculate_maximum_changed_disk_io:
|
|
161
|
+
if results['disk_io_read'] > maximum_disk_io['read_bytes_per_sec']:
|
|
162
|
+
maximum_disk_io['read_bytes_per_sec'] = results['disk_io_read']
|
|
163
|
+
if results['disk_io_write'] > maximum_disk_io['write_bytes_per_sec']:
|
|
164
|
+
maximum_disk_io['write_bytes_per_sec'] = results['disk_io_write']
|
|
165
|
+
if results['disk_files_count_read'] > maximum_disk_io['read_files_count_per_sec']:
|
|
166
|
+
maximum_disk_io['read_files_count_per_sec'] = results['disk_files_count_read']
|
|
167
|
+
if results['disk_files_count_write'] > maximum_disk_io['write_files_count_per_sec']:
|
|
168
|
+
maximum_disk_io['write_files_count_per_sec'] = results['disk_files_count_write']
|
|
169
|
+
results['maximum_disk_io'] = maximum_disk_io
|
|
170
|
+
|
|
171
|
+
if queue_list is not None:
|
|
172
|
+
for queue in queue_list:
|
|
173
|
+
queue.put(results)
|
|
174
|
+
|
|
175
|
+
# Update the shared results dictionary with the temporary results' dictionary.
|
|
176
|
+
# This is done in separate steps to avoid overwriting the special 'multiprocessing.Manager.dict' object.
|
|
177
|
+
# So we update the shared results dictionary with the temporary results' dictionary.
|
|
178
|
+
if manager_dict is not None:
|
|
179
|
+
manager_dict.update(results)
|
|
180
|
+
|
|
181
|
+
self.results = results
|
|
172
182
|
|
|
173
183
|
def get_results(self) -> dict:
|
|
174
184
|
"""
|
|
@@ -203,6 +213,7 @@ def start_monitoring(
|
|
|
203
213
|
calculate_maximum_changed_disk_io: bool = False,
|
|
204
214
|
queue_list: list = None,
|
|
205
215
|
manager_dict: multiprocessing.managers.DictProxy = None, # multiprocessing.Manager().dict()
|
|
216
|
+
get_results_thread_as_daemon: bool = True,
|
|
206
217
|
print_kwargs: dict = None
|
|
207
218
|
):
|
|
208
219
|
"""
|
|
@@ -233,6 +244,10 @@ def start_monitoring(
|
|
|
233
244
|
multiprocessing.Process(
|
|
234
245
|
target=system_resource_monitor.start_monitoring, kwargs={'manager_dict': shared_dict}).start()
|
|
235
246
|
|
|
247
|
+
:param get_results_thread_as_daemon: bool, set the thread as daemon. If you're running the monitoring process in the
|
|
248
|
+
main process, set it to True. If you're running the monitoring process in a separate process, set it to False.
|
|
249
|
+
In child processes created by multiprocessing.Process, the thread works differently.
|
|
250
|
+
You might not get the desired result.
|
|
236
251
|
:param print_kwargs: dict, print kwargs.
|
|
237
252
|
:return:
|
|
238
253
|
"""
|
|
@@ -255,9 +270,9 @@ def start_monitoring(
|
|
|
255
270
|
queue_list=queue_list,
|
|
256
271
|
manager_dict=manager_dict
|
|
257
272
|
)
|
|
258
|
-
SYSTEM_RESOURCES_MONITOR.start()
|
|
273
|
+
SYSTEM_RESOURCES_MONITOR.start(thread_as_daemon=get_results_thread_as_daemon)
|
|
259
274
|
else:
|
|
260
|
-
print_api("System resources monitoring is already running.", color='yellow', **(print_kwargs or {}))
|
|
275
|
+
print_api.print_api("System resources monitoring is already running.", color='yellow', **(print_kwargs or {}))
|
|
261
276
|
|
|
262
277
|
|
|
263
278
|
def stop_monitoring():
|
atomicshop/system_resources.py
CHANGED
|
@@ -5,9 +5,8 @@ import shutil
|
|
|
5
5
|
import threading
|
|
6
6
|
import multiprocessing.managers
|
|
7
7
|
|
|
8
|
-
from .print_api import print_api
|
|
9
8
|
from .wrappers.psutilw import cpus, memories, disks
|
|
10
|
-
from . import system_resource_monitor
|
|
9
|
+
from . import system_resource_monitor, print_api
|
|
11
10
|
|
|
12
11
|
|
|
13
12
|
def check_system_resources(
|
|
@@ -129,13 +128,13 @@ def wait_for_resource_availability(
|
|
|
129
128
|
|
|
130
129
|
if result['cpu_usage'] < cpu_percent_max and result['memory_usage'] < memory_percent_max:
|
|
131
130
|
break
|
|
132
|
-
print_api(
|
|
131
|
+
print_api.print_api(
|
|
133
132
|
f"Waiting for resources to be available... "
|
|
134
133
|
f"CPU: {result['cpu_usage']}%, Memory: {result['memory_usage']}%", color='yellow')
|
|
135
134
|
time.sleep(wait_time) # Wait for 'wait_time' seconds before checking again
|
|
136
135
|
|
|
137
136
|
|
|
138
|
-
def
|
|
137
|
+
def _test_disk_speed_with_monitoring(
|
|
139
138
|
file_settings: list[dict],
|
|
140
139
|
remove_file_after_each_copy: bool = False,
|
|
141
140
|
target_directory=None,
|
|
@@ -144,6 +143,7 @@ def test_disk_speed_with_monitoring(
|
|
|
144
143
|
print_kwargs: dict = None
|
|
145
144
|
):
|
|
146
145
|
"""
|
|
146
|
+
THIS IS NOT TESTED.
|
|
147
147
|
Generates files and performs write and read operations in the specified target directory,
|
|
148
148
|
while monitoring disk I/O speeds in a separate thread. Returns the maximum read and write rates,
|
|
149
149
|
and the total operation time.
|
|
@@ -171,7 +171,7 @@ def test_disk_speed_with_monitoring(
|
|
|
171
171
|
if monitoring:
|
|
172
172
|
system_resource_monitor.start_monitoring(
|
|
173
173
|
interval=1, get_cpu=False, get_memory=False, get_disk_io_bytes=True, get_disk_used_percent=False,
|
|
174
|
-
get_disk_files_count=True, calculate_maximum_changed_disk_io=True
|
|
174
|
+
get_disk_files_count=True, calculate_maximum_changed_disk_io=True)
|
|
175
175
|
|
|
176
176
|
if target_directory is None:
|
|
177
177
|
target_directory = tempfile.mkdtemp()
|
|
@@ -196,7 +196,7 @@ def test_disk_speed_with_monitoring(
|
|
|
196
196
|
shutil.copy(src_file_path, dest_directory)
|
|
197
197
|
|
|
198
198
|
target_file_path = os.path.join(dest_directory, os.path.basename(src_file_path))
|
|
199
|
-
print_api(f"Copied: {target_file_path}", **(print_kwargs or {}))
|
|
199
|
+
print_api.print_api(f"Copied: {target_file_path}", **(print_kwargs or {}))
|
|
200
200
|
|
|
201
201
|
# Measure read speed.
|
|
202
202
|
with open(target_file_path, "rb") as file:
|
|
@@ -212,7 +212,7 @@ def test_disk_speed_with_monitoring(
|
|
|
212
212
|
|
|
213
213
|
overall_end_time = time.time()
|
|
214
214
|
total_execution_time = overall_end_time - overall_start_time
|
|
215
|
-
print_api(f"Total execution time: {total_execution_time}", **(print_kwargs or {}))
|
|
215
|
+
print_api.print_api(f"Total execution time: {total_execution_time}", **(print_kwargs or {}))
|
|
216
216
|
|
|
217
217
|
# Cleanup. Remove all created files and directories.
|
|
218
218
|
shutil.rmtree(source_directory)
|
|
@@ -220,7 +220,7 @@ def test_disk_speed_with_monitoring(
|
|
|
220
220
|
|
|
221
221
|
if monitoring:
|
|
222
222
|
# Stop the I/O monitoring.
|
|
223
|
-
max_io_changes = system_resource_monitor.
|
|
223
|
+
max_io_changes = system_resource_monitor.get_results()['maximum_disk_io']
|
|
224
224
|
system_resource_monitor.stop_monitoring()
|
|
225
225
|
|
|
226
226
|
return total_execution_time, max_io_changes
|
atomicshop/tempfiles.py
CHANGED
atomicshop/timer.py
CHANGED
|
@@ -7,7 +7,10 @@ class TimerError(Exception):
|
|
|
7
7
|
|
|
8
8
|
class Timer:
|
|
9
9
|
""" Custom timer class to measure elapsed time. Returns time in seconds (float) or nanoseconds with setting. """
|
|
10
|
-
def __init__(
|
|
10
|
+
def __init__(
|
|
11
|
+
self,
|
|
12
|
+
nanoseconds: bool = False
|
|
13
|
+
):
|
|
11
14
|
"""
|
|
12
15
|
Set up timer in seconds or nanoseconds
|
|
13
16
|
Seconds are returned as float, nanoseconds as int.
|
|
@@ -17,6 +20,9 @@ class Timer:
|
|
|
17
20
|
self._start_time = None
|
|
18
21
|
self._nanoseconds: bool = nanoseconds
|
|
19
22
|
|
|
23
|
+
self.running: bool = False
|
|
24
|
+
self.last_measure = None
|
|
25
|
+
|
|
20
26
|
def start(self):
|
|
21
27
|
"""Start a new timer"""
|
|
22
28
|
|
|
@@ -28,6 +34,8 @@ class Timer:
|
|
|
28
34
|
else:
|
|
29
35
|
self._start_time = time.perf_counter()
|
|
30
36
|
|
|
37
|
+
self.running = True
|
|
38
|
+
|
|
31
39
|
def restart(self):
|
|
32
40
|
"""Reset the timer"""
|
|
33
41
|
|
|
@@ -37,20 +45,31 @@ class Timer:
|
|
|
37
45
|
def measure(self):
|
|
38
46
|
"""Measure the elapsed time"""
|
|
39
47
|
|
|
40
|
-
if self._start_time is None:
|
|
41
|
-
|
|
48
|
+
# if self._start_time is None and self.last_measure is None:
|
|
49
|
+
# raise TimerError(f"Timer is not running. Use .start() to start it")
|
|
42
50
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
51
|
+
# If the timer is running, measure the elapsed time. If not, return the last measured time.
|
|
52
|
+
if self.running:
|
|
53
|
+
if self._nanoseconds:
|
|
54
|
+
self.last_measure = time.perf_counter_ns() - self._start_time
|
|
55
|
+
else:
|
|
56
|
+
self.last_measure = time.perf_counter() - self._start_time
|
|
47
57
|
|
|
48
|
-
return
|
|
58
|
+
return self.last_measure
|
|
59
|
+
|
|
60
|
+
def stop(self, measure: bool = True):
|
|
61
|
+
"""
|
|
62
|
+
Stop the timer, and report the elapsed time
|
|
63
|
+
|
|
64
|
+
:param measure: True to measure the elapsed time, False to stop the timer without measuring.
|
|
65
|
+
Measuring, means that the timer will return the elapsed time and 'self.last_measure' will be updated.
|
|
66
|
+
"""
|
|
49
67
|
|
|
50
|
-
|
|
51
|
-
|
|
68
|
+
elapsed_time = None
|
|
69
|
+
if measure:
|
|
70
|
+
elapsed_time = self.measure()
|
|
52
71
|
|
|
53
|
-
elapsed_time = self.measure()
|
|
54
72
|
self._start_time = None
|
|
73
|
+
self.running = False
|
|
55
74
|
|
|
56
75
|
return elapsed_time
|
atomicshop/urls.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from urllib.parse import urlparse
|
|
2
|
+
import re
|
|
2
3
|
|
|
3
4
|
|
|
4
5
|
def url_parser(url):
|
|
@@ -7,6 +8,11 @@ def url_parser(url):
|
|
|
7
8
|
directories = parts.path.strip('/').split('/')
|
|
8
9
|
queries = parts.query.strip('&').split('&')
|
|
9
10
|
|
|
11
|
+
if len(directories) > 1 and '.' in directories[-1]:
|
|
12
|
+
file = directories[-1]
|
|
13
|
+
else:
|
|
14
|
+
file = ''
|
|
15
|
+
|
|
10
16
|
elements = {
|
|
11
17
|
'scheme': parts.scheme,
|
|
12
18
|
'netloc': parts.netloc,
|
|
@@ -16,6 +22,41 @@ def url_parser(url):
|
|
|
16
22
|
'fragment': parts.fragment,
|
|
17
23
|
'directories': directories,
|
|
18
24
|
'queries': queries,
|
|
25
|
+
'file': file
|
|
19
26
|
}
|
|
20
27
|
|
|
21
28
|
return elements
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def is_valid_url(url):
|
|
32
|
+
"""
|
|
33
|
+
Check if a URL is valid.
|
|
34
|
+
:param url:
|
|
35
|
+
:return:
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
parsed = urlparse(url)
|
|
39
|
+
return all([parsed.scheme, parsed.netloc])
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def find_urls_in_text(text: str) -> list[str]:
|
|
43
|
+
"""
|
|
44
|
+
Find URLs in text
|
|
45
|
+
|
|
46
|
+
:param text: string, text to search for URLs.
|
|
47
|
+
:return: list of strings, URLs found in text.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
url_pattern = re.compile(
|
|
51
|
+
r'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\\(\\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+'
|
|
52
|
+
)
|
|
53
|
+
urls = url_pattern.findall(text)
|
|
54
|
+
|
|
55
|
+
# Filter URLs to remove common false positives
|
|
56
|
+
cleaned_urls = []
|
|
57
|
+
for u in urls:
|
|
58
|
+
cleaned_url = u.strip('",.:;!?)(')
|
|
59
|
+
if is_valid_url(cleaned_url):
|
|
60
|
+
cleaned_urls.append(cleaned_url)
|
|
61
|
+
|
|
62
|
+
return cleaned_urls
|
atomicshop/venvs.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import os
|
|
3
|
+
from typing import Union
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def is_running_venv() -> Union[str, None]:
|
|
7
|
+
"""
|
|
8
|
+
Check if the script is running in a virtual environment.
|
|
9
|
+
|
|
10
|
+
:return: string of the virtual environment path if it is running in a virtual environment, None otherwise.
|
|
11
|
+
"""
|
|
12
|
+
if hasattr(sys, 'real_prefix') or (hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix):
|
|
13
|
+
return sys.prefix
|
|
14
|
+
else:
|
|
15
|
+
return None
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def add_venv_to_path():
|
|
19
|
+
"""
|
|
20
|
+
Add the virtual environment to the PATH environment variable.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
venv_environment = is_running_venv()
|
|
24
|
+
if venv_environment:
|
|
25
|
+
# We're in a virtual environment, so modify the PATH
|
|
26
|
+
venv_bin = os.path.join(venv_environment, 'bin')
|
|
27
|
+
# Prepend the virtual environment's bin directory to the existing PATH
|
|
28
|
+
os.environ['PATH'] = f"{venv_bin}:{os.environ['PATH']}"
|
atomicshop/versioning.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from typing import Union
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def is_target_newer(
|
|
5
|
+
base_version: Union[str, tuple],
|
|
6
|
+
target_version: Union[str, tuple]
|
|
7
|
+
):
|
|
8
|
+
"""
|
|
9
|
+
Check if the target version is newer than the base version.
|
|
10
|
+
Example: is_target_newer('1.0.0', '1.0.1') -> True
|
|
11
|
+
Example: is_target_newer('1.0.0', '1.0.0') -> False
|
|
12
|
+
Example: is_target_newer('1.0.1', '1.0.0') -> False
|
|
13
|
+
Example: is_target_newer('1.0.1', '1.0.2') -> True
|
|
14
|
+
Example: is_target_newer((1,0,1), (1,1,0)) -> True
|
|
15
|
+
|
|
16
|
+
:param base_version: The base version to compare against.
|
|
17
|
+
:param target_version: The target version to compare.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
# Convert string to tuple if string was passed.
|
|
21
|
+
if isinstance(base_version, str):
|
|
22
|
+
base_version = tuple(map(int, base_version.split('.')))
|
|
23
|
+
if isinstance(target_version, str):
|
|
24
|
+
target_version = tuple(map(int, target_version.split('.')))
|
|
25
|
+
|
|
26
|
+
# Compare the versions.
|
|
27
|
+
return target_version > base_version
|