atomicshop 2.8.1__py3-none-any.whl → 2.9.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of atomicshop might be problematic. Click here for more details.
- atomicshop/__init__.py +1 -1
- atomicshop/print_api.py +2 -0
- atomicshop/system_resource_monitor.py +220 -0
- atomicshop/system_resources.py +131 -6
- atomicshop/wrappers/psutilw/disks.py +190 -3
- {atomicshop-2.8.1.dist-info → atomicshop-2.9.0.dist-info}/METADATA +1 -1
- {atomicshop-2.8.1.dist-info → atomicshop-2.9.0.dist-info}/RECORD +10 -9
- {atomicshop-2.8.1.dist-info → atomicshop-2.9.0.dist-info}/LICENSE.txt +0 -0
- {atomicshop-2.8.1.dist-info → atomicshop-2.9.0.dist-info}/WHEEL +0 -0
- {atomicshop-2.8.1.dist-info → atomicshop-2.9.0.dist-info}/top_level.txt +0 -0
atomicshop/__init__.py
CHANGED
atomicshop/print_api.py
CHANGED
|
@@ -39,6 +39,8 @@ def print_api(
|
|
|
39
39
|
|
|
40
40
|
Then, in the function, you can use it like this:
|
|
41
41
|
print_api(message, **print_kwargs)
|
|
42
|
+
# If 'print_kwargs' is 'None'.
|
|
43
|
+
print_api(message, **(print_kwargs or {}))
|
|
42
44
|
print_api(message, print_kwargs=print_kwargs)
|
|
43
45
|
print_api(message, print_kwargs={'logger': self.logger})
|
|
44
46
|
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
from typing import Union
|
|
2
|
+
import multiprocessing
|
|
3
|
+
|
|
4
|
+
from .print_api import print_api
|
|
5
|
+
from . import system_resources
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def run_check_system_resources(
|
|
9
|
+
interval, get_cpu, get_memory, get_disk_io, get_disk_used_percent, shared_results, queue=None):
|
|
10
|
+
"""
|
|
11
|
+
Continuously update the system resources in the shared results dictionary.
|
|
12
|
+
This function runs in a separate process.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
while True:
|
|
16
|
+
# Get the results of the system resources check function and store them in temporary results dictionary.
|
|
17
|
+
results = system_resources.check_system_resources(
|
|
18
|
+
interval=interval, get_cpu=get_cpu, get_memory=get_memory,
|
|
19
|
+
get_disk_io=get_disk_io,
|
|
20
|
+
get_disk_used_percent=get_disk_used_percent)
|
|
21
|
+
# Update the shared results dictionary with the temporary results dictionary.
|
|
22
|
+
# This is done in separate steps to avoid overwriting the special 'multiprocessing.Manager.dict' object.
|
|
23
|
+
# So we update the shared results dictionary with the temporary results dictionary.
|
|
24
|
+
shared_results.update(results)
|
|
25
|
+
|
|
26
|
+
if queue is not None:
|
|
27
|
+
queue.put(results)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class SystemResourceMonitor:
|
|
31
|
+
"""
|
|
32
|
+
A class to monitor system resources in a separate process.
|
|
33
|
+
"""
|
|
34
|
+
def __init__(
|
|
35
|
+
self,
|
|
36
|
+
interval: float = 1,
|
|
37
|
+
get_cpu: bool = True,
|
|
38
|
+
get_memory: bool = True,
|
|
39
|
+
get_disk_io: bool = True,
|
|
40
|
+
get_disk_used_percent: bool = True,
|
|
41
|
+
use_queue: bool = False
|
|
42
|
+
):
|
|
43
|
+
"""
|
|
44
|
+
Initialize the system resource monitor.
|
|
45
|
+
:param interval: float, the interval in seconds to check the system resources.
|
|
46
|
+
Default is 1 second.
|
|
47
|
+
:param get_cpu: bool, get the CPU usage.
|
|
48
|
+
:param get_memory: bool, get the memory usage.
|
|
49
|
+
:param get_disk_io: bool, get the disk I/O utilization.
|
|
50
|
+
:param get_disk_used_percent: bool, get the disk used percentage.
|
|
51
|
+
:param use_queue: bool, use queue to store results.
|
|
52
|
+
If you need ot get the queue, you can access it through the 'queue' attribute:
|
|
53
|
+
SystemResourceMonitor.queue
|
|
54
|
+
|
|
55
|
+
Example:
|
|
56
|
+
system_resource_monitor = SystemResourceMonitor()
|
|
57
|
+
your_queue = system_resource_monitor.queue
|
|
58
|
+
|
|
59
|
+
while True:
|
|
60
|
+
if not your_queue.empty():
|
|
61
|
+
results = your_queue.get()
|
|
62
|
+
print(results)
|
|
63
|
+
|
|
64
|
+
================
|
|
65
|
+
|
|
66
|
+
Usage Example with queue:
|
|
67
|
+
system_resource_monitor = SystemResourceMonitor(use_queue=True)
|
|
68
|
+
system_resource_monitor.start()
|
|
69
|
+
queue = system_resource_monitor.queue
|
|
70
|
+
while True:
|
|
71
|
+
if not queue.empty():
|
|
72
|
+
results = queue.get()
|
|
73
|
+
print(results)
|
|
74
|
+
|
|
75
|
+
================
|
|
76
|
+
|
|
77
|
+
Usage Example without queue:
|
|
78
|
+
interval = 1
|
|
79
|
+
system_resource_monitor = SystemResourceMonitor(interval=interval, use_queue=False)
|
|
80
|
+
system_resource_monitor.start()
|
|
81
|
+
while True:
|
|
82
|
+
time.sleep(interval)
|
|
83
|
+
results = system_resource_monitor.get_latest_results()
|
|
84
|
+
print(results)
|
|
85
|
+
"""
|
|
86
|
+
# Store parameters as instance attributes
|
|
87
|
+
self.interval = interval
|
|
88
|
+
self.get_cpu = get_cpu
|
|
89
|
+
self.get_memory = get_memory
|
|
90
|
+
self.get_disk_io = get_disk_io
|
|
91
|
+
self.get_disk_used_percent = get_disk_used_percent
|
|
92
|
+
|
|
93
|
+
self.manager = multiprocessing.Manager()
|
|
94
|
+
self.shared_results = self.manager.dict()
|
|
95
|
+
self.process = None
|
|
96
|
+
|
|
97
|
+
if use_queue:
|
|
98
|
+
self.queue = multiprocessing.Queue()
|
|
99
|
+
else:
|
|
100
|
+
self.queue = None
|
|
101
|
+
|
|
102
|
+
def start(self, print_kwargs: dict = None):
|
|
103
|
+
"""
|
|
104
|
+
Start the monitoring process.
|
|
105
|
+
:param print_kwargs:
|
|
106
|
+
:return:
|
|
107
|
+
"""
|
|
108
|
+
if print_kwargs is None:
|
|
109
|
+
print_kwargs = {}
|
|
110
|
+
|
|
111
|
+
if self.process is None or not self.process.is_alive():
|
|
112
|
+
self.process = multiprocessing.Process(target=run_check_system_resources, args=(
|
|
113
|
+
self.interval, self.get_cpu, self.get_memory, self.get_disk_io,
|
|
114
|
+
self.get_disk_used_percent, self.shared_results))
|
|
115
|
+
self.process.start()
|
|
116
|
+
else:
|
|
117
|
+
print_api("Monitoring process is already running.", color='yellow', **print_kwargs)
|
|
118
|
+
|
|
119
|
+
def get_latest_results(self) -> dict:
|
|
120
|
+
"""
|
|
121
|
+
Retrieve the latest results from the shared results dictionary.
|
|
122
|
+
"""
|
|
123
|
+
return dict(self.shared_results)
|
|
124
|
+
|
|
125
|
+
def stop(self):
|
|
126
|
+
"""
|
|
127
|
+
Stop the monitoring process.
|
|
128
|
+
"""
|
|
129
|
+
if self.process is not None:
|
|
130
|
+
self.process.terminate()
|
|
131
|
+
self.process.join()
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
# === END OF SYSTEM RESOURCE MONITOR. ==================================================================================
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
SYSTEM_RESOURCES_MONITOR: Union[SystemResourceMonitor, None] = None
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def start_system_resources_monitoring(
|
|
141
|
+
interval: float = 1,
|
|
142
|
+
get_cpu: bool = True,
|
|
143
|
+
get_memory: bool = True,
|
|
144
|
+
get_disk_io: bool = True,
|
|
145
|
+
get_disk_used_percent: bool = True,
|
|
146
|
+
print_kwargs: dict = None
|
|
147
|
+
):
|
|
148
|
+
"""
|
|
149
|
+
Start monitoring system resources.
|
|
150
|
+
:param interval: float, interval in seconds.
|
|
151
|
+
:param get_cpu: bool, get CPU usage.
|
|
152
|
+
:param get_memory: bool, get memory usage.
|
|
153
|
+
:param get_disk_io: bool, get TOTAL disk I/O utilization in bytes/s.
|
|
154
|
+
:param get_disk_used_percent: bool, get TOTAL disk used percentage.
|
|
155
|
+
:param print_kwargs: dict, print kwargs.
|
|
156
|
+
:return: SystemResourceMonitor
|
|
157
|
+
"""
|
|
158
|
+
|
|
159
|
+
# if print_kwargs is None:
|
|
160
|
+
# print_kwargs = {}
|
|
161
|
+
|
|
162
|
+
global SYSTEM_RESOURCES_MONITOR
|
|
163
|
+
|
|
164
|
+
if not SYSTEM_RESOURCES_MONITOR:
|
|
165
|
+
SYSTEM_RESOURCES_MONITOR = SystemResourceMonitor(
|
|
166
|
+
interval=interval,
|
|
167
|
+
get_cpu=get_cpu,
|
|
168
|
+
get_memory=get_memory,
|
|
169
|
+
get_disk_io=get_disk_io,
|
|
170
|
+
get_disk_used_percent=get_disk_used_percent
|
|
171
|
+
)
|
|
172
|
+
SYSTEM_RESOURCES_MONITOR.start()
|
|
173
|
+
else:
|
|
174
|
+
print_api("System resources monitoring is already running.", color='yellow', **(print_kwargs or {}))
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def stop_system_resources_monitoring():
|
|
178
|
+
"""
|
|
179
|
+
Stop monitoring system resources.
|
|
180
|
+
:return: None
|
|
181
|
+
"""
|
|
182
|
+
global SYSTEM_RESOURCES_MONITOR
|
|
183
|
+
if SYSTEM_RESOURCES_MONITOR is not None:
|
|
184
|
+
SYSTEM_RESOURCES_MONITOR.stop()
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def get_system_resources_monitoring_instance() -> SystemResourceMonitor:
|
|
188
|
+
"""
|
|
189
|
+
Get the system resources monitoring instance.
|
|
190
|
+
:return: SystemResourceMonitor
|
|
191
|
+
"""
|
|
192
|
+
global SYSTEM_RESOURCES_MONITOR
|
|
193
|
+
return SYSTEM_RESOURCES_MONITOR
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def get_system_resources_monitoring_result():
|
|
197
|
+
"""
|
|
198
|
+
Get system resources monitoring result.
|
|
199
|
+
|
|
200
|
+
Usage Example:
|
|
201
|
+
system_resources.start_system_resources_monitoring()
|
|
202
|
+
|
|
203
|
+
while True:
|
|
204
|
+
time.sleep(1)
|
|
205
|
+
result = system_resources.get_system_resources_monitoring_result()
|
|
206
|
+
|
|
207
|
+
if result:
|
|
208
|
+
print(
|
|
209
|
+
f"{str(result['cpu_usage'])} | {str(result['memory_usage'])} | "
|
|
210
|
+
f"{str(result['disk_io_read'])} | {str(result['disk_io_write'])} | "
|
|
211
|
+
f"{str(result['disk_used_percent'])}"
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
:return: dict
|
|
215
|
+
"""
|
|
216
|
+
global SYSTEM_RESOURCES_MONITOR
|
|
217
|
+
if SYSTEM_RESOURCES_MONITOR is not None:
|
|
218
|
+
return SYSTEM_RESOURCES_MONITOR.get_latest_results()
|
|
219
|
+
else:
|
|
220
|
+
return {}
|
atomicshop/system_resources.py
CHANGED
|
@@ -1,15 +1,72 @@
|
|
|
1
|
-
import
|
|
1
|
+
import os
|
|
2
|
+
import shutil
|
|
3
|
+
import tempfile
|
|
2
4
|
import time
|
|
5
|
+
import threading
|
|
3
6
|
|
|
4
7
|
from .print_api import print_api
|
|
5
8
|
from .wrappers.psutilw import cpus, memories, disks
|
|
6
9
|
|
|
7
10
|
|
|
8
|
-
def check_system_resources(
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
def check_system_resources(
|
|
12
|
+
interval: float = 1,
|
|
13
|
+
get_cpu: bool = True,
|
|
14
|
+
get_memory: bool = True,
|
|
15
|
+
get_disk_io: bool = True,
|
|
16
|
+
get_disk_used_percent: bool = True
|
|
17
|
+
):
|
|
18
|
+
"""
|
|
19
|
+
Check system resources.
|
|
20
|
+
:param interval: float, interval in seconds.
|
|
21
|
+
:param get_cpu: bool, get CPU usage.
|
|
22
|
+
:param get_memory: bool, get memory usage.
|
|
23
|
+
:param get_disk_io: bool, get TOTAL disk I/O utilization in bytes/s.
|
|
24
|
+
:param get_disk_used_percent: bool, get TOTAL disk used percentage.
|
|
25
|
+
:return:
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
threads: list = []
|
|
29
|
+
result: dict = {
|
|
30
|
+
'cpu_usage': None,
|
|
31
|
+
'memory_usage': None,
|
|
32
|
+
'disk_io_write': None,
|
|
33
|
+
'disk_io_read': None,
|
|
34
|
+
'disk_used_percent': None
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
def set_cpu_usage():
|
|
38
|
+
result['cpu_usage'] = cpus.get_cpu_usage(interval=interval)
|
|
39
|
+
|
|
40
|
+
def set_memory_usage():
|
|
41
|
+
result['memory_usage'] = memories.get_memory_usage()
|
|
42
|
+
|
|
43
|
+
def set_disk_io_utilization():
|
|
44
|
+
aggregated_disk_io_utilization = disks.get_disk_io_utilization(interval=interval)['aggregated']
|
|
45
|
+
result['disk_io_read'] = aggregated_disk_io_utilization['read_change_per_sec']
|
|
46
|
+
result['disk_io_write'] = aggregated_disk_io_utilization['write_change_per_sec']
|
|
47
|
+
|
|
48
|
+
def set_disk_used_percent():
|
|
49
|
+
result['disk_used_percent'] = disks.get_disk_usage()['total'].percent
|
|
50
|
+
|
|
51
|
+
# Create threads for each system resource check.
|
|
52
|
+
if get_cpu:
|
|
53
|
+
threads.append(threading.Thread(target=set_cpu_usage))
|
|
54
|
+
if get_memory:
|
|
55
|
+
threads.append(threading.Thread(target=set_memory_usage))
|
|
56
|
+
if get_disk_io:
|
|
57
|
+
threads.append(threading.Thread(target=set_disk_io_utilization))
|
|
58
|
+
if get_disk_used_percent:
|
|
59
|
+
threads.append(threading.Thread(target=set_disk_used_percent))
|
|
60
|
+
|
|
61
|
+
# Start threads.
|
|
62
|
+
for thread in threads:
|
|
63
|
+
thread.start()
|
|
64
|
+
|
|
65
|
+
# Wait for all threads to complete.
|
|
66
|
+
for thread in threads:
|
|
67
|
+
thread.join()
|
|
68
|
+
|
|
69
|
+
return result
|
|
13
70
|
|
|
14
71
|
|
|
15
72
|
def wait_for_resource_availability(cpu_percent_max: int = 80, memory_percent_max: int = 80, wait_time: float = 5):
|
|
@@ -26,3 +83,71 @@ def wait_for_resource_availability(cpu_percent_max: int = 80, memory_percent_max
|
|
|
26
83
|
break
|
|
27
84
|
print_api(f"Waiting for resources to be available... CPU: {cpu}%, Memory: {memory}%", color='yellow')
|
|
28
85
|
time.sleep(wait_time) # Wait for 'wait_time' seconds before checking again
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def test_disk_speed(file_size_bytes, file_count, remove_file_after_each_copy: bool = True, target_directory=None):
|
|
89
|
+
"""
|
|
90
|
+
Tests disk write and read speeds by generating files in a 'source' directory, copying them to a 'dest' directory
|
|
91
|
+
within the target directory, and optionally removing them after each copy. Now also returns the total execution time.
|
|
92
|
+
|
|
93
|
+
:param file_size_bytes: Size of each file in bytes.
|
|
94
|
+
:param file_count: Number of files to generate and copy.
|
|
95
|
+
:param remove_file_after_each_copy: Whether to remove the file after copying to target directory.
|
|
96
|
+
:param target_directory: Directory where files will be copied. Uses a temporary directory if None.
|
|
97
|
+
:return: Tuple of peak write speed, peak read speed, and total execution time in seconds.
|
|
98
|
+
"""
|
|
99
|
+
if target_directory is None:
|
|
100
|
+
target_directory = tempfile.mkdtemp()
|
|
101
|
+
else:
|
|
102
|
+
os.makedirs(target_directory, exist_ok=True)
|
|
103
|
+
|
|
104
|
+
source_directory = os.path.join(target_directory, 'source')
|
|
105
|
+
dest_directory = os.path.join(target_directory, 'dest')
|
|
106
|
+
os.makedirs(source_directory, exist_ok=True)
|
|
107
|
+
os.makedirs(dest_directory, exist_ok=True)
|
|
108
|
+
|
|
109
|
+
write_speeds = []
|
|
110
|
+
read_speeds = []
|
|
111
|
+
created_files = [] # Keep track of all created files for cleanup
|
|
112
|
+
|
|
113
|
+
overall_start_time = time.time() # Start timing the entire operation
|
|
114
|
+
|
|
115
|
+
for i in range(file_count):
|
|
116
|
+
# Generate file in source directory
|
|
117
|
+
src_file_path = os.path.join(source_directory, f"tempfile_{i}")
|
|
118
|
+
with open(src_file_path, "wb") as file:
|
|
119
|
+
file.write(os.urandom(file_size_bytes))
|
|
120
|
+
created_files.append(src_file_path) # Add the file for cleanup
|
|
121
|
+
|
|
122
|
+
# Measure write speed
|
|
123
|
+
start_time = time.time()
|
|
124
|
+
shutil.copy(src_file_path, dest_directory)
|
|
125
|
+
end_time = time.time()
|
|
126
|
+
write_speeds.append(file_size_bytes / (end_time - start_time))
|
|
127
|
+
|
|
128
|
+
target_file_path = os.path.join(dest_directory, os.path.basename(src_file_path))
|
|
129
|
+
|
|
130
|
+
# Measure read speed
|
|
131
|
+
with open(target_file_path, "rb") as file:
|
|
132
|
+
start_time = time.time()
|
|
133
|
+
while file.read(1024 * 1024): # Read in chunks of 1 MB
|
|
134
|
+
pass
|
|
135
|
+
end_time = time.time()
|
|
136
|
+
read_speeds.append(file_size_bytes / (end_time - start_time))
|
|
137
|
+
|
|
138
|
+
if remove_file_after_each_copy:
|
|
139
|
+
os.remove(target_file_path)
|
|
140
|
+
os.remove(src_file_path)
|
|
141
|
+
|
|
142
|
+
overall_end_time = time.time()
|
|
143
|
+
|
|
144
|
+
# Calculate peak speeds in Bytes/s and total execution time
|
|
145
|
+
peak_write_speed = max(write_speeds)
|
|
146
|
+
peak_read_speed = max(read_speeds)
|
|
147
|
+
total_execution_time = overall_end_time - overall_start_time
|
|
148
|
+
|
|
149
|
+
# Cleanup. Remove all created files and directories.
|
|
150
|
+
shutil.rmtree(source_directory)
|
|
151
|
+
shutil.rmtree(dest_directory)
|
|
152
|
+
|
|
153
|
+
return peak_write_speed, peak_read_speed, total_execution_time
|
|
@@ -1,5 +1,12 @@
|
|
|
1
|
-
import
|
|
1
|
+
import os
|
|
2
2
|
import time
|
|
3
|
+
import tempfile
|
|
4
|
+
import shutil
|
|
5
|
+
import threading
|
|
6
|
+
|
|
7
|
+
import psutil
|
|
8
|
+
|
|
9
|
+
from ... import system_resources
|
|
3
10
|
|
|
4
11
|
|
|
5
12
|
def get_disk_io_utilization(
|
|
@@ -10,6 +17,9 @@ def get_disk_io_utilization(
|
|
|
10
17
|
) -> dict:
|
|
11
18
|
"""
|
|
12
19
|
Get disk utilization based on disk I/O changes, allowing for both aggregated and separated values.
|
|
20
|
+
Windows: because 'psutil.disk_io_counters' before using this function, you may need to execute the following
|
|
21
|
+
command in the command prompt:
|
|
22
|
+
diskperf -y
|
|
13
23
|
|
|
14
24
|
:param interval: Sampling interval in seconds to measure I/O changes.
|
|
15
25
|
:param disk_list: List of disks to measure. If None, measure all disks. Affects only when separated is True.
|
|
@@ -41,7 +51,7 @@ def get_disk_io_utilization(
|
|
|
41
51
|
'read_change_bytes': read_change,
|
|
42
52
|
'write_change_bytes': write_change,
|
|
43
53
|
'read_change_per_sec': read_change_per_sec,
|
|
44
|
-
'write_change_per_sec': write_change_per_sec
|
|
54
|
+
'write_change_per_sec': write_change_per_sec,
|
|
45
55
|
}
|
|
46
56
|
|
|
47
57
|
if aggregated:
|
|
@@ -55,7 +65,184 @@ def get_disk_io_utilization(
|
|
|
55
65
|
'read_change_bytes': total_read_change,
|
|
56
66
|
'write_change_bytes': total_write_change,
|
|
57
67
|
'read_change_per_sec': total_read_change_per_sec,
|
|
58
|
-
'write_change_per_sec': total_write_change_per_sec
|
|
68
|
+
'write_change_per_sec': total_write_change_per_sec,
|
|
59
69
|
}
|
|
60
70
|
|
|
61
71
|
return io_change
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _get_disk_busy_time(
|
|
75
|
+
interval: float = 1,
|
|
76
|
+
disk_list: list = None,
|
|
77
|
+
aggregated: bool = True,
|
|
78
|
+
separated: bool = False
|
|
79
|
+
) -> dict:
|
|
80
|
+
"""
|
|
81
|
+
!!! For some reason on Windows it gets the count of files read or written and not the time in ms.
|
|
82
|
+
|
|
83
|
+
Get disk busy time, allowing for both aggregated and separated values.
|
|
84
|
+
Windows: because 'psutil.disk_io_counters' before using this function, you may need to execute the following
|
|
85
|
+
command in the command prompt:
|
|
86
|
+
diskperf -y
|
|
87
|
+
|
|
88
|
+
:param interval: Sampling interval in seconds to measure I/O changes.
|
|
89
|
+
:param disk_list: List of disks to measure. If None, measure all disks. Affects only when separated is True.
|
|
90
|
+
:param aggregated: Boolean indicating whether to return aggregated utilization.
|
|
91
|
+
:param separated: Boolean indicating whether to return separate utilizations for each disk.
|
|
92
|
+
:return: Disk utilization data.
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
io_start_aggregated = None
|
|
96
|
+
io_end_aggregated = None
|
|
97
|
+
|
|
98
|
+
io_start_separated = psutil.disk_io_counters(perdisk=True)
|
|
99
|
+
if aggregated:
|
|
100
|
+
io_start_aggregated = psutil.disk_io_counters(perdisk=False)
|
|
101
|
+
time.sleep(interval)
|
|
102
|
+
io_end_separated = psutil.disk_io_counters(perdisk=True)
|
|
103
|
+
if aggregated:
|
|
104
|
+
io_end_aggregated = psutil.disk_io_counters(perdisk=False)
|
|
105
|
+
|
|
106
|
+
busy_time = {}
|
|
107
|
+
if separated:
|
|
108
|
+
for disk in io_start_separated.keys():
|
|
109
|
+
if disk_list is None or disk in disk_list:
|
|
110
|
+
read_time = io_end_separated[disk].read_time - io_start_separated[disk].read_time
|
|
111
|
+
write_time = io_end_separated[disk].write_time - io_start_separated[disk].write_time
|
|
112
|
+
read_time_per_sec = read_time / interval
|
|
113
|
+
write_time_per_sec = write_time / interval
|
|
114
|
+
busy_time[disk] = {
|
|
115
|
+
'read_time_ms': read_time,
|
|
116
|
+
'write_time_ms': write_time,
|
|
117
|
+
'read_time_per_sec': read_time_per_sec,
|
|
118
|
+
'write_time_per_sec': write_time_per_sec,
|
|
119
|
+
'busy_time': read_time + write_time,
|
|
120
|
+
'busy_time_per_sec': read_time_per_sec + write_time_per_sec,
|
|
121
|
+
'busy_time_percent': (read_time + write_time) / interval
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if aggregated:
|
|
125
|
+
if not io_start_aggregated or not io_end_aggregated:
|
|
126
|
+
raise ValueError('Aggregated disk I/O counters are not available.')
|
|
127
|
+
# total_read_time = io_end_aggregated.read_time - io_start_aggregated.read_time
|
|
128
|
+
# total_write_time = io_end_aggregated.write_time - io_start_aggregated.write_time
|
|
129
|
+
total_read_time = io_end_aggregated.read_time
|
|
130
|
+
total_write_time = io_end_aggregated.write_time
|
|
131
|
+
total_read_time_per_sec = total_read_time / interval
|
|
132
|
+
total_write_time_per_sec = total_write_time / interval
|
|
133
|
+
busy_time['aggregated'] = {
|
|
134
|
+
'read_time_ms': total_read_time,
|
|
135
|
+
'write_time_ms': total_write_time,
|
|
136
|
+
'read_time_per_sec': total_read_time_per_sec,
|
|
137
|
+
'write_time_per_sec': total_write_time_per_sec,
|
|
138
|
+
'busy_time': total_read_time + total_write_time,
|
|
139
|
+
'busy_time_per_sec': total_read_time_per_sec + total_write_time_per_sec,
|
|
140
|
+
'busy_time_percent': (total_read_time + total_write_time) / interval
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return busy_time
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def get_disk_usage(disk_list: list = None) -> dict:
|
|
147
|
+
"""
|
|
148
|
+
Get the usage statistics of disks.
|
|
149
|
+
|
|
150
|
+
:param disk_list: List of disks to measure. If None, measure all disks.
|
|
151
|
+
:return:
|
|
152
|
+
"""
|
|
153
|
+
|
|
154
|
+
disk_usage: dict = {}
|
|
155
|
+
for disk in psutil.disk_partitions():
|
|
156
|
+
if disk_list is None or disk.device in disk_list:
|
|
157
|
+
try:
|
|
158
|
+
disk_usage[disk.device] = psutil.disk_usage(disk.mountpoint)
|
|
159
|
+
except PermissionError as e:
|
|
160
|
+
disk_usage[disk.device] = str(e)
|
|
161
|
+
|
|
162
|
+
# Get total disk usage.
|
|
163
|
+
disk_usage['total'] = psutil.disk_usage('/')
|
|
164
|
+
|
|
165
|
+
return disk_usage
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def test_disk_speed_with_monitoring(
|
|
169
|
+
file_size_bytes: int, file_count: int, remove_file_after_each_copy: bool = False, target_directory=None):
|
|
170
|
+
"""
|
|
171
|
+
Generates files and performs write and read operations in the specified target directory,
|
|
172
|
+
while monitoring disk I/O speeds in a separate thread. Returns the maximum read and write rates,
|
|
173
|
+
and the total operation time.
|
|
174
|
+
|
|
175
|
+
:param file_size_bytes: Size of each file in bytes.
|
|
176
|
+
:param file_count: Number of files to generate and copy.
|
|
177
|
+
:param remove_file_after_each_copy: Whether to remove the file after copying to target directory.
|
|
178
|
+
:param target_directory: Directory where files will be copied. Uses a temporary directory if None.
|
|
179
|
+
:return: A tuple containing the maximum write speed, maximum read speed, and total operation time in seconds.
|
|
180
|
+
"""
|
|
181
|
+
io_speeds = {'write_speed': [], 'read_speed': []}
|
|
182
|
+
stop_thread = False
|
|
183
|
+
|
|
184
|
+
def io_monitor():
|
|
185
|
+
"""
|
|
186
|
+
Monitors disk I/O speed by checking psutil disk_io_counters every second and calculates the speed.
|
|
187
|
+
This function is intended to run in a separate thread.
|
|
188
|
+
"""
|
|
189
|
+
|
|
190
|
+
while not stop_thread:
|
|
191
|
+
result = system_resources.check_system_resources(
|
|
192
|
+
interval=1, get_disk_io=True, get_cpu=False, get_memory=False, get_disk_used_percent=False)
|
|
193
|
+
|
|
194
|
+
io_speeds['write_speed'].append(result['disk_io_write'])
|
|
195
|
+
io_speeds['read_speed'].append(result['disk_io_read'])
|
|
196
|
+
|
|
197
|
+
# Start the I/O monitoring thread
|
|
198
|
+
monitor_thread = threading.Thread(target=io_monitor)
|
|
199
|
+
monitor_thread.start()
|
|
200
|
+
|
|
201
|
+
if target_directory is None:
|
|
202
|
+
target_directory = tempfile.mkdtemp()
|
|
203
|
+
else:
|
|
204
|
+
os.makedirs(target_directory, exist_ok=True)
|
|
205
|
+
|
|
206
|
+
source_directory = os.path.join(target_directory, 'source')
|
|
207
|
+
dest_directory = os.path.join(target_directory, 'dest')
|
|
208
|
+
os.makedirs(source_directory, exist_ok=True)
|
|
209
|
+
os.makedirs(dest_directory, exist_ok=True)
|
|
210
|
+
|
|
211
|
+
write_speeds = []
|
|
212
|
+
read_speeds = []
|
|
213
|
+
created_files = [] # Keep track of all created files for cleanup
|
|
214
|
+
|
|
215
|
+
overall_start_time = time.time() # Start timing the entire operation
|
|
216
|
+
|
|
217
|
+
for i in range(file_count):
|
|
218
|
+
# Generate file in source directory
|
|
219
|
+
src_file_path = os.path.join(source_directory, f"tempfile_{i}")
|
|
220
|
+
with open(src_file_path, "wb") as file:
|
|
221
|
+
file.write(os.urandom(file_size_bytes))
|
|
222
|
+
created_files.append(src_file_path) # Add the file for cleanup
|
|
223
|
+
|
|
224
|
+
shutil.copy(src_file_path, dest_directory)
|
|
225
|
+
target_file_path = os.path.join(dest_directory, os.path.basename(src_file_path))
|
|
226
|
+
|
|
227
|
+
# Measure read speed
|
|
228
|
+
with open(target_file_path, "rb") as file:
|
|
229
|
+
start_time = time.time()
|
|
230
|
+
while file.read(1024 * 1024): # Read in chunks of 1 MB
|
|
231
|
+
pass
|
|
232
|
+
end_time = time.time()
|
|
233
|
+
read_speeds.append(file_size_bytes / (end_time - start_time))
|
|
234
|
+
|
|
235
|
+
if remove_file_after_each_copy:
|
|
236
|
+
os.remove(target_file_path)
|
|
237
|
+
os.remove(src_file_path)
|
|
238
|
+
|
|
239
|
+
overall_end_time = time.time()
|
|
240
|
+
|
|
241
|
+
stop_thread = True
|
|
242
|
+
monitor_thread.join()
|
|
243
|
+
|
|
244
|
+
# Determine maximum speeds
|
|
245
|
+
max_write_speed = max(io_speeds['write_speed']) if io_speeds['write_speed'] else 0
|
|
246
|
+
max_read_speed = max(io_speeds['read_speed']) if io_speeds['read_speed'] else 0
|
|
247
|
+
|
|
248
|
+
return max_write_speed, max_read_speed, total_operation_time
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
atomicshop/__init__.py,sha256=
|
|
1
|
+
atomicshop/__init__.py,sha256=IbBAx01GvuncS_hxpSRIAExpJW75V-EhMbbVnwzyzVo,122
|
|
2
2
|
atomicshop/_basics_temp.py,sha256=6cu2dd6r2dLrd1BRNcVDKTHlsHs_26Gpw8QS6v32lQ0,3699
|
|
3
3
|
atomicshop/_patch_import.py,sha256=ENp55sKVJ0e6-4lBvZnpz9PQCt3Otbur7F6aXDlyje4,6334
|
|
4
4
|
atomicshop/appointment_management.py,sha256=N3wVGJgrqJfsj_lqiRfaL3FxMEe57by5Stzanh189mk,7263
|
|
@@ -22,7 +22,7 @@ atomicshop/ip_addresses.py,sha256=Hvi4TumEFoTEpKWaq5WNF-YzcRzt24IxmNgv-Mgax1s,11
|
|
|
22
22
|
atomicshop/keyboard_press.py,sha256=1W5kRtOB75fulVx-uF2yarBhW0_IzdI1k73AnvXstk0,452
|
|
23
23
|
atomicshop/pbtkmultifile_argparse.py,sha256=aEk8nhvoQVu-xyfZosK3ma17CwIgOjzO1erXXdjwtS4,4574
|
|
24
24
|
atomicshop/permissions.py,sha256=GYWxm9niuJWY0pWCnASF34_HiTuvhTamn8BCgcFyVAk,2540
|
|
25
|
-
atomicshop/print_api.py,sha256=
|
|
25
|
+
atomicshop/print_api.py,sha256=DhbCQd0MWZZ5GYEk4oTu1opRFC-b31g1VWZgTGewG2Y,11568
|
|
26
26
|
atomicshop/process.py,sha256=5lqepm1psRbz66J4MB43ou6HHyac9F34QJedGukwAnk,14188
|
|
27
27
|
atomicshop/process_name_cmd.py,sha256=TNAK6kQZm5JKWzEW6QLqVHEG98ZLNDQiSS4YwDk8V8c,3830
|
|
28
28
|
atomicshop/process_poller.py,sha256=WfmwCLALfTYOq8ri0vkPeqq8ruEyA_43DaN--CU2_XY,10854
|
|
@@ -36,7 +36,8 @@ atomicshop/sound.py,sha256=KSzWRF8dkpEVXmFidIv-Eftc3kET-hQzQOxZRE7rMto,24297
|
|
|
36
36
|
atomicshop/speech_recognize.py,sha256=55-dIjgkpF93mvJnJuxSFuft5H5eRvGNlUj9BeIOZxk,5903
|
|
37
37
|
atomicshop/ssh_remote.py,sha256=Sas3nrQv8ardxR51t59xZZsYm8nvUcA7tMSqEDViRLk,17155
|
|
38
38
|
atomicshop/sys_functions.py,sha256=MTBxRve5bh58SPvhX3gMiGqHlSBuI_rdNN1NnnBBWqI,906
|
|
39
|
-
atomicshop/
|
|
39
|
+
atomicshop/system_resource_monitor.py,sha256=SF376RE952sYbZJxVqHt32Bek0zMFxBVDocbUfon6-E,7538
|
|
40
|
+
atomicshop/system_resources.py,sha256=y5SjM87k3h4mffvBHn6P4DqNiMmLjtdgUb6gkZU_p0U,5882
|
|
40
41
|
atomicshop/tempfiles.py,sha256=uq1ve2WlWehZ3NOTXJnpBBMt6HyCdBufqedF0HyzA6k,2517
|
|
41
42
|
atomicshop/timer.py,sha256=KxBBgVM8po6pUJDW8TgY1UXj0iiDmRmL5XDCq0VHAfU,1670
|
|
42
43
|
atomicshop/urls.py,sha256=CQl1j1kjEVDlAuYJqYD9XxPF1SUSgrmG8PjlcXNEKsQ,597
|
|
@@ -204,7 +205,7 @@ atomicshop/wrappers/playwrightw/mouse.py,sha256=-2FZbQtjgH7tdXWld6ZPGqlKFUdf5in0
|
|
|
204
205
|
atomicshop/wrappers/playwrightw/scenarios.py,sha256=Wz7aVYfG7K4fuSe_TUAc1jhFXVq5jYvZKbDtvqUiONc,5236
|
|
205
206
|
atomicshop/wrappers/playwrightw/waits.py,sha256=308fdOu6YDqQ7K7xywj7R27sSmFanPBQqpZyBC-NFmo,7015
|
|
206
207
|
atomicshop/wrappers/psutilw/cpus.py,sha256=w6LPBMINqS-T_X8vzdYkLS2Wzuve28Ydp_GafTCngrc,236
|
|
207
|
-
atomicshop/wrappers/psutilw/disks.py,sha256=
|
|
208
|
+
atomicshop/wrappers/psutilw/disks.py,sha256=AfeQIi-ugZIzAd7dmkMwFku1rb6Yl0Uzt8dFYvJMRnI,10490
|
|
208
209
|
atomicshop/wrappers/psutilw/memories.py,sha256=wpdKEkQ9Wty_r7ZJKkfli7wIHMXdQOMlmDlzmc_0FWo,161
|
|
209
210
|
atomicshop/wrappers/psutilw/psutilw.py,sha256=W9PSEZmrm_Ct_-6oKqAcbgbyF21CwcIbbHOkVqgMiow,20866
|
|
210
211
|
atomicshop/wrappers/psycopgw/psycopgw.py,sha256=XJvVf0oAUjCHkrYfKeFuGCpfn0Oxj3u4SbKMKA1508E,7118
|
|
@@ -225,8 +226,8 @@ atomicshop/wrappers/socketw/socket_server_tester.py,sha256=AhpurHJmP2kgzHaUbq5ey
|
|
|
225
226
|
atomicshop/wrappers/socketw/socket_wrapper.py,sha256=aXBwlEIJhFT0-c4i8iNlFx2It9VpCEpsv--5Oqcpxao,11624
|
|
226
227
|
atomicshop/wrappers/socketw/ssl_base.py,sha256=k4V3gwkbq10MvOH4btU4onLX2GNOsSfUAdcHmL1rpVE,2274
|
|
227
228
|
atomicshop/wrappers/socketw/statistics_csv.py,sha256=t3dtDEfN47CfYVi0CW6Kc2QHTEeZVyYhc57IYYh5nmA,826
|
|
228
|
-
atomicshop-2.
|
|
229
|
-
atomicshop-2.
|
|
230
|
-
atomicshop-2.
|
|
231
|
-
atomicshop-2.
|
|
232
|
-
atomicshop-2.
|
|
229
|
+
atomicshop-2.9.0.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
|
|
230
|
+
atomicshop-2.9.0.dist-info/METADATA,sha256=6vLhMQYgyIHS0NE4S8s-m8cpEaK4gm-8chZ9LnUpZgk,10369
|
|
231
|
+
atomicshop-2.9.0.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
|
|
232
|
+
atomicshop-2.9.0.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
|
|
233
|
+
atomicshop-2.9.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|