File: __init__.py

package info (click to toggle)
grass 7.2.0-2
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 135,976 kB
  • ctags: 44,148
  • sloc: ansic: 410,300; python: 166,939; cpp: 34,819; sh: 9,358; makefile: 6,618; xml: 3,551; sql: 769; lex: 519; yacc: 450; asm: 387; perl: 282; sed: 17; objc: 7
file content (358 lines) | stat: -rw-r--r-- 11,548 bytes parent folder | download | duplicates (2)
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
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
# -*- coding: utf-8 -*-
"""@package grass.pygrass.messages

@brief PyGRASS message interface

Fast and exit-safe interface to GRASS C-library message functions


(C) 2013 by the GRASS Development Team
This program is free software under the GNU General Public
License (>=v2). Read the file COPYING that comes with GRASS
for details.

@author Soeren Gebbert
"""
import sys
from multiprocessing import Process, Lock, Pipe

import grass.lib.gis as libgis

from grass.exceptions import FatalError


def message_server(lock, conn):
    """The GRASS message server function designed to be a target for
       multiprocessing.Process


       :param lock: A multiprocessing.Lock
       :param conn: A multiprocessing.Pipe

       This function will use the G_* message C-functions from grass.lib.gis
       to provide an interface to the GRASS C-library messaging system.

       The data that is send through the pipe must provide an
       identifier string to specify which C-function should be called.

       The following identifiers are supported:

       - "INFO"       Prints an info message, see G_message() for details
       - "IMPORTANT"  Prints an important info message,
                      see G_important_message() for details
       - "VERBOSE"    Prints a verbose message if the verbosity level is
                      set accordingly, see G_verbose_message() for details
       - "WARNING"    Prints a warning message, see G_warning() for details
       - "ERROR"      Prints a message with a leading "ERROR: " string,
                      see G_important_message() for details
       - "PERCENT"    Prints a percent value based on three integer values: n, d and s
                      see G_percent() for details
       - "STOP"       Stops the server function and closes the pipe
       - "FATAL"      Calls G_fatal_error(), this functions is only for
                      testing purpose

       The that is end through the pipe must be a list of values:

       - Messages: ["INFO|VERBOSE|WARNING|ERROR|FATAL", "MESSAGE"]
       - Debug:    ["DEBUG", level, "MESSAGE"]
       - Percent:  ["PERCENT", n, d, s]

    """
    libgis.G_debug(1, "Start messenger server")

    while True:
        # Avoid busy waiting
        conn.poll(None)
        data = conn.recv()
        message_type = data[0]

        # Only one process is allowed to write to stderr
        lock.acquire()

        # Stop the pipe and the infinite loop
        if message_type == "STOP":
            conn.close()
            lock.release()
            libgis.G_debug(1, "Stop messenger server")
            sys.exit()

        message = data[1]
        # libgis limitation
        if isinstance(message,  type(" ")):
            if len(message) >= 2000:
                message = message[:1999]

        if message_type == "PERCENT":
            n = int(data[1])
            d = int(data[2])
            s = int(data[3])
            libgis.G_percent(n, d, s)
        elif message_type == "DEBUG":
            level = data[1]
            message = data[2]
            libgis.G_debug(level, message)
        elif message_type == "VERBOSE":
            libgis.G_verbose_message(message)
        elif message_type == "INFO":
            libgis.G_message(message)
        elif message_type == "IMPORTANT":
            libgis.G_important_message(message)
        elif message_type == "WARNING":
            libgis.G_warning(message)
        elif message_type == "ERROR":
            libgis.G_important_message("ERROR: %s"%message)
        # This is for testing only
        elif message_type == "FATAL":
            libgis.G_fatal_error(message)

        lock.release()


class Messenger(object):
    """Fast and exit-safe interface to GRASS C-library message functions

       This class implements a fast and exit-safe interface to the GRASS
       C-library message functions like: G_message(), G_warning(),
       G_important_message(), G_verbose_message(), G_percent() and G_debug().

       Note:

       The C-library message functions a called via ctypes in a subprocess
       using a pipe (multiprocessing.Pipe) to transfer the text messages.
       Hence, the process that uses the Messenger interface will not be
       exited, if a G_fatal_error() was invoked in the subprocess.
       In this case the Messenger object will simply start a new subprocess
       and restarts the pipeline.


       Usage:

       >>> msgr = Messenger()
       >>> msgr.debug(0, "debug 0")
       >>> msgr.verbose("verbose message")
       >>> msgr.message("message")
       >>> msgr.important("important message")
       >>> msgr.percent(1, 1, 1)
       >>> msgr.warning("Ohh")
       >>> msgr.error("Ohh no")

       >>> msgr = Messenger()
       >>> msgr.fatal("Ohh no no no!")
       Traceback (most recent call last):
         File "__init__.py", line 239, in fatal
           sys.exit(1)
       SystemExit: 1

       >>> msgr = Messenger(raise_on_error=True)
       >>> msgr.fatal("Ohh no no no!")
       Traceback (most recent call last):
         File "__init__.py", line 241, in fatal
           raise FatalError(message)
       FatalError: Ohh no no no!

       >>> msgr = Messenger(raise_on_error=True)
       >>> msgr.set_raise_on_error(False)
       >>> msgr.fatal("Ohh no no no!")
       Traceback (most recent call last):
         File "__init__.py", line 239, in fatal
           sys.exit(1)
       SystemExit: 1

       >>> msgr = Messenger(raise_on_error=False)
       >>> msgr.set_raise_on_error(True)
       >>> msgr.fatal("Ohh no no no!")
       Traceback (most recent call last):
         File "__init__.py", line 241, in fatal
           raise FatalError(message)
       FatalError: Ohh no no no!

    """
    def __init__(self, raise_on_error=False):
        self.client_conn = None
        self.server_conn = None
        self.server = None
        self.raise_on_error = raise_on_error
        self.start_server()

    def start_server(self):
        """Start the messenger server and open the pipe
        """
        self.client_conn, self.server_conn = Pipe()
        self.lock = Lock()
        self.server = Process(target=message_server, args=(self.lock,
                                                           self.server_conn))
        self.server.daemon = True
        self.server.start()

    def _check_restart_server(self):
        """Restart the server if it was terminated
        """
        if self.server.is_alive() is True:
            return
        self.client_conn.close()
        self.server_conn.close()
        self.start_server()
        self.warning("Needed to restart the messenger server")

    def message(self, message):
        """Send a message to stderr

        :param message: the text of message
        :type message: str

           G_message() will be called in the messenger server process
        """
        self._check_restart_server()
        self.client_conn.send(["INFO", message])

    def verbose(self, message):
        """Send a verbose message to stderr

        :param message: the text of message
        :type message: str

           G_verbose_message() will be called in the messenger server process
        """
        self._check_restart_server()
        self.client_conn.send(["VERBOSE", message])

    def important(self, message):
        """Send an important message to stderr

        :param message: the text of message
        :type message: str

           G_important_message() will be called in the messenger server process
        """
        self._check_restart_server()
        self.client_conn.send(["IMPORTANT", message])

    def warning(self, message):
        """Send a warning message to stderr

        :param message: the text of message
        :type message: str

           G_warning() will be called in the messenger server process
        """
        self._check_restart_server()
        self.client_conn.send(["WARNING", message])

    def error(self, message):
        """Send an error message to stderr

        :param message: the text of message
        :type message: str

           G_important_message() with an additional "ERROR:" string at
           the start will be called in the messenger server process
        """
        self._check_restart_server()
        self.client_conn.send(["ERROR", message])

    def fatal(self, message):
        """Send an error message to stderr, call sys.exit(1) or raise FatalError

        :param message: the text of message
        :type message: str

           This function emulates the behavior of G_fatal_error(). It prints
           an error message to stderr and calls sys.exit(1). If raise_on_error
           is set True while creating the messenger object, a FatalError
           exception will be raised instead of calling sys.exit(1).
        """
        self._check_restart_server()
        self.client_conn.send(["ERROR", message])
        self.stop()

        if self.raise_on_error is True:
            raise FatalError(message)
        else:
            sys.exit(1)

    def debug(self, level, message):
        """Send a debug message to stderr

        :param message: the text of message
        :type message: str

           G_debug() will be called in the messenger server process
        """
        self._check_restart_server()
        self.client_conn.send(["DEBUG", level, message])

    def percent(self, n, d, s):
        """Send a percentage to stderr

        :param message: the text of message
        :type message: str


           G_percent() will be called in the messenger server process
        """
        self._check_restart_server()
        self.client_conn.send(["PERCENT", n, d, s])

    def stop(self):
        """Stop the messenger server and close the pipe
        """
        if self.server is not None and self.server.is_alive():
            self.client_conn.send(["STOP", ])
            self.server.join(5)
            self.server.terminate()
        if self.client_conn is not None:
            self.client_conn.close()

    def set_raise_on_error(self, raise_on_error=True):
        """Set the fatal error behavior

           :param raise_on_error: if True a FatalError exception will be
                                  raised instead of calling sys.exit(1)
           :type raise_on_error: bool

           - If raise_on_error == True, a FatalError exception will be raised
             if fatal() is called
           - If raise_on_error == False, sys.exit(1) will be invoked if
             fatal() is called

        """
        self.raise_on_error = raise_on_error

    def get_raise_on_error(self):
        """Get the fatal error behavior

           :returns: True if a FatalError exception will be raised or False if
                     sys.exit(1) will be called in case of invoking fatal()
        """
        return self.raise_on_error

    def test_fatal_error(self, message):
        """Force the messenger server to call G_fatal_error()
        """
        import time
        self._check_restart_server()
        self.client_conn.send(["FATAL", message])
        time.sleep(1)


def get_msgr(_instance=[None, ], *args, **kwargs):
    """Return a Messenger instance.

       :returns: the Messenger instance.

    >>> msgr0 = get_msgr()
    >>> msgr1 = get_msgr()
    >>> msgr2 = Messenger()
    >>> msgr0 is msgr1
    True
    >>> msgr0 is msgr2
    False
    """
    if not _instance[0]:
        _instance[0] = Messenger(*args, **kwargs)
    return _instance[0]


if __name__ == "__main__":
    import doctest
    doctest.testmod()