easy-utils-dev 2.139__py3-none-any.whl → 2.141__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 easy-utils-dev might be problematic. Click here for more details.
- easy_utils_dev/debugger-C-PF4PAMMP.py +486 -0
- easy_utils_dev/debugger.py +9 -0
- easy_utils_dev/temp_memory.py +15 -8
- easy_utils_dev/uiserver.py +115 -81
- easy_utils_dev/wsnoclib.py +18 -6
- {easy_utils_dev-2.139.dist-info → easy_utils_dev-2.141.dist-info}/METADATA +8 -6
- {easy_utils_dev-2.139.dist-info → easy_utils_dev-2.141.dist-info}/RECORD +9 -8
- {easy_utils_dev-2.139.dist-info → easy_utils_dev-2.141.dist-info}/WHEEL +1 -1
- {easy_utils_dev-2.139.dist-info → easy_utils_dev-2.141.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from logging.handlers import RotatingFileHandler
|
|
6
|
+
from .utils import getRandomKey , convert_mb_to_bytes , getTimestamp , start_thread
|
|
7
|
+
from .custom_env import custom_env , setupEnvironment
|
|
8
|
+
from .Events import EventEmitter
|
|
9
|
+
from threading import Thread
|
|
10
|
+
from time import sleep
|
|
11
|
+
|
|
12
|
+
gEvent = EventEmitter()
|
|
13
|
+
logging.addLevelName(25, "SCREEN")
|
|
14
|
+
|
|
15
|
+
def setGlobalHomePath( path ) :
|
|
16
|
+
env = custom_env()
|
|
17
|
+
env['debugger_homepath'] = path
|
|
18
|
+
gEvent.dispatchEvent('update_home_path')
|
|
19
|
+
|
|
20
|
+
def setGlobalDisableOnScreen(on_screen=False) :
|
|
21
|
+
env = custom_env()
|
|
22
|
+
env['debugger_on_screen'] = on_screen
|
|
23
|
+
if not on_screen :
|
|
24
|
+
gEvent.dispatchEvent('disable_global_printing')
|
|
25
|
+
else :
|
|
26
|
+
gEvent.dispatchEvent('enable_global_printing')
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def setGlobalDebugLevel(level='info') :
|
|
30
|
+
env = custom_env()
|
|
31
|
+
env['debugger_global_level'] = level
|
|
32
|
+
gEvent.dispatchEvent('update_debug_level')
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class DEBUGGER:
|
|
36
|
+
def __init__(self
|
|
37
|
+
, name
|
|
38
|
+
, level='info',
|
|
39
|
+
fullSyntax=True,
|
|
40
|
+
onscreen=True,
|
|
41
|
+
log_rotation=3,
|
|
42
|
+
homePath=None,
|
|
43
|
+
id=getRandomKey(9) ,
|
|
44
|
+
global_debugger=None,
|
|
45
|
+
disable_log_write=False,
|
|
46
|
+
file_name=None,
|
|
47
|
+
seperate_files=True
|
|
48
|
+
):
|
|
49
|
+
env = custom_env()
|
|
50
|
+
setupEnvironment( 'debugger' )
|
|
51
|
+
debugger_on_screen = env.get('debugger_on_screen' , True)
|
|
52
|
+
env['debugger_on_screen'] = debugger_on_screen
|
|
53
|
+
self.env = env
|
|
54
|
+
self.events = gEvent
|
|
55
|
+
self.debuggerLabel = f"{name}"
|
|
56
|
+
self.logger = logging.getLogger(self.debuggerLabel)
|
|
57
|
+
self.set_level(level)
|
|
58
|
+
self.file_handler_class=None
|
|
59
|
+
self.LOG_SIZE_THRESHOLD_IN_BYTES = 10 * 1024 * 1024
|
|
60
|
+
self.BACKUP_COUNT = log_rotation
|
|
61
|
+
self.homePath = homePath
|
|
62
|
+
self.lastAbsoluteHomePath= None
|
|
63
|
+
self.fullSyntax=fullSyntax
|
|
64
|
+
self.onScreen= onscreen
|
|
65
|
+
self.id = id
|
|
66
|
+
self.how_many_times_write= 0
|
|
67
|
+
self.stream_service = None
|
|
68
|
+
if not env['debugger'].get(name) :
|
|
69
|
+
self.console = console_handler = logging.StreamHandler()
|
|
70
|
+
else :
|
|
71
|
+
self.console = console_handler = env['debugger'].get(name).console
|
|
72
|
+
if not self.logger.hasHandlers() :
|
|
73
|
+
self.logger.addHandler(self.console)
|
|
74
|
+
self.name = name
|
|
75
|
+
self.rotate_disabled=False
|
|
76
|
+
self.isInPyinstaller = False
|
|
77
|
+
self.log_iterations=0
|
|
78
|
+
self.log_iterations_threshold = 200
|
|
79
|
+
self.global_debugger = global_debugger
|
|
80
|
+
self.isLogWriteDisabled = disable_log_write
|
|
81
|
+
self.type = "CUSTOM_DEBUGGER"
|
|
82
|
+
self.seperate_files=seperate_files
|
|
83
|
+
if fullSyntax :
|
|
84
|
+
f = f"[%(asctime)s]-[{self.name}]-[%(levelname)s]: %(message)s"
|
|
85
|
+
else :
|
|
86
|
+
f = f"[{self.name}]-[%(levelname)s]: %(message)s"
|
|
87
|
+
self.syntax = f
|
|
88
|
+
self.formatter = logging.Formatter(f , datefmt='%Y-%m-%d %H:%M:%S' )
|
|
89
|
+
self.filename = file_name
|
|
90
|
+
path = self.homepath(homePath)
|
|
91
|
+
if not env['debugger'].get(name) :
|
|
92
|
+
console_handler.setFormatter(self.formatter)
|
|
93
|
+
if not disable_log_write :
|
|
94
|
+
if not env['debugger'].get(name) :
|
|
95
|
+
self.file_handler_class = self.createRotateFileHandler(path)
|
|
96
|
+
if onscreen :
|
|
97
|
+
self.enable_print()
|
|
98
|
+
elif not onscreen :
|
|
99
|
+
self.disable_print()
|
|
100
|
+
self.events.addEventListener('disable_global_printing' , self.disable_print )
|
|
101
|
+
self.events.addEventListener('enable_global_printing' , self.enable_print )
|
|
102
|
+
self.events.addEventListener('update_home_path' , self.updateGlobalHomePath )
|
|
103
|
+
self.events.addEventListener('update_debug_level' , self.updateGlobalSetLevel )
|
|
104
|
+
if env['debugger'].get(name) :
|
|
105
|
+
self = env['debugger'].get(name)
|
|
106
|
+
else:
|
|
107
|
+
env['debugger'][id] = self
|
|
108
|
+
env['debugger'][name] = self
|
|
109
|
+
if not env.get('debugger_on_screen' , True ) :
|
|
110
|
+
self.disable_print()
|
|
111
|
+
if env.get('debugger_on_screen' , True ) :
|
|
112
|
+
self.enable_print()
|
|
113
|
+
if os.environ.get("EASY_UTILS_DEBUG_LEVEL") :
|
|
114
|
+
EASY_UTILS_DEBUG_LEVEL = os.environ.get("EASY_UTILS_DEBUG_LEVEL")
|
|
115
|
+
if not EASY_UTILS_DEBUG_LEVEL.lower() in ['info' , 'debug' , 'warning' , 'error' , 'critical'] :
|
|
116
|
+
self.logger.error(f'EASY_UTILS_DEBUG_LEVEL ENV must be one of [info,debug,warning,error,critical] | Current Env Variable Is "{EASY_UTILS_DEBUG_LEVEL}". Skipping ')
|
|
117
|
+
else :
|
|
118
|
+
self.set_level(EASY_UTILS_DEBUG_LEVEL)
|
|
119
|
+
if os.environ.get("EASY_UTILS_ENABLE_PRINT" , '' ).lower() == 'true' :
|
|
120
|
+
self.enable_print()
|
|
121
|
+
|
|
122
|
+
def switch_full_syntax(self , toggle) :
|
|
123
|
+
if toggle :
|
|
124
|
+
f = f"[%(asctime)s]-[{self.name}]-[%(levelname)s]: %(message)s"
|
|
125
|
+
else :
|
|
126
|
+
f = f"[{self.name}]-[%(levelname)s]: %(message)s"
|
|
127
|
+
self.syntax = f
|
|
128
|
+
self.formatter = logging.Formatter(f , datefmt='%Y-%m-%d %H:%M:%S' )
|
|
129
|
+
self.console.setFormatter(self.formatter)
|
|
130
|
+
|
|
131
|
+
def custom_log_syntax(self , syntax) :
|
|
132
|
+
'''
|
|
133
|
+
f"[%(asctime)s]-[{self.name}]-[%(levelname)s]: %(message)s"
|
|
134
|
+
'''
|
|
135
|
+
f = syntax
|
|
136
|
+
self.syntax = f
|
|
137
|
+
self.formatter = logging.Formatter(f , datefmt='%Y-%m-%d %H:%M:%S' )
|
|
138
|
+
self.console.setFormatter(self.formatter)
|
|
139
|
+
|
|
140
|
+
def updateGlobalHomePath(self ) :
|
|
141
|
+
if not self.isLogWriteDisabled :
|
|
142
|
+
getFromEnv = self.env.get('debugger_homepath' , None )
|
|
143
|
+
self.homepath(getFromEnv)
|
|
144
|
+
if getFromEnv :
|
|
145
|
+
self.file_handler_class = self.createRotateFileHandler(self.homePath)
|
|
146
|
+
|
|
147
|
+
def updateGlobalSetLevel( self ) :
|
|
148
|
+
self.set_level(self.env['debugger_global_level'])
|
|
149
|
+
|
|
150
|
+
def advertiseGlobalDebugLevel(self , level) :
|
|
151
|
+
setGlobalDebugLevel(level)
|
|
152
|
+
|
|
153
|
+
def disable_rotate(self) :
|
|
154
|
+
self.rotate_disabled = True
|
|
155
|
+
|
|
156
|
+
def enable_rotate(self) :
|
|
157
|
+
self.rotate_disabled = False
|
|
158
|
+
|
|
159
|
+
def createRotateFileHandler( self , path ) :
|
|
160
|
+
old = self.file_handler_class
|
|
161
|
+
if old :
|
|
162
|
+
self.logger.removeHandler(old)
|
|
163
|
+
file_handler = RotatingFileHandler(path , maxBytes=self.LOG_SIZE_THRESHOLD_IN_BYTES , backupCount=self.BACKUP_COUNT , delay=True )
|
|
164
|
+
self.file_handler= file_handler.setFormatter(self.formatter)
|
|
165
|
+
self.logger.addHandler(file_handler)
|
|
166
|
+
return file_handler
|
|
167
|
+
|
|
168
|
+
def update_log_iterantions_threshold(self,threshold : int ):
|
|
169
|
+
'''
|
|
170
|
+
set value when rotation should be checked. when every on_log function called.
|
|
171
|
+
by default rotation will be checked every 200 on_log function call.
|
|
172
|
+
'''
|
|
173
|
+
self.log_iterations_threshold = threshold
|
|
174
|
+
|
|
175
|
+
def updateGlobalDebugger(self , logger ) :
|
|
176
|
+
'''
|
|
177
|
+
this function pass the log message to other logger to write the same log message to it.
|
|
178
|
+
logger must be debugger class.
|
|
179
|
+
'''
|
|
180
|
+
if logger.type != 'CUSTOM_DEBUGGER' :
|
|
181
|
+
raise Exception(f'Invalid logger type. must pass debugger class.')
|
|
182
|
+
self.global_debugger = logger
|
|
183
|
+
|
|
184
|
+
def getStreamServiceUrlPath(self) :
|
|
185
|
+
return self.streampath
|
|
186
|
+
|
|
187
|
+
def getStreamService(self) :
|
|
188
|
+
return self.stream_service
|
|
189
|
+
|
|
190
|
+
def isStreamServiceAvailable(self) :
|
|
191
|
+
if self.stream_service :
|
|
192
|
+
return True
|
|
193
|
+
return False
|
|
194
|
+
|
|
195
|
+
def addStreamService( self , socketio , streampath='/debugger/stream/log' ) :
|
|
196
|
+
"""
|
|
197
|
+
This function takes a live socketio server. it emit the log message using default path which is /debugger/stream/log
|
|
198
|
+
"""
|
|
199
|
+
self.stream_service = socketio
|
|
200
|
+
self.streampath = streampath
|
|
201
|
+
|
|
202
|
+
def updateLogName( self , name ) :
|
|
203
|
+
self.name = name
|
|
204
|
+
|
|
205
|
+
def disable_log_write(self) :
|
|
206
|
+
'''
|
|
207
|
+
this function is used to disable the log write to file. if onScreen is enabled, logs will be displayed only on screen.
|
|
208
|
+
'''
|
|
209
|
+
self.isLogWriteDisabled = True
|
|
210
|
+
if self.file_handler_class :
|
|
211
|
+
self.logger.removeHandler(self.file_handler_class)
|
|
212
|
+
|
|
213
|
+
def enable_log_write(self) :
|
|
214
|
+
self.createRotateFileHandler(self.homePath)
|
|
215
|
+
|
|
216
|
+
def manage_file_rotation(self, record ) :
|
|
217
|
+
handler = self.get_rotate_handler()
|
|
218
|
+
if handler.shouldRollover(record) :
|
|
219
|
+
handler.doRollover()
|
|
220
|
+
self.log_iterations = 0
|
|
221
|
+
|
|
222
|
+
def get_rotate_handler(self) :
|
|
223
|
+
return self.file_handler_class
|
|
224
|
+
|
|
225
|
+
def change_log_size(self, size) -> bool:
|
|
226
|
+
'''
|
|
227
|
+
change the size of each log file rotation.
|
|
228
|
+
default is 10M
|
|
229
|
+
size should be passed as MB
|
|
230
|
+
'''
|
|
231
|
+
size = convert_mb_to_bytes(size)
|
|
232
|
+
self.LOG_SIZE_THRESHOLD_IN_BYTES = size
|
|
233
|
+
handler = self.get_rotate_handler()
|
|
234
|
+
handler.maxBytes = size
|
|
235
|
+
return True
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def checks_in_bg(self) :
|
|
239
|
+
while True :
|
|
240
|
+
if self.env.get('GLOBAL_DEBUGGER_STREAM_SERVICE') :
|
|
241
|
+
self.addStreamService(socketio=self.env.get('GLOBAL_DEBUGGER_STREAM_SERVICE'))
|
|
242
|
+
if self.env.get('debugger_global_level' , None) :
|
|
243
|
+
self.set_level( level=self.env.get('debugger_global_level') )
|
|
244
|
+
if not self.env.get('debugger_on_screen' , True ) :
|
|
245
|
+
self.disable_print()
|
|
246
|
+
if self.env.get('debugger_on_screen' , True ) :
|
|
247
|
+
self.enable_print()
|
|
248
|
+
if os.environ.get("EASY_UTILS_DEBUG_LEVEL") :
|
|
249
|
+
EASY_UTILS_DEBUG_LEVEL = os.environ.get("EASY_UTILS_DEBUG_LEVEL")
|
|
250
|
+
if not EASY_UTILS_DEBUG_LEVEL.lower() in ['info' , 'debug' , 'warning' , 'error' , 'critical'] :
|
|
251
|
+
self.logger.error(f'EASY_UTILS_DEBUG_LEVEL ENV must be one of [info,debug,warning,error,critical] | Current Env Variable Is "{EASY_UTILS_DEBUG_LEVEL}". Skipping ')
|
|
252
|
+
else :
|
|
253
|
+
self.set_level(EASY_UTILS_DEBUG_LEVEL)
|
|
254
|
+
self.updateGlobalHomePath()
|
|
255
|
+
if os.environ.get("EASY_UTILS_ENABLE_PRINT" , '' ).lower() == 'true' :
|
|
256
|
+
self.enable_print()
|
|
257
|
+
sleep(5)
|
|
258
|
+
|
|
259
|
+
def close(self) :
|
|
260
|
+
try :
|
|
261
|
+
logging.shutdown()
|
|
262
|
+
except :
|
|
263
|
+
pass
|
|
264
|
+
|
|
265
|
+
def homepath(self , path=None ) :
|
|
266
|
+
env = custom_env()
|
|
267
|
+
getFromEnv = env.get('debugger_homepath' , None )
|
|
268
|
+
if getFromEnv is not None :
|
|
269
|
+
self.homePath = getFromEnv
|
|
270
|
+
else :
|
|
271
|
+
if path is not None :
|
|
272
|
+
if self.homePath and '.log' in str(self.homePath):
|
|
273
|
+
self.lastAbsoluteHomePath= self.homePath
|
|
274
|
+
self.homePath = path
|
|
275
|
+
else :
|
|
276
|
+
self.homePath = os.path.join(os.getcwd() , 'debug')
|
|
277
|
+
if not os.path.exists( self.homePath ) :
|
|
278
|
+
try :
|
|
279
|
+
os.makedirs( self.homePath )
|
|
280
|
+
except :
|
|
281
|
+
pass
|
|
282
|
+
if self.filename :
|
|
283
|
+
self.homePath = os.path.join( self.homePath, f'{self.filename}.log' )
|
|
284
|
+
else :
|
|
285
|
+
self.homePath = os.path.join( self.homePath, f'{self.name}.log' )
|
|
286
|
+
return self.homePath
|
|
287
|
+
|
|
288
|
+
def get_current_levels(self):
|
|
289
|
+
"""
|
|
290
|
+
Returns a list of log levels that will be printed based on the current logging level.
|
|
291
|
+
"""
|
|
292
|
+
levels_order = [
|
|
293
|
+
('debug', logging.DEBUG),
|
|
294
|
+
('info', logging.INFO),
|
|
295
|
+
('warning', logging.WARNING),
|
|
296
|
+
('error', logging.ERROR),
|
|
297
|
+
('critical', logging.CRITICAL),
|
|
298
|
+
]
|
|
299
|
+
# Optional custom level
|
|
300
|
+
if hasattr(logging, 'SCREEN'):
|
|
301
|
+
levels_order.append(('screen', logging.SCREEN))
|
|
302
|
+
current_level = self.logger.level
|
|
303
|
+
# Return all levels with numeric value >= current_level
|
|
304
|
+
return [name for name, value in levels_order if value >= current_level]
|
|
305
|
+
|
|
306
|
+
def enable_print(self) :
|
|
307
|
+
self.onScreen = True
|
|
308
|
+
self.logger.addHandler(self.console)
|
|
309
|
+
|
|
310
|
+
def disable_print(self) :
|
|
311
|
+
self.onScreen = False
|
|
312
|
+
self.logger.removeHandler(self.console)
|
|
313
|
+
|
|
314
|
+
def changeHomePath( self , path ) :
|
|
315
|
+
p = self.homepath(path)
|
|
316
|
+
self.file_handler_class = self.createRotateFileHandler(p)
|
|
317
|
+
if self.lastAbsoluteHomePath :
|
|
318
|
+
os.remove(self.lastAbsoluteHomePath)
|
|
319
|
+
|
|
320
|
+
def isGlobalDebuggerDefined(self) :
|
|
321
|
+
if self.global_debugger :
|
|
322
|
+
return True
|
|
323
|
+
else :
|
|
324
|
+
return False
|
|
325
|
+
|
|
326
|
+
def set_level(self, level : str):
|
|
327
|
+
if 'info' in level.lower() : lvl = logging.INFO
|
|
328
|
+
elif 'warn' in level.lower() : lvl = logging.WARNING
|
|
329
|
+
elif 'warning' in level.lower() : lvl = logging.WARNING
|
|
330
|
+
elif 'critical' in level.lower() : lvl = logging.CRITICAL
|
|
331
|
+
elif 'debug' in level.lower() : lvl = logging.DEBUG
|
|
332
|
+
elif 'error' in level.lower() : lvl = logging.ERROR
|
|
333
|
+
elif 'screen' in level.lower() : lvl = logging.SCREEN
|
|
334
|
+
else : raise ValueError('Unknown level, not one of [info,warn,warning,critical,debug,error,screen]')
|
|
335
|
+
self.currentDebugLevel = level
|
|
336
|
+
self.logger.setLevel(lvl)
|
|
337
|
+
|
|
338
|
+
def get_current_debug_level(self) :
|
|
339
|
+
return self.currentDebugLevel
|
|
340
|
+
|
|
341
|
+
def get_logger(self) :
|
|
342
|
+
return self.logger
|
|
343
|
+
|
|
344
|
+
def before_log(self , message , level) :
|
|
345
|
+
|
|
346
|
+
def __call_thread__() :
|
|
347
|
+
if not level in self.get_current_levels() :
|
|
348
|
+
return
|
|
349
|
+
if self.isStreamServiceAvailable() :
|
|
350
|
+
d = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
|
351
|
+
streamUrl = self.getStreamServiceUrlPath()
|
|
352
|
+
self.stream_service.emit( streamUrl , {
|
|
353
|
+
'message' : message ,
|
|
354
|
+
'level' : level ,
|
|
355
|
+
'msg' : message,
|
|
356
|
+
'date' : d ,
|
|
357
|
+
'id' : self.id,
|
|
358
|
+
'formate' : 'json' ,
|
|
359
|
+
'source' : self.name ,
|
|
360
|
+
'getTimestamp' : getTimestamp()
|
|
361
|
+
})
|
|
362
|
+
try :
|
|
363
|
+
t= Thread(target=__call_thread__)
|
|
364
|
+
# t.daemon=True
|
|
365
|
+
t.start()
|
|
366
|
+
except :
|
|
367
|
+
__call_thread__()
|
|
368
|
+
|
|
369
|
+
params = {
|
|
370
|
+
'screen' : True ,
|
|
371
|
+
'file': True
|
|
372
|
+
}
|
|
373
|
+
if self.onScreen and self.env['debugger_on_screen'] == True :
|
|
374
|
+
params['screen'] = True
|
|
375
|
+
else :
|
|
376
|
+
params['screen'] = False
|
|
377
|
+
return params
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
def info(self, message , external_debugger=None,source=None):
|
|
381
|
+
def __call__(message) :
|
|
382
|
+
if source :
|
|
383
|
+
message = f'[{source}]-{message}'
|
|
384
|
+
self.before_log(message , 'info')
|
|
385
|
+
self.logger.info(message)
|
|
386
|
+
if self.isGlobalDebuggerDefined() :
|
|
387
|
+
self.global_debugger.info(message)
|
|
388
|
+
if external_debugger :
|
|
389
|
+
external_debugger.info(message)
|
|
390
|
+
try :
|
|
391
|
+
r=Thread(target=__call__,args=[message])
|
|
392
|
+
r.daemon=True
|
|
393
|
+
r.start()
|
|
394
|
+
r.join()
|
|
395
|
+
except :
|
|
396
|
+
__call__(message)
|
|
397
|
+
|
|
398
|
+
def debug(self, message , external_debugger=None,source=None):
|
|
399
|
+
def __call__(message) :
|
|
400
|
+
if source :
|
|
401
|
+
message = f'[{source}]-{message}'
|
|
402
|
+
self.before_log(message , 'debug')
|
|
403
|
+
self.logger.debug(message)
|
|
404
|
+
if self.isGlobalDebuggerDefined() :
|
|
405
|
+
self.global_debugger.debug(message)
|
|
406
|
+
if external_debugger :
|
|
407
|
+
external_debugger.debug(message)
|
|
408
|
+
try :
|
|
409
|
+
r=Thread(target=__call__,args=[message])
|
|
410
|
+
r.daemon=True
|
|
411
|
+
r.start()
|
|
412
|
+
r.join()
|
|
413
|
+
except :
|
|
414
|
+
__call__(message)
|
|
415
|
+
|
|
416
|
+
def warning(self, message , external_debugger=None,source=None):
|
|
417
|
+
def __call__(message) :
|
|
418
|
+
if source :
|
|
419
|
+
message = f'[{source}]-{message}'
|
|
420
|
+
self.before_log(message , 'warning')
|
|
421
|
+
self.logger.warning(message)
|
|
422
|
+
if self.isGlobalDebuggerDefined() :
|
|
423
|
+
self.global_debugger.warning(message)
|
|
424
|
+
if external_debugger :
|
|
425
|
+
external_debugger.warning(message)
|
|
426
|
+
try :
|
|
427
|
+
r=Thread(target=__call__,args=[message])
|
|
428
|
+
r.daemon=True
|
|
429
|
+
r.start()
|
|
430
|
+
r.join()
|
|
431
|
+
except :
|
|
432
|
+
__call__(message)
|
|
433
|
+
|
|
434
|
+
def error(self, message,external_debugger=None,source=None):
|
|
435
|
+
def __call__(message) :
|
|
436
|
+
if source :
|
|
437
|
+
message = f'[{source}]-{message}'
|
|
438
|
+
self.before_log(message , 'error')
|
|
439
|
+
self.logger.error(message)
|
|
440
|
+
if self.isGlobalDebuggerDefined() :
|
|
441
|
+
self.global_debugger.error(message)
|
|
442
|
+
if external_debugger :
|
|
443
|
+
external_debugger.error(message)
|
|
444
|
+
try :
|
|
445
|
+
r=Thread(target=__call__,args=[message])
|
|
446
|
+
r.daemon=True
|
|
447
|
+
r.start()
|
|
448
|
+
r.join()
|
|
449
|
+
except :
|
|
450
|
+
__call__(message)
|
|
451
|
+
|
|
452
|
+
def critical(self, message,external_debugger=None,source=None):
|
|
453
|
+
def __call__() :
|
|
454
|
+
if source :
|
|
455
|
+
message = f'[{source}]-{message}'
|
|
456
|
+
self.before_log(message , 'critical')
|
|
457
|
+
self.logger.critical(message)
|
|
458
|
+
if self.isGlobalDebuggerDefined() :
|
|
459
|
+
self.global_debugger.critical(message)
|
|
460
|
+
if external_debugger :
|
|
461
|
+
external_debugger.critical(message)
|
|
462
|
+
try :
|
|
463
|
+
r=Thread(target=__call__,args=[message])
|
|
464
|
+
r.daemon=True
|
|
465
|
+
r.start()
|
|
466
|
+
r.join()
|
|
467
|
+
except :
|
|
468
|
+
__call__(message)
|
|
469
|
+
|
|
470
|
+
def screen(self, message,external_debugger=None,source=None):
|
|
471
|
+
def __call__() :
|
|
472
|
+
if source :
|
|
473
|
+
message = f'[{source}]-{message}'
|
|
474
|
+
self.before_log(message , 'critical')
|
|
475
|
+
print(f"{self.syntax}")
|
|
476
|
+
if self.isGlobalDebuggerDefined() :
|
|
477
|
+
self.global_debugger.critical(message)
|
|
478
|
+
if external_debugger :
|
|
479
|
+
external_debugger.critical(message)
|
|
480
|
+
try :
|
|
481
|
+
r=Thread(target=__call__,args=[message])
|
|
482
|
+
r.daemon=True
|
|
483
|
+
r.start()
|
|
484
|
+
r.join()
|
|
485
|
+
except :
|
|
486
|
+
__call__(message)
|
easy_utils_dev/debugger.py
CHANGED
|
@@ -128,6 +128,15 @@ class DEBUGGER:
|
|
|
128
128
|
self.formatter = logging.Formatter(f , datefmt='%Y-%m-%d %H:%M:%S' )
|
|
129
129
|
self.console.setFormatter(self.formatter)
|
|
130
130
|
|
|
131
|
+
def custom_log_syntax(self , syntax) :
|
|
132
|
+
'''
|
|
133
|
+
f"[%(asctime)s]-[{self.name}]-[%(levelname)s]: %(message)s"
|
|
134
|
+
'''
|
|
135
|
+
f = syntax
|
|
136
|
+
self.syntax = f
|
|
137
|
+
self.formatter = logging.Formatter(f , datefmt='%Y-%m-%d %H:%M:%S' )
|
|
138
|
+
self.console.setFormatter(self.formatter)
|
|
139
|
+
|
|
131
140
|
def updateGlobalHomePath(self ) :
|
|
132
141
|
if not self.isLogWriteDisabled :
|
|
133
142
|
getFromEnv = self.env.get('debugger_homepath' , None )
|
easy_utils_dev/temp_memory.py
CHANGED
|
@@ -17,21 +17,28 @@ class TemporaryMemory :
|
|
|
17
17
|
sleep(5)
|
|
18
18
|
now = getTimestamp()
|
|
19
19
|
for key, value in list(self.store.items()) :
|
|
20
|
-
if
|
|
21
|
-
if value.get('
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
if value.get('removeTimestamp') :
|
|
21
|
+
if now >= value.get('removeTimestamp' , 0) :
|
|
22
|
+
if value.get('store_deleted_key' , False) :
|
|
23
|
+
self.deletions.append(key)
|
|
24
|
+
self.delete(key)
|
|
24
25
|
|
|
25
26
|
def delete(self , key ) :
|
|
26
|
-
|
|
27
|
+
try :
|
|
28
|
+
del self.store[key]
|
|
29
|
+
except :
|
|
30
|
+
pass
|
|
27
31
|
gc.collect()
|
|
28
32
|
|
|
29
|
-
def get(self , key ) :
|
|
30
|
-
return self.store.get(key , {}).get('item')
|
|
33
|
+
def get(self , key , default=None ) :
|
|
34
|
+
return self.store.get(key , {}).get('item' , default)
|
|
31
35
|
|
|
32
36
|
def save(self, item , custom_key=None ,auto_destroy_period=60 , store_deleted_key=True) :
|
|
33
37
|
now = getTimestamp()
|
|
34
|
-
|
|
38
|
+
if auto_destroy_period :
|
|
39
|
+
later = getTimestamp(after_seconds=auto_destroy_period)
|
|
40
|
+
else :
|
|
41
|
+
later = None
|
|
35
42
|
if not custom_key :
|
|
36
43
|
custom_key = f"{getTimestamp()}-{generateToken(iter=4)}".upper()
|
|
37
44
|
self.store[custom_key] = {
|
easy_utils_dev/uiserver.py
CHANGED
|
@@ -1,18 +1,23 @@
|
|
|
1
|
+
import copy
|
|
2
|
+
import gc
|
|
3
|
+
import time
|
|
1
4
|
from werkzeug.serving import ThreadedWSGIServer
|
|
2
5
|
from easy_utils_dev.utils import getRandomKey , generateToken , getTimestamp
|
|
3
6
|
from flask_socketio import SocketIO
|
|
4
7
|
from engineio.async_drivers import gevent
|
|
5
|
-
from engineio.async_drivers import threading
|
|
6
8
|
from flask_cors import CORS
|
|
7
9
|
import logging , os
|
|
10
|
+
from flask import jsonify, request , current_app
|
|
8
11
|
from flask import Flask
|
|
9
12
|
from threading import Thread
|
|
13
|
+
import threading
|
|
10
14
|
from easy_utils_dev.custom_env import cenv
|
|
11
15
|
from easy_utils_dev.utils import kill_thread
|
|
12
16
|
from multiprocessing import Process
|
|
13
17
|
from werkzeug.serving import make_ssl_devcert
|
|
14
18
|
from time import sleep
|
|
15
19
|
from easy_utils_dev.utils import start_thread , getRandomKeysAndStr , mkdirs
|
|
20
|
+
from easy_utils_dev.temp_memory import TemporaryMemory
|
|
16
21
|
|
|
17
22
|
def getClassById( id ) :
|
|
18
23
|
return cenv[id]
|
|
@@ -23,87 +28,54 @@ def create_ssl(host,output) :
|
|
|
23
28
|
output: the output locaiton to generate the ssl certificate. it should end with filename without extension
|
|
24
29
|
'''
|
|
25
30
|
return make_ssl_devcert( output , host=host)
|
|
26
|
-
|
|
27
|
-
|
|
28
31
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
32
|
+
def clone_request(request):
|
|
33
|
+
"""Return a plain dict clone of Flask request data."""
|
|
34
|
+
return {
|
|
35
|
+
"method": request.method,
|
|
36
|
+
"path": request.path,
|
|
37
|
+
"url": request.url,
|
|
38
|
+
"headers": dict(request.headers),
|
|
39
|
+
"args": request.args.to_dict(flat=False),
|
|
40
|
+
"form": request.form.to_dict(flat=False),
|
|
41
|
+
"json": request.get_json(silent=True),
|
|
42
|
+
"data": request.get_data(), # raw body bytes
|
|
43
|
+
"files": {k: v.filename for k, v in request.files.items()},
|
|
44
|
+
"remote_addr": request.remote_addr,
|
|
45
|
+
"cookies": request.cookies,
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
class AbortRequest :
|
|
49
|
+
def __init__(self, request ) :
|
|
50
|
+
self.request = clone_request(request)
|
|
51
|
+
self.abort_id = None
|
|
52
|
+
self.abortable = False
|
|
33
53
|
self.thread = None
|
|
34
|
-
self.
|
|
35
|
-
self.
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
self.starttimestamp = getTimestamp()
|
|
39
|
-
self.endtimestamp = 0
|
|
40
|
-
self.kill = self.api_abort
|
|
41
|
-
pass
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
def abortable_process(self , operation , args=[] , kwargs={}) :
|
|
45
|
-
def thread_run() :
|
|
46
|
-
try :
|
|
47
|
-
self.result = operation( *args, **kwargs )
|
|
48
|
-
self.error = False
|
|
49
|
-
self.message = ''
|
|
50
|
-
self.endtimestamp = getTimestamp()
|
|
51
|
-
except Exception as error :
|
|
52
|
-
self.error = True
|
|
53
|
-
self.message = str(error)
|
|
54
|
-
thread = self.thread = start_thread( target = thread_run )
|
|
55
|
-
while thread.is_alive() :
|
|
56
|
-
sleep(.1)
|
|
57
|
-
if self.aborted :
|
|
58
|
-
self.response = {
|
|
59
|
-
'message' : 'request aborted.' ,
|
|
60
|
-
'id' : self.requestId ,
|
|
61
|
-
'status' : 405 ,
|
|
62
|
-
'result' : None,
|
|
63
|
-
'error' : self.error ,
|
|
64
|
-
'error_message' : '',
|
|
65
|
-
'starttimestamp' : self.starttimestamp,
|
|
66
|
-
'endtimestamp' : self.endtimestamp,
|
|
67
|
-
'aborted' : True,
|
|
68
|
-
'threadIsAlive' : thread.is_alive()
|
|
69
|
-
}
|
|
70
|
-
return self.response
|
|
71
|
-
sleep(.2)
|
|
72
|
-
self.response = {
|
|
73
|
-
'message' : 'request completed.' ,
|
|
74
|
-
'id' : self.requestId ,
|
|
75
|
-
'status' : 200 ,
|
|
76
|
-
'result' : self.result ,
|
|
77
|
-
'error' : self.error ,
|
|
78
|
-
'error_message' : self.message ,
|
|
79
|
-
'starttimestamp' : self.starttimestamp,
|
|
80
|
-
'endtimestamp' : self.endtimestamp,
|
|
81
|
-
'aborted' : False ,
|
|
82
|
-
'threadIsAlive' : thread.is_alive()
|
|
83
|
-
}
|
|
84
|
-
return self.response
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
def api_abort(self) :
|
|
88
|
-
self.endtimestamp = getTimestamp()
|
|
54
|
+
self.cache = None
|
|
55
|
+
self.start_ts = getTimestamp()
|
|
56
|
+
|
|
57
|
+
def abort(self) :
|
|
89
58
|
kill_thread(self.thread)
|
|
90
|
-
|
|
91
|
-
|
|
59
|
+
self.cache.delete(self.abort_id)
|
|
60
|
+
try :
|
|
61
|
+
gc.collect()
|
|
62
|
+
except :
|
|
63
|
+
pass
|
|
92
64
|
|
|
93
65
|
|
|
94
66
|
class UISERVER :
|
|
95
67
|
def __init__(self ,
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
68
|
+
id=getRandomKey(n=15),
|
|
69
|
+
secretkey=generateToken(),
|
|
70
|
+
address='localhost',
|
|
71
|
+
port=5312 ,
|
|
72
|
+
https=False ,
|
|
73
|
+
ssl_crt=None,
|
|
74
|
+
ssl_key=None,
|
|
75
|
+
template_folder='templates/' ,
|
|
76
|
+
static_folder = 'templates/assets'
|
|
77
|
+
,**kwargs
|
|
78
|
+
) -> None:
|
|
107
79
|
self.id = id
|
|
108
80
|
self.static_folder = static_folder
|
|
109
81
|
self.app = app = Flask(self.id , template_folder=template_folder , static_folder=self.static_folder )
|
|
@@ -116,6 +88,7 @@ class UISERVER :
|
|
|
116
88
|
self.ssl_key=ssl_key
|
|
117
89
|
self.enable_test_url=True
|
|
118
90
|
self.abort_requests = {}
|
|
91
|
+
self.abort_base_url = '/request/abort'
|
|
119
92
|
if https :
|
|
120
93
|
self.httpProtocol = 'https'
|
|
121
94
|
else :
|
|
@@ -123,18 +96,78 @@ class UISERVER :
|
|
|
123
96
|
self.socketio = SocketIO(app , cors_allowed_origins="*" ,async_mode='threading' , engineio_logger=False , always_connect=True ,**kwargs )
|
|
124
97
|
cenv[id] = self
|
|
125
98
|
self.fullAddress = f"{self.httpProtocol}://{self.address}:{self.port}"
|
|
99
|
+
self.cache = TemporaryMemory()
|
|
126
100
|
|
|
127
101
|
def update_cert(self , crt, ssl ) :
|
|
128
102
|
self.ssl_crt=crt
|
|
129
103
|
self.ssl_key=ssl
|
|
130
104
|
|
|
105
|
+
def register_abortable_request(self , request , abort_id = None ) :
|
|
106
|
+
path = request.path
|
|
107
|
+
Abort = AbortRequest(request)
|
|
108
|
+
if not path.startswith(self.abort_base_url) :
|
|
109
|
+
if not abort_id :
|
|
110
|
+
if not request.headers.get('abortid') :
|
|
111
|
+
abort_id = getRandomKeysAndStr(n=20)
|
|
112
|
+
else :
|
|
113
|
+
abort_id = request.headers.get('abortid')
|
|
114
|
+
|
|
115
|
+
Abort.abort_id = abort_id
|
|
116
|
+
current_thread = threading.current_thread()
|
|
117
|
+
Abort.thread = current_thread
|
|
118
|
+
Abort.cache = self.cache
|
|
119
|
+
Abort.start_ts = getTimestamp()
|
|
120
|
+
self.cache.set( Abort , custom_key=abort_id , auto_destroy_period=120 , store_deleted_key=False )
|
|
121
|
+
return Abort
|
|
122
|
+
|
|
123
|
+
def start_before_request(self) :
|
|
124
|
+
|
|
125
|
+
@self.app.route(f'{self.abort_base_url}/<id>' , methods=['DELETE'])
|
|
126
|
+
def abort_request(id : str ) :
|
|
127
|
+
abort : AbortRequest = self.cache.get(id)
|
|
128
|
+
timestamp = getTimestamp()
|
|
129
|
+
if abort :
|
|
130
|
+
abort.abort()
|
|
131
|
+
for i in range(30) :
|
|
132
|
+
th = abort.thread
|
|
133
|
+
alive = th.is_alive()
|
|
134
|
+
if not alive :
|
|
135
|
+
break
|
|
136
|
+
time.sleep(.25)
|
|
137
|
+
return { 'status' : 200 , 'message' : 'Request aborted' , 'abort_timestamp' : timestamp , 'abort_id' : id , 'alive' : alive , 'url' : abort.request.get('path')}
|
|
138
|
+
else :
|
|
139
|
+
return { 'status' : 404 , 'message' : 'Request not found or request is not abortable. Check request headers for abortable flag.'}
|
|
140
|
+
|
|
141
|
+
@self.app.before_request
|
|
142
|
+
def before_request() :
|
|
143
|
+
abortable = request.headers.get('abortable')
|
|
144
|
+
if abortable :
|
|
145
|
+
abort = self.register_abortable_request(request)
|
|
146
|
+
request.abortable = True
|
|
147
|
+
request.abort_id = abort.abort_id
|
|
148
|
+
# check here if async in the headers
|
|
149
|
+
# if yes . i will trigger the function in thread
|
|
150
|
+
# start_tread(#how to get the target function here ? )
|
|
151
|
+
# now i want to return response to UI { status : 200 , message : 'request now in running bg' , abort_id : abort.abort_id }
|
|
152
|
+
# the flask function should not be called again
|
|
153
|
+
if request.headers.get('async') == 'false' :
|
|
154
|
+
target_func = current_app.view_functions.get(request.endpoint)
|
|
155
|
+
if not target_func:
|
|
156
|
+
return jsonify({"error": "Route not found"}), 404
|
|
157
|
+
th = start_thread(target=target_func, args=request.args, kwargs=request.form)
|
|
158
|
+
abort.thread = th
|
|
159
|
+
return {"status": 200, "message": "Request now in running bg", "abort_id": abort.abort_id} , 200
|
|
160
|
+
|
|
161
|
+
@self.app.after_request
|
|
162
|
+
def after_request(response) :
|
|
163
|
+
try :
|
|
164
|
+
if request.abortable :
|
|
165
|
+
response.headers['abortid'] = request.abort_id
|
|
166
|
+
response.headers['abortable'] = True
|
|
167
|
+
except :
|
|
168
|
+
response.headers['abortable'] = False
|
|
169
|
+
return response
|
|
131
170
|
|
|
132
|
-
def getAbort(self , id ) :
|
|
133
|
-
result : Abort = self.abort_requests.get(id , Abort)
|
|
134
|
-
return result
|
|
135
|
-
|
|
136
|
-
def updateAbort( self , id , abort ) :
|
|
137
|
-
self.abort_requests[id] = abort
|
|
138
171
|
|
|
139
172
|
def getInstance(self) :
|
|
140
173
|
return self.getFlask() , self.getSocketio() , self.getWsgi()
|
|
@@ -181,6 +214,7 @@ class UISERVER :
|
|
|
181
214
|
wsgi_server.serve_forever()
|
|
182
215
|
|
|
183
216
|
def startUi(self ,daemon , suppress_prints=True) :
|
|
217
|
+
self.start_before_request()
|
|
184
218
|
self.thread = self.flaskprocess = Thread(target=self.thrStartUi , args=[suppress_prints])
|
|
185
219
|
self.flaskprocess.daemon = False
|
|
186
220
|
self.flaskprocess.start()
|
easy_utils_dev/wsnoclib.py
CHANGED
|
@@ -80,6 +80,7 @@ class WSNOCLIB :
|
|
|
80
80
|
self.refresh_inprogress = False
|
|
81
81
|
self.kafka = kafka
|
|
82
82
|
self.refresh_thread = None
|
|
83
|
+
self.token_refresh_count = 0
|
|
83
84
|
self.session = WSNOCSession(self)
|
|
84
85
|
if register_atexit :
|
|
85
86
|
atexit.register(self.goodbye)
|
|
@@ -135,7 +136,7 @@ class WSNOCLIB :
|
|
|
135
136
|
data = {
|
|
136
137
|
"grant_type": "client_credentials"
|
|
137
138
|
}
|
|
138
|
-
r = self.session.post(url = URL , headers=headers , auth=BAuth(self.username, self.password), json=data)
|
|
139
|
+
r = self.session.post(url = URL , headers=headers , auth=BAuth(self.username, self.password), json=data , retries=5)
|
|
139
140
|
if not r.ok :
|
|
140
141
|
self.logger.debug(f'fail message {r.text}')
|
|
141
142
|
raise Exception(f'Failed to authenticate WSNOC. Return status code : {r.status_code}')
|
|
@@ -200,6 +201,7 @@ class WSNOCLIB :
|
|
|
200
201
|
if self.kafka.enable_auto_refresh :
|
|
201
202
|
self.renewSubscription()
|
|
202
203
|
self.kafka.refresh_inprogress = False
|
|
204
|
+
self.token_refresh_count += 1
|
|
203
205
|
self.runAutoRefreshThread()
|
|
204
206
|
|
|
205
207
|
def kafka_connect( self ,
|
|
@@ -704,17 +706,18 @@ class WSNOCSession(requests.Session):
|
|
|
704
706
|
self._wsnoc = wsnoc
|
|
705
707
|
self.verify = False
|
|
706
708
|
self.retries = 0
|
|
709
|
+
self.debug_this_request = False
|
|
707
710
|
self.skip_hold_for_token_refresh = False
|
|
708
711
|
|
|
709
712
|
def rebuild_auth(self, prepared_request, response):
|
|
710
713
|
return
|
|
711
714
|
|
|
712
|
-
def hold_for_token_refresh(self, url) :
|
|
715
|
+
def hold_for_token_refresh(self, url=None) :
|
|
713
716
|
while self._wsnoc.refresh_inprogress :
|
|
714
|
-
self._wsnoc.logger.
|
|
715
|
-
sleep(.
|
|
717
|
+
self._wsnoc.logger.info(f'Waiting for token refresh. {url if url else "No URL"}')
|
|
718
|
+
sleep(.5)
|
|
716
719
|
|
|
717
|
-
def request(self, method, url , retries=0 , skip_hold_for_token_refresh=False , **kwargs):
|
|
720
|
+
def request(self, method, url , retries=0 , skip_hold_for_token_refresh=False , debug_this_request=False , **kwargs):
|
|
718
721
|
self._wsnoc.logger.debug(f'[{method}] : {url}')
|
|
719
722
|
if not skip_hold_for_token_refresh :
|
|
720
723
|
self.hold_for_token_refresh(url)
|
|
@@ -726,12 +729,21 @@ class WSNOCSession(requests.Session):
|
|
|
726
729
|
request_headers['Authorization'] = token
|
|
727
730
|
kwargs['headers'] = request_headers
|
|
728
731
|
request = super().request(method, url, **kwargs )
|
|
732
|
+
if debug_this_request :
|
|
733
|
+
self._wsnoc.logger.info(f'''
|
|
734
|
+
[DEBUG] [{method}] : {url}
|
|
735
|
+
[DEBUG] Headers: {request_headers}
|
|
736
|
+
[DEBUG] Body: {kwargs.get('data' , {})}
|
|
737
|
+
[DEBUG] Response: {request.text}
|
|
738
|
+
[DEBUG] OK: {request.ok}
|
|
739
|
+
[DEBUG] Method: {request.request.method}
|
|
740
|
+
''')
|
|
729
741
|
for i in range(retries) :
|
|
730
742
|
if request.ok :
|
|
731
743
|
break
|
|
732
744
|
if not request.ok :
|
|
733
745
|
sleep(1)
|
|
734
|
-
self.hold_for_token_refresh()
|
|
746
|
+
self.hold_for_token_refresh(url)
|
|
735
747
|
request = super().request(method, url, **kwargs )
|
|
736
748
|
self._wsnoc.logger.debug(f'[Try-{i}] [{method}] : {url}- {request.status_code}')
|
|
737
749
|
self._wsnoc.logger.info(f'[{method}] : {url} - [{request.status_code}]')
|
|
@@ -1,21 +1,23 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
2
|
-
Name:
|
|
3
|
-
Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: easy_utils_dev
|
|
3
|
+
Version: 2.141
|
|
4
4
|
Keywords: python3
|
|
5
5
|
Classifier: Programming Language :: Python :: 3
|
|
6
6
|
Requires-Dist: psutil
|
|
7
7
|
Requires-Dist: ping3
|
|
8
8
|
Requires-Dist: flask
|
|
9
|
-
Requires-Dist:
|
|
9
|
+
Requires-Dist: flask_cors
|
|
10
10
|
Requires-Dist: xmltodict
|
|
11
11
|
Requires-Dist: paramiko
|
|
12
12
|
Requires-Dist: oracledb
|
|
13
13
|
Requires-Dist: requests
|
|
14
|
-
Requires-Dist:
|
|
14
|
+
Requires-Dist: flask_socketio
|
|
15
15
|
Requires-Dist: python-dotenv
|
|
16
16
|
Requires-Dist: gevent
|
|
17
17
|
Requires-Dist: pyzipper
|
|
18
18
|
Requires-Dist: pyjwt
|
|
19
19
|
Requires-Dist: authlib
|
|
20
20
|
Requires-Dist: kafka-python
|
|
21
|
-
|
|
21
|
+
Dynamic: classifier
|
|
22
|
+
Dynamic: keywords
|
|
23
|
+
Dynamic: requires-dist
|
|
@@ -8,7 +8,8 @@ easy_utils_dev/brevosmtp.py,sha256=A5n13MnVQnDuSjYQ91-MLftmqfg3VQ-36Zqw9OtoTB4,3
|
|
|
8
8
|
easy_utils_dev/check_license.py,sha256=C8vKXwaduoF3FSuDJ-J_j5jStNNyUdS-mOdLNfsCjmc,4825
|
|
9
9
|
easy_utils_dev/cplib.py,sha256=TPCFNQh4jYSGsm_CrMdivmD3G5B1rprco2O4B0BSnzo,19570
|
|
10
10
|
easy_utils_dev/custom_env.py,sha256=vxrjikpSNJlKfoBE-ef88UExlpXucUe-HcwHMn3gfB0,1510
|
|
11
|
-
easy_utils_dev/debugger.py,sha256=
|
|
11
|
+
easy_utils_dev/debugger-C-PF4PAMMP.py,sha256=5GmZU1l9R3CSZVIGff-l-gYaf-imJGSGTsh-cxNz6og,18637
|
|
12
|
+
easy_utils_dev/debugger.py,sha256=5GmZU1l9R3CSZVIGff-l-gYaf-imJGSGTsh-cxNz6og,18637
|
|
12
13
|
easy_utils_dev/easy_oracle.py,sha256=Jyc3HSl6eyLayjS8NoE4GOaf8otQlonR5_qOg2h1DjE,2157
|
|
13
14
|
easy_utils_dev/encryptor.py,sha256=f5Zjn0DGtXCyhldpVnBtfcTb4h4Wp0eQPHusEYwIags,1512
|
|
14
15
|
easy_utils_dev/ept.py,sha256=X-Z0_XCNfKK1TxQZquPMBqo3ZuxnK4TvWAYPnBUMlPI,25363
|
|
@@ -23,15 +24,15 @@ easy_utils_dev/openid_server.py,sha256=_odeg6omuizSUEJLtbAVn2PnG9vkcUAQ7rU3K5yXk
|
|
|
23
24
|
easy_utils_dev/optics_utils.py,sha256=G-hFX2iiUCSJjk7BICBRGvVoDq0IBONLZSjagoB5FMg,964
|
|
24
25
|
easy_utils_dev/require_auth.py,sha256=UsYAxfLX5wda6hd0nfLR_tl0bGQ4DYIpaTowsYSku-E,881
|
|
25
26
|
easy_utils_dev/simple_sqlite.py,sha256=J-mcTUnHmAn0eCPD8j-WEoA19uzHRXJ4YRJsyx9B-do,13113
|
|
26
|
-
easy_utils_dev/temp_memory.py,sha256=
|
|
27
|
-
easy_utils_dev/uiserver.py,sha256=
|
|
27
|
+
easy_utils_dev/temp_memory.py,sha256=0Dx_vNUSFQRMtJZNT8tUZXubcG7T6jxevw2WYuGmVe8,1658
|
|
28
|
+
easy_utils_dev/uiserver.py,sha256=zR-3DCo2FUF6Z7vpnzrKuWUl1MC0TparS8_LEDOyuuc,8886
|
|
28
29
|
easy_utils_dev/utils.py,sha256=BmVnbxc336c6WTeDFcEHN6Mavt7fJrIEyK4GXODV3gI,13345
|
|
29
30
|
easy_utils_dev/winserviceapi.py,sha256=2ZP6jaSt1-5vEJYXqwBhwX-1-eQ3V3YzntsoOoko2cw,18804
|
|
30
|
-
easy_utils_dev/wsnoclib.py,sha256=
|
|
31
|
+
easy_utils_dev/wsnoclib.py,sha256=kqkOkiafXnrIgdlV__3uyrAoqxG5dmIHnwqm0EkwS2U,33457
|
|
31
32
|
easy_utils_dev/wsselib.py,sha256=YweScnoAAH_t29EeIjBpkQ6HtX0Rp9mQudRsRce2SE8,7920
|
|
32
33
|
easy_utils_dev/ept_sql/create_dirs.sql,sha256=KWfX-Nc6lvr_BC-P6O97NE0idoPW4GNKUKUCgonJhto,3508
|
|
33
34
|
easy_utils_dev/ept_sql/create_ept_tables.sql,sha256=WDHyIyeReV8_QaYBPIpSy-lto3OKvZtex1tWs-FPURQ,67737
|
|
34
|
-
easy_utils_dev-2.
|
|
35
|
-
easy_utils_dev-2.
|
|
36
|
-
easy_utils_dev-2.
|
|
37
|
-
easy_utils_dev-2.
|
|
35
|
+
easy_utils_dev-2.141.dist-info/METADATA,sha256=dvjJ7N765oLyHIafIY7Q6mh53jjxqeJlLS7vjiSEUxg,572
|
|
36
|
+
easy_utils_dev-2.141.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
37
|
+
easy_utils_dev-2.141.dist-info/top_level.txt,sha256=7vBsrpq7NmilkdU3YUvfd5iVDNBaT07u_-ut4F7zc7A,15
|
|
38
|
+
easy_utils_dev-2.141.dist-info/RECORD,,
|
|
File without changes
|