[GRASS-SVN] r66104 - in grass/trunk/lib/python/pygrass: . rpc rpc/testsuite vector
svn_grass at osgeo.org
svn_grass at osgeo.org
Fri Sep 4 17:41:31 PDT 2015
Author: huhabla
Date: 2015-09-04 17:41:30 -0700 (Fri, 04 Sep 2015)
New Revision: 66104
Added:
grass/trunk/lib/python/pygrass/rpc/
grass/trunk/lib/python/pygrass/rpc/Makefile
grass/trunk/lib/python/pygrass/rpc/__init__.py
grass/trunk/lib/python/pygrass/rpc/base.py
grass/trunk/lib/python/pygrass/rpc/testsuite/
grass/trunk/lib/python/pygrass/rpc/testsuite/test_doctests.py
Modified:
grass/trunk/lib/python/pygrass/Makefile
grass/trunk/lib/python/pygrass/utils.py
grass/trunk/lib/python/pygrass/vector/__init__.py
Log:
pygrass: Added rpc data provider that deliver WKB representations of vector maps, numpy image
representations of raster maps and vector map attribute tables as dict using cats as keys.
Modified: grass/trunk/lib/python/pygrass/Makefile
===================================================================
--- grass/trunk/lib/python/pygrass/Makefile 2015-09-04 21:44:31 UTC (rev 66103)
+++ grass/trunk/lib/python/pygrass/Makefile 2015-09-05 00:41:30 UTC (rev 66104)
@@ -9,7 +9,7 @@
MODULES = errors utils orderdict
-CLEAN_SUBDIRS = messages modules raster vector gis shell tests
+CLEAN_SUBDIRS = messages modules raster vector gis shell tests rpc
PYFILES := $(patsubst %,$(DSTDIR)/%.py,$(MODULES) __init__)
PYCFILES := $(patsubst %,$(DSTDIR)/%.pyc,$(MODULES) __init__)
@@ -22,6 +22,7 @@
-$(MAKE) -C gis || echo $(CURDIR)/gis >> $(ERRORLOG)
-$(MAKE) -C shell || echo $(CURDIR)/shell >> $(ERRORLOG)
-$(MAKE) -C tests || echo $(CURDIR)/tests >> $(ERRORLOG)
+ -$(MAKE) -C rpc || echo $(CURDIR)/gis >> $(ERRORLOG)
$(PYDIR):
$(MKDIR) $@
Added: grass/trunk/lib/python/pygrass/rpc/Makefile
===================================================================
--- grass/trunk/lib/python/pygrass/rpc/Makefile (rev 0)
+++ grass/trunk/lib/python/pygrass/rpc/Makefile 2015-09-05 00:41:30 UTC (rev 66104)
@@ -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)/rpc
+
+MODULES = base
+
+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/rpc/__init__.py
===================================================================
--- grass/trunk/lib/python/pygrass/rpc/__init__.py (rev 0)
+++ grass/trunk/lib/python/pygrass/rpc/__init__.py 2015-09-05 00:41:30 UTC (rev 66104)
@@ -0,0 +1,424 @@
+# -*- coding: utf-8 -*-
+"""
+Fast and exit-safe interface to PyGRASS Raster and Vector layer
+using multiprocessing
+
+(C) 2015 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.
+
+:authors: Soeren Gebbert
+"""
+
+import time
+import threading
+import sys
+from multiprocessing import Process, Lock, Pipe
+from ctypes import *
+
+from grass.exceptions import FatalError
+from grass.pygrass.vector import *
+from grass.pygrass.raster import *
+import grass.lib.gis as libgis
+from base import RPCServerBase
+from grass.pygrass.gis.region import Region
+import logging
+
+###############################################################################
+###############################################################################
+
+class RPCDefs(object):
+ # Function identifier and index
+ STOP = 0
+ GET_VECTOR_TABLE_AS_DICT = 1
+ GET_VECTOR_FEATURES_AS_WKB = 2
+ GET_RASTER_IMAGE_AS_NP = 3
+ G_FATAL_ERROR = 14
+
+
+def _get_raster_image_as_np(lock, conn, data):
+ """Convert a raster map into an image and return
+ a numpy array with RGB or Gray values.
+
+ :param lock: A multiprocessing.Lock instance
+ :param conn: A multiprocessing.Pipe instance used to send True or False
+ :param data: The list of data entries [function_id, raster_name, extent, color]
+ """
+ raster_name = data[1]
+ extent = data[2]
+ color = data[3]
+
+ rast = RasterRow(raster_name)
+ array = None
+
+ if rast.exist():
+
+ reg = Region()
+ reg.from_rast(raster_name)
+
+ if extent is not None:
+ if "north" in extent:
+ reg.north = extent["north"]
+ if "south" in extent:
+ reg.south = extent["south"]
+ if "east" in extent:
+ reg.east = extent["east"]
+ if "west" in extent:
+ reg.west = extent["west"]
+ if "rows" in extent:
+ reg.rows = extent["rows"]
+ if "cols" in extent:
+ reg.cols = extent["cols"]
+ reg.adjust()
+
+ array = raster2numpy_img(raster_name, reg, color)
+
+ conn.send(array)
+
+def _get_vector_table_as_dict(lock, conn, data):
+ """Get the table of a vector map layer as dictionary
+
+ The value to be send via pipe is True in case the map exists and False
+ if not.
+
+ :param lock: A multiprocessing.Lock instance
+ :param conn: A multiprocessing.Pipe instance used to send True or False
+ :param data: The list of data entries [function_id, name, where]
+
+ """
+ name = data[1]
+ where = data[2]
+ layer = VectorTopo(name)
+ ret = None
+
+ if layer.exist() is True:
+ layer.open("r")
+ columns = None
+ table = None
+ if layer.table is not None:
+ columns = layer.table.columns
+ table = layer.table_to_dict(where=where)
+ layer.close()
+
+ ret = {}
+ ret["table"] = table
+ ret["columns"] = columns
+
+ conn.send(ret)
+
+def _get_vector_features_as_wkb_list(lock, conn, data):
+ """Return vector layer features as wkb list
+
+ supported feature types:
+ point, centroid, line, boundary, area
+
+ The value to be send via pipe is True in case the map exists and False
+ if not.
+
+ :param lock: A multiprocessing.Lock instance
+ :param conn: A multiprocessing.Pipe instance used to send True or False
+ :param data: The list of data entries [function_id,name,extent,
+ feature_type, field]
+
+ """
+ name = data[1]
+ extent = data[2]
+ feature_type = data[3]
+ field = data[4]
+
+ wkb_list = None
+ bbox = None
+
+ layer = VectorTopo(name)
+
+ try:
+ if layer.exist() is True:
+ if extent is not None:
+ bbox = basic.Bbox(north=extent["north"],
+ south=extent["south"],
+ east=extent["east"],
+ west=extent["west"])
+ logging.warning(str(bbox))
+ layer.open("r")
+ if feature_type.lower() == "area":
+ wkb_list = layer.areas_to_wkb_list(bbox=bbox, field=field)
+ else:
+ wkb_list = layer.features_to_wkb_list(bbox=bbox,
+ feature_type=feature_type,
+ field=field)
+ layer.close()
+ except Exception, e:
+ print(e)
+
+ conn.send(wkb_list)
+
+###############################################################################
+
+def _fatal_error(lock, conn, data):
+ """Calls G_fatal_error()"""
+ libgis.G_fatal_error("Fatal Error in C library server")
+
+
+###############################################################################
+
+def _stop(lock, conn, data):
+ conn.close()
+ lock.release()
+ sys.exit()
+
+###############################################################################
+
+def data_provider_server(lock, conn):
+ """The PyGRASS data provider server designed to be a target for
+ multiprocessing.Process
+
+ :param lock: A multiprocessing.Lock
+ :param conn: A multiprocessing.Pipe
+ """
+
+ def error_handler(data):
+ """This function will be called in case of a fatal error in libgis"""
+ #sys.stderr.write("Error handler was called\n")
+ # We send an exeption that will be handled in
+ # the parent process, then close the pipe
+ # and release any possible lock
+ conn.send(FatalError())
+ conn.close()
+ lock.release()
+
+ CALLBACK = CFUNCTYPE(c_void_p, c_void_p)
+ CALLBACK.restype = c_void_p
+ CALLBACK.argtypes = c_void_p
+
+ cerror_handler = CALLBACK(error_handler)
+
+ libgis.G_add_error_handler(cerror_handler, None)
+
+ # Crerate the function array
+ functions = [0]*15
+ functions[RPCDefs.GET_VECTOR_TABLE_AS_DICT] = _get_vector_table_as_dict
+ functions[RPCDefs.GET_VECTOR_FEATURES_AS_WKB] = _get_vector_features_as_wkb_list
+ functions[RPCDefs.GET_RASTER_IMAGE_AS_NP] = _get_raster_image_as_np
+ functions[RPCDefs.STOP] = _stop
+ functions[RPCDefs.G_FATAL_ERROR] = _fatal_error
+
+ while True:
+ # Avoid busy waiting
+ conn.poll(None)
+ data = conn.recv()
+ lock.acquire()
+ functions[data[0]](lock, conn, data)
+ lock.release()
+
+test_vector_name="data_provider_vector_map"
+test_raster_name="data_provider_raster_map"
+
+class DataProvider(RPCServerBase):
+ """Fast and exit-safe interface to PyGRASS data delivery functions
+
+ """
+ def __init__(self):
+ RPCServerBase.__init__(self)
+
+ def start_server(self):
+ """This function must be re-implemented in the subclasses
+ """
+ self.client_conn, self.server_conn = Pipe(True)
+ self.lock = Lock()
+ self.server = Process(target=data_provider_server, args=(self.lock,
+ self.server_conn))
+ self.server.daemon = True
+ self.server.start()
+
+ def get_raster_image_as_np(self, name, extent=None, color="RGB"):
+ """Return the attribute table of a vector map as dictionary.
+
+ See documentation of: pygrass.raster.raster2numpy_img
+
+ Usage:
+
+ .. code-block:: python
+
+ >>> from grass.pygrass.rpc import DataProvider
+ >>> provider = DataProvider()
+ >>> ret = provider.get_raster_image_as_np(name=test_raster_name)
+ >>> len(ret)
+ 64
+
+ >>> extent = {"north":30, "south":10, "east":30, "west":10,
+ ... "rows":2, "cols":2}
+ >>> ret = provider.get_raster_image_as_np(name=test_raster_name,
+ ... extent=extent)
+ >>> len(ret)
+ 16
+ >>> ret # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
+ array([169, 255, 0, 255, 255, 0, 46, 255, 208, 255,
+ 0, 255, 255, 0, 84, 255], dtype=uint8)
+
+ >>> extent = {"rows":3, "cols":1}
+ >>> ret = provider.get_raster_image_as_np(name=test_raster_name,
+ ... extent=extent)
+ >>> len(ret)
+ 12
+ >>> ret # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
+ array([255, 0, 7, 255, 255, 0, 84, 255, 255,
+ 0, 123, 255], dtype=uint8)
+ >>> provider.stop()
+
+ ..
+ """
+ self.check_server()
+ self.client_conn.send([RPCDefs.GET_RASTER_IMAGE_AS_NP,
+ name, extent, color])
+ return self.safe_receive("get_raster_image_as_np")
+
+ def get_vector_table_as_dict(self, name, where=None):
+ """Return the attribute table of a vector map as dictionary.
+
+ See documentation of: pygrass.vector.VectorTopo::table_to_dict
+
+ Usage:
+
+ .. code-block:: python
+
+ >>> from grass.pygrass.rpc import DataProvider
+ >>> provider = DataProvider()
+ >>> ret = provider.get_vector_table_as_dict(name=test_vector_name)
+ >>> ret["table"]
+ {1: [1, u'point', 1.0], 2: [2, u'line', 2.0], 3: [3, u'centroid', 3.0]}
+ >>> ret["columns"]
+ Columns([(u'cat', u'INTEGER'), (u'name', u'varchar(50)'), (u'value', u'double precision')])
+ >>> ret = provider.get_vector_table_as_dict(name=test_vector_name,
+ ... where="value > 1")
+ >>> ret["table"]
+ {2: [2, u'line', 2.0], 3: [3, u'centroid', 3.0]}
+ >>> ret["columns"]
+ Columns([(u'cat', u'INTEGER'), (u'name', u'varchar(50)'), (u'value', u'double precision')])
+ >>> provider.get_vector_table_as_dict(name="no_map",
+ ... where="value > 1")
+ >>> provider.stop()
+
+ ..
+ """
+ self.check_server()
+ self.client_conn.send([RPCDefs.GET_VECTOR_TABLE_AS_DICT,
+ name, where])
+ return self.safe_receive("get_vector_table_as_dict")
+
+ def get_vector_features_as_wkb_list(self, name, extent=None,
+ feature_type="point", field=1):
+ """Return the features of a vector map as wkb list.
+
+ :param extent: A dictionary of {"north":double, "south":double,
+ "east":double, "west":double}
+ :param feature_type: point, centroid, line, boundary or area
+
+ See documentation: pygrass.vector.VectorTopo::features_to_wkb_list
+ pygrass.vector.VectorTopo::areas_to_wkb_list
+
+
+ Usage:
+
+ .. code-block:: python
+
+ >>> from grass.pygrass.rpc import DataProvider
+ >>> provider = DataProvider()
+ >>> wkb = provider.get_vector_features_as_wkb_list(name=test_vector_name,
+ ... extent=None,
+ ... feature_type="point")
+ >>> for entry in wkb:
+ ... f_id, cat, string = entry
+ ... print(f_id, cat, len(string))
+ 1 1 21
+ 2 1 21
+ 3 1 21
+
+ >>> extent = {"north":6.6, "south":5.5, "east":14.5, "west":13.5}
+ >>> wkb = provider.get_vector_features_as_wkb_list(name=test_vector_name,
+ ... extent=extent,
+ ... feature_type="point")
+ >>> for entry in wkb:
+ ... f_id, cat, string = entry
+ ... print(f_id, cat, len(string))
+ 3 1 21
+
+ >>> wkb = provider.get_vector_features_as_wkb_list(name=test_vector_name,
+ ... extent=None,
+ ... feature_type="line")
+ >>> for entry in wkb:
+ ... f_id, cat, string = entry
+ ... print(f_id, cat, len(string))
+ 4 2 57
+ 5 2 57
+ 6 2 57
+
+
+ >>> wkb = provider.get_vector_features_as_wkb_list(name=test_vector_name,
+ ... extent=None,
+ ... feature_type="centroid")
+ >>> for entry in wkb:
+ ... f_id, cat, string = entry
+ ... print(f_id, cat, len(string))
+ 19 3 21
+ 18 3 21
+ 20 3 21
+ 21 3 21
+
+ >>> wkb = provider.get_vector_features_as_wkb_list(name=test_vector_name,
+ ... extent=None,
+ ... feature_type="area")
+ >>> for entry in wkb:
+ ... f_id, cat, string = entry
+ ... print(f_id, cat, len(string))
+ 1 3 225
+ 2 3 141
+ 3 3 93
+ 4 3 141
+
+ >>> wkb = provider.get_vector_features_as_wkb_list(name=test_vector_name,
+ ... extent=None,
+ ... feature_type="boundary")
+ >>> for entry in wkb:
+ ... f_id, cat, string = entry
+ ... print(f_id, cat, len(string))
+ 10 None 41
+ 7 None 41
+ 8 None 41
+ 9 None 41
+ 11 None 89
+ 12 None 41
+ 14 None 41
+ 13 None 41
+ 17 None 41
+ 15 None 41
+ 16 None 41
+
+ >>> provider.stop()
+
+ ..
+ """
+ self.check_server()
+ self.client_conn.send([RPCDefs.GET_VECTOR_FEATURES_AS_WKB,
+ name, extent, feature_type, field])
+ return self.safe_receive("get_vector_features_as_wkb_list")
+
+
+if __name__ == "__main__":
+ import doctest
+ from grass.pygrass import utils
+ from grass.pygrass.modules import Module
+ Module("g.region", n=40, s=0, e=40, w=0, res=10)
+ Module("r.mapcalc", expression="%s = row() + (10 * col())"%(test_raster_name),
+ overwrite=True)
+ utils.create_test_vector_map(test_vector_name)
+
+ doctest.testmod()
+
+ """Remove the generated maps, if exist"""
+ mset = utils.get_mapset_raster(test_raster_name, mapset='')
+ if mset:
+ Module("g.remove", flags='f', type='raster', name=test_raster_name)
+ mset = utils.get_mapset_vector(test_vector_name, mapset='')
+ if mset:
+ Module("g.remove", flags='f', type='vector', name=test_vector_name)
Added: grass/trunk/lib/python/pygrass/rpc/base.py
===================================================================
--- grass/trunk/lib/python/pygrass/rpc/base.py (rev 0)
+++ grass/trunk/lib/python/pygrass/rpc/base.py 2015-09-05 00:41:30 UTC (rev 66104)
@@ -0,0 +1,184 @@
+# -*- coding: utf-8 -*-
+"""
+Fast and exit-safe interface to PyGRASS Raster and Vector layer
+using multiprocessing
+
+(C) 2015 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.
+
+:authors: Soeren Gebbert
+"""
+
+from grass.exceptions import FatalError
+import time
+import threading
+import sys
+from multiprocessing import Process, Lock, Pipe
+import logging
+
+###############################################################################
+
+def dummy_server(lock, conn):
+ """Dummy server process
+
+ :param lock: A multiprocessing.Lock
+ :param conn: A multiprocessing.Pipe
+ """
+
+ while True:
+ # Avoid busy waiting
+ conn.poll(None)
+ data = conn.recv()
+ lock.acquire()
+ if data[0] == 0:
+ conn.close()
+ lock.release()
+ sys.exit()
+ if data[0] == 1:
+ raise Exception("Server process intentionally killed by exception")
+ lock.release()
+
+class RPCServerBase(object):
+ """This is the base class for send and receive RPC server
+ It uses a Pipe for IPC.
+
+
+ >>> import grass.script as gscript
+ >>> from grass.pygrass.rpc.base import RPCServerBase
+ >>> provider = RPCServerBase()
+
+ >>> provider.is_server_alive()
+ True
+
+ >>> provider.is_check_thread_alive()
+ True
+
+ >>> provider.stop()
+ >>> provider.is_server_alive()
+ False
+
+ >>> provider.is_check_thread_alive()
+ False
+
+ >>> provider = RPCServerBase()
+ >>> provider.is_server_alive()
+ True
+ >>> provider.is_check_thread_alive()
+ True
+
+ Kill the server process with an exception, it should restart
+
+ >>> provider.client_conn.send([1])
+ >>> provider.is_server_alive()
+ True
+
+ >>> provider.is_check_thread_alive()
+ True
+
+ """
+
+ def __init__(self):
+ self.client_conn = None
+ self.server_conn = None
+ self.queue = None
+ self.server = None
+ self.checkThread = None
+ self.threadLock = threading.Lock()
+ self.start_server()
+ self.start_checker_thread()
+ self.stopThread = False
+
+ def is_server_alive(self):
+ return self.server.is_alive()
+
+ def is_check_thread_alive(self):
+ return self.checkThread.is_alive()
+
+ def start_checker_thread(self):
+ if self.checkThread is not None and self.checkThread.is_alive():
+ self.stop_checker_thread()
+
+ self.checkThread = threading.Thread(target=self.thread_checker)
+ self.checkThread.daemon = True
+ self.stopThread = False
+ self.checkThread.start()
+
+ def stop_checker_thread(self):
+ self.threadLock.acquire()
+ self.stopThread = True
+ self.threadLock.release()
+ self.checkThread.join(None)
+
+ def thread_checker(self):
+ """Check every 200 micro seconds if the server process is alive"""
+ while True:
+ time.sleep(0.2)
+ #sys.stderr.write("Check server process\n")
+ self._check_restart_server()
+ self.threadLock.acquire()
+ if self.stopThread == True:
+ #sys.stderr.write("Stop thread\n")
+ self.threadLock.release()
+ return
+ self.threadLock.release()
+
+ def start_server(self):
+ """This function must be re-implemented in the subclasses
+ """
+ self.client_conn, self.server_conn = Pipe(True)
+ self.lock = Lock()
+ self.server = Process(target=dummy_server, args=(self.lock,
+ self.server_conn))
+ self.server.daemon = True
+ self.server.start()
+
+ def check_server(self):
+ self._check_restart_server()
+
+ def _check_restart_server(self):
+ """Restart the server if it was terminated
+ """
+ self.threadLock.acquire()
+ if self.server.is_alive() is True:
+ self.threadLock.release()
+ return
+ self.client_conn.close()
+ self.server_conn.close()
+ self.start_server()
+
+ logging.warning("Needed to restart the libgis server")
+
+ self.threadLock.release()
+
+ def safe_receive(self, message):
+ """Receive the data and throw an FatalError exception in case the server
+ process was killed and the pipe was closed by the checker thread"""
+ try:
+ ret = self.client_conn.recv()
+ if isinstance(ret, FatalError):
+ raise FatalError()
+ return ret
+ except (EOFError, IOError, FatalError):
+ # The pipe was closed by the checker thread because
+ # the server process was killed
+ raise FatalError(message)
+
+ def stop(self):
+ """Stop the check thread, the libgis server and close the pipe
+
+ This method should be called at exit using the package atexit
+ """
+ #sys.stderr.write("###### Stop was called\n")
+ self.stop_checker_thread()
+ if self.server is not None and self.server.is_alive():
+ self.client_conn.send([0, ])
+ self.server.join()
+ if self.client_conn is not None:
+ self.client_conn.close()
+
+
+if __name__ == "__main__":
+ import doctest
+ doctest.testmod()
Added: grass/trunk/lib/python/pygrass/rpc/testsuite/test_doctests.py
===================================================================
--- grass/trunk/lib/python/pygrass/rpc/testsuite/test_doctests.py (rev 0)
+++ grass/trunk/lib/python/pygrass/rpc/testsuite/test_doctests.py 2015-09-05 00:41:30 UTC (rev 66104)
@@ -0,0 +1,49 @@
+# -*- coding: utf-8 -*-
+"""
+Tests checkers
+"""
+
+import doctest
+
+import grass.gunittest.case
+import grass.gunittest.main
+import grass.gunittest.utils
+
+import grass.pygrass.rpc as pygrpc
+
+
+# doctest does not allow changing the base classes of test case, skip test case
+# and test suite, so we need to create a new type which inherits from our class
+# and contains doctest's methods
+# the alternative is to copy 500 from doctest and change what is needed
+# (this might be necessary anyway beacuse of the reports and stdout and stderr)
+doctest.DocFileCase = type('DocFileCase',
+ (grass.gunittest.case.TestCase,),
+ dict(doctest.DocFileCase.__dict__))
+doctest.SkipDocTestCase = type('SkipDocTestCase',
+ (grass.gunittest.case.TestCase,),
+ dict(doctest.SkipDocTestCase.__dict__))
+
+
+def load_tests(loader, tests, ignore):
+ # TODO: this must be somewhere when doctest is called, not here
+ # TODO: ultimate solution is not to use _ as a buildin in lib/python
+ # for now it is the only place where it works
+ grass.gunittest.utils.do_doctest_gettext_workaround()
+ # this should be called at some top level
+
+ from grass.pygrass import utils
+ from grass.pygrass.modules import Module
+ Module("g.region", n=40, s=0, e=40, w=0, res=10)
+ Module("r.mapcalc", expression="%s = row() + (10 * col())"%(pygrpc.test_raster_name),
+ overwrite=True)
+ utils.create_test_vector_map(pygrpc.test_vector_name)
+
+
+ tests.addTests(doctest.DocTestSuite(pygrpc))
+ tests.addTests(doctest.DocTestSuite(pygrpc.base))
+ return tests
+
+
+if __name__ == '__main__':
+ grass.gunittest.main.test()
Modified: grass/trunk/lib/python/pygrass/utils.py
===================================================================
--- grass/trunk/lib/python/pygrass/utils.py 2015-09-04 21:44:31 UTC (rev 66103)
+++ grass/trunk/lib/python/pygrass/utils.py 2015-09-05 00:41:30 UTC (rev 66104)
@@ -225,8 +225,8 @@
True
>>> get_raster_for_points(fire, ele, column=test_raster_name, region=region)
True
- >>> fire.table.filters.select('LABEL', test_raster_name)
- Filters(u'SELECT LABEL, Utils_test_raster FROM test_vect_2;')
+ >>> fire.table.filters.select('name', test_raster_name)
+ Filters(u'SELECT name, Utils_test_raster FROM test_vect_2;')
>>> cur = fire.table.execute()
>>> r = cur.fetchall()
>>> r[0] # doctest: +ELLIPSIS
@@ -285,8 +285,6 @@
def get_lib_path(modname, libname):
"""Return the path of the libname contained in the module.
-
- >>> get_lib_path(modname='r.modis', libname='libmodis')
"""
from os.path import isdir, join
from os import getenv
Modified: grass/trunk/lib/python/pygrass/vector/__init__.py
===================================================================
--- grass/trunk/lib/python/pygrass/vector/__init__.py 2015-09-04 21:44:31 UTC (rev 66103)
+++ grass/trunk/lib/python/pygrass/vector/__init__.py 2015-09-05 00:41:30 UTC (rev 66104)
@@ -716,7 +716,7 @@
supported = ['point', 'line', 'boundary', 'centroid']
- if feature_type not in supported:
+ if feature_type.lower() not in supported:
raise GrassError("Unsupported feature type <%s>, "\
"supported are <%s>"%(feature_type,
",".join(supported)))
@@ -724,7 +724,7 @@
if bbox is None:
bbox = self.bbox()
- bboxlist = self.find_by_bbox.geos(bbox, type=feature_type,
+ bboxlist = self.find_by_bbox.geos(bbox, type=feature_type.lower(),
bboxlist_only = True)
if bboxlist is not None and len(bboxlist) > 0:
More information about the grass-commit
mailing list