1#
2# This file is part of pyasn1 software.
3#
4# Copyright (c) 2005-2019, Ilya Etingof <etingof@gmail.com>
5# License: http://snmplabs.com/pyasn1/license.html
6#
7import logging
8import sys
9
10from pyasn1 import __version__
11from pyasn1 import error
12from pyasn1.compat.octets import octs2ints
13
14__all__ = ['Debug', 'setLogger', 'hexdump']
15
16DEBUG_NONE = 0x0000
17DEBUG_ENCODER = 0x0001
18DEBUG_DECODER = 0x0002
19DEBUG_ALL = 0xffff
20
21FLAG_MAP = {
22    'none': DEBUG_NONE,
23    'encoder': DEBUG_ENCODER,
24    'decoder': DEBUG_DECODER,
25    'all': DEBUG_ALL
26}
27
28LOGGEE_MAP = {}
29
30
31class Printer(object):
32    # noinspection PyShadowingNames
33    def __init__(self, logger=None, handler=None, formatter=None):
34        if logger is None:
35            logger = logging.getLogger('pyasn1')
36
37        logger.setLevel(logging.DEBUG)
38
39        if handler is None:
40            handler = logging.StreamHandler()
41
42        if formatter is None:
43            formatter = logging.Formatter('%(asctime)s %(name)s: %(message)s')
44
45        handler.setFormatter(formatter)
46        handler.setLevel(logging.DEBUG)
47        logger.addHandler(handler)
48
49        self.__logger = logger
50
51    def __call__(self, msg):
52        self.__logger.debug(msg)
53
54    def __str__(self):
55        return '<python logging>'
56
57
58if hasattr(logging, 'NullHandler'):
59    NullHandler = logging.NullHandler
60
61else:
62    # Python 2.6 and older
63    class NullHandler(logging.Handler):
64        def emit(self, record):
65            pass
66
67
68class Debug(object):
69    defaultPrinter = Printer()
70
71    def __init__(self, *flags, **options):
72        self._flags = DEBUG_NONE
73
74        if 'loggerName' in options:
75            # route our logs to parent logger
76            self._printer = Printer(
77                logger=logging.getLogger(options['loggerName']),
78                handler=NullHandler()
79            )
80
81        elif 'printer' in options:
82            self._printer = options.get('printer')
83
84        else:
85            self._printer = self.defaultPrinter
86
87        self._printer('running pyasn1 %s, debug flags %s' % (__version__, ', '.join(flags)))
88
89        for flag in flags:
90            inverse = flag and flag[0] in ('!', '~')
91            if inverse:
92                flag = flag[1:]
93            try:
94                if inverse:
95                    self._flags &= ~FLAG_MAP[flag]
96                else:
97                    self._flags |= FLAG_MAP[flag]
98            except KeyError:
99                raise error.PyAsn1Error('bad debug flag %s' % flag)
100
101            self._printer("debug category '%s' %s" % (flag, inverse and 'disabled' or 'enabled'))
102
103    def __str__(self):
104        return 'logger %s, flags %x' % (self._printer, self._flags)
105
106    def __call__(self, msg):
107        self._printer(msg)
108
109    def __and__(self, flag):
110        return self._flags & flag
111
112    def __rand__(self, flag):
113        return flag & self._flags
114
115_LOG = DEBUG_NONE
116
117
118def setLogger(userLogger):
119    global _LOG
120
121    if userLogger:
122        _LOG = userLogger
123    else:
124        _LOG = DEBUG_NONE
125
126    # Update registered logging clients
127    for module, (name, flags) in LOGGEE_MAP.items():
128        setattr(module, name, _LOG & flags and _LOG or DEBUG_NONE)
129
130
131def registerLoggee(module, name='LOG', flags=DEBUG_NONE):
132    LOGGEE_MAP[sys.modules[module]] = name, flags
133    setLogger(_LOG)
134    return _LOG
135
136
137def hexdump(octets):
138    return ' '.join(
139        ['%s%.2X' % (n % 16 == 0 and ('\n%.5d: ' % n) or '', x)
140         for n, x in zip(range(len(octets)), octs2ints(octets))]
141    )
142
143
144class Scope(object):
145    def __init__(self):
146        self._list = []
147
148    def __str__(self): return '.'.join(self._list)
149
150    def push(self, token):
151        self._list.append(token)
152
153    def pop(self):
154        return self._list.pop()
155
156
157scope = Scope()
158