[GRASS-SVN] r58201 - in grass/trunk/lib/python/pygrass: . messages

svn_grass at osgeo.org svn_grass at osgeo.org
Tue Nov 12 06:01:05 PST 2013


Author: huhabla
Date: 2013-11-12 06:00:53 -0800 (Tue, 12 Nov 2013)
New Revision: 58201

Added:
   grass/trunk/lib/python/pygrass/messages/
   grass/trunk/lib/python/pygrass/messages/Makefile
   grass/trunk/lib/python/pygrass/messages/__init__.py
Modified:
   grass/trunk/lib/python/pygrass/Makefile
   grass/trunk/lib/python/pygrass/__init__.py
Log:
Fast and exit-safe interface to GRASS C-library message functions


Modified: grass/trunk/lib/python/pygrass/Makefile
===================================================================
--- grass/trunk/lib/python/pygrass/Makefile	2013-11-12 13:49:10 UTC (rev 58200)
+++ grass/trunk/lib/python/pygrass/Makefile	2013-11-12 14:00:53 UTC (rev 58201)
@@ -10,12 +10,13 @@
 
 MODULES = errors functions orderdict
 
-CLEAN_SUBDIRS = modules raster vector gis shell tests
+CLEAN_SUBDIRS = messages modules raster vector gis shell tests
 
 PYFILES := $(patsubst %,$(DSTDIR)/%.py,$(MODULES) __init__)
 PYCFILES := $(patsubst %,$(DSTDIR)/%.pyc,$(MODULES) __init__)
 
 default: $(PYFILES) $(PYCFILES) $(GDIR)/__init__.py $(GDIR)/__init__.pyc
+	-$(MAKE) -C messages || echo $(CURDIR)/messages >> $(ERRORLOG)
 	-$(MAKE) -C modules || echo $(CURDIR)/modules >> $(ERRORLOG)
 	-$(MAKE) -C raster || echo $(CURDIR)/raster >> $(ERRORLOG)
 	-$(MAKE) -C vector || echo $(CURDIR)/vector >> $(ERRORLOG)

Modified: grass/trunk/lib/python/pygrass/__init__.py
===================================================================
--- grass/trunk/lib/python/pygrass/__init__.py	2013-11-12 13:49:10 UTC (rev 58200)
+++ grass/trunk/lib/python/pygrass/__init__.py	2013-11-12 14:00:53 UTC (rev 58201)
@@ -16,3 +16,4 @@
 import vector
 import modules
 import shell
+import messages

Added: grass/trunk/lib/python/pygrass/messages/Makefile
===================================================================
--- grass/trunk/lib/python/pygrass/messages/Makefile	                        (rev 0)
+++ grass/trunk/lib/python/pygrass/messages/Makefile	2013-11-12 14:00:53 UTC (rev 58201)
@@ -0,0 +1,32 @@
+MODULE_TOPDIR = ../../../..
+
+include $(MODULE_TOPDIR)/include/Make/Other.make
+include $(MODULE_TOPDIR)/include/Make/Python.make
+include $(MODULE_TOPDIR)/include/Make/Doxygen.make
+
+PYDIR = $(ETC)/python
+GDIR = $(PYDIR)/grass
+PGDIR = $(GDIR)/pygrass
+DSTDIR= $(PGDIR)/messages
+
+MODULES = 
+
+PYFILES := $(patsubst %,$(DSTDIR)/%.py,$(MODULES) __init__)
+PYCFILES := $(patsubst %,$(DSTDIR)/%.pyc,$(MODULES) __init__)
+
+default: $(PYFILES) $(PYCFILES) $(GDIR)/__init__.py $(GDIR)/__init__.pyc
+
+$(PYDIR):
+	$(MKDIR) $@
+
+$(GDIR): | $(PYDIR)
+	$(MKDIR) $@
+
+$(DSTDIR): | $(GDIR)
+	$(MKDIR) $@
+
+$(DSTDIR)/%: % | $(DSTDIR)
+	$(INSTALL_DATA) $< $@
+
+#doxygen:
+DOXNAME = pythonpygrass

Added: grass/trunk/lib/python/pygrass/messages/__init__.py
===================================================================
--- grass/trunk/lib/python/pygrass/messages/__init__.py	                        (rev 0)
+++ grass/trunk/lib/python/pygrass/messages/__init__.py	2013-11-12 14:00:53 UTC (rev 58201)
@@ -0,0 +1,238 @@
+# -*- coding: utf-8 -*-
+"""!@package grass.pygrass.massages
+
+ at 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.
+
+ at author Soeren Gebbert
+"""
+
+import grass.lib.gis as libgis
+from multiprocessing import Process, Lock, Pipe
+
+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 identifers 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]
+    """
+    while True:
+        # Avoid busy waiting
+        conn.poll(None)
+        data = conn.recv()
+        message_type = data[0]
+
+        # Only one process is allowed to write to stdout
+        lock.acquire()
+
+        # Stop the pipe and the infinite loop
+        if message_type == "STOP":
+            conn.close()
+            lock.release()
+            return
+
+        message = data[1]
+
+        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:
+
+       @code
+       >>> 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")
+
+       D0/0: debug 0
+       message
+       important message
+        100%
+       WARNING: Ohh
+       ERROR: Ohh no
+
+       @endcode
+    """
+    def __init__(self):
+        self.client_conn = None
+        self.server_conn = None
+        self.server = None
+        self.start_server()
+
+    def __del__(self):
+        self.stop()
+
+    def start_server(self):
+        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 stdout
+
+           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 stdout
+
+           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 stdout
+
+           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 stdout
+
+           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 stdout
+
+           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 debug(self, level, message):
+        """!Send a debug message to stdout
+
+           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 stdout
+
+           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 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)
+
+if __name__ == "__main__":
+    import doctest
+    doctest.testmod()



More information about the grass-commit mailing list