codenerix-lib 1.1.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.
@@ -0,0 +1,640 @@
1
+ #
2
+ # django-codenerix
3
+ #
4
+ # Codenerix GNU
5
+ #
6
+ # Project URL : http://www.codenerix.com
7
+ #
8
+ # Licensed under the Apache License, Version 2.0 (the "License");
9
+ # you may not use this file except in compliance with the License.
10
+ # You may obtain a copy of the License at
11
+ #
12
+ # http://www.apache.org/licenses/LICENSE-2.0
13
+ #
14
+ # Unless required by applicable law or agreed to in writing, software
15
+ # distributed under the License is distributed on an "AS IS" BASIS,
16
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
+ # See the License for the specific language governing permissions and
18
+ # limitations under the License.
19
+ """
20
+ Debugger helps to debug the system
21
+ """
22
+
23
+ from datetime import datetime
24
+ from inspect import currentframe
25
+ from os import getcwd
26
+ from pathlib import Path
27
+ from typing import Any, TextIO
28
+
29
+ from codenerix_lib.colors import colors, html_colors
30
+
31
+ __version__ = "2023033000"
32
+
33
+ __all__ = ["Debugger", "lineno", "__FILE__", "__LINE__"]
34
+
35
+
36
+ def lineno():
37
+ """
38
+ Returns the current line number in our program.
39
+ """
40
+ return currentframe().f_back.f_lineno
41
+
42
+
43
+ def __LINE__(): # noqa: N802,N807
44
+ """
45
+ Returns the current line number in our program.
46
+ """
47
+ return currentframe().f_back.f_lineno
48
+
49
+
50
+ def __FILE__(): # noqa: N802,N807
51
+ """
52
+ Returns the current line number in our program.
53
+ """
54
+ filename = currentframe().f_back.f_code.co_filename.replace(getcwd(), ".")
55
+ if len(filename) >= 2 and filename[0] == "." and filename[1] == "/":
56
+ filename = filename[2:]
57
+ return filename
58
+
59
+
60
+ class Debugger:
61
+ __indebug: dict[str, tuple[str | Path | TextIO, list[str]]] = {}
62
+ __inname = None
63
+
64
+ KINDS = [
65
+ "primary",
66
+ "secondary",
67
+ "success",
68
+ "danger",
69
+ "warning",
70
+ "info",
71
+ "error",
72
+ ]
73
+
74
+ def __init__(self, **kwargs):
75
+ self.set_debug(debug=kwargs.get("debug", None))
76
+ super().__init__()
77
+
78
+ def __autoconfig(self):
79
+ # Define debug configuration
80
+ import sys
81
+
82
+ debugger = {}
83
+ debugger["screen"] = (sys.stdout, ["*"])
84
+ # debugger['log'] = (open("log/debug.log","a"), ['*'] )
85
+ self.set_debug(debugger)
86
+
87
+ def set_debug(
88
+ self,
89
+ debug: dict[str, Any] | None = None,
90
+ ):
91
+ self.__html = None
92
+ self.__html_bgcolor = None
93
+ if debug is None:
94
+ self.__autoconfig()
95
+ else:
96
+ if isinstance(debug, dict):
97
+ # Set the deepness system
98
+ idebug = debug.copy()
99
+ if "deepness" in debug: # pragma: no cover
100
+ if debug["deepness"]:
101
+ idebug["deepness"] -= 1
102
+ else:
103
+ for key in idebug:
104
+ if key not in ["tabular", "deepness"]:
105
+ newlist = []
106
+ for element in idebug[key][1]:
107
+ if element in ["-*error", "-*warning"]:
108
+ newlist.append(element)
109
+ idebug[key][1] = newlist
110
+
111
+ # Save internal debugger
112
+ self.__indebug = idebug
113
+ else: # pragma: no cover
114
+ raise OSError("Argument is not a dictionary")
115
+
116
+ def set_html_color(self, bgcolor=None):
117
+ self.__html = True
118
+ self.__html_bgcolor = bgcolor
119
+
120
+ def set_shell_color(self):
121
+ self.__html = False
122
+
123
+ def set_origin(self, origin): # pragma: no cover
124
+ self.origin = origin
125
+
126
+ def get_debug(self):
127
+ return self.__indebug
128
+
129
+ def set_name(self, name):
130
+ self.__inname = name
131
+
132
+ def get_name(self):
133
+ return self.__inname
134
+
135
+ def __shell_color(self, color):
136
+ # Colors
137
+ if color in colors:
138
+ (darkbit, subcolor) = colors[color]
139
+ return f"\033[{darkbit:1d};{subcolor:02d}m"
140
+ else:
141
+ if color:
142
+ self.debug(
143
+ f"\033[1;31mColor '{color}' unknown\033[1;00m\n",
144
+ )
145
+ return ""
146
+
147
+ def __html_color(self, color):
148
+ # Colors
149
+ if color == "close":
150
+ return "</span>"
151
+ elif color in html_colors:
152
+ (bold, subcolor) = html_colors[color]
153
+ if not bold:
154
+ bolder = ""
155
+ else:
156
+ bolder = "; font-weight: bolder"
157
+ if not self.__html_bgcolor:
158
+ bgcolor = ""
159
+ else:
160
+ bgcolor = f"; background-color:rgb{self.__html_bgcolor}"
161
+ return f'<span style="color: rgb{subcolor}{bgcolor}{bolder}">'
162
+ else:
163
+ if color:
164
+ (bold, subcolor) = html_colors["red"]
165
+ self.debug(
166
+ f'<span style="color: rgb{subcolor}; font-weight: bolder">'
167
+ f"Color '{color}' unknown"
168
+ "</span>\n",
169
+ )
170
+ return ""
171
+
172
+ def color(self, color, html=None):
173
+ if html is None:
174
+ html = self.__html
175
+ if not html:
176
+ return self.__shell_color(color)
177
+ else:
178
+ return self.__html_color(color)
179
+
180
+ def debug(
181
+ self,
182
+ msg=None,
183
+ header=None,
184
+ color=None,
185
+ tail=None,
186
+ head=None,
187
+ footer=None,
188
+ origin=False,
189
+ kind="",
190
+ html=None,
191
+ ):
192
+ # If origin has been requested
193
+ if origin or getattr(self, "origin", False): # pragma: no cover
194
+ origin = True
195
+ line = currentframe().f_back.f_lineno
196
+ filename = currentframe().f_back.f_code.co_filename.replace(
197
+ getcwd(),
198
+ ".",
199
+ )
200
+ if len(filename) >= 2 and filename[0] == "." and filename[1] == "/":
201
+ filename = filename[2:]
202
+
203
+ # Allow better names for debug calls
204
+ if header is None:
205
+ if head is None:
206
+ header = True
207
+ else:
208
+ header = head
209
+ if tail is None:
210
+ if footer is None:
211
+ tail = "\n"
212
+ else:
213
+ tail = footer
214
+
215
+ # Retrieve the name of the class
216
+ clname = self.__class__.__name__
217
+
218
+ # Retrieve tabular
219
+ if "tabular" in self.__indebug: # pragma: no cover
220
+ tabular = self.__indebug["tabular"]
221
+ else:
222
+ tabular = ""
223
+
224
+ # For each element inside indebug
225
+ for name in self.__indebug:
226
+ # If this is no deepeness key, keep going
227
+ if name not in ["deepness", "tabular"]:
228
+ # Get color
229
+ if name == "screen" or name[-1] == "*":
230
+ color_ini = self.color(color, html)
231
+ color_end = self.color("close", html)
232
+ else:
233
+ color_ini = self.color(None, html)
234
+ color_end = color_ini
235
+
236
+ # Get file output handler and indebug list
237
+ (handler, indebug) = self.__indebug[name]
238
+
239
+ if not kind or f"-*{kind}" not in indebug:
240
+ if msg and isinstance(handler, str):
241
+ # Open handler buffer
242
+ handlerbuf = open(handler, "a")
243
+ else:
244
+ handlerbuf = handler
245
+
246
+ # Look up if the name of the class is inside indebug
247
+ if (clname in indebug) or (("*" in indebug) and (f"-{clname}" not in indebug)):
248
+ # Set line head name
249
+ if self.__inname:
250
+ headname = self.__inname
251
+ else:
252
+ headname = clname
253
+
254
+ # Build the message
255
+ message = color_ini
256
+ if header:
257
+ now = datetime.now()
258
+ message += (
259
+ f"{now.day:02d}/"
260
+ f"{now.month:02d}/"
261
+ f"{now.year} "
262
+ f"{now.hour:02d}:"
263
+ f"{now.minute:02d}:"
264
+ f"{now.second:02d} "
265
+ )
266
+ if origin:
267
+ message += str(filename) + ":" + str(line) + ": "
268
+ message += f"{headname:<15s}" + " - " + str(tabular)
269
+
270
+ if msg:
271
+ try:
272
+ message += str(msg)
273
+ except UnicodeEncodeError:
274
+ message += str(msg.encode("ascii", "ignore"))
275
+ message += color_end
276
+ if tail:
277
+ message += tail
278
+
279
+ # Print it on the buffer handler
280
+ if msg:
281
+ handlerbuf.write(message)
282
+ handlerbuf.flush()
283
+ else:
284
+ # If we shouldn't show the output, say to the
285
+ # caller we should output something
286
+ return True
287
+
288
+ # Autoclose handler when done
289
+ if msg and isinstance(handler, str):
290
+ handlerbuf.close()
291
+
292
+ # If we shouldn't show the output
293
+ if not msg:
294
+ # Say to the caller we shouldn't output anything
295
+ return False
296
+
297
+ def primary(
298
+ self,
299
+ msg,
300
+ header=True,
301
+ tail=True,
302
+ show_line_info=False,
303
+ html=None,
304
+ ):
305
+ if show_line_info: # pragma: no cover
306
+ line = currentframe().f_back.f_lineno
307
+ filename = currentframe().f_back.f_code.co_filename.replace(
308
+ getcwd(),
309
+ ".",
310
+ )
311
+ if len(filename) >= 2 and filename[0] == "." and filename[1] == "/":
312
+ filename = filename[2:]
313
+ else:
314
+ line = None
315
+ filename = None
316
+
317
+ self.__warningerror(
318
+ msg,
319
+ "PRIMARY",
320
+ "blue",
321
+ "primary",
322
+ line,
323
+ filename,
324
+ header,
325
+ tail,
326
+ html,
327
+ )
328
+
329
+ def secondary(
330
+ self,
331
+ msg,
332
+ header=True,
333
+ tail=True,
334
+ show_line_info=False,
335
+ html=None,
336
+ ):
337
+ if show_line_info: # pragma: no cover
338
+ line = currentframe().f_back.f_lineno
339
+ filename = currentframe().f_back.f_code.co_filename.replace(
340
+ getcwd(),
341
+ ".",
342
+ )
343
+ if len(filename) >= 2 and filename[0] == "." and filename[1] == "/":
344
+ filename = filename[2:]
345
+ else:
346
+ line = None
347
+ filename = None
348
+
349
+ self.__warningerror(
350
+ msg,
351
+ "SECONDARY",
352
+ "purple",
353
+ "secondary",
354
+ line,
355
+ filename,
356
+ header,
357
+ tail,
358
+ html,
359
+ )
360
+
361
+ def success(
362
+ self,
363
+ msg,
364
+ header=True,
365
+ tail=True,
366
+ show_line_info=False,
367
+ html=None,
368
+ ):
369
+ if show_line_info: # pragma: no cover
370
+ line = currentframe().f_back.f_lineno
371
+ filename = currentframe().f_back.f_code.co_filename.replace(
372
+ getcwd(),
373
+ ".",
374
+ )
375
+ if len(filename) >= 2 and filename[0] == "." and filename[1] == "/":
376
+ filename = filename[2:]
377
+ else:
378
+ line = None
379
+ filename = None
380
+
381
+ self.__warningerror(
382
+ msg,
383
+ "SUCCESS",
384
+ "green",
385
+ "success",
386
+ line,
387
+ filename,
388
+ header,
389
+ tail,
390
+ html,
391
+ )
392
+
393
+ def danger(
394
+ self,
395
+ msg,
396
+ header=True,
397
+ tail=True,
398
+ show_line_info=False,
399
+ html=None,
400
+ ):
401
+ if show_line_info: # pragma: no cover
402
+ line = currentframe().f_back.f_lineno
403
+ filename = currentframe().f_back.f_code.co_filename.replace(
404
+ getcwd(),
405
+ ".",
406
+ )
407
+ if len(filename) >= 2 and filename[0] == "." and filename[1] == "/":
408
+ filename = filename[2:]
409
+ else:
410
+ line = None
411
+ filename = None
412
+
413
+ self.__warningerror(
414
+ msg,
415
+ "DANGER",
416
+ "simplered",
417
+ "danger",
418
+ line,
419
+ filename,
420
+ header,
421
+ tail,
422
+ html,
423
+ )
424
+
425
+ def warning(
426
+ self,
427
+ msg,
428
+ header=True,
429
+ tail=True,
430
+ show_line_info=True,
431
+ html=None,
432
+ ):
433
+ if show_line_info: # pragma: no cover
434
+ line = currentframe().f_back.f_lineno
435
+ filename = currentframe().f_back.f_code.co_filename.replace(
436
+ getcwd(),
437
+ ".",
438
+ )
439
+ if len(filename) >= 2 and filename[0] == "." and filename[1] == "/":
440
+ filename = filename[2:]
441
+ else:
442
+ line = None
443
+ filename = None
444
+
445
+ self.__warningerror(
446
+ msg,
447
+ "WARNING",
448
+ "yellow",
449
+ "warning",
450
+ line,
451
+ filename,
452
+ header,
453
+ tail,
454
+ html,
455
+ )
456
+
457
+ def info(
458
+ self,
459
+ msg,
460
+ header=True,
461
+ tail=True,
462
+ show_line_info=False,
463
+ html=None,
464
+ ):
465
+ if show_line_info: # pragma: no cover
466
+ line = currentframe().f_back.f_lineno
467
+ filename = currentframe().f_back.f_code.co_filename.replace(
468
+ getcwd(),
469
+ ".",
470
+ )
471
+ if len(filename) >= 2 and filename[0] == "." and filename[1] == "/":
472
+ filename = filename[2:]
473
+ else:
474
+ line = None
475
+ filename = None
476
+
477
+ self.__warningerror(
478
+ msg,
479
+ "INFO",
480
+ "cyan",
481
+ "info",
482
+ line,
483
+ filename,
484
+ header,
485
+ tail,
486
+ html,
487
+ )
488
+
489
+ def error(
490
+ self,
491
+ msg,
492
+ header=True,
493
+ tail=True,
494
+ show_line_info=True,
495
+ html=None,
496
+ ):
497
+ if show_line_info: # pragma: no cover
498
+ line = currentframe().f_back.f_lineno
499
+ filename = currentframe().f_back.f_code.co_filename.replace(
500
+ getcwd(),
501
+ ".",
502
+ )
503
+ if len(filename) >= 2 and filename[0] == "." and filename[1] == "/":
504
+ filename = filename[2:]
505
+ else:
506
+ line = None
507
+ filename = None
508
+
509
+ self.__warningerror(
510
+ msg,
511
+ "ERROR",
512
+ "red",
513
+ "error",
514
+ line,
515
+ filename,
516
+ header,
517
+ tail,
518
+ html,
519
+ )
520
+
521
+ def debug_with_style(
522
+ self,
523
+ msg,
524
+ title,
525
+ color,
526
+ kind,
527
+ show_line_info,
528
+ header=True,
529
+ tail=True,
530
+ html=False,
531
+ ):
532
+ if show_line_info: # pragma: no cover
533
+ line = currentframe().f_back.f_lineno
534
+ filename = currentframe().f_back.f_code.co_filename.replace(
535
+ getcwd(),
536
+ ".",
537
+ )
538
+ if len(filename) >= 2 and filename[0] == "." and filename[1] == "/":
539
+ filename = filename[2:]
540
+ else:
541
+ line = None
542
+ filename = None
543
+
544
+ self.__warningerror(
545
+ msg,
546
+ title,
547
+ color,
548
+ kind,
549
+ line,
550
+ filename,
551
+ header,
552
+ tail,
553
+ html,
554
+ )
555
+
556
+ def __warningerror(
557
+ self,
558
+ msg,
559
+ prefix,
560
+ color,
561
+ kind,
562
+ line,
563
+ filename,
564
+ header,
565
+ tail,
566
+ html=False,
567
+ ):
568
+ # Retrieve the name of the class
569
+ clname = self.__class__.__name__
570
+
571
+ # Retrieve tabular
572
+ if "tabular" in self.__indebug: # pragma: no cover
573
+ tabular = self.__indebug["tabular"]
574
+ else:
575
+ tabular = ""
576
+
577
+ # For each element inside indebug
578
+ for name in self.__indebug:
579
+ # If this is no deepeness key, keep going
580
+ if name not in ["deepness", "tabular"]:
581
+ # Get file output handler and indebug list
582
+ (handler, indebug) = self.__indebug[name]
583
+
584
+ if f"-*{kind}" not in indebug:
585
+ if isinstance(handler, str):
586
+ # Open handler buffer
587
+ handlerbuf = open(handler, "a")
588
+ else:
589
+ handlerbuf = handler
590
+
591
+ # Get color
592
+ if name == "screen" or name[-1] == "*":
593
+ color_ini = self.color(color, html)
594
+ color_end = self.color("close", html)
595
+ else:
596
+ color_ini = self.color(None, html)
597
+ color_end = color_ini
598
+
599
+ # Build the message
600
+ message = color_ini
601
+ if header:
602
+ # Set line head name
603
+ if self.__inname:
604
+ headname = self.__inname
605
+ else:
606
+ headname = clname
607
+
608
+ now = datetime.now()
609
+ message += (
610
+ f"\n{prefix} - {now.day:02d}/{now.month:02d}/{now.year:d} "
611
+ f"{now.hour:02d}:{now.minute:02d}:{now.second:02d} "
612
+ )
613
+ if filename or line:
614
+ message += str(filename) + ":" + str(line) + ": "
615
+ message += f"{headname:<15s}" + " - " + str(tabular)
616
+ if msg:
617
+ try:
618
+ message += str(msg)
619
+ except UnicodeEncodeError:
620
+ try:
621
+ message += str(msg.encode("ascii", "ignore"))
622
+ except Exception:
623
+ message += (
624
+ "*** "
625
+ "Message is in Binary format, "
626
+ "I tried to convert to ASCII ignoring "
627
+ "encoding errors but it failed as well. "
628
+ "***"
629
+ )
630
+ message += color_end
631
+ if tail:
632
+ message += "\n"
633
+
634
+ # Print it on the buffer handler
635
+ handlerbuf.write(message)
636
+ handlerbuf.flush()
637
+
638
+ # Autoclose handler when done
639
+ if isinstance(handler, str):
640
+ handlerbuf.close()