SwiftGUI_Logging 0.1.3__tar.gz → 0.1.4__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: SwiftGUI_Logging
3
- Version: 0.1.3
3
+ Version: 0.1.4
4
4
  Summary: A collection of helpful logging-functionality based on the logging package
5
5
  License-Expression: Apache-2.0
6
6
  License-File: LICENSE
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "SwiftGUI_Logging"
3
- version = "0.1.3"
3
+ version = "0.1.4"
4
4
  packages = [
5
5
  { include = "SwiftGUI_Logging", from = "src" }
6
6
  ]
@@ -11,6 +11,9 @@ def exceptions_to_file(
11
11
  buffer_size: int = 5000,
12
12
  trigger_level: int = logging.ERROR,
13
13
  log_level: int = logging.DEBUG,
14
+ include_main_thread: bool = True,
15
+ include_threads: bool = True,
16
+ include_tkinter: bool = True,
14
17
  reraise: bool = False,
15
18
  datetime_format: str = "_%Y-%m-%d_%H-%M-%S",
16
19
  formatter_format: str = "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
@@ -29,6 +32,9 @@ def exceptions_to_file(
29
32
  :param buffer_size: How many reports are saved before the first ones are overritten again
30
33
  :param trigger_level: Level at which the exceptions are treated. Reports at and above this level trigger a file-write
31
34
  :param log_level: Logs below this level are ignored and not written to the file
35
+ :param include_main_thread: True, if the "normal thread's" exceptions should be caught
36
+ :param include_threads: True, if Thread-exceptions should be caught too
37
+ :param include_tkinter: True, if Tkinter-exceptions should be caught too
32
38
  :param reraise: True, if the exception should still be raised, even though it was logged. Good for debugging purposes
33
39
  :param datetime_format: Format of the timestamp that extends the filename
34
40
  :param formatter_format: Format of the log-entries in the file
@@ -71,5 +77,13 @@ def exceptions_to_file(
71
77
 
72
78
  logger.addHandler(buffer_handler)
73
79
 
74
- sgl.reroute_exceptions(logger, reraise=reraise, loglevel=trigger_level, pass_text_to_function=exception_occured)
80
+ sgl.reroute_exceptions(
81
+ logger,
82
+ reraise=reraise,
83
+ loglevel=trigger_level,
84
+ pass_text_to_function=exception_occured,
85
+ include_main_thread=include_main_thread,
86
+ include_threads=include_threads,
87
+ include_tkinter=include_tkinter,
88
+ )
75
89
 
@@ -1,8 +1,13 @@
1
1
  import logging.handlers
2
+ import threading
2
3
  import traceback
3
4
  import sys
4
5
  from typing import Callable, Any
5
6
 
7
+ try:
8
+ import tkinter as tk
9
+ except ImportError:
10
+ tk = None
6
11
 
7
12
  def reroute_exceptions(
8
13
  logger: logging.Logger = logging.getLogger(),
@@ -10,6 +15,9 @@ def reroute_exceptions(
10
15
  *,
11
16
  logger_warnings: logging.Logger = None,
12
17
  loglevel_warnings: int = logging.WARNING,
18
+ include_main_thread: bool = True,
19
+ include_threads: bool = True,
20
+ include_tkinter: bool = True,
13
21
  reraise: bool = False,
14
22
  print_to_console: bool = False,
15
23
  pass_text_to_function: Callable[[str], Any] = None,
@@ -21,7 +29,10 @@ def reroute_exceptions(
21
29
  :param loglevel: The loglevel for EXCEPTIONS
22
30
  :param logger_warnings: The logger where WARNINGS go
23
31
  :param loglevel_warnings: The level of logging for WARNINGS
24
- :param reraise: True, if the exception should be raised again
32
+ :param reraise: True, if the exception should be raised again. THIS DOESN'T WORK FOR THREAD EXCEPTIONS!
33
+ :param include_main_thread: True, if the "normal thread's" exceptions should be caught
34
+ :param include_threads: True, if Thread-exceptions should be caught too
35
+ :param include_tkinter: True, if Tkinter-exceptions should be caught too
25
36
  :param pass_text_to_function: Pass a function/method and the exception-text is passed to it
26
37
  :param print_to_console: True, if the text should be printed to the console using print(...)
27
38
  :return:
@@ -32,8 +43,8 @@ def reroute_exceptions(
32
43
  if loglevel_warnings is None:
33
44
  loglevel_warnings = loglevel
34
45
 
35
- def catch(exctype, value, tb):
36
- text = "".join(traceback.format_exception(exctype, value, tb))
46
+ def catch(exctype, value, tb, additional_text: str = ""):
47
+ text = additional_text + "".join(traceback.format_exception(exctype, value, tb))
37
48
 
38
49
  if issubclass(exctype, Warning): # Warnings
39
50
  if logger_warnings is not None:
@@ -58,7 +69,31 @@ def reroute_exceptions(
58
69
  if print_to_console:
59
70
  print(text)
60
71
 
72
+ def catch_normal(exctype, value, tb):
73
+ catch(exctype, value, tb)
61
74
  if reraise:
62
75
  sys.__excepthook__(exctype, value, tb)
63
76
 
64
- sys.excepthook = catch
77
+ def catch_thread(exc: threading.ExceptHookArgs):
78
+ catch(exc.exc_type, exc.exc_value, exc.exc_traceback, f"On thread {exc.thread}:\n")
79
+
80
+ if include_main_thread:
81
+ sys.excepthook = catch_normal
82
+
83
+ if include_threads:
84
+ threading.excepthook = catch_thread
85
+
86
+ if include_tkinter and tk is not None:
87
+ # Catch tkinter exceptions
88
+ actual_callback = tk.Tk.report_callback_exception
89
+ def catch_tkinter(self, exctype, value, tb):
90
+ catch(exctype, value, tb, additional_text="Tkinter exception:\n")
91
+
92
+ if reraise:
93
+ actual_callback(self, exctype, value, tb) # I know this shows warnings, but it works
94
+
95
+ tk.Tk.report_callback_exception = catch_tkinter
96
+
97
+
98
+
99
+
@@ -20,7 +20,6 @@ class MemoryHandlerRotatingBuffer(logging.handlers.MemoryHandler):
20
20
  """
21
21
  super().__init__(capacity, flushLevel, target, flushOnClose=False)
22
22
  self.call_after_flushing = call_after_flushing if call_after_flushing else lambda *_:_
23
- self._should_flush = False
24
23
 
25
24
  def shouldFlush(self, record):
26
25
  """
@@ -31,17 +30,10 @@ class MemoryHandlerRotatingBuffer(logging.handlers.MemoryHandler):
31
30
  if len(self.buffer) > self.capacity: # Remove 0th element so the buffer doesn't "overflow"
32
31
  self.buffer.pop(0)
33
32
 
34
- if record.levelno >= self.flushLevel:
35
- self._should_flush = True
36
- return True
37
-
38
- return False
33
+ return record.levelno >= self.flushLevel
39
34
 
40
35
  def flush(self):
41
- super().flush() # Don't put this in the if, this is necessary due to Python-magic...
42
-
43
- if self._should_flush:
44
- self.call_after_flushing()
45
- self._should_flush = False
36
+ super().flush()
37
+ self.call_after_flushing()
46
38
 
47
39