radnn 0.0.7.3__py3-none-any.whl → 0.0.9__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.
- radnn/__init__.py +3 -1
- radnn/data/__init__.py +2 -0
- radnn/data/data_feed.py +5 -0
- radnn/data/dataset_base.py +17 -5
- radnn/data/dataset_folder.py +55 -0
- radnn/data/image_dataset_files.py +175 -0
- radnn/data/subset_type.py +8 -2
- radnn/data/tf_classification_data_feed.py +22 -6
- radnn/experiment/ml_experiment_config.py +54 -29
- radnn/images/__init__.py +2 -0
- radnn/images/colors.py +28 -0
- radnn/images/image_processor.py +513 -0
- radnn/ml_system.py +1 -0
- radnn/plots/plot_auto_multi_image.py +6 -5
- radnn/stats/__init__.py +1 -0
- radnn/stats/descriptive_stats.py +45 -0
- radnn/system/files/__init__.py +1 -0
- radnn/system/files/filelist.py +40 -0
- radnn/system/files/jsonfile.py +3 -0
- radnn/system/files/textfile.py +29 -6
- radnn/system/filestore.py +26 -10
- radnn/system/filesystem.py +1 -1
- radnn/system/hosts/windows_host.py +10 -0
- radnn/system/threads/__init__.py +5 -0
- radnn/system/threads/semaphore_lock.py +58 -0
- radnn/system/threads/thread_context.py +175 -0
- radnn/system/threads/thread_safe_queue.py +163 -0
- radnn/system/threads/thread_safe_string_collection.py +66 -0
- radnn/system/threads/thread_worker.py +68 -0
- radnn/utils.py +43 -0
- {radnn-0.0.7.3.dist-info → radnn-0.0.9.dist-info}/METADATA +4 -25
- {radnn-0.0.7.3.dist-info → radnn-0.0.9.dist-info}/RECORD +35 -21
- {radnn-0.0.7.3.dist-info → radnn-0.0.9.dist-info}/WHEEL +1 -1
- {radnn-0.0.7.3.dist-info → radnn-0.0.9.dist-info/licenses}/LICENSE.txt +0 -0
- {radnn-0.0.7.3.dist-info → radnn-0.0.9.dist-info}/top_level.txt +0 -0
radnn/system/files/textfile.py
CHANGED
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
# .......................................................................................
|
|
30
30
|
import os
|
|
31
31
|
import numpy as np
|
|
32
|
+
import locale
|
|
32
33
|
from .fileobject import FileObject
|
|
33
34
|
|
|
34
35
|
|
|
@@ -37,8 +38,33 @@ class TextFile(FileObject):
|
|
|
37
38
|
def __init__(self, filename, parent_folder=None, error_template=None, is_verbose=False):
|
|
38
39
|
super(TextFile, self).__init__(filename, parent_folder, error_template)
|
|
39
40
|
self.is_verbose = is_verbose
|
|
41
|
+
|
|
42
|
+
# ----------------------------------------------------------------------------------
|
|
43
|
+
def load(self, filename=None, encoding=None):
|
|
44
|
+
filename = self._useFileName(filename)
|
|
45
|
+
|
|
46
|
+
sText = None
|
|
47
|
+
if os.path.isfile(filename):
|
|
48
|
+
oEncodingToTry = ["utf-8", "utf-16", "latin1", "ascii"] # Add more if needed
|
|
49
|
+
if encoding is None:
|
|
50
|
+
bIsLoaded = False
|
|
51
|
+
for sEnc in oEncodingToTry:
|
|
52
|
+
try:
|
|
53
|
+
with open(filename, "r", encoding=sEnc) as oFile:
|
|
54
|
+
sText = oFile.read()
|
|
55
|
+
bIsLoaded = True
|
|
56
|
+
break
|
|
57
|
+
except (UnicodeDecodeError, UnicodeError):
|
|
58
|
+
continue
|
|
59
|
+
if not bIsLoaded:
|
|
60
|
+
raise ValueError("Unsupported encoding")
|
|
61
|
+
else:
|
|
62
|
+
with open(filename, "r", encoding=encoding) as oFile:
|
|
63
|
+
sText = oFile.read()
|
|
64
|
+
|
|
65
|
+
return sText
|
|
40
66
|
# --------------------------------------------------------------------------------------------------------------------
|
|
41
|
-
def save(self, text_obj, filename=None):
|
|
67
|
+
def save(self, text_obj, filename=None, encoding="utf-8"):
|
|
42
68
|
sFilename = self._useFileName(filename)
|
|
43
69
|
|
|
44
70
|
"""
|
|
@@ -48,9 +74,6 @@ class TextFile(FileObject):
|
|
|
48
74
|
p_sFileName : Full path to the text file
|
|
49
75
|
p_sText : Text to write
|
|
50
76
|
"""
|
|
51
|
-
if (self.parent_folder is not None):
|
|
52
|
-
sFilename = os.path.join(self.parent_folder, sFilename)
|
|
53
|
-
|
|
54
77
|
if self.is_verbose:
|
|
55
78
|
print(" {.} Saving text to %s" % sFilename)
|
|
56
79
|
|
|
@@ -61,12 +84,12 @@ class TextFile(FileObject):
|
|
|
61
84
|
bIsIterable = text_obj.dtype = str
|
|
62
85
|
|
|
63
86
|
if bIsIterable:
|
|
64
|
-
with open(sFilename, "w") as oFile:
|
|
87
|
+
with open(sFilename, "w", encoding=encoding) as oFile:
|
|
65
88
|
for sLine in text_obj:
|
|
66
89
|
print(sLine, file=oFile)
|
|
67
90
|
oFile.close()
|
|
68
91
|
else:
|
|
69
|
-
with open(sFilename, "w") as oFile:
|
|
92
|
+
with open(sFilename, "w", encoding=encoding) as oFile:
|
|
70
93
|
print(text_obj, file=oFile)
|
|
71
94
|
oFile.close()
|
|
72
95
|
return True
|
radnn/system/filestore.py
CHANGED
|
@@ -35,7 +35,7 @@ if (sys.version_info.major == 3) and (sys.version_info.minor <= 7):
|
|
|
35
35
|
import pickle5 as pickle
|
|
36
36
|
else:
|
|
37
37
|
import pickle
|
|
38
|
-
|
|
38
|
+
from radnn.system.files import FileList
|
|
39
39
|
from radnn.system.files import JSONFile
|
|
40
40
|
from radnn.system.files import PickleFile
|
|
41
41
|
from radnn.system.files import TextFile
|
|
@@ -53,6 +53,7 @@ class FileStore(object):
|
|
|
53
53
|
def __init__(self, base_folder, is_verbose=False, must_exist=False):
|
|
54
54
|
#.......................... | Instance Attributes | ............................
|
|
55
55
|
self.base_folder = base_folder
|
|
56
|
+
self.absolute_path = os.path.abspath(base_folder)
|
|
56
57
|
if not os.path.exists(self.base_folder):
|
|
57
58
|
if must_exist:
|
|
58
59
|
raise Exception(f"File store folder {self.base_folder} does not exist.")
|
|
@@ -98,7 +99,7 @@ class FileStore(object):
|
|
|
98
99
|
return self.folder(subfolder_name)
|
|
99
100
|
# --------------------------------------------------------------------------------------------------------
|
|
100
101
|
def folder(self, folder_name):
|
|
101
|
-
sFolder = os.path.join(self.
|
|
102
|
+
sFolder = os.path.join(self.absolute_path, folder_name)
|
|
102
103
|
if not os.path.exists(sFolder):
|
|
103
104
|
os.makedirs(sFolder)
|
|
104
105
|
|
|
@@ -110,13 +111,19 @@ class FileStore(object):
|
|
|
110
111
|
file_name += "." + file_ext
|
|
111
112
|
else:
|
|
112
113
|
file_name += file_ext
|
|
113
|
-
return os.path.join(self.
|
|
114
|
+
return os.path.join(self.absolute_path, file_name)
|
|
114
115
|
# --------------------------------------------------------------------------------------------------------
|
|
115
116
|
def entries(self):
|
|
116
117
|
return os.listdir(self.base_folder)
|
|
117
118
|
# --------------------------------------------------------------------------------------------------------
|
|
118
|
-
def
|
|
119
|
-
|
|
119
|
+
def _ls(self, file_matching_pattern, is_removing_extension, sort_filename_key):
|
|
120
|
+
if ";" in file_matching_pattern:
|
|
121
|
+
sEntries = []
|
|
122
|
+
for sExt in file_matching_pattern.split(";"):
|
|
123
|
+
sEntries += glob.glob1(self.base_folder, sExt.strip())
|
|
124
|
+
else:
|
|
125
|
+
sEntries = glob.glob1(self.base_folder, file_matching_pattern)
|
|
126
|
+
|
|
120
127
|
if is_removing_extension:
|
|
121
128
|
oFileNamesOnly = []
|
|
122
129
|
for sEntry in sEntries:
|
|
@@ -124,15 +131,24 @@ class FileStore(object):
|
|
|
124
131
|
oFileNamesOnly.append(sFileNameOnly)
|
|
125
132
|
sEntries = sorted(oFileNamesOnly, key=sort_filename_key)
|
|
126
133
|
|
|
134
|
+
return sEntries
|
|
135
|
+
# --------------------------------------------------------------------------------------------------------
|
|
136
|
+
def list_files(self, file_matching_pattern, is_full_path=True, is_removing_extension=False, sort_filename_key=None):
|
|
137
|
+
sEntries = self._ls(file_matching_pattern, is_removing_extension, sort_filename_key)
|
|
127
138
|
if is_full_path:
|
|
128
139
|
oResult = [os.path.join(self.base_folder, x) for x in sEntries]
|
|
129
140
|
else:
|
|
130
141
|
oResult = [x for x in sEntries]
|
|
131
|
-
|
|
132
142
|
return oResult
|
|
133
143
|
# --------------------------------------------------------------------------------------------------------
|
|
134
|
-
|
|
135
|
-
|
|
144
|
+
def filelist(self, file_matching_pattern, is_removing_extension=False, sort_filename_key=None):
|
|
145
|
+
sEntries = self._ls(file_matching_pattern, is_removing_extension, sort_filename_key)
|
|
146
|
+
oFileList = FileList(self.base_folder)
|
|
147
|
+
for x in sEntries:
|
|
148
|
+
oFileList.append(x)
|
|
149
|
+
return oFileList
|
|
150
|
+
# --------------------------------------------------------------------------------------------------------
|
|
151
|
+
def list_folders(self, is_full_path=True):
|
|
136
152
|
sResult = []
|
|
137
153
|
for sFolder in os.listdir(self.base_folder):
|
|
138
154
|
sFullPath = os.path.join(self.base_folder, sFolder)
|
|
@@ -163,10 +179,10 @@ class FileStore(object):
|
|
|
163
179
|
pass
|
|
164
180
|
# ----------------------------------------------------------------------------------
|
|
165
181
|
def __repr__(self)->str:
|
|
166
|
-
return self.
|
|
182
|
+
return self.absolute_path
|
|
167
183
|
# --------------------------------------------------------------------------------------------------------
|
|
168
184
|
def __str__(self)->str:
|
|
169
|
-
return self.
|
|
185
|
+
return self.absolute_path
|
|
170
186
|
# --------------------------------------------------------------------------------------------------------
|
|
171
187
|
# ======================================================================================================================
|
|
172
188
|
|
radnn/system/filesystem.py
CHANGED
|
@@ -35,7 +35,7 @@ from radnn.system.files import JSONFile
|
|
|
35
35
|
# =======================================================================================================================
|
|
36
36
|
class FileSystem(object):
|
|
37
37
|
# --------------------------------------------------------------------------------------------------------
|
|
38
|
-
def __init__(self, config_folder="MLConfig", model_folder="MLModels", dataset_folder="MLData", must_exist=False, setup_filename="*auto*", is_custom_setup=
|
|
38
|
+
def __init__(self, config_folder="MLConfig", model_folder="MLModels", dataset_folder="MLData", must_exist=False, setup_filename="*auto*", is_custom_setup=True):
|
|
39
39
|
'''
|
|
40
40
|
Initializes the file system settings for an experiment
|
|
41
41
|
:param config_folder: The folder that contains the experiment hyperparameter files.
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
|
|
29
29
|
# .......................................................................................
|
|
30
30
|
import win32api
|
|
31
|
+
import ctypes
|
|
31
32
|
class WindowsHost(object):
|
|
32
33
|
# --------------------------------------------------------------------------------------------------------------------
|
|
33
34
|
@classmethod
|
|
@@ -66,4 +67,13 @@ class WindowsHost(object):
|
|
|
66
67
|
str_info = u'\\StringFileInfo\\%04X%04X\\%s' % (sLanguageCode, sCodePage, sVersionString)
|
|
67
68
|
dInfo[sVersionString] = repr(win32api.GetFileVersionInfo(dll_filename, str_info))
|
|
68
69
|
return dInfo
|
|
70
|
+
# --------------------------------------------------------------------------------------------------------------------
|
|
71
|
+
@classmethod
|
|
72
|
+
def set_windows_sleep_resolution(cls, msecs=1):
|
|
73
|
+
"""
|
|
74
|
+
Requests a minimum resolution for periodic timers. This increases accuracy
|
|
75
|
+
for the waiting interval of the time.sleep function
|
|
76
|
+
"""
|
|
77
|
+
oWinMM = ctypes.WinDLL('oWinMM')
|
|
78
|
+
oWinMM.timeBeginPeriod(msecs)
|
|
69
79
|
# --------------------------------------------------------------------------------------------------------------------
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# ======================================================================================
|
|
2
|
+
#
|
|
3
|
+
# Rapid Deep Neural Networks
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the MIT License
|
|
6
|
+
# ______________________________________________________________________________________
|
|
7
|
+
# ......................................................................................
|
|
8
|
+
|
|
9
|
+
# Copyright (c) 2018-2025 Pantelis I. Kaplanoglou
|
|
10
|
+
|
|
11
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
12
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
13
|
+
# in the Software without restriction, including without limitation the rights
|
|
14
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
15
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
16
|
+
# furnished to do so, subject to the following conditions:
|
|
17
|
+
|
|
18
|
+
# The above copyright notice and this permission notice shall be included in all
|
|
19
|
+
# copies or substantial portions of the Software.
|
|
20
|
+
|
|
21
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
22
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
23
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
24
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
25
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
26
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
27
|
+
# SOFTWARE.
|
|
28
|
+
|
|
29
|
+
# .......................................................................................
|
|
30
|
+
from threading import BoundedSemaphore
|
|
31
|
+
|
|
32
|
+
#=======================================================================================================================
|
|
33
|
+
class SemaphoreLock(BoundedSemaphore):
|
|
34
|
+
# ------------------------------------------------------------------------------------------------------------------
|
|
35
|
+
def __init__(self, value=1):
|
|
36
|
+
super(SemaphoreLock, self).__init__(value)
|
|
37
|
+
# ------------------------------------------------------------------------------------------------------------------
|
|
38
|
+
def lock(self, blocking=True, timeout=None):
|
|
39
|
+
self.acquire(blocking, timeout)
|
|
40
|
+
# ------------------------------------------------------------------------------------------------------------------
|
|
41
|
+
def unlock(self):
|
|
42
|
+
self.release()
|
|
43
|
+
# ------------------------------------------------------------------------------------------------------------------
|
|
44
|
+
# //TODO: Not working, check if this can be fixed
|
|
45
|
+
'''
|
|
46
|
+
def __enter__(self):
|
|
47
|
+
self.Lock()
|
|
48
|
+
return self
|
|
49
|
+
|
|
50
|
+
def __exit__(self, exception_type, exception_val, trace):
|
|
51
|
+
try:
|
|
52
|
+
self.Unlock()
|
|
53
|
+
except:
|
|
54
|
+
print("Could not unlock semaphore")
|
|
55
|
+
return True
|
|
56
|
+
'''
|
|
57
|
+
# ------------------------------------------------------------------------------------------------------------------
|
|
58
|
+
#=======================================================================================================================
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# ======================================================================================
|
|
2
|
+
#
|
|
3
|
+
# Rapid Deep Neural Networks
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the MIT License
|
|
6
|
+
# ______________________________________________________________________________________
|
|
7
|
+
# ......................................................................................
|
|
8
|
+
|
|
9
|
+
# Copyright (c) 2018-2025 Pantelis I. Kaplanoglou
|
|
10
|
+
|
|
11
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
12
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
13
|
+
# in the Software without restriction, including without limitation the rights
|
|
14
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
15
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
16
|
+
# furnished to do so, subject to the following conditions:
|
|
17
|
+
|
|
18
|
+
# The above copyright notice and this permission notice shall be included in all
|
|
19
|
+
# copies or substantial portions of the Software.
|
|
20
|
+
|
|
21
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
22
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
23
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
24
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
25
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
26
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
27
|
+
# SOFTWARE.
|
|
28
|
+
|
|
29
|
+
# .......................................................................................
|
|
30
|
+
import time
|
|
31
|
+
from threading import Thread, Event, get_ident
|
|
32
|
+
from datetime import datetime
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
#=======================================================================================================================
|
|
36
|
+
class ThreadEx(Thread):
|
|
37
|
+
# ------------------------------------------------------------------------------------------------------------------
|
|
38
|
+
def __init__(self,target=None, name=None, args=(), kwargs=None):
|
|
39
|
+
#//TODO: delegate target, args and kwargs properly
|
|
40
|
+
super(ThreadEx, self).__init__(target=target, name=name, args=args, kwargs=kwargs)
|
|
41
|
+
self._stop_event = Event()
|
|
42
|
+
# ------------------------------------------------------------------------------------------------------------------
|
|
43
|
+
def stop(self):
|
|
44
|
+
self._stop_event.set()
|
|
45
|
+
# ------------------------------------------------------------------------------------------------------------------
|
|
46
|
+
@property
|
|
47
|
+
def is_stopped(self):
|
|
48
|
+
return self._stop_event.is_set()
|
|
49
|
+
# ------------------------------------------------------------------------------------------------------------------
|
|
50
|
+
#=======================================================================================================================
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
#=======================================================================================================================
|
|
61
|
+
class ThreadContext(object):
|
|
62
|
+
__NEXT_ID = 0
|
|
63
|
+
|
|
64
|
+
# ------------------------------------------------------------------------------------------------------------------
|
|
65
|
+
def __init__(self, name=None, is_daemon_thread=True, verbose_level=1):
|
|
66
|
+
#........................... | Instance Attributes | ...........................
|
|
67
|
+
type(self).__NEXT_ID += 1
|
|
68
|
+
if name is None:
|
|
69
|
+
self.name = str(type(self).__NEXT_ID)
|
|
70
|
+
else:
|
|
71
|
+
self.name = f"{name}{str(type(self).__NEXT_ID)}"
|
|
72
|
+
self.thread_id = None
|
|
73
|
+
self.verbose_level = verbose_level
|
|
74
|
+
|
|
75
|
+
#// Settings \\
|
|
76
|
+
self.is_daemon_thread = is_daemon_thread
|
|
77
|
+
self.join_timeout = 5 #secs
|
|
78
|
+
|
|
79
|
+
# // Control Variables \\
|
|
80
|
+
self.must_continue = False
|
|
81
|
+
self.has_finished = False
|
|
82
|
+
self.has_started = False
|
|
83
|
+
|
|
84
|
+
# // Agregated Objects \\
|
|
85
|
+
self.thread_handle = None
|
|
86
|
+
self.thread_args = None
|
|
87
|
+
self.on_after_finish_handler = None
|
|
88
|
+
self.run_once_func = None
|
|
89
|
+
#................................................................................
|
|
90
|
+
# ------------------------------------------------------------------------------------------------------------------
|
|
91
|
+
def sleep(self, interval_in_msecs):
|
|
92
|
+
time.sleep(interval_in_msecs / 1000.0)
|
|
93
|
+
# ------------------------------------------------------------------------------------------------------------------
|
|
94
|
+
def _thread_start(self, args):
|
|
95
|
+
self.thread_id = get_ident()
|
|
96
|
+
self.thread_args = args
|
|
97
|
+
self.has_started = True
|
|
98
|
+
|
|
99
|
+
if self.verbose_level > 0:
|
|
100
|
+
print(f"{self} is starting with arguments:", args)
|
|
101
|
+
|
|
102
|
+
self.has_finished = False
|
|
103
|
+
try:
|
|
104
|
+
if self.run_once_func is None:
|
|
105
|
+
self.loop()
|
|
106
|
+
else:
|
|
107
|
+
self.run_once_func(args)
|
|
108
|
+
finally:
|
|
109
|
+
self.has_finished = True
|
|
110
|
+
# ------------------------------------------------------------------------------------------------------------------
|
|
111
|
+
def _thread_finish(self):
|
|
112
|
+
while not self.has_finished:
|
|
113
|
+
pass
|
|
114
|
+
|
|
115
|
+
# Callback after finishing
|
|
116
|
+
if self.on_after_finish_handler is not None:
|
|
117
|
+
self.on_after_finish_handler()
|
|
118
|
+
|
|
119
|
+
# Signal the thread to stop via the event
|
|
120
|
+
self.thread_handle.stop()
|
|
121
|
+
|
|
122
|
+
# Wait for the thread to finish
|
|
123
|
+
if self.verbose_level > 1:
|
|
124
|
+
print(f"{self} joining...")
|
|
125
|
+
|
|
126
|
+
self.thread_handle.join(self.join_timeout)
|
|
127
|
+
|
|
128
|
+
if self.verbose_level > 1:
|
|
129
|
+
nTimeDelta = datetime.now() - self._stop_action_start
|
|
130
|
+
print(f"{self} joined after {(nTimeDelta.microseconds / 1000):.3f} msecs")
|
|
131
|
+
# ------------------------------------------------------------------------------------------------------------------
|
|
132
|
+
def start(self, args=None):
|
|
133
|
+
self.must_continue = True
|
|
134
|
+
|
|
135
|
+
self.thread_handle = ThreadEx(target=self._thread_start, args=(), kwargs={"args": args})
|
|
136
|
+
self.thread_handle.setDaemon(True)
|
|
137
|
+
self.thread_handle.start()
|
|
138
|
+
# ------------------------------------------------------------------------------------------------------------------
|
|
139
|
+
def resume(self, args=None):
|
|
140
|
+
if not self.has_started:
|
|
141
|
+
self.start(args)
|
|
142
|
+
# ------------------------------------------------------------------------------------------------------------------
|
|
143
|
+
def stop(self):
|
|
144
|
+
self._stop_action_start = datetime.now()
|
|
145
|
+
if self.verbose_level > 0:
|
|
146
|
+
print(f"{self} is stopping ...")
|
|
147
|
+
# Break the loop and wait for the method to exit
|
|
148
|
+
self.must_continue = False
|
|
149
|
+
self._thread_finish()
|
|
150
|
+
# ------------------------------------------------------------------------------------------------------------------
|
|
151
|
+
def terminate(self):
|
|
152
|
+
self.stop()
|
|
153
|
+
# ------------------------------------------------------------------------------------------------------------------
|
|
154
|
+
def loop(self):
|
|
155
|
+
pass
|
|
156
|
+
# ------------------------------------------------------------------------------------------------------------------
|
|
157
|
+
def __str__(self):
|
|
158
|
+
return f"{self.name} ({self.thread_id})"
|
|
159
|
+
# ------------------------------------------------------------------------------------------------------------------
|
|
160
|
+
def __repr__(self):
|
|
161
|
+
return self.__str__()
|
|
162
|
+
# ------------------------------------------------------------------------------------------------------------------
|
|
163
|
+
#=======================================================================================================================
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# ======================================================================================
|
|
2
|
+
#
|
|
3
|
+
# Rapid Deep Neural Networks
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the MIT License
|
|
6
|
+
# ______________________________________________________________________________________
|
|
7
|
+
# ......................................................................................
|
|
8
|
+
|
|
9
|
+
# Copyright (c) 2018-2025 Pantelis I. Kaplanoglou
|
|
10
|
+
|
|
11
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
12
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
13
|
+
# in the Software without restriction, including without limitation the rights
|
|
14
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
15
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
16
|
+
# furnished to do so, subject to the following conditions:
|
|
17
|
+
|
|
18
|
+
# The above copyright notice and this permission notice shall be included in all
|
|
19
|
+
# copies or substantial portions of the Software.
|
|
20
|
+
|
|
21
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
22
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
23
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
24
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
25
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
26
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
27
|
+
# SOFTWARE.
|
|
28
|
+
|
|
29
|
+
# .......................................................................................
|
|
30
|
+
from .semaphore_lock import SemaphoreLock
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class ThreadSafeQueue(object):
|
|
35
|
+
#-------------------------------------------------------------------------------------------------------------------
|
|
36
|
+
def __init__(self, name="Queue", max_queued_items=None):
|
|
37
|
+
#........................... | Instance Attributes | ...........................
|
|
38
|
+
self.name = name
|
|
39
|
+
self.queue=[]
|
|
40
|
+
|
|
41
|
+
# // Settings \\
|
|
42
|
+
self.max_queued_items = max_queued_items
|
|
43
|
+
self.can_delete = True
|
|
44
|
+
|
|
45
|
+
# // Control Params \\
|
|
46
|
+
self.is_queue_locked = False
|
|
47
|
+
|
|
48
|
+
# // Composites \\
|
|
49
|
+
self.update_lock = SemaphoreLock()
|
|
50
|
+
#................................................................................
|
|
51
|
+
|
|
52
|
+
# -------------------------------------------------------------------------------------------------------------------
|
|
53
|
+
@property
|
|
54
|
+
def count(self):
|
|
55
|
+
return len(self.queue)
|
|
56
|
+
|
|
57
|
+
# -------------------------------------------------------------------------------------------------------------------
|
|
58
|
+
@property
|
|
59
|
+
def is_empty(self):
|
|
60
|
+
bResult = (len(self.queue) == 0)
|
|
61
|
+
|
|
62
|
+
return bResult
|
|
63
|
+
#-------------------------------------------------------------------------------------------------------------------
|
|
64
|
+
def push(self, value):
|
|
65
|
+
self.update_lock.lock()
|
|
66
|
+
try:
|
|
67
|
+
if self.max_queued_items is None:
|
|
68
|
+
bCanAppend = True
|
|
69
|
+
else:
|
|
70
|
+
bCanAppend = len(self.queue) < self.max_queued_items
|
|
71
|
+
|
|
72
|
+
if bCanAppend:
|
|
73
|
+
self.queue.append(value)
|
|
74
|
+
finally:
|
|
75
|
+
self.update_lock.unlock()
|
|
76
|
+
#-------------------------------------------------------------------------------------------------------------------
|
|
77
|
+
def pop(self, index=0):
|
|
78
|
+
nValue = None
|
|
79
|
+
if self.can_delete:
|
|
80
|
+
self.update_lock.lock()
|
|
81
|
+
try:
|
|
82
|
+
bMustPop = len(self.queue) > 0
|
|
83
|
+
if bMustPop:
|
|
84
|
+
if index == -1:
|
|
85
|
+
index = len(self.queue) - 1
|
|
86
|
+
nValue = self.queue.pop(index)
|
|
87
|
+
finally:
|
|
88
|
+
self.update_lock.unlock()
|
|
89
|
+
|
|
90
|
+
return nValue
|
|
91
|
+
#-------------------------------------------------------------------------------------------------------------------
|
|
92
|
+
def clear(self):
|
|
93
|
+
self.update_lock.lock()
|
|
94
|
+
try:
|
|
95
|
+
self.queue = []
|
|
96
|
+
finally:
|
|
97
|
+
self.update_lock.unlock()
|
|
98
|
+
#-------------------------------------------------------------------------------------------------------------------
|
|
99
|
+
'''
|
|
100
|
+
def pop_ex(self, index=0):
|
|
101
|
+
nValue,nRemainingItemCount = [None,None]
|
|
102
|
+
if self.can_delete:
|
|
103
|
+
self.update_lock.Lock()
|
|
104
|
+
try:
|
|
105
|
+
nRemainingItemCount = len(self.queue)
|
|
106
|
+
bMustPop = nRemainingItemCount > 0
|
|
107
|
+
if bMustPop:
|
|
108
|
+
if index == -1:
|
|
109
|
+
index = len(self.queue) - 1
|
|
110
|
+
nValue = self.queue.pop(index)
|
|
111
|
+
nRemainingItemCount = len(self.queue)
|
|
112
|
+
finally:
|
|
113
|
+
self.update_lock.UnLock()
|
|
114
|
+
|
|
115
|
+
return nValue, nRemainingItemCount
|
|
116
|
+
'''
|
|
117
|
+
#-------------------------------------------------------------------------------------------------------------------
|
|
118
|
+
def push_ex(self, message):
|
|
119
|
+
while self.is_queue_locked:
|
|
120
|
+
pass
|
|
121
|
+
|
|
122
|
+
if self.max_queued_items is None:
|
|
123
|
+
bCanAppend = True
|
|
124
|
+
else:
|
|
125
|
+
bCanAppend = len(self.queue) < self.max_queued_items
|
|
126
|
+
|
|
127
|
+
if not bCanAppend:
|
|
128
|
+
return
|
|
129
|
+
|
|
130
|
+
self.is_queue_locked = True
|
|
131
|
+
try:
|
|
132
|
+
self.queue.append(message)
|
|
133
|
+
finally:
|
|
134
|
+
self.is_queue_locked = False
|
|
135
|
+
#-------------------------------------------------------------------------------------------------------------------
|
|
136
|
+
def pop_ex(self):
|
|
137
|
+
sMessage = None
|
|
138
|
+
if self.can_delete:
|
|
139
|
+
while self.is_queue_locked:
|
|
140
|
+
pass
|
|
141
|
+
|
|
142
|
+
self.is_queue_locked = True
|
|
143
|
+
try:
|
|
144
|
+
if len(self.queue) > 0:
|
|
145
|
+
sMessage = self.queue.pop(0)
|
|
146
|
+
finally:
|
|
147
|
+
self.is_queue_locked = False
|
|
148
|
+
|
|
149
|
+
return sMessage
|
|
150
|
+
#-------------------------------------------------------------------------------------------------------------------
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
from .thread_safe_queue import ThreadSafeQueue
|
|
2
|
+
|
|
3
|
+
class StringCollectionThreadSafe(ThreadSafeQueue):
|
|
4
|
+
# -------------------------------------------------------------------------------------------------------------------
|
|
5
|
+
def __init__(self, filename=None, name="Text"):
|
|
6
|
+
super(StringCollectionThreadSafe, self).__init__(name)
|
|
7
|
+
|
|
8
|
+
self.filename = filename
|
|
9
|
+
self.__count = 0
|
|
10
|
+
self.__index = 0
|
|
11
|
+
# -------------------------------------------------------------------------------------------------------------------
|
|
12
|
+
def append(self, text):
|
|
13
|
+
self.push(text)
|
|
14
|
+
# -------------------------------------------------------------------------------------------------------------------
|
|
15
|
+
def print(self, text, *args):
|
|
16
|
+
if args is None:
|
|
17
|
+
self.push(text)
|
|
18
|
+
else:
|
|
19
|
+
self.push(text + " " + " ".join(map(str, args)))
|
|
20
|
+
# -------------------------------------------------------------------------------------------------------------------
|
|
21
|
+
def flush(self):
|
|
22
|
+
if self.CanDelete:
|
|
23
|
+
with open(self.filename, "a") as oFile:
|
|
24
|
+
for sLine in self.queue:
|
|
25
|
+
print(sLine, file=oFile)
|
|
26
|
+
self.clear()
|
|
27
|
+
# -------------------------------------------------------------------------------------------------------------------
|
|
28
|
+
def display_and_flush(self):
|
|
29
|
+
if self.CanDelete:
|
|
30
|
+
if self.filename is None:
|
|
31
|
+
for sLine in self.queue:
|
|
32
|
+
print(sLine)
|
|
33
|
+
else:
|
|
34
|
+
with open(self.filename, "a") as oFile:
|
|
35
|
+
for sLine in self.queue:
|
|
36
|
+
print(sLine, file=oFile)
|
|
37
|
+
self.clear()
|
|
38
|
+
# -------------------------------------------------------------------------------------------------------------------
|
|
39
|
+
def __iter__(self):
|
|
40
|
+
self.update_lock.Lock()
|
|
41
|
+
try:
|
|
42
|
+
self.__index = 0
|
|
43
|
+
self.__count = len(self.queue)
|
|
44
|
+
self.CanDelete = False
|
|
45
|
+
finally:
|
|
46
|
+
self.update_lock.UnLock()
|
|
47
|
+
|
|
48
|
+
return self
|
|
49
|
+
# -------------------------------------------------------------------------------------------------------------------
|
|
50
|
+
def __next__(self):
|
|
51
|
+
if self.__index < self.__count:
|
|
52
|
+
oResult = self.queue[self.__index]
|
|
53
|
+
self.__index += 1
|
|
54
|
+
return oResult
|
|
55
|
+
else:
|
|
56
|
+
self.CanDelete = True
|
|
57
|
+
raise StopIteration()
|
|
58
|
+
# -------------------------------------------------------------------------------------------------------------------
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
|