pytree2 0.1.4__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.
- pytree/__init__.py +1 -0
- pytree/classes/ProgressTracker.py +514 -0
- pytree/classes/PyTree.py +741 -0
- pytree/classes/__init__.py +1 -0
- pytree/main.py +186 -0
- pytree/utils/__init__.py +1 -0
- pytree/utils/aux_funcs.py +253 -0
- pytree/utils/global_vars.py +22 -0
- pytree2-0.1.4.dist-info/METADATA +106 -0
- pytree2-0.1.4.dist-info/RECORD +14 -0
- pytree2-0.1.4.dist-info/WHEEL +5 -0
- pytree2-0.1.4.dist-info/entry_points.txt +2 -0
- pytree2-0.1.4.dist-info/licenses/LICENSE.md +21 -0
- pytree2-0.1.4.dist-info/top_level.txt +1 -0
pytree/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# pytree/__init__.py
|
|
@@ -0,0 +1,514 @@
|
|
|
1
|
+
# ProgressTracker module
|
|
2
|
+
|
|
3
|
+
# Code destined to defining
|
|
4
|
+
# ProgressTracker class and
|
|
5
|
+
# related attributes/methods.
|
|
6
|
+
|
|
7
|
+
######################################################################
|
|
8
|
+
# imports
|
|
9
|
+
|
|
10
|
+
# importing required libraries
|
|
11
|
+
from time import time
|
|
12
|
+
from time import sleep
|
|
13
|
+
from sys import stdout
|
|
14
|
+
from threading import Lock
|
|
15
|
+
from threading import Event
|
|
16
|
+
from os import _exit # noqa
|
|
17
|
+
from threading import Thread
|
|
18
|
+
from pytree.utils.aux_funcs import flush_string
|
|
19
|
+
from pytree.utils.aux_funcs import get_time_str
|
|
20
|
+
from pytree.utils.global_vars import UPDATE_TIME
|
|
21
|
+
from pytree.utils.aux_funcs import get_number_string
|
|
22
|
+
|
|
23
|
+
#####################################################################
|
|
24
|
+
# ProgressTracker definition
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class ProgressTracker:
|
|
28
|
+
"""
|
|
29
|
+
Defines ProgressTracker class.
|
|
30
|
+
"""
|
|
31
|
+
def __init__(self) -> None:
|
|
32
|
+
"""
|
|
33
|
+
Initializes a ProgressTracker instance
|
|
34
|
+
and defines class attributes.
|
|
35
|
+
"""
|
|
36
|
+
# defining class attributes (shared by all subclasses)
|
|
37
|
+
|
|
38
|
+
# time
|
|
39
|
+
self.start_time = self.get_current_time()
|
|
40
|
+
self.current_time = self.get_current_time()
|
|
41
|
+
self.elapsed_time = 0
|
|
42
|
+
self.elapsed_time_str = ''
|
|
43
|
+
self.etc = 0
|
|
44
|
+
self.etc_str = ''
|
|
45
|
+
|
|
46
|
+
# iteration
|
|
47
|
+
self.iterations_num = 0
|
|
48
|
+
self.current_iteration = 0
|
|
49
|
+
self.totals_updated = Event()
|
|
50
|
+
|
|
51
|
+
# progress
|
|
52
|
+
self.progress_percentage = 0
|
|
53
|
+
self.progress_percentage_str = ''
|
|
54
|
+
self.progress_string = ''
|
|
55
|
+
|
|
56
|
+
# threads
|
|
57
|
+
self.progress_thread = Thread(target=self.update_progress) # used to start separate thread for monitoring progress
|
|
58
|
+
self.lock = Lock() # used to prevent race conditions
|
|
59
|
+
self.process_complete = Event() # used to signal end or break, offering a clean shutdown
|
|
60
|
+
|
|
61
|
+
# wheel
|
|
62
|
+
self.wheel_symbol_list = ['\\',
|
|
63
|
+
'|',
|
|
64
|
+
'/',
|
|
65
|
+
'-']
|
|
66
|
+
self.wheel_index = 0
|
|
67
|
+
self.wheel_symbol = ''
|
|
68
|
+
|
|
69
|
+
# totals
|
|
70
|
+
self.totals_string = ''
|
|
71
|
+
|
|
72
|
+
# end string
|
|
73
|
+
self.end_string = 'analysis complete!'
|
|
74
|
+
|
|
75
|
+
@staticmethod
|
|
76
|
+
def wait(seconds: float = 0.005) -> None:
|
|
77
|
+
"""
|
|
78
|
+
Waits given time in seconds
|
|
79
|
+
before proceeding with execution.
|
|
80
|
+
"""
|
|
81
|
+
# sleeping
|
|
82
|
+
sleep(seconds)
|
|
83
|
+
|
|
84
|
+
def print_totals(self) -> None:
|
|
85
|
+
"""
|
|
86
|
+
Prints iterations totals.
|
|
87
|
+
"""
|
|
88
|
+
# clearing console
|
|
89
|
+
self.clear_progress()
|
|
90
|
+
|
|
91
|
+
# printing totals string
|
|
92
|
+
print(self.totals_string)
|
|
93
|
+
|
|
94
|
+
def signal_totals_updated(self) -> None:
|
|
95
|
+
"""
|
|
96
|
+
Sets threading.Event as set,
|
|
97
|
+
signaling totals updated.
|
|
98
|
+
"""
|
|
99
|
+
# printing totals
|
|
100
|
+
self.print_totals()
|
|
101
|
+
|
|
102
|
+
# signaling the progress tracker to stop
|
|
103
|
+
self.totals_updated.set()
|
|
104
|
+
|
|
105
|
+
# waiting some time to ensure signal is perceived
|
|
106
|
+
self.wait(seconds=0.8)
|
|
107
|
+
|
|
108
|
+
def update_totals(self,
|
|
109
|
+
args_dict: dict
|
|
110
|
+
) -> None:
|
|
111
|
+
"""
|
|
112
|
+
Defines base method to obtain
|
|
113
|
+
and update total iterations num.
|
|
114
|
+
"""
|
|
115
|
+
# updating attributes
|
|
116
|
+
self.iterations_num = 1
|
|
117
|
+
|
|
118
|
+
# updating totals string
|
|
119
|
+
totals_string = f'totals...'
|
|
120
|
+
totals_string += f' | iterations: {self.iterations_num}'
|
|
121
|
+
self.totals_string = totals_string
|
|
122
|
+
|
|
123
|
+
# signaling totals updated
|
|
124
|
+
self.signal_totals_updated()
|
|
125
|
+
|
|
126
|
+
def update_wheel_symbol(self) -> None:
|
|
127
|
+
"""
|
|
128
|
+
Updates wheel symbol.
|
|
129
|
+
"""
|
|
130
|
+
# getting updated wheel index
|
|
131
|
+
if self.wheel_index == 3:
|
|
132
|
+
self.wheel_index = 0
|
|
133
|
+
else:
|
|
134
|
+
self.wheel_index += 1
|
|
135
|
+
|
|
136
|
+
# updating current wheel symbol
|
|
137
|
+
self.wheel_symbol = self.wheel_symbol_list[self.wheel_index]
|
|
138
|
+
|
|
139
|
+
# checking if process complete event is set
|
|
140
|
+
if self.process_complete.is_set():
|
|
141
|
+
|
|
142
|
+
# overwriting if final event is set
|
|
143
|
+
self.wheel_symbol = '\b'
|
|
144
|
+
|
|
145
|
+
@staticmethod
|
|
146
|
+
def get_current_time() -> int:
|
|
147
|
+
"""
|
|
148
|
+
Gets current UTC time, in seconds.
|
|
149
|
+
"""
|
|
150
|
+
# getting current time
|
|
151
|
+
current_time = time()
|
|
152
|
+
|
|
153
|
+
# getting seconds
|
|
154
|
+
current_seconds = int(current_time)
|
|
155
|
+
|
|
156
|
+
# returning current time in seconds
|
|
157
|
+
return current_seconds
|
|
158
|
+
|
|
159
|
+
def reset_timer(self) -> None:
|
|
160
|
+
"""
|
|
161
|
+
Resets start time to be more
|
|
162
|
+
reliable when after user inputs
|
|
163
|
+
or iterations calculations.
|
|
164
|
+
"""
|
|
165
|
+
# resetting start time
|
|
166
|
+
self.start_time = self.get_current_time()
|
|
167
|
+
|
|
168
|
+
def get_elapsed_time(self) -> int:
|
|
169
|
+
"""
|
|
170
|
+
Returns time difference
|
|
171
|
+
between start time and
|
|
172
|
+
current time, in seconds.
|
|
173
|
+
"""
|
|
174
|
+
# getting elapsed time (time difference)
|
|
175
|
+
elapsed_time = self.current_time - self.start_time
|
|
176
|
+
|
|
177
|
+
# returning elapsed time
|
|
178
|
+
return elapsed_time
|
|
179
|
+
|
|
180
|
+
def get_etc(self) -> int:
|
|
181
|
+
"""
|
|
182
|
+
Based on iteration and time
|
|
183
|
+
attributes, returns estimated time
|
|
184
|
+
of completion (ETC).
|
|
185
|
+
"""
|
|
186
|
+
# defining base value for etc
|
|
187
|
+
etc = 3600
|
|
188
|
+
|
|
189
|
+
# getting iterations to go
|
|
190
|
+
iterations_to_go = self.iterations_num - self.current_iteration
|
|
191
|
+
|
|
192
|
+
# checking if first iteration is running
|
|
193
|
+
if self.current_iteration >= 1:
|
|
194
|
+
|
|
195
|
+
# calculating estimated time of completion
|
|
196
|
+
etc = iterations_to_go * self.elapsed_time / self.current_iteration
|
|
197
|
+
|
|
198
|
+
# rounding time
|
|
199
|
+
etc = round(etc)
|
|
200
|
+
|
|
201
|
+
# converting estimated time of completion to int
|
|
202
|
+
etc = int(etc)
|
|
203
|
+
|
|
204
|
+
# returning estimated time of completion
|
|
205
|
+
return etc
|
|
206
|
+
|
|
207
|
+
def update_time_attributes(self) -> None:
|
|
208
|
+
"""
|
|
209
|
+
Updates time related attributes.
|
|
210
|
+
"""
|
|
211
|
+
# updating time attributes
|
|
212
|
+
self.current_time = self.get_current_time()
|
|
213
|
+
self.elapsed_time = self.get_elapsed_time()
|
|
214
|
+
self.elapsed_time_str = get_time_str(time_in_seconds=self.elapsed_time)
|
|
215
|
+
self.etc = self.get_etc()
|
|
216
|
+
self.etc_str = get_time_str(time_in_seconds=self.etc)
|
|
217
|
+
|
|
218
|
+
@staticmethod
|
|
219
|
+
def get_percentage_string(percentage: int) -> str:
|
|
220
|
+
"""
|
|
221
|
+
Given a value in percentage,
|
|
222
|
+
returns value as a string,
|
|
223
|
+
adding '%' to the right side.
|
|
224
|
+
"""
|
|
225
|
+
# updating value to be in range of 2 digits
|
|
226
|
+
percentage_str = get_number_string(num=percentage,
|
|
227
|
+
digits=2)
|
|
228
|
+
|
|
229
|
+
# assembling percentage string
|
|
230
|
+
percentage_string = f'{percentage_str}%'
|
|
231
|
+
|
|
232
|
+
# checking if percentage is 100%
|
|
233
|
+
if percentage == 100:
|
|
234
|
+
|
|
235
|
+
# updating percentage string
|
|
236
|
+
percentage_string = '100%'
|
|
237
|
+
|
|
238
|
+
# returning percentage string
|
|
239
|
+
return percentage_string
|
|
240
|
+
|
|
241
|
+
def get_progress_percentage(self) -> int:
|
|
242
|
+
"""
|
|
243
|
+
Returns a formated progress
|
|
244
|
+
string based on current iteration
|
|
245
|
+
and iterations num.
|
|
246
|
+
"""
|
|
247
|
+
# getting percentage progress
|
|
248
|
+
try:
|
|
249
|
+
progress_ratio = self.current_iteration / self.iterations_num
|
|
250
|
+
except ZeroDivisionError:
|
|
251
|
+
progress_ratio = 0
|
|
252
|
+
progress_percentage = progress_ratio * 100
|
|
253
|
+
|
|
254
|
+
# rounding value for pretty print
|
|
255
|
+
progress_percentage = round(progress_percentage)
|
|
256
|
+
|
|
257
|
+
# returning progress percentage
|
|
258
|
+
return progress_percentage
|
|
259
|
+
|
|
260
|
+
def get_progress_string(self) -> str:
|
|
261
|
+
"""
|
|
262
|
+
Returns a formated progress
|
|
263
|
+
string, based on current progress
|
|
264
|
+
attributes.
|
|
265
|
+
!Provides a generalist progress bar.
|
|
266
|
+
Can be overwritten to consider module
|
|
267
|
+
specific attributes!
|
|
268
|
+
"""
|
|
269
|
+
# assembling current progress string
|
|
270
|
+
progress_string = f''
|
|
271
|
+
|
|
272
|
+
# checking if iterations total has already been obtained
|
|
273
|
+
if not self.totals_updated.is_set():
|
|
274
|
+
|
|
275
|
+
# updating progress string based on attributes
|
|
276
|
+
progress_string += f'calculating iterations...'
|
|
277
|
+
progress_string += f' | total iterations: {self.iterations_num}'
|
|
278
|
+
progress_string += f' | elapsed time: {self.elapsed_time_str}'
|
|
279
|
+
|
|
280
|
+
# if total iterations already obtained
|
|
281
|
+
else:
|
|
282
|
+
|
|
283
|
+
# updating progress string based on attributes
|
|
284
|
+
progress_string += f'running analysis...'
|
|
285
|
+
progress_string += f' {self.wheel_symbol}'
|
|
286
|
+
progress_string += f' | iteration: {self.current_iteration}/{self.iterations_num}'
|
|
287
|
+
progress_string += f' | progress: {self.progress_percentage}'
|
|
288
|
+
progress_string += f' | elapsed time: {self.elapsed_time_str}'
|
|
289
|
+
progress_string += f' | ETC: {self.etc_str}'
|
|
290
|
+
|
|
291
|
+
# returning progress string
|
|
292
|
+
return progress_string
|
|
293
|
+
|
|
294
|
+
def update_progress_string(self) -> None:
|
|
295
|
+
"""
|
|
296
|
+
Updates progress string related attributes.
|
|
297
|
+
"""
|
|
298
|
+
# updating progress percentage attributes
|
|
299
|
+
self.progress_percentage = self.get_progress_percentage()
|
|
300
|
+
self.progress_percentage_str = self.get_percentage_string(percentage=self.progress_percentage)
|
|
301
|
+
self.progress_string = self.get_progress_string()
|
|
302
|
+
|
|
303
|
+
def flush_progress(self) -> None:
|
|
304
|
+
"""
|
|
305
|
+
Gets updated progress string and
|
|
306
|
+
flushes it on the console.
|
|
307
|
+
"""
|
|
308
|
+
# updating wheel symbol attributes
|
|
309
|
+
self.update_wheel_symbol()
|
|
310
|
+
|
|
311
|
+
# updating progress string
|
|
312
|
+
self.update_progress_string()
|
|
313
|
+
|
|
314
|
+
# showing progress message
|
|
315
|
+
flush_string(string=self.progress_string)
|
|
316
|
+
|
|
317
|
+
def clear_progress(self) -> None:
|
|
318
|
+
"""
|
|
319
|
+
Given a string, writes empty space
|
|
320
|
+
to cover string size in console.
|
|
321
|
+
"""
|
|
322
|
+
# getting current progress string
|
|
323
|
+
string = self.progress_string
|
|
324
|
+
|
|
325
|
+
# getting string length
|
|
326
|
+
string_len = len(string)
|
|
327
|
+
|
|
328
|
+
# creating empty line
|
|
329
|
+
empty_line = ' ' * string_len
|
|
330
|
+
|
|
331
|
+
# creating backspace line
|
|
332
|
+
backspace_line = '\b' * string_len
|
|
333
|
+
|
|
334
|
+
# writing string
|
|
335
|
+
stdout.write(empty_line)
|
|
336
|
+
|
|
337
|
+
# flushing console
|
|
338
|
+
stdout.flush()
|
|
339
|
+
|
|
340
|
+
# resetting cursor to start of the line
|
|
341
|
+
stdout.write(backspace_line)
|
|
342
|
+
|
|
343
|
+
def signal_stop(self) -> None:
|
|
344
|
+
"""
|
|
345
|
+
Sets threading.Event as set,
|
|
346
|
+
signaling progress tracker
|
|
347
|
+
to stop.
|
|
348
|
+
"""
|
|
349
|
+
# signaling the progress tracker to stop
|
|
350
|
+
self.process_complete.set()
|
|
351
|
+
|
|
352
|
+
# waiting some time to ensure signal is perceived
|
|
353
|
+
self.wait(seconds=0.8)
|
|
354
|
+
|
|
355
|
+
@staticmethod
|
|
356
|
+
def force_quit() -> None:
|
|
357
|
+
"""
|
|
358
|
+
Uses os _exit to force
|
|
359
|
+
quit all running threads.
|
|
360
|
+
"""
|
|
361
|
+
# using os exit to exit program
|
|
362
|
+
_exit(1)
|
|
363
|
+
|
|
364
|
+
def exit(self,
|
|
365
|
+
message: str = 'DebugExit'
|
|
366
|
+
) -> None:
|
|
367
|
+
"""
|
|
368
|
+
Prints message and
|
|
369
|
+
kills all threads.
|
|
370
|
+
"""
|
|
371
|
+
# printing spacer
|
|
372
|
+
print()
|
|
373
|
+
|
|
374
|
+
# printing debug message
|
|
375
|
+
print(message)
|
|
376
|
+
|
|
377
|
+
# quitting code
|
|
378
|
+
self.force_quit()
|
|
379
|
+
|
|
380
|
+
def update_progress(self) -> None:
|
|
381
|
+
"""
|
|
382
|
+
Runs progress tracking loop, updating
|
|
383
|
+
progress attributes and printing
|
|
384
|
+
progress message on each iteration.
|
|
385
|
+
"""
|
|
386
|
+
# checking stop condition and running loop until stop event is set
|
|
387
|
+
while not self.process_complete.is_set():
|
|
388
|
+
|
|
389
|
+
# checking lock to avoid race conditions
|
|
390
|
+
with self.lock:
|
|
391
|
+
|
|
392
|
+
# updating time attributes
|
|
393
|
+
self.update_time_attributes()
|
|
394
|
+
|
|
395
|
+
# printing progress
|
|
396
|
+
self.flush_progress()
|
|
397
|
+
|
|
398
|
+
# sleeping for a short period of time to avoid too many prints
|
|
399
|
+
self.wait(seconds=UPDATE_TIME)
|
|
400
|
+
|
|
401
|
+
def start_thread(self) -> None:
|
|
402
|
+
"""
|
|
403
|
+
Starts progress thread.
|
|
404
|
+
"""
|
|
405
|
+
# starting progress tracker in a separate thread
|
|
406
|
+
self.progress_thread.start()
|
|
407
|
+
|
|
408
|
+
def stop_thread(self) -> None:
|
|
409
|
+
"""
|
|
410
|
+
Stops progress bar monitoring
|
|
411
|
+
and finished execution thread.
|
|
412
|
+
"""
|
|
413
|
+
# joining threads to ensure progress thread finished cleanly
|
|
414
|
+
self.progress_thread.join()
|
|
415
|
+
|
|
416
|
+
def normal_exit(self) -> None:
|
|
417
|
+
"""
|
|
418
|
+
Prints process complete message
|
|
419
|
+
before terminating execution.
|
|
420
|
+
"""
|
|
421
|
+
# defining final message
|
|
422
|
+
f_string = f'\n'
|
|
423
|
+
f_string += f'{self.end_string}'
|
|
424
|
+
|
|
425
|
+
# printing final message
|
|
426
|
+
print(f_string)
|
|
427
|
+
|
|
428
|
+
def keyboard_interrupt_exit(self) -> None:
|
|
429
|
+
"""
|
|
430
|
+
Prints exception message
|
|
431
|
+
before terminating execution.
|
|
432
|
+
"""
|
|
433
|
+
# defining error message
|
|
434
|
+
e_string = f'\n'
|
|
435
|
+
e_string += f'Keyboard Interrupt!'
|
|
436
|
+
|
|
437
|
+
# quitting with error message
|
|
438
|
+
self.exit(message=e_string)
|
|
439
|
+
|
|
440
|
+
def exception_exit(self,
|
|
441
|
+
exception: Exception
|
|
442
|
+
) -> None:
|
|
443
|
+
"""
|
|
444
|
+
Prints exception message
|
|
445
|
+
before terminating execution.
|
|
446
|
+
"""
|
|
447
|
+
# defining error message
|
|
448
|
+
e_string = f'\n'
|
|
449
|
+
e_string += f'Code break!\n'
|
|
450
|
+
e_string += f'Error:\n'
|
|
451
|
+
e_string += f'{exception}'
|
|
452
|
+
|
|
453
|
+
# quitting with error message
|
|
454
|
+
self.exit(message=e_string)
|
|
455
|
+
|
|
456
|
+
def run(self,
|
|
457
|
+
function: callable,
|
|
458
|
+
args_parser: callable
|
|
459
|
+
) -> None:
|
|
460
|
+
"""
|
|
461
|
+
Runs given function monitoring
|
|
462
|
+
progress in a separate thread.
|
|
463
|
+
"""
|
|
464
|
+
# getting args dict
|
|
465
|
+
args_dict = args_parser()
|
|
466
|
+
|
|
467
|
+
# starting progress thread
|
|
468
|
+
self.start_thread()
|
|
469
|
+
|
|
470
|
+
# running function in try/except block to catch breaks/errors!
|
|
471
|
+
try:
|
|
472
|
+
|
|
473
|
+
# updating iterations total
|
|
474
|
+
self.update_totals(args_dict=args_dict)
|
|
475
|
+
|
|
476
|
+
# resetting timer
|
|
477
|
+
self.reset_timer()
|
|
478
|
+
|
|
479
|
+
# running function with given args dict and progress tracker
|
|
480
|
+
function(args_dict,
|
|
481
|
+
self)
|
|
482
|
+
|
|
483
|
+
# signaling stop
|
|
484
|
+
self.signal_stop()
|
|
485
|
+
|
|
486
|
+
# printing final progress string
|
|
487
|
+
self.flush_progress()
|
|
488
|
+
|
|
489
|
+
# printing final message
|
|
490
|
+
self.normal_exit()
|
|
491
|
+
|
|
492
|
+
# catching Ctrl+C events
|
|
493
|
+
except KeyboardInterrupt:
|
|
494
|
+
|
|
495
|
+
# signaling stop
|
|
496
|
+
self.signal_stop()
|
|
497
|
+
|
|
498
|
+
# quitting
|
|
499
|
+
self.keyboard_interrupt_exit()
|
|
500
|
+
|
|
501
|
+
# catching every other exception
|
|
502
|
+
except Exception as exception:
|
|
503
|
+
|
|
504
|
+
# signaling stop
|
|
505
|
+
self.signal_stop()
|
|
506
|
+
|
|
507
|
+
# printing error message
|
|
508
|
+
self.exception_exit(exception=exception)
|
|
509
|
+
|
|
510
|
+
# terminating thread
|
|
511
|
+
self.stop_thread()
|
|
512
|
+
|
|
513
|
+
######################################################################
|
|
514
|
+
# end of current module
|