SwiftGUI_Logging 0.1.2__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.2
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
@@ -84,7 +84,7 @@ After the program executes, the directory contains a file like
84
84
  As you can see, the time of the crash was inserted into the
85
85
  filename, so that multiple crashlogs don't overwrite each other.
86
86
 
87
- The file contains the most recent log-entries (from `logging.debug`)
87
+ The file contains the most recent log-entries (from `logging.debug` and `logging.info`)
88
88
  and the exception with full traceback:
89
89
  ```log
90
90
  2026-02-24 16:33:49,940 - root - DEBUG - Test 0
@@ -68,7 +68,7 @@ After the program executes, the directory contains a file like
68
68
  As you can see, the time of the crash was inserted into the
69
69
  filename, so that multiple crashlogs don't overwrite each other.
70
70
 
71
- The file contains the most recent log-entries (from `logging.debug`)
71
+ The file contains the most recent log-entries (from `logging.debug` and `logging.info`)
72
72
  and the exception with full traceback:
73
73
  ```log
74
74
  2026-02-24 16:33:49,940 - root - DEBUG - Test 0
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "SwiftGUI_Logging"
3
- version = "0.1.2"
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
+