1"""
2Debugger component used to debug each event in a system by printing
3each event to sys.stderr or to a Logger Component instance.
4"""
5
6
7import os
8import sys
9from traceback import format_exc, format_exception_only
10from signal import SIGINT, SIGTERM
11
12
13from .components import BaseComponent
14from .handlers import handler, reprhandler
15
16
17class Debugger(BaseComponent):
18
19    """Create a new Debugger Component
20
21    Creates a new Debugger Component that listens to all events in the system
22    printing each event to sys.stderr or a Logger Component.
23
24    :var IgnoreEvents: list of events (str) to ignore
25    :var IgnoreChannels: list of channels (str) to ignore
26    :var enabled: Enabled/Disabled flag
27
28    :param log: Logger Component instance or None (*default*)
29    """
30
31    IgnoreEvents = ["generate_events"]
32    IgnoreChannels = []
33
34    def __init__(self, errors=True, events=True, file=None, logger=None,
35                 prefix=None, trim=None, **kwargs):
36        "initializes x; see x.__class__.__doc__ for signature"
37
38        super(Debugger, self).__init__()
39
40        self._errors = errors
41        self._events = events
42
43        if isinstance(file, str):
44            self.file = open(os.path.abspath(os.path.expanduser(file)), "a")
45        elif hasattr(file, "write"):
46            self.file = file
47        else:
48            self.file = sys.stderr
49
50        self.logger = logger
51        self.prefix = prefix
52        self.trim = trim
53
54        self.IgnoreEvents.extend(kwargs.get("IgnoreEvents", []))
55        self.IgnoreChannels.extend(kwargs.get("IgnoreChannels", []))
56
57    @handler("signal", channel="*")
58    def _on_signal(self, signo, stack):
59        if signo in [SIGINT, SIGTERM]:
60            raise SystemExit(0)
61
62    @handler("exception", channel="*", priority=100.0)
63    def _on_exception(self, error_type, value, traceback,
64                      handler=None, fevent=None):
65
66        if not self._errors:
67            return
68
69        s = []
70
71        if handler is None:
72            handler = ""
73        else:
74            handler = reprhandler(handler)
75
76        msg = "ERROR {0:s} ({1:s}) ({2:s}): {3:s}\n".format(
77            handler, repr(fevent), repr(error_type), repr(value)
78        )
79
80        s.append(msg)
81        s.append('Traceback (most recent call last):\n')
82        s.extend(traceback)
83        s.extend(format_exception_only(error_type, value))
84        s.append("\n")
85
86        if self.logger is not None:
87            self.logger.error("".join(s))
88        else:
89            try:
90                self.file.write("".join(s))
91                self.file.flush()
92            except IOError:
93                pass
94
95    @handler(priority=101.0)
96    def _on_event(self, event, *args, **kwargs):
97        """Global Event Handler
98
99        Event handler to listen to all events printing
100        each event to self.file or a Logger Component instance
101        by calling self.logger.debug
102        """
103
104        try:
105            if not self._events:
106                return
107
108            channels = event.channels
109
110            if event.name in self.IgnoreEvents:
111                return
112
113            if all(channel in self.IgnoreChannels for channel in channels):
114                return
115
116            s = repr(event)
117
118            if self.prefix:
119                s = "%s: %s" % (self.prefix, s)
120
121            if self.trim:
122                s = "%s ...>" % s[:self.trim]
123
124            if self.logger is not None:
125                self.logger.debug(s)
126            else:
127                self.file.write(s)
128                self.file.write("\n")
129                self.file.flush()
130        except Exception as e:
131            sys.stderr.write("ERROR (Debugger): {}".format(e))
132            sys.stderr.write("{}".format(format_exc()))
133