summaryrefslogtreecommitdiff
path: root/lib/spack/external/py/_log/log.py
blob: ce47e8c754ad682406042da25e7562bccf9c5fca (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
"""
basic logging functionality based on a producer/consumer scheme.

XXX implement this API: (maybe put it into slogger.py?)

        log = Logger(
                    info=py.log.STDOUT,
                    debug=py.log.STDOUT,
                    command=None)
        log.info("hello", "world")
        log.command("hello", "world")

        log = Logger(info=Logger(something=...),
                     debug=py.log.STDOUT,
                     command=None)
"""
import py, sys

class Message(object):
    def __init__(self, keywords, args):
        self.keywords = keywords
        self.args = args

    def content(self):
        return " ".join(map(str, self.args))

    def prefix(self):
        return "[%s] " % (":".join(self.keywords))

    def __str__(self):
        return self.prefix() + self.content()


class Producer(object):
    """ (deprecated) Log producer API which sends messages to be logged
        to a 'consumer' object, which then prints them to stdout,
        stderr, files, etc. Used extensively by PyPy-1.1.
    """

    Message = Message  # to allow later customization
    keywords2consumer = {}

    def __init__(self, keywords, keywordmapper=None, **kw):
        if hasattr(keywords, 'split'):
            keywords = tuple(keywords.split())
        self._keywords = keywords
        if keywordmapper is None:
            keywordmapper = default_keywordmapper
        self._keywordmapper = keywordmapper

    def __repr__(self):
        return "<py.log.Producer %s>" % ":".join(self._keywords)

    def __getattr__(self, name):
        if '_' in name:
            raise AttributeError(name)
        producer = self.__class__(self._keywords + (name,))
        setattr(self, name, producer)
        return producer

    def __call__(self, *args):
        """ write a message to the appropriate consumer(s) """
        func = self._keywordmapper.getconsumer(self._keywords)
        if func is not None:
            func(self.Message(self._keywords, args))

class KeywordMapper:
    def __init__(self):
        self.keywords2consumer = {}

    def getstate(self):
        return self.keywords2consumer.copy()
    def setstate(self, state):
        self.keywords2consumer.clear()
        self.keywords2consumer.update(state)

    def getconsumer(self, keywords):
        """ return a consumer matching the given keywords.

            tries to find the most suitable consumer by walking, starting from
            the back, the list of keywords, the first consumer matching a
            keyword is returned (falling back to py.log.default)
        """
        for i in range(len(keywords), 0, -1):
            try:
                return self.keywords2consumer[keywords[:i]]
            except KeyError:
                continue
        return self.keywords2consumer.get('default', default_consumer)

    def setconsumer(self, keywords, consumer):
        """ set a consumer for a set of keywords. """
        # normalize to tuples
        if isinstance(keywords, str):
            keywords = tuple(filter(None, keywords.split()))
        elif hasattr(keywords, '_keywords'):
            keywords = keywords._keywords
        elif not isinstance(keywords, tuple):
            raise TypeError("key %r is not a string or tuple" % (keywords,))
        if consumer is not None and not py.builtin.callable(consumer):
            if not hasattr(consumer, 'write'):
                raise TypeError(
                    "%r should be None, callable or file-like" % (consumer,))
            consumer = File(consumer)
        self.keywords2consumer[keywords] = consumer

def default_consumer(msg):
    """ the default consumer, prints the message to stdout (using 'print') """
    sys.stderr.write(str(msg)+"\n")

default_keywordmapper = KeywordMapper()

def setconsumer(keywords, consumer):
    default_keywordmapper.setconsumer(keywords, consumer)

def setstate(state):
    default_keywordmapper.setstate(state)
def getstate():
    return default_keywordmapper.getstate()

#
# Consumers
#

class File(object):
    """ log consumer wrapping a file(-like) object """
    def __init__(self, f):
        assert hasattr(f, 'write')
        #assert isinstance(f, file) or not hasattr(f, 'open')
        self._file = f

    def __call__(self, msg):
        """ write a message to the log """
        self._file.write(str(msg) + "\n")
        if hasattr(self._file, 'flush'):
            self._file.flush()

class Path(object):
    """ log consumer that opens and writes to a Path """
    def __init__(self, filename, append=False,
                 delayed_create=False, buffering=False):
        self._append = append
        self._filename = str(filename)
        self._buffering = buffering
        if not delayed_create:
            self._openfile()

    def _openfile(self):
        mode = self._append and 'a' or 'w'
        f = open(self._filename, mode)
        self._file = f

    def __call__(self, msg):
        """ write a message to the log """
        if not hasattr(self, "_file"):
            self._openfile()
        self._file.write(str(msg) + "\n")
        if not self._buffering:
            self._file.flush()

def STDOUT(msg):
    """ consumer that writes to sys.stdout """
    sys.stdout.write(str(msg)+"\n")

def STDERR(msg):
    """ consumer that writes to sys.stderr """
    sys.stderr.write(str(msg)+"\n")

class Syslog:
    """ consumer that writes to the syslog daemon """

    def __init__(self, priority = None):
        if priority is None:
            priority = self.LOG_INFO
        self.priority = priority

    def __call__(self, msg):
        """ write a message to the log """
        py.std.syslog.syslog(self.priority, str(msg))

for _prio in "EMERG ALERT CRIT ERR WARNING NOTICE INFO DEBUG".split():
    _prio = "LOG_" + _prio
    try:
        setattr(Syslog, _prio, getattr(py.std.syslog, _prio))
    except AttributeError:
        pass