[GRASS-SVN] r62382 - in grass/branches/releasebranch_7_0/lib/python/pygrass: . gis messages modules modules/grid modules/interface raster shell vector vector/testsuite

svn_grass at osgeo.org svn_grass at osgeo.org
Sun Oct 26 09:42:33 PDT 2014


Author: zarch
Date: 2014-10-26 09:42:33 -0700 (Sun, 26 Oct 2014)
New Revision: 62382

Modified:
   grass/branches/releasebranch_7_0/lib/python/pygrass/Makefile
   grass/branches/releasebranch_7_0/lib/python/pygrass/__init__.py
   grass/branches/releasebranch_7_0/lib/python/pygrass/errors.py
   grass/branches/releasebranch_7_0/lib/python/pygrass/functions.py
   grass/branches/releasebranch_7_0/lib/python/pygrass/gis/__init__.py
   grass/branches/releasebranch_7_0/lib/python/pygrass/gis/region.py
   grass/branches/releasebranch_7_0/lib/python/pygrass/messages/__init__.py
   grass/branches/releasebranch_7_0/lib/python/pygrass/modules/__init__.py
   grass/branches/releasebranch_7_0/lib/python/pygrass/modules/grid/__init__.py
   grass/branches/releasebranch_7_0/lib/python/pygrass/modules/grid/grid.py
   grass/branches/releasebranch_7_0/lib/python/pygrass/modules/interface/flag.py
   grass/branches/releasebranch_7_0/lib/python/pygrass/modules/interface/module.py
   grass/branches/releasebranch_7_0/lib/python/pygrass/modules/interface/parameter.py
   grass/branches/releasebranch_7_0/lib/python/pygrass/modules/interface/read.py
   grass/branches/releasebranch_7_0/lib/python/pygrass/modules/interface/typedict.py
   grass/branches/releasebranch_7_0/lib/python/pygrass/modules/shortcuts.py
   grass/branches/releasebranch_7_0/lib/python/pygrass/raster/__init__.py
   grass/branches/releasebranch_7_0/lib/python/pygrass/raster/abstract.py
   grass/branches/releasebranch_7_0/lib/python/pygrass/raster/buffer.py
   grass/branches/releasebranch_7_0/lib/python/pygrass/raster/category.py
   grass/branches/releasebranch_7_0/lib/python/pygrass/shell/__init__.py
   grass/branches/releasebranch_7_0/lib/python/pygrass/shell/conversion.py
   grass/branches/releasebranch_7_0/lib/python/pygrass/vector/__init__.py
   grass/branches/releasebranch_7_0/lib/python/pygrass/vector/abstract.py
   grass/branches/releasebranch_7_0/lib/python/pygrass/vector/basic.py
   grass/branches/releasebranch_7_0/lib/python/pygrass/vector/find.py
   grass/branches/releasebranch_7_0/lib/python/pygrass/vector/geometry.py
   grass/branches/releasebranch_7_0/lib/python/pygrass/vector/table.py
   grass/branches/releasebranch_7_0/lib/python/pygrass/vector/testsuite/test_geometry.py
   grass/branches/releasebranch_7_0/lib/python/pygrass/vector/testsuite/test_vector3d.py
Log:
pygrass: backport trunk version to releasebranch_7_0

Modified: grass/branches/releasebranch_7_0/lib/python/pygrass/Makefile
===================================================================
--- grass/branches/releasebranch_7_0/lib/python/pygrass/Makefile	2014-10-26 16:12:08 UTC (rev 62381)
+++ grass/branches/releasebranch_7_0/lib/python/pygrass/Makefile	2014-10-26 16:42:33 UTC (rev 62382)
@@ -2,7 +2,6 @@
 
 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
@@ -35,6 +34,3 @@
 
 $(DSTDIR)/%: % | $(DSTDIR)
 	$(INSTALL_DATA) $< $@
-
-#doxygen:
-DOXNAME = pygrass

Modified: grass/branches/releasebranch_7_0/lib/python/pygrass/__init__.py
===================================================================
--- grass/branches/releasebranch_7_0/lib/python/pygrass/__init__.py	2014-10-26 16:12:08 UTC (rev 62381)
+++ grass/branches/releasebranch_7_0/lib/python/pygrass/__init__.py	2014-10-26 16:42:33 UTC (rev 62382)
@@ -1,19 +1 @@
 # -*- coding: utf-8 -*-
-"""
-Created on Fri May 25 12:55:14 2012
-
- at author: pietro
-"""
-import grass.lib.gis as _libgis
-_libgis.G_gisinit('')
-import os as _os
-import sys as _sys
-
-from . import errors
-from . import gis
-from . import functions
-from . import raster
-from . import vector
-from . import modules
-from . import shell
-from . import messages

Modified: grass/branches/releasebranch_7_0/lib/python/pygrass/errors.py
===================================================================
--- grass/branches/releasebranch_7_0/lib/python/pygrass/errors.py	2014-10-26 16:12:08 UTC (rev 62381)
+++ grass/branches/releasebranch_7_0/lib/python/pygrass/errors.py	2014-10-26 16:42:33 UTC (rev 62382)
@@ -1,9 +1,4 @@
 # -*- coding: utf-8 -*-
-"""
-Created on Wed Aug 15 17:33:27 2012
-
- at author: pietro
-"""
 from functools import wraps
 
 from grass.exceptions import (FlagError, ParameterError, DBError,

Modified: grass/branches/releasebranch_7_0/lib/python/pygrass/functions.py
===================================================================
--- grass/branches/releasebranch_7_0/lib/python/pygrass/functions.py	2014-10-26 16:12:08 UTC (rev 62381)
+++ grass/branches/releasebranch_7_0/lib/python/pygrass/functions.py	2014-10-26 16:42:33 UTC (rev 62382)
@@ -1,20 +1,15 @@
 # -*- coding: utf-8 -*-
-"""
-Created on Tue Jun 26 12:38:48 2012
-
- at author: pietro
-"""
 import itertools
 import fnmatch
 import os
 from sqlite3 import OperationalError
 
 import grass.lib.gis as libgis
+libgis.G_gisinit('')
 import grass.lib.raster as libraster
 from grass.script import core as grasscore
 
 from grass.pygrass.errors import GrassError
-from grass.pygrass.gis.region import Region
 
 
 def looking(obj, filter_string):
@@ -48,6 +43,7 @@
 
 def findmaps(type, pattern=None, mapset='', location='', gisdbase=''):
     """Return a list of tuple contining the names of the:
+
         * map
         * mapset,
         * location,
@@ -93,10 +89,10 @@
         return find_in_gisdbase(type, pattern, gis)
 
 
-def remove(oldname, maptype, **kwargs):
+def remove(oldname, maptype):
     """Remove a map"""
-    kwargs.update({maptype: '{old}'.format(old=oldname)})
-    grasscore.run_command('g.remove', quiet=True, **kwargs)
+    grasscore.run_command('g.remove', quiet=True, flags='f',
+                          type=maptype, pattern=oldname)
 
 
 def rename(oldname, newname, maptype, **kwargs):
@@ -118,17 +114,17 @@
 
 
 def getenv(env):
-    """Return the current grass environment variables ::
+    """Return the current grass environment variables
 
-        >>> getenv("MAPSET")
-        'user1'
+    >>> getenv("MAPSET")
+    'user1'
 
     """
     return libgis.G__getenv(env)
 
 
 def get_mapset_raster(mapname, mapset=''):
-    """Return the mapset of the raster map ::
+    """Return the mapset of the raster map
 
     >>> get_mapset_raster('elevation')
     'PERMANENT'
@@ -138,7 +134,7 @@
 
 
 def get_mapset_vector(mapname, mapset=''):
-    """Return the mapset of the vector map ::
+    """Return the mapset of the vector map
 
     >>> get_mapset_vector('census')
     'PERMANENT'
@@ -148,7 +144,7 @@
 
 
 def is_clean_name(name):
-    """Return if the name is valid ::
+    """Return if the name is valid
 
     >>> is_clean_name('census')
     True
@@ -166,13 +162,14 @@
 
 
 def coor2pixel(coord, region):
-    """Convert coordinates into a pixel row and col ::
+    """Convert coordinates into a pixel row and col
 
-        >>> reg = Region()
-        >>> coor2pixel((reg.west, reg.north), reg)
-        (0.0, 0.0)
-        >>> coor2pixel((reg.east, reg.south), reg) == (reg.rows, reg.cols)
-        True
+    >>> reg = Region()
+    >>> coor2pixel((reg.west, reg.north), reg)
+    (0.0, 0.0)
+    >>> coor2pixel((reg.east, reg.south), reg) == (reg.rows, reg.cols)
+    True
+
     """
     (east, north) = coord
     return (libraster.Rast_northing_to_row(north, region.c_region),
@@ -180,58 +177,57 @@
 
 
 def pixel2coor(pixel, region):
-    """Convert row and col of a pixel into a coordinates ::
+    """Convert row and col of a pixel into a coordinates
 
-        >>> reg = Region()
-        >>> pixel2coor((0, 0), reg) == (reg.north, reg.west)
-        True
-        >>> pixel2coor((reg.cols, reg.rows), reg) == (reg.south, reg.east)
-        True
+    >>> reg = Region()
+    >>> pixel2coor((0, 0), reg) == (reg.north, reg.west)
+    True
+    >>> pixel2coor((reg.cols, reg.rows), reg) == (reg.south, reg.east)
+    True
+
     """
     (col, row) = pixel
     return (libraster.Rast_row_to_northing(row, region.c_region),
             libraster.Rast_col_to_easting(col, region.c_region))
 
 
-def get_raster_for_points(poi_vector, raster, column=None):
+def get_raster_for_points(poi_vector, raster, column=None, region=None):
     """Query a raster map for each point feature of a vector
 
-    Example ::
+    Example
 
-        >>> from grass.pygrass.vector import VectorTopo
-        >>> from grass.pygrass.raster import RasterRow
-        >>> ele = RasterRow('elevation')
-        >>> copy('schools','myschools','vect')
-        >>> sch = VectorTopo('myschools')
-        >>> sch.open()
-        >>> get_raster_for_points(sch, ele)               # doctest: +ELLIPSIS
-        [(1, 633649.2856743174, 221412.94434781274, 145.06602)...
-        >>> sch.table.columns.add('elevation','double precision')
-        >>> 'elevation' in sch.table.columns
-        True
-        >>> get_raster_for_points(sch, ele, 'elevation')
-        True
-        >>> sch.table.filters.select('NAMESHORT','elevation')
-        Filters(u'SELECT NAMESHORT, elevation FROM myschools;')
-        >>> cur = sch.table.execute()
-        >>> cur.fetchall()                                # doctest: +ELLIPSIS
-        [(u'SWIFT CREEK', 145.06602), ... (u'9TH GRADE CTR', None)]
-        >>> remove('myschools','vect')
+    >>> from grass.pygrass.vector import VectorTopo
+    >>> from grass.pygrass.raster import RasterRow
+    >>> ele = RasterRow('elevation')
+    >>> copy('schools','myschools','vect')
+    >>> sch = VectorTopo('myschools')
+    >>> sch.open(mode='r')
+    >>> get_raster_for_points(sch, ele)               # doctest: +ELLIPSIS
+    [(1, 633649.2856743174, 221412.94434781274, 145.06602)...
+    >>> sch.table.columns.add('elevation','double precision')
+    >>> 'elevation' in sch.table.columns
+    True
+    >>> get_raster_for_points(sch, ele, 'elevation')
+    True
+    >>> sch.table.filters.select('NAMESHORT','elevation')
+    Filters(u'SELECT NAMESHORT, elevation FROM myschools;')
+    >>> cur = sch.table.execute()
+    >>> cur.fetchall()                                # doctest: +ELLIPSIS
+    [(u'SWIFT CREEK', 145.06602), ... (u'9TH GRADE CTR', None)]
+    >>> remove('myschools','vect')
 
 
-    Parameters
-    -------------
+    :param point: point vector object
+    :param raster: raster object
+    :param str column: column name to update
 
-    point: point vector object
-
-    raster: raster object
-
-    column: column name to update
     """
     from math import isnan
     if not column:
         result = []
-    reg = Region()
+    if region is None:
+        from grass.pygrass.gis.region import Region
+        region = Region()
     if not poi_vector.is_open():
         poi_vector.open()
     if not raster.is_open():
@@ -239,7 +235,7 @@
     if poi_vector.num_primitive_of('point') == 0:
         raise GrassError(_("Vector doesn't contain points"))
     for poi in poi_vector.viter('points'):
-        val = raster.get_value(poi, reg)
+        val = raster.get_value(poi, region)
         if column:
             if val is not None and not isnan(val):
                 poi.attrs[column] = val
@@ -268,9 +264,9 @@
 
 
 def get_lib_path(modname, libname):
-    """Return the path of the libname contained in the module. ::
+    """Return the path of the libname contained in the module.
 
-        >>> get_lib_path(modname='r.modis', libname='libmodis')
+    >>> get_lib_path(modname='r.modis', libname='libmodis')
     """
     from os.path import isdir, join
     from os import getenv
@@ -332,3 +328,57 @@
             return False
     one = cursor.fetchone() if cursor else None
     return True if one and one[0] else False
+
+
+def docstring_property(class_doc):
+    """Property attribute for docstrings.
+    Took from: https://gist.github.com/bfroehle/4041015
+
+    >>> class A(object):
+    ...     '''Main docstring'''
+    ...     def __init__(self, x):
+    ...         self.x = x
+    ...     @docstring_property(__doc__)
+    ...     def __doc__(self):
+    ...         return "My value of x is %s." % self.x
+
+    >>> A.__doc__
+    'Main docstring'
+
+    >>> a = A(10)
+    >>> a.__doc__
+    'My value of x is 10.'
+    """
+    def wrapper(fget):
+        return DocstringProperty(class_doc, fget)
+    return wrapper
+
+
+class DocstringProperty(object):
+    """Property for the `__doc__` attribute.
+
+    Different than `property` in the following two ways:
+
+    * When the attribute is accessed from the main class, it returns the value
+      of `class_doc`, *not* the property itself. This is necessary so Sphinx
+      and other documentation tools can access the class docstring.
+
+    * Only supports getting the attribute; setting and deleting raise an
+      `AttributeError`.
+    """
+
+    def __init__(self, class_doc, fget):
+        self.class_doc = class_doc
+        self.fget = fget
+
+    def __get__(self, obj, type=None):
+        if obj is None:
+            return self.class_doc
+        else:
+            return self.fget(obj)
+
+    def __set__(self, obj, value):
+        raise AttributeError("can't set attribute")
+
+    def __delete__(self, obj):
+        raise AttributeError("can't delete attribute")

Modified: grass/branches/releasebranch_7_0/lib/python/pygrass/gis/__init__.py
===================================================================
--- grass/branches/releasebranch_7_0/lib/python/pygrass/gis/__init__.py	2014-10-26 16:12:08 UTC (rev 62381)
+++ grass/branches/releasebranch_7_0/lib/python/pygrass/gis/__init__.py	2014-10-26 16:42:33 UTC (rev 62382)
@@ -11,7 +11,7 @@
 
 
 import grass.lib.gis as libgis
-from grass.pygrass.functions import getenv
+libgis.G_gisinit('')
 from grass.pygrass.errors import GrassError
 
 
@@ -36,9 +36,26 @@
 
 
 def _check(value, path, type):
+    """Private function to check the correctness of a value.
+
+    :param value: Name of the directory
+    :type value: str
+
+    :param path: Path where the directory is located
+    :type path: path
+
+    :param type: it is a string defining the type that will e checked,
+                 valid types are: GISBASE, GISDBASE, LOCATION_NAME, MAPSET
+    :type type: str
+
+    :return: the value if verify else None and
+             if value is empty return environmental variable
+    :rtype: str
+    """
     if value and CHECK_IS[type](join(path, value)):
         return value
     elif value is '':
+        from grass.pygrass.functions import getenv
         return getenv(type)
     else:
         raise GrassError("%s <%s> not found" % (type.title(),
@@ -47,6 +64,15 @@
 
 def set_current_mapset(mapset, location=None, gisdbase=None):
     """Set the current mapset as working area
+
+    :param mapset: Name of the mapset
+    :type value: str
+
+    :param location: Name of the location
+    :type location: str
+
+    :param gisdbase: Name of the gisdbase
+    :type gisdbase: str
     """
     libgis.G_setenv('MAPSET', mapset)
     if location:
@@ -56,7 +82,16 @@
 
 
 def make_mapset(mapset, location=None, gisdbase=None):
-    """Create a new mapset"""
+    """Create a new mapset
+
+    :param mapset: Name of the mapset
+    :type value: str
+
+    :param location: Name of the location
+    :type location: str
+
+    :param gisdbase: Name of the gisdbase
+    :type gisdbase: str"""
     res = libgis.G_make_mapset(gisdbase, location, mapset)
     if res == -1:
         raise GrassError("Cannot create new mapset")
@@ -127,12 +162,8 @@
 
         ..
         """
-        locations = []
-        for loc in listdir(self.name):
-            if libgis.G_is_location(join(self.name, loc)):
-                locations.append(loc)
-        locations.sort()
-        return locations
+        return sorted([loc for loc in listdir(self.name)
+                       if libgis.G_is_location(join(self.name, loc))])
 
 
 class Location(object):
@@ -198,14 +229,15 @@
         :type pattern: str
         :param permissions: check the permission of mapset
         :type permissions: bool
-        :returns:  a list of mapset's names
+        :return: a list of mapset's names
+        :rtype: list of strings
 
         ::
 
             >>> location = Location()
-            >>> location.mapsets()
+            >>> sorted(location.mapsets())
             ['PERMANENT', 'user1']
-        ..
+
         """
         mapsets = [mapset for mapset in self]
         if permissions:
@@ -216,6 +248,7 @@
         return mapsets
 
     def path(self):
+        """Return the complete path of the location"""
         return join(self.gisdbase, self.name)
 
 
@@ -301,8 +334,8 @@
             >>> rast.sort()
             >>> rast                                      # doctest: +ELLIPSIS
             ['basins', 'elevation', ...]
-            >>> mapset.glist('rast', pattern='el*')
-            ['elevation_shade', 'elevation']
+            >>> sorted(mapset.glist('rast', pattern='el*'))
+            ['elevation', 'elevation_shade']
 
         ..
         """
@@ -338,6 +371,7 @@
         shutil.rmtree(self.path())
 
     def path(self):
+        """Return the complete path of the mapset"""
         return join(self.gisdbase, self.location, self.name)
 
 

Modified: grass/branches/releasebranch_7_0/lib/python/pygrass/gis/region.py
===================================================================
--- grass/branches/releasebranch_7_0/lib/python/pygrass/gis/region.py	2014-10-26 16:12:08 UTC (rev 62381)
+++ grass/branches/releasebranch_7_0/lib/python/pygrass/gis/region.py	2014-10-26 16:42:33 UTC (rev 62382)
@@ -15,11 +15,11 @@
 
 
 class Region(object):
-    """
-    ::
+    """This class is design to easily access and modify GRASS computational
+    region. ::
 
         >>> default = Region(default=True)
-        >>> current_good = Region()
+        >>> current_original = Region()
         >>> current = Region()
         >>> current.align('elevation')
         >>> default == current
@@ -41,7 +41,7 @@
         >>> default = Region(default=True)
         >>> default == current
         True
-        >>> current_good.set_current()
+        >>> current_original.set_current()
 
     ..
     """
@@ -225,6 +225,17 @@
         return self.__unicode__()
 
     def __eq__(self, reg):
+        """Compare two region.
+
+        >>> r0 = Region()
+        >>> r1 = Region()
+        >>> r2 = Region()
+        >>> r2.nsres = 5
+        >>> r0 == r1
+        True
+        >>> r1 == r2
+        False
+        """
         attrs = ['north', 'south', 'west', 'east', 'top', 'bottom',
                  'nsres', 'ewres', 'tbres']
         for attr in attrs:
@@ -232,12 +243,34 @@
                 return False
         return True
 
+    def __ne__(self, other):
+        return not self == other
+
+    # Restore Python 2 hashing beaviour on Python 3
+    __hash__ = object.__hash__
+
     def keys(self):
+        """Return a list of valid keys. ::
+
+            >>> reg = Region()
+            >>> reg.keys()                               # doctest: +ELLIPSIS
+            [u'proj', u'zone', ..., u'cols', u'cells']
+
+        ..
+        """
         return ['proj', 'zone', 'north', 'south', 'west', 'east',
                 'top', 'bottom', 'nsres', 'ewres', 'tbres', 'rows',
                 'cols', 'cells']
 
     def items(self):
+        """Return a list of tuple with key and value. ::
+
+            >>> reg = Region()
+            >>> reg.items()                              # doctest: +ELLIPSIS
+            [(u'proj', 99), ..., (u'cells', 2025000)]
+
+        ..
+        """
         return [(k, self.__getattribute__(k)) for k in self.keys()]
 
     #----------METHODS----------
@@ -274,19 +307,18 @@
 
         ::
 
-        >>> reg = Region()
-        >>> reg.vect('census')
-        >>> reg.get_bbox()
-        Bbox(230963.640878, 212125.562878, 645837.437393, 628769.374393)
-        >>> reg.get_default()
+            >>> reg = Region()
+            >>> reg.vect('census')
+            >>> reg.get_bbox()
+            Bbox(230963.640878, 212125.562878, 645837.437393, 628769.374393)
+            >>> reg.get_default()
 
         ..
         """
         from grass.pygrass.vector import VectorTopo
-        vect = VectorTopo(vector_name)
-        vect.open()
-        bbox = vect.bbox()
-        self.set_bbox(bbox)
+        with VectorTopo(vector_name, mode='r') as vect:
+            bbox = vect.bbox()
+            self.set_bbox(bbox)
 
     def get_current(self):
         """Set the current GRASS region to the Region object"""
@@ -313,11 +345,11 @@
             raise GrassError("Cannot change region (DEFAUL_WIND file).")
 
     def get_bbox(self):
-        """Return a Bbox object with the extension of the region ::
+        """Return a Bbox object with the extension of the region. ::
 
-        >>> reg = Region()
-        >>> reg.get_bbox()
-        Bbox(228500.0, 215000.0, 645000.0, 630000.0)
+            >>> reg = Region()
+            >>> reg.get_bbox()
+            Bbox(228500.0, 215000.0, 645000.0, 630000.0)
 
         ..
         """
@@ -334,13 +366,13 @@
 
         ::
 
-        >>> from grass.pygrass.vector.basic import Bbox
-        >>> b = Bbox(230963.640878, 212125.562878, 645837.437393, 628769.374393)
-        >>> reg = Region()
-        >>> reg.set_bbox(b)
-        >>> reg.get_bbox()
-        Bbox(230963.640878, 212125.562878, 645837.437393, 628769.374393)
-        >>> reg.get_current()
+            >>> from grass.pygrass.vector.basic import Bbox
+            >>> b = Bbox(230963.640878, 212125.562878, 645837.437393, 628769.374393)
+            >>> reg = Region()
+            >>> reg.set_bbox(b)
+            >>> reg.get_bbox()
+            Bbox(230963.640878, 212125.562878, 645837.437393, 628769.374393)
+            >>> reg.get_current()
 
         ..
         """

Modified: grass/branches/releasebranch_7_0/lib/python/pygrass/messages/__init__.py
===================================================================
--- grass/branches/releasebranch_7_0/lib/python/pygrass/messages/__init__.py	2014-10-26 16:12:08 UTC (rev 62381)
+++ grass/branches/releasebranch_7_0/lib/python/pygrass/messages/__init__.py	2014-10-26 16:42:33 UTC (rev 62382)
@@ -17,6 +17,7 @@
 from multiprocessing import Process, Lock, Pipe
 
 import grass.lib.gis as libgis
+
 from grass.exceptions import FatalError
 
 
@@ -76,6 +77,10 @@
             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])
@@ -130,12 +135,6 @@
        >>> 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
 
        >>> msgr = Messenger()
        >>> msgr.fatal("Ohh no no no!")
@@ -175,9 +174,6 @@
         self.raise_on_error = raise_on_error
         self.start_server()
 
-    def __del__(self):
-        self.stop()
-
     def start_server(self):
         """Start the messenger server and open the pipe
         """

Modified: grass/branches/releasebranch_7_0/lib/python/pygrass/modules/__init__.py
===================================================================
--- grass/branches/releasebranch_7_0/lib/python/pygrass/modules/__init__.py	2014-10-26 16:12:08 UTC (rev 62381)
+++ grass/branches/releasebranch_7_0/lib/python/pygrass/modules/__init__.py	2014-10-26 16:42:33 UTC (rev 62382)
@@ -1,14 +1,3 @@
 # -*- coding: utf-8 -*-
-"""
-Created on Thu Jul 12 10:23:15 2012
-
- at author: pietro
-
-"""
-from grass.pygrass.modules import interface
 from grass.pygrass.modules.interface import Module, ParallelModuleQueue
-
-from grass.pygrass.modules import grid
-from grass.pygrass.modules.grid.grid import GridModule
-
 from grass.pygrass.modules import shortcuts

Modified: grass/branches/releasebranch_7_0/lib/python/pygrass/modules/grid/__init__.py
===================================================================
--- grass/branches/releasebranch_7_0/lib/python/pygrass/modules/grid/__init__.py	2014-10-26 16:12:08 UTC (rev 62381)
+++ grass/branches/releasebranch_7_0/lib/python/pygrass/modules/grid/__init__.py	2014-10-26 16:42:33 UTC (rev 62382)
@@ -1,11 +1,3 @@
 # -*- coding: utf-8 -*-
-"""
-Created on Wed Apr  3 10:41:30 2013
-
- at author: pietro
-"""
-from grass.pygrass.modules.grid import split
-from grass.pygrass.modules.grid import patch
-from grass.pygrass.modules.grid import grid
 from grass.pygrass.modules.grid.grid import GridModule
 

Modified: grass/branches/releasebranch_7_0/lib/python/pygrass/modules/grid/grid.py
===================================================================
--- grass/branches/releasebranch_7_0/lib/python/pygrass/modules/grid/grid.py	2014-10-26 16:12:08 UTC (rev 62381)
+++ grass/branches/releasebranch_7_0/lib/python/pygrass/modules/grid/grid.py	2014-10-26 16:42:33 UTC (rev 62382)
@@ -1,9 +1,4 @@
 # -*- coding: utf-8 -*-
-"""
-Created on Thu Mar 28 11:06:00 2013
-
- at author: pietro
-"""
 from __future__ import (nested_scopes, generators, division, absolute_import,
                         with_statement, print_function, unicode_literals)
 import os
@@ -36,7 +31,7 @@
     ...              elevation='ele', slope='slp', aspect='asp',
     ...              run_=False)
     >>> for rast in select(slp.outputs, 'raster'):
-    ...     print rast
+    ...     print(rast)
     ...
     slp
     asp
@@ -86,8 +81,8 @@
     [u'PERMANENT', u'user1']
     >>> sorted(os.listdir(os.path.join(path, 'PERMANENT')))
     [u'DEFAULT_WIND', u'PROJ_INFO', u'PROJ_UNITS', u'VAR', u'WIND']
-    >>> sorted(os.listdir(os.path.join(path, 'user1')))
-    [u'CURGROUP', u'SEARCH_PATH', u'VAR', u'WIND']
+    >>> sorted(os.listdir(os.path.join(path, 'user1'))) # doctest: +ELLIPSIS
+    [...u'SEARCH_PATH', u'VAR', u'WIND']
     >>> import shutil
     >>> shutil.rmtree(path)
 
@@ -255,7 +250,7 @@
         mpclc(expression="%s=%s" % (name, rast), overwrite=True, env_=env)
         file_dst = "%s.pack" % os.path.join(path_dst, name)
         rpck(input=name, output=file_dst, overwrite=True, env_=env)
-        remove(rast=name, env_=env)
+        remove(flags='f', type='rast', pattern=name, env_=env)
         # change gisdbase to dst
         env['GISRC'] = gisrc_dst
         rupck(input=file_dst, output=rast_clean, overwrite=True, env_=env)
@@ -289,7 +284,7 @@
         name = nam % vect
         file_dst = "%s.pack" % os.path.join(path_dst, name)
         vpck(input=name, output=file_dst, overwrite=True, env_=env)
-        remove(vect=name, env_=env)
+        remove(flags='f', type='vect', pattern=name, env_=env)
         # change gisdbase to dst
         env['GISRC'] = gisrc_dst
         vupck(input=file_dst, output=vect, overwrite=True, env_=env)
@@ -308,7 +303,7 @@
     ...              elevation='ele', slope='slp', aspect='asp',
     ...              overwrite=True, run_=False)
     >>> get_cmd(slp.get_dict())  # doctest: +ELLIPSIS
-    ['r.slope.aspect', 'elevation=ele', 'format=degrees', ..., '--o']
+    ['r.slope.aspect', u'elevation=ele', u'format=degrees', ..., u'--o']
     """
     cmd = [cmdd['name'], ]
     cmd.extend(("%s=%s" % (k, v) for k, v in cmdd['inputs']
@@ -391,7 +386,7 @@
     :param run_: if False only instantiate the object
     :type run_: bool
     :param args: give all the parameters to the command
-    :param kargs: give all the parameters to the command 
+    :param kargs: give all the parameters to the command
 
     >>> grd = GridModule('r.slope.aspect',
     ...                  width=500, height=500, overlap=2,
@@ -540,7 +535,7 @@
             result = pool.map_async(cmd_exe, self.get_works())
             result.wait()
             if not result.successful():
-                raise RuntimeError
+                raise RuntimeError(_("Execution of subprocesses was not successful"))
 
         if patch:
             if self.move:
@@ -603,4 +598,4 @@
         if self.inlist:
             grm = Module('g.remove')
             for key in self.inlist:
-                grm(rast=self.inlist[key])
+                grm(flags='f', type='rast', pattern=self.inlist[key])

Modified: grass/branches/releasebranch_7_0/lib/python/pygrass/modules/interface/flag.py
===================================================================
--- grass/branches/releasebranch_7_0/lib/python/pygrass/modules/interface/flag.py	2014-10-26 16:12:08 UTC (rev 62381)
+++ grass/branches/releasebranch_7_0/lib/python/pygrass/modules/interface/flag.py	2014-10-26 16:42:33 UTC (rev 62382)
@@ -1,18 +1,28 @@
 # -*- coding: utf-8 -*-
-"""
-Created on Tue Apr  2 18:39:21 2013
-
- at author: pietro
-"""
 from __future__ import (nested_scopes, generators, division, absolute_import,
                         with_statement, print_function, unicode_literals)
+from grass.pygrass.functions import docstring_property
 from grass.pygrass.modules.interface import read
 
-# TODO add documentation
+
 class Flag(object):
     """The Flag object store all information about a flag of module.
 
     It is possible to set flags of command using this object.
+
+    >>> flag = Flag(diz=dict(name='a', description='Flag description',
+    ...                      default=True))
+    >>> flag.name
+    u'a'
+    >>> flag.special
+    False
+    >>> flag.description
+    u'Flag description'
+    >>> flag = Flag(diz=dict(name='overwrite'))
+    >>> flag.name
+    u'overwrite'
+    >>> flag.special
+    True
     """
     def __init__(self, xflag=None, diz=None):
         self.value = False
@@ -20,12 +30,27 @@
         self.name = diz['name']
         self.special = True if self.name in (
             'verbose', 'overwrite', 'quiet', 'run') else False
-        self.description = diz['description']
+        self.description = diz.get('description', None)
         self.default = diz.get('default', None)
         self.guisection = diz.get('guisection', None)
 
     def get_bash(self):
-        """Prova"""
+        """Return the BASH representation of a flag.
+
+        >>> flag = Flag(diz=dict(name='a', description='Flag description',
+        ...                      default=True))
+        >>> flag.get_bash()
+        u''
+        >>> flag.value = True
+        >>> flag.get_bash()
+        u'-a'
+        >>> flag = Flag(diz=dict(name='overwrite'))
+        >>> flag.get_bash()
+        u''
+        >>> flag.value = True
+        >>> flag.get_bash()
+        u'--o'
+        """
         if self.value:
             if self.special:
                 return '--%s' % self.name[0]
@@ -35,26 +60,53 @@
             return ''
 
     def get_python(self):
-        """Prova"""
+        """Return the python representation of a flag.
+
+        >>> flag = Flag(diz=dict(name='a', description='Flag description',
+        ...                      default=True))
+        >>> flag.get_python()
+        u''
+        >>> flag.value = True
+        >>> flag.get_python()
+        u'a'
+        >>> flag = Flag(diz=dict(name='overwrite'))
+        >>> flag.get_python()
+        u''
+        >>> flag.value = True
+        >>> flag.get_python()
+        u'overwrite=True'
+        """
         if self.value:
-            if self.special:
-                return '%s=True' % self.name
-            else:
-                return self.name
-        else:
-            return ''
+            return '%s=True' % self.name if self.special else self.name
+        return ''
 
     def __str__(self):
+        """Return the BASH representation of the flag."""
         return self.get_bash()
 
     def __repr__(self):
+        """Return a string with the python representation of the instance."""
         return "Flag <%s> (%s)" % (self.name, self.description)
 
-    @property
+    @docstring_property(__doc__)
     def __doc__(self):
+        """Return a documentation string, something like:
+
+        {name}: {default}
+            {description}
+
+        >>>  flag = Flag(diz=dict(name='a', description='Flag description',
+        ...                      default=True))
+        >>> print(flag.__doc__)
+        a: True
+            Flag description
+
+        >>> flag = Flag(diz=dict(name='overwrite'))
+        >>> print(flag.__doc__)
+        overwrite: None
+            None
+
         """
-        {name}: {default}
-            {description}"""
         return read.DOC['flag'].format(name=self.name,
                                        default=repr(self.default),
                                        description=self.description)

Modified: grass/branches/releasebranch_7_0/lib/python/pygrass/modules/interface/module.py
===================================================================
--- grass/branches/releasebranch_7_0/lib/python/pygrass/modules/interface/module.py	2014-10-26 16:12:08 UTC (rev 62381)
+++ grass/branches/releasebranch_7_0/lib/python/pygrass/modules/interface/module.py	2014-10-26 16:42:33 UTC (rev 62382)
@@ -1,87 +1,9 @@
 # -*- coding: utf-8 -*-
-"""
-Created on Tue Apr  2 18:41:27 2013
-
- at author: pietro
-
- at code
-
->>> import grass.pygrass.modules as pymod
->>> import copy
->>> region = pymod.Module("g.region")
->>> region.flags["p"].value = True
->>> region.flags["u"].value = True
->>> region.flags["3"].value = True
->>> region.get_bash()
-u'g.region -p -3 -u'
->>> new_region = copy.deepcopy(region)
->>> new_region.inputs["res"].value = "10"
->>> new_region.get_bash()
-u'g.region res=10 -p -3 -u'
-
->>> neighbors = pymod.Module("r.neighbors")
->>> neighbors.inputs["input"].value = "mapA"
->>> neighbors.outputs["output"].value = "mapB"
->>> neighbors.inputs["size"].value = 5
->>> neighbors.get_bash()
-u'r.neighbors input=mapA method=average size=5 quantile=0.5 output=mapB'
-
->>> new_neighbors1 = copy.deepcopy(neighbors)
->>> new_neighbors1.inputs["input"].value = "mapD"
->>> new_neighbors1.inputs["size"].value = 3
->>> new_neighbors1.get_bash()
-u'r.neighbors input=mapD method=average size=3 quantile=0.5 output=mapB'
-
->>> new_neighbors2 = copy.deepcopy(neighbors)
->>> new_neighbors2(input="mapD", size=3, run_=False)
->>> new_neighbors2.get_bash()
-u'r.neighbors input=mapD method=average size=3 quantile=0.5 output=mapB'
-
->>> neighbors = pymod.Module("r.neighbors")
->>> neighbors.get_bash()
-u'r.neighbors method=average size=3 quantile=0.5'
-
->>> new_neighbors3 = copy.deepcopy(neighbors)
->>> new_neighbors3(input="mapA", size=3, output="mapB", run_=False)
->>> new_neighbors3.get_bash()
-u'r.neighbors input=mapA method=average size=3 quantile=0.5 output=mapB'
-
-Run a second time
->>> colors.run()
-Module('r.colors')
->>> stdout, stderr = colors.popen.communicate(input="1 blue")
->>> colors.popen.returncode
-0
->>> stdout
->>> stderr.strip()
-"Color table for raster map <test_a> set to 'rules'"
-
-Multiple run test
->>> colors = pymod.Module("r.colors", map="test_a",
-...                                            color="ryb", run_=False)
->>> colors.run()
-Module('r.colors')
->>> colors(color="gyr")
->>> colors.run()
-Module('r.colors')
->>> colors(color="ryg")
->>> colors(stderr_=PIPE)
->>> colors.run()
-Module('r.colors')
->>> print(colors.outputs["stderr"].value.strip())
-Color table for raster map <test_a> set to 'ryg'
->>> colors(color="byg")
->>> colors(stdout_=PIPE)
->>> colors.run()
-Module('r.colors')
->>> print(colors.outputs["stderr"].value.strip())
-Color table for raster map <test_a> set to 'byg'
-
- at endcode
-"""
 from __future__ import (nested_scopes, generators, division, absolute_import,
                         with_statement, print_function, unicode_literals)
 import sys
+from multiprocessing import cpu_count
+from functools import wraps
 
 if sys.version_info[0] == 2:
     from itertools import izip_longest as zip_longest
@@ -90,74 +12,120 @@
 from xml.etree.ElementTree import fromstring
 import time
 
+from grass.exceptions import CalledModuleError
 from grass.script.core import Popen, PIPE
 from grass.pygrass.errors import GrassError, ParameterError
+from grass.pygrass.functions import docstring_property
 from grass.pygrass.modules.interface.parameter import Parameter
 from grass.pygrass.modules.interface.flag import Flag
 from grass.pygrass.modules.interface.typedict import TypeDict
 from grass.pygrass.modules.interface.read import GETFROMTAG, DOC
-from grass.pygrass.messages import Messenger
+from grass.pygrass.messages import get_msgr
 
+
+def mdebug(level, msg='', extra=None):
+    """Debug decorators for class methods.
+
+    :param level: the debug level
+    :type level: int
+    :param msg: Debug message
+    :type msg: str
+    :param extra: Function that return a string
+    :type msg: func
+    """
+    msgr = get_msgr()
+
+    def decorator(method):
+
+        @wraps(method)
+        def wrapper(self, *args, **kargs):
+            sargs = ', ' + ' , '.join([repr(a) for a in args]) if args else ''
+            skargs = (' , '.join(['%s=%r' % (k, v) for k, v in kargs.items()])
+                      if kargs else '')
+            opts = "%s%s%s" % (sargs, ',' if sargs and skargs else '', skargs)
+            dmsg = "%s.%s(self%s): %s %s" % (self.__class__.__name__,
+                                             method.__name__,
+                                             opts, msg,
+                                             extra(self, *args, **kargs)
+                                             if extra else '')
+            msgr.debug(level, dmsg)
+            return method(self, *args, **kargs)
+        return wrapper
+    return decorator
+
+
+def _get_bash(self, *args, **kargs):
+    return self.get_bash()
+
+
 class ParallelModuleQueue(object):
     """This class is designed to run an arbitrary number of pygrass Module
-       processes in parallel.
+    processes in parallel.
 
-       Objects of type grass.pygrass.modules.Module can be put into the
-       queue using put() method. When the queue is full with the maximum
-       number of parallel processes it will wait for all processes to finish,
-       sets the stdout and stderr of the Module object and removes it
-       from the queue when its finished.
+    Objects of type grass.pygrass.modules.Module can be put into the
+    queue using put() method. When the queue is full with the maximum
+    number of parallel processes it will wait for all processes to finish,
+    sets the stdout and stderr of the Module object and removes it
+    from the queue when its finished.
 
-       This class will raise a GrassError in case a Module process exits
-       with a return code other than 0.
+    This class will raise a GrassError in case a Module process exits
+    with a return code other than 0.
 
-       Usage:
+    Usage:
 
-       >>> import copy
-       >>> import grass.pygrass.modules as pymod
-       >>> mapcalc_list = []
-       >>> mapcalc = pymod.Module("r.mapcalc",
-       ...                        overwrite=True,
-       ...                        run_=False,
-       ...                        finish_=False)
-       >>> queue = pymod.ParallelModuleQueue(max_num_procs=3)
-       >>> for i in xrange(5):
-       ...     new_mapcalc = copy.deepcopy(mapcalc)
-       ...     mapcalc_list.append(new_mapcalc)
-       ...     new_mapcalc(expression="test_pygrass_%i = %i"%(i, i))
-       ...     queue.put(new_mapcalc)
-       >>> queue.wait()
-       >>> for mapcalc in mapcalc_list:
-       ...     print(mapcalc.popen.returncode)
-       0
-       0
-       0
-       0
-       0
+    >>> import copy
+    >>> from grass.pygrass.modules import Module, ParallelModuleQueue
+    >>> mapcalc_list = []
+    >>> mapcalc = Module("r.mapcalc", overwrite=True, run_=False)
+    >>> queue = ParallelModuleQueue(nprocs=3)
+    >>> for i in xrange(5):
+    ...     new_mapcalc = copy.deepcopy(mapcalc)
+    ...     mapcalc_list.append(new_mapcalc)
+    ...     new_mapcalc(expression="test_pygrass_%i = %i"%(i, i))
+    ...     queue.put(new_mapcalc)
+    Module('r.mapcalc')
+    Module('r.mapcalc')
+    Module('r.mapcalc')
+    Module('r.mapcalc')
+    Module('r.mapcalc')
 
+    >>> queue.wait()
+    >>> for mapcalc in mapcalc_list:
+    ...     print(mapcalc.popen.returncode)
+    0
+    0
+    0
+    0
+    0
+
     """
-    def __init__(self, max_num_procs=1):
+    def __init__(self, nprocs=1):
         """Constructor
 
-        :param max_num_procs: The maximum number of Module processes that
-                              can be run in parallel
-        :type max_num_procs: int
+        :param nprocs: The maximum number of Module processes that
+                       can be run in parallel, defualt is 1, if None
+                       then use all the available CPUs.
+        :type nprocs: int
         """
-        self._num_procs = int(max_num_procs)
-        self._list = int(max_num_procs) * [None]
+        nprocs = int(nprocs) if nprocs else cpu_count()
+        self._num_procs = nprocs
+        self._list = nprocs * [None]
         self._proc_count = 0
 
     def put(self, module):
         """Put the next Module object in the queue
 
-           To run the Module objects in parallel the run_ and finish_ options
-           of the Module must be set to False.
+        To run the Module objects in parallel the run\_ and finish\_ options
+        of the Module must be set to False.
 
-           :param module: a preconfigured Module object with run_ and finish_
-                          set to False
-           :type module: Module object
+        :param module: a preconfigured Module object with run\_ and finish\_
+                       set to False
+        :type module: Module object
         """
         self._list[self._proc_count] = module
+        # Force that finish is False, otherwise the execution
+        # will not be parallel
+        self._list[self._proc_count].finish_ = False
         self._list[self._proc_count].run()
         self._proc_count += 1
 
@@ -167,9 +135,9 @@
     def get(self, num):
         """Get a Module object from the queue
 
-           :param num: the number of the object in queue
-           :type num: int
-           :returns: the Module object or None if num is not in the queue
+        :param num: the number of the object in queue
+        :type num: int
+        :returns: the Module object or None if num is not in the queue
         """
         if num < self._num_procs:
             return self._list[num]
@@ -177,10 +145,10 @@
 
     def get_num_run_procs(self):
         """Get the number of Module processes that are in the queue running
-           or finished
+        or finished
 
-           :returns: the maximum number fo Module processes running/finished in
-                     the queue
+        :returns: the maximum number fo Module processes running/finished in
+                  the queue
         """
         return len(self._list)
 
@@ -189,20 +157,20 @@
         """
         return self._num_procs
 
-    def set_max_num_procs(self, max_num_procs):
+    def set_max_num_procs(self, nprocs):
         """Set the maximum number of Module processes that should run
         in parallel
 
-        :param max_num_procs: The maximum number of Module processes that
-                              can be run in parallel
-        :type max_num_procs: int   
+        :param nprocs: The maximum number of Module processes that can be
+                       run in parallel
+        :type nprocs: int
         """
-        self._num_procs = int(max_num_procs)
+        self._num_procs = int(nprocs)
         self.wait()
 
     def wait(self):
         """Wait for all Module processes that are in the list to finish
-           and set the modules stdout and stderr output options
+        and set the modules stdout and stderr output options
         """
         for proc in self._list:
             if proc:
@@ -218,31 +186,162 @@
 
 
 class Module(object):
-    """
+    """This class is design to wrap/run/interact with the GRASS modules.
+
+    The class during the init phase read the XML description generate using
+    the ``--interface-description`` in order to understand which parameters
+    are required which optionals. ::
+
+    >>> from grass.pygrass.modules import Module
+    >>> from subprocess import PIPE
+    >>> import copy
+
+    >>> region = Module("g.region")
+    >>> region.flags.p = True  # set flags
+    >>> region.flags.u = True
+    >>> region.flags["3"].value = True  # set numeric flags
+    >>> region.get_bash()
+    u'g.region -p -3 -u'
+    >>> new_region = copy.deepcopy(region)
+    >>> new_region.inputs.res = "10"
+    >>> new_region.get_bash()
+    u'g.region res=10 -p -3 -u'
+
+    >>> neighbors = Module("r.neighbors")
+    >>> neighbors.inputs.input = "mapA"
+    >>> neighbors.outputs.output = "mapB"
+    >>> neighbors.inputs.size = 5
+    >>> neighbors.inputs.quantile = 0.5
+    >>> neighbors.get_bash()
+    u'r.neighbors input=mapA method=average size=5 quantile=0.5 output=mapB'
+
+    >>> new_neighbors1 = copy.deepcopy(neighbors)
+    >>> new_neighbors1.inputs.input = "mapD"
+    >>> new_neighbors1.inputs.size = 3
+    >>> new_neighbors1.inputs.quantile = 0.5
+    >>> new_neighbors1.get_bash()
+    u'r.neighbors input=mapD method=average size=3 quantile=0.5 output=mapB'
+
+    >>> new_neighbors2 = copy.deepcopy(neighbors)
+    >>> new_neighbors2(input="mapD", size=3, run_=False)
+    Module('r.neighbors')
+    >>> new_neighbors2.get_bash()
+    u'r.neighbors input=mapD method=average size=3 quantile=0.5 output=mapB'
+
+    >>> neighbors = Module("r.neighbors")
+    >>> neighbors.get_bash()
+    u'r.neighbors method=average size=3'
+
+    >>> new_neighbors3 = copy.deepcopy(neighbors)
+    >>> new_neighbors3(input="mapA", size=3, output="mapB", run_=False)
+    Module('r.neighbors')
+    >>> new_neighbors3.get_bash()
+    u'r.neighbors input=mapA method=average size=3 output=mapB'
+
+    >>> mapcalc = Module("r.mapcalc", expression="test_a = 1",
+    ...                  overwrite=True, run_=False)
+    >>> mapcalc.run()
+    Module('r.mapcalc')
+    >>> mapcalc.popen.returncode
+    0
+
+    >>> colors = Module("r.colors", map="test_a", rules="-",
+    ...                 run_=False, stdout_=PIPE,
+    ...                 stderr_=PIPE, stdin_="1 red")
+    >>> colors.run()
+    Module('r.colors')
+    >>> colors.popen.returncode
+    0
+    >>> colors.inputs["stdin"].value
+    u'1 red'
+    >>> colors.outputs["stdout"].value
+    u''
+    >>> colors.outputs["stderr"].value.strip()
+    "Color table for raster map <test_a> set to 'rules'"
+
+    >>> colors = Module("r.colors", map="test_a", rules="-",
+    ...                 run_=False, finish_=False, stdin_=PIPE)
+    >>> colors.run()
+    Module('r.colors')
+    >>> stdout, stderr = colors.popen.communicate(input="1 red")
+    >>> colors.popen.returncode
+    0
+    >>> stdout
+    >>> stderr
+
+    >>> colors = Module("r.colors", map="test_a", rules="-",
+    ...                 run_=False, finish_=False,
+    ...                 stdin_=PIPE, stderr_=PIPE)
+    >>> colors.run()
+    Module('r.colors')
+    >>> stdout, stderr = colors.popen.communicate(input="1 red")
+    >>> colors.popen.returncode
+    0
+    >>> stdout
+    >>> stderr.strip()
+    "Color table for raster map <test_a> set to 'rules'"
+
+    Run a second time
+    >>> colors.run()
+    Module('r.colors')
+    >>> stdout, stderr = colors.popen.communicate(input="1 blue")
+    >>> colors.popen.returncode
+    0
+    >>> stdout
+    >>> stderr.strip()
+    "Color table for raster map <test_a> set to 'rules'"
+
+    Multiple run test
+    >>> colors = Module("r.colors", map="test_a",
+    ...                                            color="ryb", run_=False)
+    >>> colors.run()
+    Module('r.colors')
+    >>> colors(color="gyr")
+    Module('r.colors')
+    >>> colors.run()
+    Module('r.colors')
+    >>> colors(color="ryg")
+    Module('r.colors')
+    >>> colors(stderr_=PIPE)
+    Module('r.colors')
+    >>> colors.run()
+    Module('r.colors')
+    >>> print(colors.outputs["stderr"].value.strip())
+    Color table for raster map <test_a> set to 'ryg'
+    >>> colors(color="byg")
+    Module('r.colors')
+    >>> colors(stdout_=PIPE)
+    Module('r.colors')
+    >>> colors.run()
+    Module('r.colors')
+    >>> print(colors.outputs["stderr"].value.strip())
+    Color table for raster map <test_a> set to 'byg'
+
+    Often in the Module class you can find ``*args`` and ``kwargs`` annotation
+    in methods, like in the __call__ method.
     Python allow developers to not specify all the arguments and
-    keyword arguments of a method or function.
+    keyword arguments of a method or function. ::
 
-
         def f(*args):
             for arg in args:
                 print arg
 
     therefore if we call the function like:
 
-        >>> f('grass', 'gis', 'modules')
-        grass
-        gis
-        modules
+    >>> f('grass', 'gis', 'modules')                     # doctest: +SKIP
+    grass
+    gis
+    modules
 
     or we can define a new list:
 
-        >>> words = ['grass', 'gis', 'modules']
-        >>> f(*words)
-        grass
-        gis
-        modules
+    >>> words = ['grass', 'gis', 'modules']              # doctest: +SKIP
+    >>> f(*words)                                        # doctest: +SKIP
+    grass
+    gis
+    modules
 
-    we can do the same with keyword arguments, rewrite the above function:
+    we can do the same with keyword arguments, rewrite the above function: ::
 
         def f(*args, **kargs):
             for arg in args:
@@ -252,30 +351,29 @@
 
     now we can use the new function, with:
 
-        >>> f('grass', 'gis', 'modules', os = 'linux', language = 'python')
-        grass
-        gis
-        modules
-        os = 'linux'
-        language = 'python'
+    >>> f('grass', 'gis', 'modules', os = 'linux', language = 'python')
+    ...                                                  # doctest: +SKIP
+    grass
+    gis
+    modules
+    os = 'linux'
+    language = 'python'
 
     or, as before we can, define a dictionary and give the dictionary to
     the function, like:
 
-        >>> keywords = {'os' : 'linux', 'language' : 'python'}
-        >>> f(*words, **keywords)
-        grass
-        gis
-        modules
-        os = 'linux'
-        language = 'python'
+    >>> keywords = {'os' : 'linux', 'language' : 'python'}  # doctest: +SKIP
+    >>> f(*words, **keywords)                            # doctest: +SKIP
+    grass
+    gis
+    modules
+    os = 'linux'
+    language = 'python'
 
     In the Module class we heavily use this language feature to pass arguments
     and keyword arguments to the grass module.
     """
     def __init__(self, cmd, *args, **kargs):
-        self._msgr = Messenger()
-        
         if isinstance(cmd, unicode):
             self.name = str(cmd)
         elif isinstance(cmd, str):
@@ -350,10 +448,15 @@
         self.__call__.__func__.__doc__ = self.__doc__
 
     def __call__(self, *args, **kargs):
+        """Set module paramters to the class and, if run_ is True execute the
+        module, therefore valid parameters are all the module parameters
+        plus some extra parameters that are: run_, stdin_, stdout_, stderr_,
+        env_ and finish_.
+        """
         if not args and not kargs:
             self.run()
-            return
-        
+            return self
+
         #
         # check for extra kargs, set attribute and remove from dictionary
         #
@@ -363,18 +466,14 @@
             del(kargs['flags'])
 
         # set attributs
-        for key in ('run_', 'env_', 'finish_'):
+        for key in ('run_', 'env_', 'finish_', 'stdout_', 'stderr_'):
             if key in kargs:
                 setattr(self, key, kargs.pop(key))
 
         # set inputs
         for key in ('stdin_', ):
             if key in kargs:
-                self.inputs[key].value = kargs.pop(key)
-        # set outputs
-        for key in ('stdout_', 'stderr_'):
-            if key in kargs:
-                self.outputs[key].value = kargs.pop(key)
+                self.inputs[key[:-1]].value = kargs.pop(key)
 
         #
         # check args
@@ -394,11 +493,6 @@
                 raise ParameterError('%s is not a valid parameter.' % key)
 
         #
-        # print debug message
-        #
-        self._msgr.debug(1, "Module.__call__(): %s" % (self.get_bash()))
-
-        #
         # check if execute
         #
         if self.run_:
@@ -411,13 +505,14 @@
                     msg = "Required parameter <%s> not set."
                     raise ParameterError(msg % k)
             return self.run()
+        return self
 
     def get_bash(self):
-        """Prova"""
+        """Return a BASH rapresentation of the Module."""
         return ' '.join(self.make_cmd())
 
     def get_python(self):
-        """Prova"""
+        """Return a Python rapresentation of the Module."""
         prefix = self.name.split('.')[0]
         name = '_'.join(self.name.split('.')[1:])
         params = ', '.join([par.get_python() for par in self.params_list
@@ -440,14 +535,13 @@
             return "%s.%s(%s)" % (prefix, name, params)
 
     def __str__(self):
-        """Return the command string that can be executed in a shell
-        """
+        """Return the command string that can be executed in a shell"""
         return ' '.join(self.make_cmd())
 
     def __repr__(self):
         return "Module(%r)" % self.name
 
-    @property
+    @docstring_property(__doc__)
     def __doc__(self):
         """{cmd_name}({cmd_params})
         """
@@ -494,7 +588,8 @@
                 args.append(str(self.flags[flg]))
         return args
 
-    def run(self, node=None):
+    @mdebug(1, extra=_get_bash)
+    def run(self):
         """Run the module
 
         :param node:
@@ -502,7 +597,7 @@
 
         This function will wait for the process to terminate in case
         finish_==True and sets up stdout and stderr. If finish_==False this
-        function will return after starting the process. Use 
+        function will return after starting the process. Use
         self.popen.communicate() of self.popen.wait() to wait for the process
         termination. The handling of stdout and stderr must then be done
         outside of this function.
@@ -510,7 +605,7 @@
         if self.inputs['stdin'].value:
             self.stdin = self.inputs['stdin'].value
             self.stdin_ = PIPE
-        
+
         cmd = self.make_cmd()
         start = time.time()
         self.popen = Popen(cmd,
@@ -523,9 +618,10 @@
             self.outputs['stdout'].value = stdout if stdout else ''
             self.outputs['stderr'].value = stderr if stderr else ''
             self.time = time.time() - start
-            #if self.popen.poll():
-            #    raise CalledModuleError(self.popen.returncode, self.get_bash(),
-            #                            {}, stderr)
+            if self.popen.poll():
+                raise CalledModuleError(returncode=self.popen.returncode,
+                                        code=self.get_bash(),
+                                        module=self.name, errors=stderr)
         return self
 
 ###############################################################################

Modified: grass/branches/releasebranch_7_0/lib/python/pygrass/modules/interface/parameter.py
===================================================================
--- grass/branches/releasebranch_7_0/lib/python/pygrass/modules/interface/parameter.py	2014-10-26 16:12:08 UTC (rev 62381)
+++ grass/branches/releasebranch_7_0/lib/python/pygrass/modules/interface/parameter.py	2014-10-26 16:42:33 UTC (rev 62382)
@@ -8,18 +8,114 @@
                         with_statement, print_function, unicode_literals)
 import re
 
-
+from grass.pygrass.functions import docstring_property
 from grass.pygrass.modules.interface.read import GETTYPE, element2dict, DOC
 
 
+def _check_value(param, value):
+    """Function to check the correctness of a value and
+    return the checked value and the original.
+    """
+    must_val = 'The Parameter <%s>, must be one of the following values: %r'
+    req = 'The Parameter <%s>, require: %s, get: %s instead: %r\n%s'
+
+    def raiseexcpet(exc, param, ptype, value):
+        """Function to modifa the error message"""
+        msg = req % (param.name, param.typedesc, ptype, value, exc.message)
+        if exc is ValueError:
+            raise ValueError(msg)
+        elif exc is TypeError:
+            raise TypeError(msg)
+        else:
+            exc.message = msg
+            raise exc
+
+    def check_string(value):
+        """Function to check that a string parameter is already a string"""
+        string = (str, unicode)
+        if param.type in string:
+            if type(value) in (int, float):
+                value = str(value)
+            if type(value) not in string:
+                msg = ("The Parameter <%s> require a string,"
+                       " %s instead is provided: %r")
+                raise ValueError(msg % (param.name, type(value), value))
+        return value
+
+    # return None if None
+    if value is None:
+        return param.default, param.default
+
+    # find errors with multiple parmeters
+    if isinstance(value, (list, tuple)):
+        if param.keydescvalues:
+            return (([value, ], value) if isinstance(value, tuple)
+                    else (value, value))
+        if param.multiple:
+            # everything looks fine, so check each value
+            try:
+                return [param.type(check_string(val)) for val in value], value
+            except Exception as exc:
+                raiseexcpet(exc, param, param.type, value)
+        else:
+            msg = 'The Parameter <%s> does not accept multiple inputs'
+            raise TypeError(msg % param.name)
+
+    if param.keydescvalues:
+        msg = 'The Parameter <%s> require multiple inputs in the form: %s'
+        raise TypeError(msg % (param.name, param.keydescvalues))
+
+    if param.typedesc == 'all':
+        return value, value
+
+    # check string before trying to convert value to the correct type
+    check_string(value)
+    # the value is a scalar
+    try:
+        newvalue = param.type(value)
+    except Exception as exc:
+        raiseexcpet(exc, param, type(value), value)
+
+    # check values
+    if hasattr(param, 'values'):
+        if param.type in (float, int):
+            # check for value in range
+            if ((param.min is not None and newvalue < param.min) or
+                    (param.max is not None and newvalue > param.max)):
+                err_str = ('The Parameter <%s>, must be between: '
+                           '%g<=value<=%g, %r is outside.')
+                raise ValueError(err_str % (param.name, param.min,
+                                            param.max, newvalue))
+        # check if value is in the list of valid values
+        if param.values is not None and newvalue not in param.values:
+            raise ValueError(must_val % (param.name, param.values))
+    return (([newvalue, ] if (param.multiple or param.keydescvalues)
+             else newvalue), value)
+
+
 # TODO add documentation
 class Parameter(object):
-    """The Parameter object store all information about a parameter of module.
+    """The Parameter object store all information about a parameter of a
+    GRASS GIS module. ::
 
-    It is possible to set parameter of command using this object.
+        >>> param = Parameter(diz=dict(name='int_number', required='yes',
+        ...                            multiple='no', type='integer',
+        ...                            values=[2, 4, 6, 8]))
+        >>> param.value = 2
+        >>> param.value
+        2
+        >>> param.value = 3
+        Traceback (most recent call last):
+           ...
+        ValueError: The Parameter <int_number>, must be one of the following values: [2, 4, 6, 8]
+
+    ...
     """
     def __init__(self, xparameter=None, diz=None):
         self._value = None
+        self._rawvalue = None
+        self.min = None
+        self.max = None
         diz = element2dict(xparameter) if xparameter is not None else diz
         if diz is None:
             raise TypeError('Xparameter or diz are required')
@@ -41,23 +137,16 @@
         #
         if 'values' in diz:
             try:
-                # Check for integer ranges: "3-30"
-                isrange = re.match("(?P<min>-*\d+)-(?P<max>\d+)",
+                # Check for integer ranges: "3-30" or float ranges: "0.0-1.0"
+                isrange = re.match("(?P<min>-*\d+.*\d*)-(?P<max>\d+.*\d*)",
                                    diz['values'][0])
                 if isrange:
-                    range_min, range_max = isrange.groups()
-                    self.values = range(int(range_min), int(range_max) + 1)
+                    mn, mx = isrange.groups()
+                    self.min, self.max = float(mn), float(mx)
+                    self.values = None
                     self.isrange = diz['values'][0]
-                # Check for float ranges: "0.0-1.0"
-                if not isrange:
-                    isrange = re.match("(?P<min>-*\d+.\d+)-(?P<max>\d+.\d+)",
-                                       diz['values'][0])
-                    if isrange:
-                        # We are not able to create range values from
-                        # floating point ranges
-                        self.isrange = diz['values'][0]
                 # No range was found
-                if not isrange:
+                else:
                     self.values = [self.type(i) for i in diz['values']]
                     self.isrange = False
             except TypeError:
@@ -67,22 +156,21 @@
         #
         # default
         #
-        if 'default' in diz:
+        if 'default' in diz and diz['default']:
             if self.multiple or self.keydescvalues:
                 self.default = [self.type(v)
                                 for v in diz['default'].split(',')]
             else:
                 self.default = self.type(diz['default'])
-            self._value = self.default
         else:
             self.default = None
-
+        self._value, self._rawvalue = self.default, self.default
         self.guisection = diz.get('guisection', None)
 
         #
         # gisprompt
         #
-        if 'gisprompt' in diz:
+        if 'gisprompt' in diz and diz['gisprompt']:
             self.typedesc = diz['gisprompt'].get('prompt', '')
             self.input = False if diz['gisprompt']['age'] == 'new' else True
         else:
@@ -92,92 +180,86 @@
         return self._value
 
     def _set_value(self, value):
-        values_error = 'The Parameter <%s>, must be a python list ' \
-                       'containing one or more of the following values: %r'
-        if value is None:
-            self._value = value
-        elif isinstance(value, list) or isinstance(value, tuple):
-            if self.multiple or self.keydescvalues:
-                # check each value
-                self._value = [self.type(val) for val in value]
-            else:
-                str_err = 'The Parameter <%s> does not accept multiple inputs'
-                raise TypeError(str_err % self.name)
-        elif self.typedesc == 'all':
-            self._value = value
-        elif isinstance(value, self.type):
-            if hasattr(self, 'values'):
-                if self.type is float:
-                    if self.values[0] <= value <= self.values[-1]:
-                        self._value = value
-                    else:
-                        err_str = 'The Parameter <%s>, must be: %g<=value<=%g'
-                        raise ValueError(err_str % (self.name, self.values[0],
-                                                    self.values[-1]))
-                elif value in self.values:
-                    self._value = value
-                else:
-                    raise ValueError(values_error % (self.name, self.values))
-            else:
-                self._value = value
-        elif self.type is str and isinstance(value, unicode):
-            if hasattr(self, 'values'):
-                if value in self.values:
-                    self._value = str(value)
-                else:
-                    raise ValueError(values_error % (self.name, self.values))
-            else:
-                self._value = str(value)
-        elif self.type is str and isinstance(value, str):
-            if hasattr(self, 'values'):
-                if value in self.values:
-                    self._value = value
-                else:
-                    raise ValueError(values_error % (self.name, self.values))
-            else:
-                self._value = value
-        else:
-            str_err = 'The Parameter <%s>, require: %s, get: %s instead'
-            raise TypeError(str_err % (self.name, self.typedesc, type(value)))
+        self._value, self._rawvalue = _check_value(self, value)
 
     # here the property function is used to transform value in an attribute
     # in this case we define which function must be use to get/set the value
     value = property(fget=_get_value, fset=_set_value,
-                     doc="Set or obtain value")
+                     doc="Parameter value transformed and validated.")
 
+    @property
+    def rawvalue(self):
+        """Parameter value as insert by user without transformation"""
+        return self._rawvalue
+
     def get_bash(self):
-        """Prova"""
-        if isinstance(self._value, list) or isinstance(self._value, tuple):
-            value = ','.join([str(v) for v in self._value])
+        """Return the BASH representation of the parameter. ::
+
+            >>> param = Parameter(diz=dict(name='int_number', required='yes',
+            ...                            multiple='no', type='integer',
+            ...                            values=[2, 4, 6, 8], default=8))
+            >>> param.get_bash()
+            u'int_number=8'
+
+        ..
+        """
+        sep = ','
+        if isinstance(self.rawvalue, (list, tuple)):
+            value = sep.join([sep.join([str(v) for v in val])
+                              if isinstance(val, tuple) else str(val)
+                              for val in self.rawvalue])
         else:
-            value = str(self._value)
-        return """%s=%s""" % (self.name, value)
+            value = str(self.rawvalue)
+        return "%s=%s" % (self.name, value)
 
     def get_python(self):
-        """Prova"""
-        if not self.value:
+        """Return a string with the Python representation of the parameter. ::
+
+            >>> param = Parameter(diz=dict(name='int_number', required='yes',
+            ...                            multiple='no', type='integer',
+            ...                            values=[2, 4, 6, 8], default=8))
+            >>> param.get_python()
+            u'int_number=8'
+
+        ..
+        """
+        if self.value is None:
             return ''
-        return """%s=%r""" % (self.name, self._value)
+        return """%s=%r""" % (self.name, self.value)
 
     def __str__(self):
+        """Return the BASH representation of the GRASS module parameter."""
         return self.get_bash()
 
     def __repr__(self):
+        """Return the python representation of the GRASS module parameter."""
         str_repr = "Parameter <%s> (required:%s, type:%s, multiple:%s)"
+        mtype = ('raster', 'vector')  # map type
         return str_repr % (self.name,
                            "yes" if self.required else "no",
-                           self.type if self.type in (
-                           'raster', 'vector') else self.typedesc,
+                           self.type if self.type in mtype else self.typedesc,
                            "yes" if self.multiple else "no")
 
-    # here we use property with a decorator, in this way we mask a method as
-    # a class attribute
-    @property
+    @docstring_property(__doc__)
     def __doc__(self):
         """Return the docstring of the parameter
 
         {name}: {default}{required}{multi}{ptype}
-            {description}{values}"","""
+            {description}{values}"",
+
+        ::
+
+            >>> param = Parameter(diz=dict(name='int_number',
+            ...                            description="Set an number",
+            ...                            required='yes',
+            ...                            multiple='no', type='integer',
+            ...                            values=[2, 4, 6, 8], default=8))
+            >>> print(param.__doc__)
+            int_number: 8, required, integer
+                Set an number
+                Values: 2, 4, 6, 8
+        ..
+        """
         if hasattr(self, 'values'):
             if self.isrange:
                 vals = self.isrange

Modified: grass/branches/releasebranch_7_0/lib/python/pygrass/modules/interface/read.py
===================================================================
--- grass/branches/releasebranch_7_0/lib/python/pygrass/modules/interface/read.py	2014-10-26 16:12:08 UTC (rev 62381)
+++ grass/branches/releasebranch_7_0/lib/python/pygrass/modules/interface/read.py	2014-10-26 16:42:33 UTC (rev 62382)
@@ -8,34 +8,44 @@
                         with_statement, print_function, unicode_literals)
 
 
+def do_nothing(p):
+    return p
+
+
+def get_None(p):
+    return None
+
+
+def get_dict(p):
+    return dict(p.items())
+
+
+def get_values(p):
+    return [e.text.strip() for e in p.findall('value/name')]
+
+
+def read_text(p):
+    return p.text.strip()
+
+
 def read_keydesc(par):
     name = par.text.strip()
     items = [e.text.strip() for e in par.findall('item')]
-    #import ipdb; ipdb.set_trace()
     return name, tuple(items) if len(items) > 1 else None
 
 
-#
-# this dictionary is used to extract the value of interest from the xml
-# the lambda experssion is used to define small simple functions,
-# is equivalent to: ::
-#
-# def f(p):
-#     return p.text.strip()
-#
-# and then we call f(p)
-#
 GETFROMTAG = {
-    'description': lambda p: p.text.strip(),
+    'description': read_text,
     'keydesc': read_keydesc,
-    'gisprompt': lambda p: dict(p.items()),
-    'default': lambda p: p.text.strip(),
-    'values': lambda p: [e.text.strip() for e in p.findall('value/name')],
-    'value': lambda p: None,
-    'guisection': lambda p: p.text.strip(),
-    'label': lambda p: p.text.strip(),
-    'suppress_required': lambda p: None,
-    'keywords': lambda p: p.text.strip(),
+    'gisprompt': get_dict,
+    'default': read_text,
+    'values': get_values,
+    'value': get_None,
+    'guisection': read_text,
+    'label': read_text,
+    'suppress_required': get_None,
+    'keywords': read_text,
+    'guidependency': read_text,
 }
 
 GETTYPE = {
@@ -44,7 +54,7 @@
     'float': float,
     'double': float,
     'file': str,
-    'all': lambda x: x,
+    'all': do_nothing,
 }
 
 

Modified: grass/branches/releasebranch_7_0/lib/python/pygrass/modules/interface/typedict.py
===================================================================
--- grass/branches/releasebranch_7_0/lib/python/pygrass/modules/interface/typedict.py	2014-10-26 16:12:08 UTC (rev 62381)
+++ grass/branches/releasebranch_7_0/lib/python/pygrass/modules/interface/typedict.py	2014-10-26 16:42:33 UTC (rev 62382)
@@ -12,7 +12,9 @@
 except ImportError:
     from grass.pygrass.orderdict import OrderedDict
 
+from grass.pygrass.functions import docstring_property
 
+
 class TypeDict(OrderedDict):
     def __init__(self, dict_type, *args, **kargs):
         self._type = dict_type
@@ -36,11 +38,10 @@
         if isinstance(value, self._type):
             super(TypeDict, self).__setitem__(key, value)
         else:
-            cl = repr(self._type).translate(None, "'<> ").split('.')
-            str_err = 'The value: %r is not a %s object'
-            raise TypeError(str_err % (value, cl[-1].title()))
+            str_err = 'The value: %r is not a %s instance.'
+            raise TypeError(str_err % (value, self._type.__name__))
 
-    @property
+    @docstring_property(__doc__)
     def __doc__(self):
         return '\n'.join([self.__getitem__(obj).__doc__
                           for obj in self.__iter__()])

Modified: grass/branches/releasebranch_7_0/lib/python/pygrass/modules/shortcuts.py
===================================================================
--- grass/branches/releasebranch_7_0/lib/python/pygrass/modules/shortcuts.py	2014-10-26 16:12:08 UTC (rev 62381)
+++ grass/branches/releasebranch_7_0/lib/python/pygrass/modules/shortcuts.py	2014-10-26 16:42:33 UTC (rev 62382)
@@ -1,9 +1,4 @@
 # -*- coding: utf-8 -*-
-"""
-Created on Tue Apr  2 18:49:11 2013
-
- at author: pietro
-"""
 from __future__ import (nested_scopes, generators, division, absolute_import,
                         with_statement, print_function, unicode_literals)
 import fnmatch
@@ -20,16 +15,17 @@
     """Example how to use MetaModule
 
        >>> g = MetaModule('g')
-       >>> g_mlist = g.mlist
-       >>> g_mlist.name
-       'g.mlist'
-       >>> g_mlist.required
+       >>> g_list = g.list
+       >>> g_list.name
+       'g.list'
+       >>> g_list.required
        ['type']
-       >>> g_mlist.inputs.type = 'rast'
-       >>> g_mlist.stdout_ = -1
-       >>> g_mlist.run()
-       >>> g_mlist.outputs.stdout                         # doctest: +ELLIPSIS
-       'basins...soils...'
+       >>> g_list.inputs.type = 'rast'
+       >>> g_list.stdout_ = -1
+       >>> g_list.run()
+       Module('g.list')
+       >>> g_list.outputs.stdout                         # doctest: +ELLIPSIS
+       '...basins...soils...'
        >>> r = MetaModule('r')
        >>> what = r.what
        >>> what.description

Modified: grass/branches/releasebranch_7_0/lib/python/pygrass/raster/__init__.py
===================================================================
--- grass/branches/releasebranch_7_0/lib/python/pygrass/raster/__init__.py	2014-10-26 16:12:08 UTC (rev 62381)
+++ grass/branches/releasebranch_7_0/lib/python/pygrass/raster/__init__.py	2014-10-26 16:42:33 UTC (rev 62382)
@@ -1,9 +1,4 @@
 # -*- coding: utf-8 -*-
-"""
-Created on Fri May 25 12:56:33 2012
-
- at author: pietro
-"""
 from __future__ import (nested_scopes, generators, division, absolute_import,
                         with_statement, print_function, unicode_literals)
 import os
@@ -20,6 +15,8 @@
 import grass.lib.raster as libraster
 import grass.lib.rowio as librowio
 
+libgis.G_gisinit('')
+
 #
 # import pygrass modules
 #
@@ -40,6 +37,7 @@
 class RasterRow(RasterAbstractBase):
     """Raster_row_access": Inherits: "Raster_abstract_base" and implements
     the default row access of the Rast library.
+
         * Implements row access using row id
         * The get_row() method must accept a Row object as argument that will
           be used for value storage, so no new buffer will be allocated
@@ -53,52 +51,57 @@
           object (only for rows), since r.mapcalc is more sophisticated and
           faster
 
-    Examples
-    --------
+    Examples:
 
-        >>> elev = RasterRow('elevation')
-        >>> elev.exist()
-        True
-        >>> elev.is_open()
-        False
-        >>> elev.info.cols
-        >>> elev.open()
-        >>> elev.is_open()
-        True
-        >>> type(elev.info.cols)
-        <type 'int'>
-        >>> elev.has_cats()
-        False
-        >>> elev.mode
-        u'r'
-        >>> elev.mtype
-        'FCELL'
-        >>> elev.num_cats()
-        0
-        >>> elev.info.range
-        (56, 156)
+    >>> elev = RasterRow('elevation')
+    >>> elev.exist()
+    True
+    >>> elev.is_open()
+    False
+    >>> elev.open()
+    >>> elev.is_open()
+    True
+    >>> elev.has_cats()
+    False
+    >>> elev.mode
+    u'r'
+    >>> elev.mtype
+    'FCELL'
+    >>> elev.num_cats()
+    0
+    >>> elev.info.range
+    (56, 156)
+    >>> elev.info
+    elevation@
+    rows: 1350
+    cols: 1500
+    north: 228500.0 south: 215000.0 nsres:10.0
+    east:  645000.0 west: 630000.0 ewres:10.0
+    range: 56, 156
+    proj: 99
+    <BLANKLINE>
 
     Each Raster map have an attribute call ``cats`` that allow user
-    to interact with the raster categories. ::
+    to interact with the raster categories.
 
-        >>> land = RasterRow('geology')
-        >>> land.open()
-        >>> land.cats               # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
-        [('Zml', 1.0, None),
-         ...
-         ('Tpyw', 1832.0, None)]
+    >>> land = RasterRow('geology')
+    >>> land.open()
+    >>> land.cats               # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
+    [('Zml', 1, None),
+     ...
+     ('Tpyw', 1832, None)]
 
-    Open a raster map using the *with statement*: ::
+    Open a raster map using the *with statement*:
 
-        >>> with RasterRow('elevation') as elev:
-        ...     for row in elev[:3]:
-        ...         row[:4]
-        ...
-        Buffer([ 141.99613953,  141.27848816,  141.37904358,  142.29821777], dtype=float32)
-        Buffer([ 142.90461731,  142.39450073,  142.68611145,  143.59086609], dtype=float32)
-        Buffer([ 143.81854248,  143.54707336,  143.83972168,  144.59527588], dtype=float32)
-        >>> elev.is_open()
-        False
+    >>> with RasterRow('elevation') as elev:
+    ...     for row in elev[:3]:
+    ...         row[:4]
+    ...
+    Buffer([ 141.99613953,  141.27848816,  141.37904358,  142.29821777], dtype=float32)
+    Buffer([ 142.90461731,  142.39450073,  142.68611145,  143.59086609], dtype=float32)
+    Buffer([ 143.81854248,  143.54707336,  143.83972168,  144.59527588], dtype=float32)
+    >>> elev.is_open()
+    False
 
     """
     def __init__(self, name, mapset='', *args, **kargs):
@@ -112,8 +115,8 @@
 
         :param row: the number of row to obtain
         :type row: int
-        :param row_buffer: specify the Buffer object that will be instantiate
-        :type row_buffer: bool
+        :param row_buffer: Buffer object instance with the right dim and type
+        :type row_buffer: Buffer
 
         >>> elev = RasterRow('elevation')
         >>> elev.open()
@@ -142,59 +145,52 @@
     def open(self, mode=None, mtype=None, overwrite=None):
         """Open the raster if exist or created a new one.
 
-        :param mode: Specify if the map will be open with read or write mode
+        :param str mode: Specify if the map will be open with read or write mode
                      ('r', 'w')
-        :type mode: str
-        :param type: If a new map is open, specify the type of the map(`CELL`,
+        :param str type: If a new map is open, specify the type of the map(`CELL`,
                      `FCELL`, `DCELL`)
-        :type type: str
-        :param overwrite: Use this flag to set the overwrite mode of existing
+        :param bool overwrite: Use this flag to set the overwrite mode of existing
                           raster maps
-        :type overwrite: bool
 
+        if the map already exist, automatically check the type and set:
 
-        if the map already exist, automatically check the type and set:
             * self.mtype
 
         Set all the privite, attributes:
+
             * self._fd;
             * self._gtype
             * self._rows and self._cols
+
         """
         self.mode = mode if mode else self.mode
         self.mtype = mtype if mtype else self.mtype
         self.overwrite = overwrite if overwrite is not None else self.overwrite
 
-        # check if exist and instantiate all the private attributes
-        if self.exist():
-            self.info.read()
-            self.cats.mtype = self.mtype
-            self.cats.read()
-            self.hist.read()
-            if self.mode == 'r':
-                # the map exist, read mode
+        if self.mode == 'r':
+            if self.exist():
+                self.info.read()
+                self.cats.mtype = self.mtype
+                self.cats.read()
+                self.hist.read()
                 self._fd = libraster.Rast_open_old(self.name, self.mapset)
                 self._gtype = libraster.Rast_get_map_type(self._fd)
                 self.mtype = RTYPE_STR[self._gtype]
-#                try:
-#                    self.cats.read(self)
-#                    self.hist.read(self.name)
-#                except:
-#                    import ipdb; ipdb.set_trace()
-            elif self.overwrite:
-                if self._gtype is None:
-                    raise OpenError(_("Raster type not defined"))
-                self._fd = libraster.Rast_open_new(self.name, self._gtype)
             else:
-                str_err = _("Raster map <{0}> already exists")
-                raise OpenError(str_err.format(self))
-        else:
-            # Create a new map
-            if self.mode == 'r':
-                # check if we are in read mode
                 str_err = _("The map does not exist, I can't open in 'r' mode")
                 raise OpenError(str_err)
+        elif self.mode == 'w':
+            if self.exist():
+                if not self.overwrite:
+                    str_err = _("Raster map <{0}> already exists"
+                                " and will be not overwritten")
+                    raise OpenError(str_err.format(self))
+            if self._gtype is None:
+                raise OpenError(_("Raster type not defined"))
             self._fd = libraster.Rast_open_new(self.name, self._gtype)
+        else:
+            raise OpenError("Open mode: %r not supported,"
+                            " valid mode are: r, w")
         # read rows and cols from the active region
         self._rows = libraster.Rast_window_rows()
         self._cols = libraster.Rast_window_cols()
@@ -256,6 +252,7 @@
 class RasterSegment(RasterAbstractBase):
     """Raster_segment_access": Inherits "Raster_abstract_base" and uses the
     segment library for cached randomly reading and writing access.
+
         * Implements the [row][col] operator for read and write access using
           Segment_get() and Segment_put() functions internally
         * Implements row read and write access with the [row] operator using
@@ -268,6 +265,7 @@
         * No mathematical operation like __add__ and stuff for the Raster
           object (only for rows), since r.mapcalc is more sophisticated and
           faster
+
     """
     def __init__(self, name, srows=64, scols=64, maxmem=100,
                  *args, **kargs):
@@ -469,6 +467,7 @@
     """Raster_cached_narray": Inherits "Raster_abstract_base" and
     "numpy.memmap". Its purpose is to allow numpy narray like access to
     raster maps without loading the map into the main memory.
+
     * Behaves like a numpy array and supports all kind of mathematical
       operations: __add__, ...
     * Overrides the open and close methods
@@ -490,7 +489,12 @@
            [1, 1, 1],
            [0, 0, 0],
            [0, 0, 0]], dtype=int32)
-    >>> el._write()
+    >>> el.exist()
+    False
+    >>> el.close('elev_bool', overwrite=True)
+    >>> el.exist()
+    True
+    >>> el.remove()
     """
     def __new__(cls, name, mapset="", mtype='CELL', mode='r+',
                 overwrite=False):
@@ -582,26 +586,26 @@
     def _read(self):
         """!Read raster map into array
 
-        @return 0 on success
-        @return non-zero code on failure
+        :return: 0 on success
+        :return: non-zero code on failure
         """
         with RasterRow(self.name, self.mapset, mode='r') as rst:
             buff = rst[0]
             for i in range(len(rst)):
                 self[i] = rst.get_row(i, buff)
 
-    def _write(self):
+    def _write(self, name, overwrite):
         """Write the numpy array into map
         """
-        #r.in.bin input=/home/pietro/docdat/phd/thesis/gis/north_carolina/user1/.tmp/eraclito/14325.0 output=new title='' bytes=1,anull='' --verbose --overwrite north=228500.0 south=215000.0 east=645000.0 west=630000.0 rows=1350 cols=1500
         if not self.exist() or self.mode != 'r':
             self.flush()
             buff = Buffer(self[0].shape, mtype=self.mtype)
-            with RasterRow(self.name, self.mapset, mode='w',
-                           mtype=self.mtype) as rst:
+            with RasterRow(name, self.mapset, mode='w',
+                           mtype=self.mtype, overwrite=overwrite) as rst:
                 for i in range(len(rst)):
                     buff[:] = self[i][:]
                     rst.put_row(buff[:])
+            self.name = name
 
     def open(self, mtype='', null=None, overwrite=None):
         """Open the map, if the map already exist: determine the map type
@@ -635,18 +639,18 @@
         # if the map is open or not
         self._fd = 1
 
-    def close(self, name=''):
+    def close(self, name='', overwrite=False):
         """Function to close the map
 
         :param name: the name of raster
-        :type name: str        
+        :type name: str
         """
         if self.is_open():
             name = name if name else self.name
             if not name:
                 raise RuntimeError('Raster name not set neither '
                                    'given as parameter.')
-            self._write()
+            self._write(name, overwrite)
             os.remove(self.filename)
             self._fd = None
 

Modified: grass/branches/releasebranch_7_0/lib/python/pygrass/raster/abstract.py
===================================================================
--- grass/branches/releasebranch_7_0/lib/python/pygrass/raster/abstract.py	2014-10-26 16:12:08 UTC (rev 62381)
+++ grass/branches/releasebranch_7_0/lib/python/pygrass/raster/abstract.py	2014-10-26 16:42:33 UTC (rev 62382)
@@ -27,7 +27,7 @@
 #
 # import raster classes
 #
-from grass.pygrass.raster.raster_type import TYPE as RTYPE
+from grass.pygrass.raster.raster_type import TYPE as RTYPE, RTYPE_STR
 from grass.pygrass.raster.category import Category
 from grass.pygrass.raster.history import History
 
@@ -50,7 +50,8 @@
         """Read the information for a raster map. ::
 
             >>> info = Info('elevation')
-            >>> info                                      # doctest: +ELLIPSIS
+            >>> info.read()
+            >>> info
             elevation@
             rows: 1350
             cols: 1500
@@ -58,20 +59,24 @@
             east:  645000.0 west: 630000.0 ewres:10.0
             range: 56, 156
             proj: 99
-            ...
+            <BLANKLINE>
 
         """
         self.name = name
         self.mapset = mapset
         self.c_region = ctypes.pointer(libgis.Cell_head())
-        self.c_range = ctypes.pointer(libraster.Range())
+        self.c_range = None
 
     def _get_range(self):
-        libraster.Rast_read_range(self.name, self.mapset, self.c_range)
+        if self.mtype == 'CELL':
+            self.c_range = ctypes.pointer(libraster.Range())
+            libraster.Rast_read_range(self.name, self.mapset, self.c_range)
+        else:
+            self.c_range = ctypes.pointer(libraster.FPRange())
+            libraster.Rast_read_fp_range(self.name, self.mapset, self.c_range)
 
     def _get_raster_region(self):
-        if self.name and self.mapset:
-            libraster.Rast_get_cellhd(self.name, self.mapset, self.c_region)
+        libraster.Rast_get_cellhd(self.name, self.mapset, self.c_region)
 
     def read(self):
         self._get_range()
@@ -131,16 +136,42 @@
 
     @property
     def min(self):
+        if self.c_range is None:
+            return None
         return self.c_range.contents.min
 
     @property
     def max(self):
+        if self.c_range is None:
+            return None
         return self.c_range.contents.max
 
     @property
     def range(self):
+        if self.c_range is None:
+            return None, None
         return self.c_range.contents.min, self.c_range.contents.max
 
+    @property
+    def mtype(self):
+        return RTYPE_STR[libraster.Rast_map_type(self.name, self.mapset)]
+
+    def _get_units(self):
+        return libraster.Rast_read_units(self.name, self.mapset)
+
+    def _set_units(self, units):
+        libraster.Rast_write_units(self.name, units)
+
+    units = property(_get_units, _set_units)
+
+    def _get_vdatum(self):
+        return libraster.Rast_read_vdatum(self.name, self.mapset)
+
+    def _set_vdatum(self, vdatum):
+        libraster.Rast_write_vdatum(self.name, vdatum)
+
+    vdatum = property(_get_vdatum, _set_vdatum)
+
     def __repr__(self):
         return INFO.format(name=self.name, mapset=self.mapset,
                            rows=self.rows, cols=self.cols,
@@ -167,6 +198,7 @@
 class RasterAbstractBase(object):
     """Raster_abstract_base: The base class from which all sub-classes
     inherit. It does not implement any row or map access methods:
+
     * Implements raster metadata information access (Type, ...)
     * Implements an open method that will be overwritten by the sub-classes
     * Implements the close method that might be overwritten by sub-classes
@@ -174,19 +206,19 @@
     * Implements get and set region methods
     * Implements color, history and category handling
     * Renaming, deletion, ...
+
     """
-    def __init__(self, name, mapset="",
-                 mode='r', mtype='FCELL', overwrite=False):
+    def __init__(self, name, mapset="", *aopen, **kwopen):
         """The constructor need at least the name of the map
-        *optional* field is the `mapset`. ::
+        *optional* field is the `mapset`.
 
-            >>> ele = RasterAbstractBase('elevation')
-            >>> ele.name
-            u'elevation'
-            >>> ele.exist()
-            True
-            >>> ele.mapset
-            'PERMANENT'
+        >>> ele = RasterAbstractBase('elevation')
+        >>> ele.name
+        u'elevation'
+        >>> ele.exist()
+        True
+        >>> ele.mapset
+        'PERMANENT'
 
         ..
         """
@@ -206,12 +238,14 @@
         self.hist = History(self.name, self.mapset)
         self.cats = Category(self.name, self.mapset)
         self.info = Info(self.name, self.mapset)
-        self.mode = mode
-        self.mtype = mtype
-        self.overwrite = overwrite
+        self._aopen = aopen
+        self._kwopen = kwopen
+        self._mtype = 'CELL'
+        self._mode = 'r'
+        self._overwrite = False
 
     def __enter__(self):
-        self.open()
+        self.open(*self._aopen, **self._kwopen)
         return self
 
     def __exit__(self, exc_type, exc_value, traceback):
@@ -319,11 +353,11 @@
         """Return True if the map already exist, and
         set the mapset if were not set.
 
-        call the C function `G_find_raster`. ::
+        call the C function `G_find_raster`.
 
-            >>> ele = RasterAbstractBase('elevation')
-            >>> ele.exist()
-            True
+        >>> ele = RasterAbstractBase('elevation')
+        >>> ele.exist()
+        True
         """
         if self.name:
             if self.mapset == '':
@@ -335,11 +369,11 @@
             return False
 
     def is_open(self):
-        """Return True if the map is open False otherwise. ::
+        """Return True if the map is open False otherwise.
 
-            >>> ele = RasterAbstractBase('elevation')
-            >>> ele.is_open()
-            False
+        >>> ele = RasterAbstractBase('elevation')
+        >>> ele.is_open()
+        False
 
         """
         return True if self._fd is not None and self._fd >= 0 else False
@@ -364,11 +398,11 @@
         return "{name}@{mapset}".format(name=self.name, mapset=self.mapset)
 
     def name_mapset(self, name=None, mapset=None):
-        """Return the full name of the Raster. ::
+        """Return the full name of the Raster.
 
-            >>> ele = RasterAbstractBase('elevation')
-            >>> ele.name_mapset()
-            u'elevation at PERMANENT'
+        >>> ele = RasterAbstractBase('elevation')
+        >>> ele.name_mapset()
+        u'elevation at PERMANENT'
 
         """
         if name is None:
@@ -414,10 +448,7 @@
     def get_value(self, point, region=None):
         """This method returns the pixel value of a given pair of coordinates:
 
-        Parameters
-        ------------
-
-        point = pair of coordinates in tuple object
+        :param point: pair of coordinates in tuple object
         """
         if not region:
             region = Region()
@@ -431,8 +462,7 @@
     def has_cats(self):
         """Return True if the raster map has categories"""
         if self.exist():
-            self.cats.read(self)
-            self.close()
+            self.cats.read()
             if len(self.cats) != 0:
                 return True
         return False

Modified: grass/branches/releasebranch_7_0/lib/python/pygrass/raster/buffer.py
===================================================================
--- grass/branches/releasebranch_7_0/lib/python/pygrass/raster/buffer.py	2014-10-26 16:12:08 UTC (rev 62381)
+++ grass/branches/releasebranch_7_0/lib/python/pygrass/raster/buffer.py	2014-10-26 16:42:33 UTC (rev 62382)
@@ -1,18 +1,30 @@
 # -*- coding: utf-8 -*-
-"""
-Created on Fri Jun  8 18:46:34 2012
-
- at author: pietro
-"""
 from grass.pygrass.raster.raster_type import TYPE as RTYPE
 import ctypes
 import numpy as np
 
 
+CELL = (np.int, np.int0, np.int8, np.int16, np.int32, np.int64)
+FCELL = (np.float, np.float16, np.float32)
+_DCELL = 'float64', 'float128'
+DCELL = tuple([getattr(np, attr) for attr in _DCELL if hasattr(np, attr)])
+
+
 class Buffer(np.ndarray):
     """shape, mtype='FCELL', buffer=None, offset=0,
     strides=None, order=None
     """
+    @property
+    def mtype(self):
+        if self.dtype in CELL:
+            return 'CELL'
+        elif self.dtype in FCELL:
+            return 'FCELL'
+        elif self.dtype in DCELL:
+            return DCELL
+        else:
+            err = "Raster type: %r not supported by GRASS."
+            raise TypeError(err % self.dtype)
 
     def __new__(cls, shape, mtype='FCELL', buffer=None, offset=0,
                 strides=None, order=None):
@@ -21,13 +33,11 @@
                                  buffer, offset, strides, order)
         obj.pointer_type = ctypes.POINTER(RTYPE[mtype]['ctypes'])
         obj.p = obj.ctypes.data_as(obj.pointer_type)
-        obj.mtype = mtype
         return obj
 
     def __array_finalize__(self, obj):
         if obj is None:
             return
-        self.mtype = getattr(obj, 'mtype', None)
         self.pointer_type = getattr(obj, 'pointer_type', None)
         self.p = getattr(obj, 'p', None)
 

Modified: grass/branches/releasebranch_7_0/lib/python/pygrass/raster/category.py
===================================================================
--- grass/branches/releasebranch_7_0/lib/python/pygrass/raster/category.py	2014-10-26 16:12:08 UTC (rev 62381)
+++ grass/branches/releasebranch_7_0/lib/python/pygrass/raster/category.py	2014-10-26 16:42:33 UTC (rev 62382)
@@ -53,19 +53,17 @@
 
     >>> import grass.lib.raster as libraster
     >>> import ctypes
-    >>> import grass.pygrass as pygrass
-    >>> land = pygrass.raster.RasterRow('geology')
-    >>> cats = pygrass.raster.Category()
-    >>> cats.read(land) # or with cats.read(land.name, land.mapset, land.mtype)
-    >>> cats.labels()
-    ['pond', 'forest', 'developed', 'bare', 'paved road', 'dirt road',
-        'vineyard', 'agriculture', 'wetland', 'bare ground path', 'grass']
-    >>> min_cat = ctypes.c_void_p()
-    >>> max_cat = ctypes.c_void_p()
-    >>> libraster.Rast_get_ith_c_cat(ctypes.byref(cats.cats), 0,
-    ...                              min_cat, max_cat)
+    >>> from grass.pygrass.raster.category import Category
+    >>> cats = Category('landuse')
+    >>> cats.read()
+    >>> cats.labels()                                     # doctest: +ELLIPSIS
+    ['undefined', 'developed', 'agriculture', ..., 'water', 'sediment']
+    >>> cats[0]
+    ('undefined', 0, None)
+    >>> cats[1]
+    ('developed', 1, None)
     """
-    def __init__(self, name, mapset='', mtype=None, *args, **kargs):
+    def __init__(self, name, mapset='', mtype='CELL', *args, **kargs):
         self.name = name
         self.mapset = mapset
         self.c_cats = libraster.Categories()
@@ -299,11 +297,9 @@
             0.5:1.0:road
             1.0:1.5:urban
 
-        :param filename: the name of file with categories rules
-        :type filename: str
-        :param sep: the separator used to divide values and category
-        :type sep: str
-        ..
+        :param str filename: the name of file with categories rules
+        :param str sep: the separator used to divide values and category
+
         """
         self.reset()
         with open(filename, 'r') as f:
@@ -331,11 +327,8 @@
             0.5:1.0:road
             1.0:1.5:urban
 
-        :param filename: the name of file with categories rules
-        :type filename: str
-        :param sep: the separator used to divide values and category
-        :type sep: str
-        ..
+        :param str filename: the name of file with categories rules
+        :param str sep: the separator used to divide values and category
         """
         with open(filename, 'w') as f:
             cats = []

Modified: grass/branches/releasebranch_7_0/lib/python/pygrass/shell/__init__.py
===================================================================
--- grass/branches/releasebranch_7_0/lib/python/pygrass/shell/__init__.py	2014-10-26 16:12:08 UTC (rev 62381)
+++ grass/branches/releasebranch_7_0/lib/python/pygrass/shell/__init__.py	2014-10-26 16:42:33 UTC (rev 62382)
@@ -1,10 +1,2 @@
 # -*- coding: utf-8 -*-
-"""
-Created on Sun Jun 23 15:06:46 2013
 
- at author: pietro
-"""
-
-from grass.pygrass.shell import conversion
-from grass.pygrass.shell import show
-

Modified: grass/branches/releasebranch_7_0/lib/python/pygrass/shell/conversion.py
===================================================================
--- grass/branches/releasebranch_7_0/lib/python/pygrass/shell/conversion.py	2014-10-26 16:12:08 UTC (rev 62381)
+++ grass/branches/releasebranch_7_0/lib/python/pygrass/shell/conversion.py	2014-10-26 16:42:33 UTC (rev 62382)
@@ -17,76 +17,61 @@
               vfmt='%s', vdec='', vfun=None):
     """Return a html repr of a dictionary.
 
-    Parameters
-    -----------
+    :param dict dic: dictionary or object with `keys` and `items` methods
+    :param keys: iterable objectwith only the keys that we want to display
+    :param str border: could be: "0", "1", etc.
+    :param str kfmt: string to format the key string (i.e. "%r", etc.)
+    :param str kdec: string to decorate the key (i.e. "b", "i", etc.)
+    :param str vfmt: string to format the value string (i.e. "%r", etc.)
+    :param str vdec: string to decorate the value (i.e. "b", "i", etc.)
 
-    dic: dictionary, required
-        Dictionary or object with `keys` and `items` methods
-    keys: iterable, optional
-        Iterable objectwith only the keys that we want to display
-    border: string, optional
-        Could be: "0", "1", etc.
-    kfmt: string, optional
-        String to format the key string (i.e. "%r", etc.)
-    kdec: string, optional
-        String to decorate the key (i.e. "b", "i", etc.)
-    vfmt: string, optional
-        String to format the value string (i.e. "%r", etc.)
-    vdec: string, optional
-        String to decorate the value (i.e. "b", "i", etc.)
-
-    Examples
-    ---------
-
-    ::
-
-        >>> dic = {'key 0': 0, 'key 1': 1}
-        >>> print dict2html(dic)
-        <table>
-            <tr>
-              <td>key 0</td>
-              <td>0</td>
-            </tr>
-            <tr>
-              <td>key 1</td>
-              <td>1</td>
-            </tr>
-        </table>
-        >>> print dict2html(dic, border="1")
-        <table border='1'>
-            <tr>
-              <td>key 0</td>
-              <td>0</td>
-            </tr>
-            <tr>
-              <td>key 1</td>
-              <td>1</td>
-            </tr>
-        </table>
-        >>> print dict2html(dic, kdec='b', vfmt='%05d', vdec='i')
-        <table>
-            <tr>
-              <td><b>key 0</b></td>
-              <td><i>00000</i></td>
-            </tr>
-            <tr>
-              <td><b>key 1</b></td>
-              <td><i>00001</i></td>
-            </tr>
-        </table>
-        >>> dic = {'key 0': (2, 3), 'key 1': (10, 5)}
-        >>> print dict2html(dic, kdec='b', vdec='i',
-        ...                 vfun=lambda x: "%d<sup>%.1f</sup>" % x)
-        <table>
-            <tr>
-              <td><b>key 0</b></td>
-              <td><i>2<sup>3.0</sup></i></td>
-            </tr>
-            <tr>
-              <td><b>key 1</b></td>
-              <td><i>10<sup>5.0</sup></i></td>
-            </tr>
-        </table>
+    >>> dic = {'key 0': 0, 'key 1': 1}
+    >>> print dict2html(dic)
+    <table>
+        <tr>
+          <td>key 0</td>
+          <td>0</td>
+        </tr>
+        <tr>
+          <td>key 1</td>
+          <td>1</td>
+        </tr>
+    </table>
+    >>> print dict2html(dic, border="1")
+    <table border='1'>
+        <tr>
+          <td>key 0</td>
+          <td>0</td>
+        </tr>
+        <tr>
+          <td>key 1</td>
+          <td>1</td>
+        </tr>
+    </table>
+    >>> print dict2html(dic, kdec='b', vfmt='%05d', vdec='i')
+    <table>
+        <tr>
+          <td><b>key 0</b></td>
+          <td><i>00000</i></td>
+        </tr>
+        <tr>
+          <td><b>key 1</b></td>
+          <td><i>00001</i></td>
+        </tr>
+    </table>
+    >>> dic = {'key 0': (2, 3), 'key 1': (10, 5)}
+    >>> print dict2html(dic, kdec='b', vdec='i',
+    ...                 vfun=lambda x: "%d<sup>%.1f</sup>" % x)
+    <table>
+        <tr>
+          <td><b>key 0</b></td>
+          <td><i>2<sup>3.0</sup></i></td>
+        </tr>
+        <tr>
+          <td><b>key 1</b></td>
+          <td><i>10<sup>5.0</sup></i></td>
+        </tr>
+    </table>
     """
     def fun(x):
         return x

Modified: grass/branches/releasebranch_7_0/lib/python/pygrass/vector/__init__.py
===================================================================
--- grass/branches/releasebranch_7_0/lib/python/pygrass/vector/__init__.py	2014-10-26 16:12:08 UTC (rev 62381)
+++ grass/branches/releasebranch_7_0/lib/python/pygrass/vector/__init__.py	2014-10-26 16:42:33 UTC (rev 62382)
@@ -1,16 +1,13 @@
 # -*- coding: utf-8 -*-
-"""
-Created on Tue Jul 17 08:51:53 2012
-
- at author: pietro
-"""
-import grass.lib.vector as libvect
-from grass.pygrass.vector.vector_type import VTYPE
 from os.path import join, exists
+import grass.lib.gis as libgis
+libgis.G_gisinit('')
+import grass.lib.vector as libvect
 
 #
 # import pygrass modules
 #
+from grass.pygrass.vector.vector_type import VTYPE
 from grass.pygrass.errors import GrassError, must_be_open
 from grass.pygrass.gis import Location
 
@@ -27,8 +24,7 @@
           "holes": libvect.Vect_get_num_holes,
           "islands": libvect.Vect_get_num_islands,
           "kernels": libvect.Vect_get_num_kernels,
-          "lines": libvect.Vect_get_num_line_points,
-          "points": libvect.Vect_get_num_lines,
+          "lines": libvect.Vect_get_num_lines,
           "nodes": libvect.Vect_get_num_nodes,
           "updated_lines": libvect.Vect_get_num_updated_lines,
           "updated_nodes": libvect.Vect_get_num_updated_nodes,
@@ -72,12 +68,12 @@
     def __iter__(self):
         """::
 
-            >>> mun = Vector('census')
-            >>> mun.open()
-            >>> features = [feature for feature in mun]
+            >>> cens = Vector('census')
+            >>> cens.open(mode='r')
+            >>> features = [feature for feature in cens]
             >>> features[:3]
             [Boundary(v_id=None), Boundary(v_id=None), Boundary(v_id=None)]
-            >>> mun.close()
+            >>> cens.close()
 
         ..
         """
@@ -88,13 +84,13 @@
     def next(self):
         """::
 
-            >>> mun = Vector('census')
-            >>> mun.open()
-            >>> mun.next()
+            >>> cens = Vector('census')
+            >>> cens.open(mode='r')
+            >>> cens.next()
             Boundary(v_id=None)
-            >>> mun.next()
+            >>> cens.next()
             Boundary(v_id=None)
-            >>> mun.close()
+            >>> cens.close()
 
         ..
         """
@@ -152,6 +148,12 @@
             >>> new.write(point0, ('pub', ))
             >>> new.write(point1, ('resturnat', ))
 
+        commit the db changes ::
+
+            >>> new.table.conn.commit()
+            >>> new.table.execute().fetchall()
+            [(1, u'pub'), (2, u'resturnat')]
+
         close the vector map ::
 
             >>> new.close()
@@ -160,15 +162,15 @@
 
         then play with the map ::
 
-            >>> new.open()
+            >>> new.open(mode='r')
             >>> new.read(1)
             Point(636981.336043, 256517.602235)
             >>> new.read(2)
             Point(637209.083058, 257970.129540)
             >>> new.read(1).attrs['name']
             u'pub'
-            >>> new.read(2).attrs['cat', 'name']
-            (2, u'resturnat')
+            >>> new.read(2).attrs['name']
+            u'resturnat'
             >>> new.close()
             >>> new.remove()
 
@@ -205,7 +207,7 @@
         Color table stored in the vector's attribute table well be not checked
 
         >>> cens = Vector('census')
-        >>> cens.open()
+        >>> cens.open(mode='r')
         >>> cens.has_color_table()
         False
 
@@ -214,9 +216,9 @@
         >>> copy('census','mycensus','vect')
         >>> from grass.pygrass.modules.shortcuts import vector as v
         >>> v.colors(map='mycensus', color='population', column='TOTAL_POP')
-
+        Module('v.colors')
         >>> mycens = Vector('mycensus')
-        >>> mycens.open()
+        >>> mycens.open(mode='r')
         >>> mycens.has_color_table()
         True
         >>> mycens.close()
@@ -236,7 +238,7 @@
 
     Open a vector map using the *with statement*: ::
 
-        >>> with VectorTopo('schools') as schools:
+        >>> with VectorTopo('schools', mode='r') as schools:
         ...     for school in schools[:3]:
         ...         print school.attrs['NAMESHORT']
         ...
@@ -259,19 +261,19 @@
     def __getitem__(self, key):
         """::
 
-            >>> mun = VectorTopo('census')
-            >>> mun.open()
-            >>> mun[:3]
+            >>> cens = VectorTopo('census')
+            >>> cens.open(mode='r')
+            >>> cens[:3]
             [Boundary(v_id=1), Boundary(v_id=2), Boundary(v_id=3)]
-            >>> mun.close()
+            >>> cens.close()
 
         ..
         """
         if isinstance(key, slice):
-            #import pdb; pdb.set_trace()
-            #Get the start, stop, and step from the slice
-            return [self.read(indx + 1)
-                    for indx in range(*key.indices(len(self)))]
+            return [self.read(indx)
+                    for indx in range(key.start if key.start else 1,
+                                      key.stop if key.stop else len(self),
+                                      key.step if key.step else 1)]
         elif isinstance(key, int):
             return self.read(key)
         else:
@@ -296,16 +298,16 @@
 
         ::
 
-            >>> cens = VectorTopo('boundary_municp_sqlite')
-            >>> cens.open()
+            >>> cens = VectorTopo('census')
+            >>> cens.open(mode='r')
             >>> cens.num_primitive_of('point')
             0
             >>> cens.num_primitive_of('line')
             0
             >>> cens.num_primitive_of('centroid')
-            3579
+            2537
             >>> cens.num_primitive_of('boundary')
-            5128
+            6383
             >>> cens.close()
 
         ..
@@ -323,26 +325,23 @@
                       *update_lines*, *update_nodes*, *volumes*
         :type vtype: str
 
-            >>> cens = VectorTopo('boundary_municp_sqlite')
-            >>> cens.open()
+            >>> cens = VectorTopo('census')
+            >>> cens.open(mode='r')
             >>> cens.number_of("areas")
-            3579
+            2547
             >>> cens.number_of("islands")
-            2629
+            49
             >>> cens.number_of("holes")
             0
             >>> cens.number_of("lines")
-            8707
+            8920
             >>> cens.number_of("nodes")
-            4178
+            3885
             >>> cens.number_of("pizza")
             ...                     # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
             Traceback (most recent call last):
                 ...
-            ValueError: vtype not supported, use one of:
-            'areas', 'dblinks', 'faces', 'holes', 'islands', 'kernels',
-            'line_points', 'lines', 'nodes', 'updated_lines', 'updated_nodes',
-            'volumes'
+            ValueError: vtype not supported, use one of: 'areas', ...
             >>> cens.close()
 
 
@@ -376,23 +375,23 @@
                        full features
         :type idonly: bool
 
-            >>> cens = VectorTopo('census')
-            >>> cens.open()
+            >>> cens = VectorTopo('census', mode='r')
+            >>> cens.open(mode='r')
             >>> big = [area for area in cens.viter('areas')
-            ...        if area.alive() and area.area >= 10000]
+            ...        if area.alive() and area.area() >= 10000]
             >>> big[:3]
-            [Area(1), Area(2), Area(3)]
+            [Area(5), Area(6), Area(13)]
 
 
         to sort the result in a efficient way, use: ::
 
             >>> from operator import methodcaller as method
-            >>> big.sort(key = method('area'), reverse = True)  # sort the list
+            >>> big.sort(key=method('area'), reverse=True)  # sort the list
             >>> for area in big[:3]:
             ...     print area, area.area()
-            Area(3102) 697521857.848
-            Area(2682) 320224369.66
-            Area(2552) 298356117.948
+            Area(2099) 5392751.5304
+            Area(2171) 4799921.30863
+            Area(495) 4055812.49695
             >>> cens.close()
 
         """
@@ -413,18 +412,18 @@
     def rewind(self):
         """Rewind vector map to cause reads to start at beginning. ::
 
-            >>> mun = VectorTopo('boundary_municp_sqlite')
-            >>> mun.open()
-            >>> mun.next()
+            >>> cens = VectorTopo('census')
+            >>> cens.open(mode='r')
+            >>> cens.next()
             Boundary(v_id=1)
-            >>> mun.next()
+            >>> cens.next()
             Boundary(v_id=2)
-            >>> mun.next()
+            >>> cens.next()
             Boundary(v_id=3)
-            >>> mun.rewind()
-            >>> mun.next()
+            >>> cens.rewind()
+            >>> cens.next()
             Boundary(v_id=1)
-            >>> mun.close()
+            >>> cens.close()
 
         ..
         """
@@ -468,33 +467,31 @@
     def read(self, feature_id):
         """Return a geometry object given the feature id.
 
-            :param feature_id: the id of feature to obtain
-            :type feature_id: int
+        :param int feature_id: the id of feature to obtain
 
+        >>> cens = VectorTopo('census')
+        >>> cens.open(mode='r')
+        >>> feature1 = cens.read(0)                     #doctest: +ELLIPSIS
+        Traceback (most recent call last):
+            ...
+        ValueError: The index must be >0, 0 given.
+        >>> feature1 = cens.read(1)
+        >>> feature1
+        Boundary(v_id=1)
+        >>> feature1.length()
+        444.54490917696944
+        >>> cens.read(-1)
+        Centoid(642963.159711, 214994.016279)
+        >>> len(cens)
+        8920
+        >>> cens.read(8920)
+        Centoid(642963.159711, 214994.016279)
+        >>> cens.read(8921)                             #doctest: +ELLIPSIS
+        Traceback (most recent call last):
+          ...
+        IndexError: Index out of range
+        >>> cens.close()
 
-            >>> mun = VectorTopo('boundary_municp_sqlite')
-            >>> mun.open()
-            >>> feature1 = mun.read(0)                     #doctest: +ELLIPSIS
-            Traceback (most recent call last):
-                ...
-            ValueError: The index must be >0, 0 given.
-            >>> feature1 = mun.read(1)
-            >>> feature1
-            Boundary(v_id=1)
-            >>> feature1.length()
-            1415.3348048582038
-            >>> mun.read(-1)
-            Centoid(649102.382010, 15945.714502)
-            >>> len(mun)
-            8707
-            >>> mun.read(8707)
-            Centoid(649102.382010, 15945.714502)
-            >>> mun.read(8708)                             #doctest: +ELLIPSIS
-            Traceback (most recent call last):
-              ...
-            IndexError: Index out of range
-            >>> mun.close()
-
         """
         return read_line(feature_id, self.c_mapinfo, self.table, self.writable,
                          is2D=not self.is_3D())

Modified: grass/branches/releasebranch_7_0/lib/python/pygrass/vector/abstract.py
===================================================================
--- grass/branches/releasebranch_7_0/lib/python/pygrass/vector/abstract.py	2014-10-26 16:12:08 UTC (rev 62381)
+++ grass/branches/releasebranch_7_0/lib/python/pygrass/vector/abstract.py	2014-10-26 16:42:33 UTC (rev 62382)
@@ -31,7 +31,7 @@
     To get access to the vector info the map must be opened. ::
 
         >>> cens = Info('census')
-        >>> cens.open()
+        >>> cens.open(mode='r')
 
     Then it is possible to read and write the following map attributes: ::
 
@@ -77,23 +77,22 @@
         >>> cens.close()
 
     """
-    def __init__(self, name, mapset='', layer=None, mode='r', with_z=False):
+    def __init__(self, name, mapset='', *aopen, **kwopen):
         self._name = ''
         self._mapset = ''
         # Set map name and mapset
         self.name = name
         self.mapset = mapset
+        self._aopen = aopen
+        self._kwopen = kwopen
         self.c_mapinfo = ctypes.pointer(libvect.Map_info())
         self._topo_level = 1
         self._class_name = 'Vector'
         self.overwrite = False
         self.date_fmt = '%a %b  %d %H:%M:%S %Y'
-        self.layer = layer
-        self.mode = mode
-        self.with_z = with_z
 
-    def __enter__(self, *args, **kwargs):
-        self.open(*args, **kwargs)
+    def __enter__(self):
+        self.open(*self._aopen, **self._kwopen)
         return self
 
     def __exit__(self, exc_type, exc_value, traceback):
@@ -133,7 +132,7 @@
 
     def _set_organization(self, org):
         """Private method to change the Vector organization"""
-        libvect.Vect_get_organization(self.c_mapinfo, ctypes.c_char_p(org))
+        libvect.Vect_set_organization(self.c_mapinfo, ctypes.c_char_p(org))
 
     organization = property(fget=_get_organization, fset=_set_organization,
                             doc="Set or obtain the Vector organization")
@@ -257,7 +256,8 @@
         """Return the project name of Vector"""
         return libvect.Vect_get_proj_name(self.c_mapinfo)
 
-    def _write_header(self):
+    def write_header(self):
+        """Save the change in the C struct permanently to disk."""
         libvect.Vect_write_header(self.c_mapinfo)
 
     def rename(self, newname):
@@ -299,7 +299,7 @@
              link_driver='sqlite'):
         """Open a Vector map.
 
-         
+
         :param mode: open a vector map in ``r`` in reading, ``w`` in writing
                      and in ``rw`` read and write mode
         :type mode: str
@@ -332,28 +332,26 @@
         See more examples in the documentation of the ``read`` and ``write``
         methods
         """
-        self.mode = mode if mode else self.mode
-        self.with_z = self.with_z if with_z is None else with_z
-        with_z = libvect.WITH_Z if self.with_z else libvect.WITHOUT_Z
+        with_z = libvect.WITH_Z if with_z else libvect.WITHOUT_Z
         # check if map exists or not
-        if not self.exist() and self.mode != 'w':
+        if not self.exist() and mode != 'w':
             raise OpenError("Map <%s> not found." % self._name)
         if libvect.Vect_set_open_level(self._topo_level) != 0:
             raise OpenError("Invalid access level.")
         # update the overwrite attribute
         self.overwrite = overwrite if overwrite is not None else self.overwrite
         # check if the mode is valid
-        if self.mode not in ('r', 'rw', 'w'):
+        if mode not in ('r', 'rw', 'w'):
             raise ValueError("Mode not supported. Use one of: 'r', 'rw', 'w'.")
 
         # check if the map exist
-        if self.exist() and self.mode in ('r', 'rw'):
+        if self.exist() and mode in ('r', 'rw'):
             # open in READ mode
-            if self.mode == 'r':
+            if mode == 'r':
                 openvect = libvect.Vect_open_old2(self.c_mapinfo, self.name,
                                                   self.mapset, str(layer))
             # open in READ and WRITE mode
-            elif self.mode == 'rw':
+            elif mode == 'rw':
                 openvect = libvect.Vect_open_update2(self.c_mapinfo, self.name,
                                                      self.mapset, str(layer))
 
@@ -361,7 +359,7 @@
             self.dblinks = DBlinks(self.c_mapinfo)
 
         # If it is opened in write mode
-        if self.mode == 'w':
+        if mode == 'w':
             openvect = libvect.Vect_open_new(self.c_mapinfo, self.name, with_z)
             self.dblinks = DBlinks(self.c_mapinfo)
             if tab_cols:

Modified: grass/branches/releasebranch_7_0/lib/python/pygrass/vector/basic.py
===================================================================
--- grass/branches/releasebranch_7_0/lib/python/pygrass/vector/basic.py	2014-10-26 16:12:08 UTC (rev 62381)
+++ grass/branches/releasebranch_7_0/lib/python/pygrass/vector/basic.py	2014-10-26 16:42:33 UTC (rev 62382)
@@ -14,27 +14,27 @@
 class Bbox(object):
     """Instantiate a Bounding Box class that contains
     a ctypes pointer to the C struct bound_box, that could be used
-    by C GRASS functions. ::
+    by C GRASS functions.
 
-        >>> bbox = Bbox()
-        >>> bbox
-        Bbox(0.0, 0.0, 0.0, 0.0)
+    >>> bbox = Bbox()
+    >>> bbox
+    Bbox(0.0, 0.0, 0.0, 0.0)
 
     The default parameters are 0. It is possible to set or change
-    the parameters later, with: ::
+    the parameters later, with:
 
-        >>> bbox.north = 10
-        >>> bbox.south = -10
-        >>> bbox.east = -20
-        >>> bbox.west = 20
-        >>> bbox
-        Bbox(10.0, -10.0, -20.0, 20.0)
+    >>> bbox.north = 10
+    >>> bbox.south = -10
+    >>> bbox.east = -20
+    >>> bbox.west = 20
+    >>> bbox
+    Bbox(10.0, -10.0, -20.0, 20.0)
 
-    Or directly istantiate the class with the values, with: ::
+    Or directly istantiate the class with the values, with:
 
-        >>> bbox = Bbox(north=100, south=0, east=0, west=100)
-        >>> bbox
-        Bbox(100.0, 0.0, 0.0, 100.0)
+    >>> bbox = Bbox(north=100, south=0, east=0, west=100)
+    >>> bbox
+    Bbox(100.0, 0.0, 0.0, 100.0)
 
     ..
     """
@@ -351,7 +351,7 @@
         with a list of integers
 
         :param ilist: the ilist to append
-        :type ilist: a Ilist object        
+        :type ilist: a Ilist object
         """
         if isinstance(ilist, Ilist):
             libvect.Vect_list_append_list(self.c_ilist, ilist.ilist)
@@ -378,36 +378,35 @@
 
 class Cats(object):
     """Instantiate a Category class that contains a ctypes pointer
-    to the C line_cats struct. ::
+    to the C line_cats struct.
 
-        >>> cats = Cats()
-        >>> for cat in xrange(100, 110): cats.set(cat, layer=cat-50)
-        >>> cats.n_cats
-        10
-        >>> cats.cat
-        [100, 101, 102, 103, 104, 105, 106, 107, 108, 109]
-        >>> cats.layer
-        [50, 51, 52, 53, 54, 55, 56, 57, 58, 59]
-        >>> cats.get()  # default layer is 1
-        (-1, 0)
-        >>> cats.get(50)
-        (100, 1)
-        >>> cats.get(51)
-        (101, 1)
-        >>> cats.set(1001, 52)
-        >>> cats.cat
-        [100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 1001]
-        >>> cats.layer
-        [50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 52]
-        >>> cats.get(52)
-        (102, 2)
-        >>> cats.reset()
-        >>> cats.layer
-        []
-        >>> cats.cat
-        []
+    >>> cats = Cats()
+    >>> for cat in xrange(100, 110): cats.set(cat, layer=cat-50)
+    >>> cats.n_cats
+    10
+    >>> cats.cat
+    [100, 101, 102, 103, 104, 105, 106, 107, 108, 109]
+    >>> cats.layer
+    [50, 51, 52, 53, 54, 55, 56, 57, 58, 59]
+    >>> cats.get()  # default layer is 1
+    (-1, 0)
+    >>> cats.get(50)
+    (100, 1)
+    >>> cats.get(51)
+    (101, 1)
+    >>> cats.set(1001, 52)
+    >>> cats.cat
+    [100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 1001]
+    >>> cats.layer
+    [50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 52]
+    >>> cats.get(52)
+    (102, 2)
+    >>> cats.reset()
+    >>> cats.layer
+    []
+    >>> cats.cat
+    []
 
-
     """
     @property
     def layer(self):
@@ -497,25 +496,25 @@
 
 
 class CatsList(object):
-    """::
+    """
 
-        >>> cats_list = CatsList()
-        >>> cats_list.min
-        []
-        >>> cats_list.max
-        []
-        >>> cats_list.n_ranges
-        0
-        >>> cats_list.layer
-        0
-        >>> string = "2,3,5-9,20"
-        >>> cats_list.from_string(string)
-        >>> cats_list.min
-        [2, 3, 5, 20]
-        >>> cats_list.max
-        [2, 3, 9, 20]
-        >>> cats_list.n_ranges
-        4
+    >>> cats_list = CatsList()
+    >>> cats_list.min
+    []
+    >>> cats_list.max
+    []
+    >>> cats_list.n_ranges
+    0
+    >>> cats_list.layer
+    0
+    >>> string = "2,3,5-9,20"
+    >>> cats_list.from_string(string)
+    >>> cats_list.min
+    [2, 3, 5, 20]
+    >>> cats_list.max
+    [2, 3, 9, 20]
+    >>> cats_list.n_ranges
+    4
 
     """
     @property

Modified: grass/branches/releasebranch_7_0/lib/python/pygrass/vector/find.py
===================================================================
--- grass/branches/releasebranch_7_0/lib/python/pygrass/vector/find.py	2014-10-26 16:12:08 UTC (rev 62381)
+++ grass/branches/releasebranch_7_0/lib/python/pygrass/vector/find.py	2014-10-26 16:42:33 UTC (rev 62382)
@@ -9,14 +9,14 @@
 from grass.pygrass.errors import must_be_open
 
 from grass.pygrass.vector.basic import Ilist, BoxList
-from grass.pygrass.vector.geometry import read_line, Isle, Area, Point
+from grass.pygrass.vector.geometry import read_line, Isle, Area, Point, Node
 
 
 class AbstractFinder(object):
     def __init__(self, c_mapinfo, table=None, writable=False):
         """AbstractFinder
         -----------------
-        
+
         Find geometry feature around a point.
         """
         self.c_mapinfo = c_mapinfo
@@ -36,39 +36,39 @@
 
 class PointFinder(AbstractFinder):
     """PointFinder
-    ------------------
-    Find the geomtry features of a vector map that are close to a point. ::
 
-        >>> from grass.pygrass.vector import VectorTopo
-        >>> zipcodes = VectorTopo('zipcodes', 'PERMANENT')
-        >>> schools = VectorTopo('schools', 'PERMANENT')
-        >>> zipcodes.open('r')
-        >>> schools.open('r')
-        >>> result = []
-        >>> for school in schools:
-        ...         zipcode = zipcodes.find.area(school)
-        ...         result.append((school.attrs['NAMESHORT'],
-        ...                        zipcode.attrs['ZIPCODE']))
-        ...
-        >>> result[0]
-        (u'SWIFT CREEK', u'RALEIGH 27606')
-        >>> result[1]
-        (u'BRIARCLIFF', u'CARY 27511')
-        >>> result[2]
-        (u'FARMINGTON WOODS', u'CARY 27511')
-        >>> from grass.pygrass.vector.geometry import Point
-        >>> pnt = Point(631213.349291, 224684.900084)
-        >>> school = schools.find.geo(pnt, maxdist=300.)
-        >>> school.attrs['NAMELONG']
-        u'ADAMS ELEMENTARY'
-        >>> for school in schools.find.geos(pnt, maxdist=1000.):
-        ...     print school.attrs['NAMELONG']
-        ...
-        CARY HIGH
-        EAST CARY MIDDLE SITE
-        ADAMS ELEMENTARY
-        >>> schools.close()
-        >>> zipcodes.close()
+    Find the geomtry features of a vector map that are close to a point.
+
+    >>> from grass.pygrass.vector import VectorTopo
+    >>> zipcodes = VectorTopo('zipcodes', 'PERMANENT')
+    >>> schools = VectorTopo('schools', 'PERMANENT')
+    >>> zipcodes.open('r')
+    >>> schools.open('r')
+    >>> result = []
+    >>> for school in schools:
+    ...     zipcode = zipcodes.find['by_point'].area(school)
+    ...     result.append((school.attrs['NAMESHORT'],
+    ...                    zipcode.attrs['ZIPCODE']))
+    ...
+    >>> result[0]
+    (u'SWIFT CREEK', u'RALEIGH 27606')
+    >>> result[1]
+    (u'BRIARCLIFF', u'CARY 27511')
+    >>> result[2]
+    (u'FARMINGTON WOODS', u'CARY 27511')
+    >>> from grass.pygrass.vector.geometry import Point
+    >>> pnt = Point(631213.349291, 224684.900084)
+    >>> school = schools.find['by_point'].geo(pnt, maxdist=300.)
+    >>> school.attrs['NAMELONG']
+    u'ADAMS ELEMENTARY'
+    >>> for school in schools.find['by_point'].geos(pnt, maxdist=1000.):
+    ...     print school.attrs['NAMELONG']
+    ...
+    CARY HIGH
+    EAST CARY MIDDLE SITE
+    ADAMS ELEMENTARY
+    >>> schools.close()
+    >>> zipcodes.close()
     """
     def __init__(self, c_mapinfo, table=None, writable=False):
         """Find geometry feature around a point.
@@ -160,8 +160,8 @@
         if libvect.Vect_select_nodes_by_box(self.c_mapinfo, bbox.c_bbox,
                                             found.c_ilist):
             for n_id in found:
-                yield Point(v_id=n_id, c_mapinfo=self.c_mapinfo,
-                            table=self.table, writable=self.writable)
+                yield Node(v_id=n_id, c_mapinfo=self.c_mapinfo,
+                           table=self.table, writable=self.writable)
 
     @must_be_open
     def areas(self, bbox, boxlist=None, bboxlist_only=False):

Modified: grass/branches/releasebranch_7_0/lib/python/pygrass/vector/geometry.py
===================================================================
--- grass/branches/releasebranch_7_0/lib/python/pygrass/vector/geometry.py	2014-10-26 16:12:08 UTC (rev 62381)
+++ grass/branches/releasebranch_7_0/lib/python/pygrass/vector/geometry.py	2014-10-26 16:42:33 UTC (rev 62382)
@@ -26,39 +26,43 @@
 def read_WKT(string):
     """Read the string and return a geometry object
 
-    WKT:
-    POINT(0 0)
-    LINESTRING(0 0,1 1,1 2)
-    POLYGON((0 0,4 0,4 4,0 4,0 0),(1 1, 2 1, 2 2, 1 2,1 1))
-    MULTIPOINT(0 0,1 2)
-    MULTILINESTRING((0 0,1 1,1 2),(2 3,3 2,5 4))
-    MULTIPOLYGON(((0 0,4 0,4 4,0 4,0 0),(1 1,2 1,2 2,1 2,1 1)),
-                 ((-1 -1,-1 -2,-2 -2,-2 -1,-1 -1)))
-    GEOMETRYCOLLECTION(POINT(2 3),LINESTRING(2 3,3 4))
+    **WKT**:
+    ::
 
+        POINT(0 0)
+        LINESTRING(0 0,1 1,1 2)
+        POLYGON((0 0,4 0,4 4,0 4,0 0),(1 1, 2 1, 2 2, 1 2,1 1))
+        MULTIPOINT(0 0,1 2)
+        MULTILINESTRING((0 0,1 1,1 2),(2 3,3 2,5 4))
+        MULTIPOLYGON(((0 0,4 0,4 4,0 4,0 0),(1 1,2 1,2 2,1 2,1 1)),
+                     ((-1 -1,-1 -2,-2 -2,-2 -1,-1 -1)))
+        GEOMETRYCOLLECTION(POINT(2 3),LINESTRING(2 3,3 4))
 
-    EWKT:
-    POINT(0 0 0) -- XYZ
-    SRID=32632;POINT(0 0) -- XY with SRID
-    POINTM(0 0 0) -- XYM
-    POINT(0 0 0 0) -- XYZM
-    SRID=4326;MULTIPOINTM(0 0 0,1 2 1) -- XYM with SRID
-    MULTILINESTRING((0 0 0,1 1 0,1 2 1),(2 3 1,3 2 1,5 4 1))
-    POLYGON((0 0 0,4 0 0,4 4 0,0 4 0,0 0 0),(1 1 0,2 1 0,2 2 0,1 2 0,1 1 0))
-    MULTIPOLYGON(((0 0 0,4 0 0,4 4 0,0 4 0,0 0 0),
-                  (1 1 0,2 1 0,2 2 0,1 2 0,1 1 0)),
-                 ((-1 -1 0,-1 -2 0,-2 -2 0,-2 -1 0,-1 -1 0)))
-    GEOMETRYCOLLECTIONM( POINTM(2 3 9), LINESTRINGM(2 3 4, 3 4 5) )
-    MULTICURVE( (0 0, 5 5), CIRCULARSTRING(4 0, 4 4, 8 4) )
-    POLYHEDRALSURFACE( ((0 0 0, 0 0 1, 0 1 1, 0 1 0, 0 0 0)),
-                       ((0 0 0, 0 1 0, 1 1 0, 1 0 0, 0 0 0)),
-                       ((0 0 0, 1 0 0, 1 0 1, 0 0 1, 0 0 0)),
-                       ((1 1 0, 1 1 1, 1 0 1, 1 0 0, 1 1 0)),
-                       ((0 1 0, 0 1 1, 1 1 1, 1 1 0, 0 1 0)),
-                       ((0 0 1, 1 0 1, 1 1 1, 0 1 1, 0 0 1)) )
-    TRIANGLE ((0 0, 0 9, 9 0, 0 0))
-    TIN( ((0 0 0, 0 0 1, 0 1 0, 0 0 0)), ((0 0 0, 0 1 0, 1 1 0, 0 0 0)) )
+    **EWKT**:
 
+    ::
+
+        POINT(0 0 0) -- XYZ
+        SRID=32632;POINT(0 0) -- XY with SRID
+        POINTM(0 0 0) -- XYM
+        POINT(0 0 0 0) -- XYZM
+        SRID=4326;MULTIPOINTM(0 0 0,1 2 1) -- XYM with SRID
+        MULTILINESTRING((0 0 0,1 1 0,1 2 1),(2 3 1,3 2 1,5 4 1))
+        POLYGON((0 0 0,4 0 0,4 4 0,0 4 0,0 0 0),(1 1 0,2 1 0,2 2 0,1 2 0,1 1 0))
+        MULTIPOLYGON(((0 0 0,4 0 0,4 4 0,0 4 0,0 0 0),
+                      (1 1 0,2 1 0,2 2 0,1 2 0,1 1 0)),
+                     ((-1 -1 0,-1 -2 0,-2 -2 0,-2 -1 0,-1 -1 0)))
+        GEOMETRYCOLLECTIONM( POINTM(2 3 9), LINESTRINGM(2 3 4, 3 4 5) )
+        MULTICURVE( (0 0, 5 5), CIRCULARSTRING(4 0, 4 4, 8 4) )
+        POLYHEDRALSURFACE( ((0 0 0, 0 0 1, 0 1 1, 0 1 0, 0 0 0)),
+                           ((0 0 0, 0 1 0, 1 1 0, 1 0 0, 0 0 0)),
+                           ((0 0 0, 1 0 0, 1 0 1, 0 0 1, 0 0 0)),
+                           ((1 1 0, 1 1 1, 1 0 1, 1 0 0, 1 1 0)),
+                           ((0 1 0, 0 1 1, 1 1 1, 1 1 0, 0 1 0)),
+                           ((0 0 1, 1 0 1, 1 1 1, 0 1 1, 0 0 1)) )
+        TRIANGLE ((0 0, 0 9, 9 0, 0 0))
+        TIN( ((0 0 0, 0 0 1, 0 1 0, 0 0 0)), ((0 0 0, 0 1 0, 1 1 0, 0 0 0)) )
+
     """
     for regexp, obj in WKT.items():
         if re.match(regexp, string):
@@ -72,12 +76,12 @@
 
 
 def intersects(lineA, lineB, with_z=False):
-    """Return a list of points ::
+    """Return a list of points
 
-        >>> lineA = Line([(0, 0), (4, 0)])
-        >>> lineB = Line([(2, 2), (2, -2)])
-        >>> intersects(lineA, lineB)
-        Line([Point(2.000000, 0.000000)])
+    >>> lineA = Line([(0, 0), (4, 0)])
+    >>> lineB = Line([(2, 2), (2, -2)])
+    >>> intersects(lineA, lineB)
+    Line([Point(2.000000, 0.000000)])
     """
     line = Line()
     if libvect.Vect_line_get_intersections(lineA.c_points, lineB.c_points,
@@ -92,21 +96,20 @@
 
 
 def get_xyz(pnt):
-    """Return a tuple with: x, y, z. ::
+    """Return a tuple with: x, y, z.
 
-        >>> pnt = Point(0, 0)
-        >>> get_xyz(pnt)
-        (0.0, 0.0, 0.0)
-        >>> get_xyz((1, 1))
-        (1, 1, 0.0)
-        >>> get_xyz((1, 1, 2))
-        (1, 1, 2)
-        >>> get_xyz((1, 1, 2, 2))                          #doctest: +ELLIPSIS
-        Traceback (most recent call last):
-            ...
-        ValueError: The the format of the point is not supported: (1, 1, 2, 2)
+    >>> pnt = Point(0, 0)
+    >>> get_xyz(pnt)
+    (0.0, 0.0, 0.0)
+    >>> get_xyz((1, 1))
+    (1, 1, 0.0)
+    >>> get_xyz((1, 1, 2))
+    (1, 1, 2)
+    >>> get_xyz((1, 1, 2, 2))                          #doctest: +ELLIPSIS
+    Traceback (most recent call last):
+        ...
+    ValueError: The the format of the point is not supported: (1, 1, 2, 2)
 
-    ..
     """
     if isinstance(pnt, Point):
         if pnt.is2D:
@@ -147,17 +150,16 @@
                    doc="Set and obtain cat value")
 
     def __getitem__(self, key):
-        """Return the value stored in the attribute table. ::
+        """Return the value stored in the attribute table.
 
-            >>> from grass.pygrass.vector import VectorTopo
-            >>> schools = VectorTopo('schools')
-            >>> schools.open('r')
-            >>> school = schools[1]
-            >>> attrs = Attrs(school.cat, schools.table)
-            >>> attrs['TAG']
-            u'568'
+        >>> from grass.pygrass.vector import VectorTopo
+        >>> schools = VectorTopo('schools')
+        >>> schools.open('r')
+        >>> school = schools[1]
+        >>> attrs = Attrs(school.cat, schools.table)
+        >>> attrs['TAG']
+        u'568'
 
-
         """
         #SELECT {cols} FROM {tname} WHERE {condition};
         try:
@@ -171,20 +173,20 @@
             return results[0] if len(results) == 1 else results
 
     def __setitem__(self, key, value):
-        """Set value of a given column of a table attribute. ::
+        """Set value of a given column of a table attribute.
 
-            >>> from grass.pygrass.vector import VectorTopo
-            >>> from grass.pygrass.functions import copy, remove
-            >>> copy('schools', 'myschools', 'vect')
-            >>> schools = VectorTopo('myschools')
-            >>> schools.open('r')
-            >>> school = schools[1]
-            >>> attrs = Attrs(school.cat, schools.table, True)
-            >>> attrs['TAG'] = 'New Label'
-            >>> attrs['TAG']
-            u'New Label'
-            >>> attrs.table.conn.close()
-            >>> remove('myschools','vect')
+        >>> from grass.pygrass.vector import VectorTopo
+        >>> from grass.pygrass.functions import copy, remove
+        >>> copy('schools', 'myschools', 'vect')
+        >>> schools = VectorTopo('myschools')
+        >>> schools.open('r')
+        >>> school = schools[1]
+        >>> attrs = Attrs(school.cat, schools.table, True)
+        >>> attrs['TAG'] = 'New Label'
+        >>> attrs['TAG']
+        u'New Label'
+        >>> attrs.table.conn.close()
+        >>> remove('myschools','vect')
         """
         if self.writable:
             #UPDATE {tname} SET {new_col} = {old_col} WHERE {condition}
@@ -194,8 +196,7 @@
                                                        condition=self.cond))
             #self.table.conn.commit()
         else:
-            str_err = "You can only read the attributes if the map is \
-in another mapset"
+            str_err = "You can only read the attributes if the map is in another mapset"
             raise GrassError(str_err)
 
     def __dict__(self):
@@ -252,11 +253,12 @@
     """
     gtype = None
 
-    def __init__(self, v_id=None, c_mapinfo=None, c_points=None, c_cats=None,
+    def __init__(self, v_id=0, c_mapinfo=None, c_points=None, c_cats=None,
                  table=None, writable=False, is2D=True):
         self.id = v_id  # vector id
         self.c_mapinfo = c_mapinfo
-        self.is2D = is2D
+        self.is2D = (is2D if is2D is not None else
+                     bool(libvect.Vect_is_3d(self.c_mapinfo) != 1))
 
         read = False
         # set c_points
@@ -278,7 +280,7 @@
         if table is not None:
             self.attrs = Attrs(self.cat, table, writable)
 
-        if self.id is not None and self.c_mapinfo is not None and read:
+        if self.id and self.c_mapinfo is not None and read:
             self.read()
 
     @property
@@ -295,8 +297,9 @@
     def read(self):
         """Read and set the coordinates of the centroid from the vector map,
         using the centroid_id and calling the Vect_read_line C function"""
-        ftype, c_points, c_cats = c_read_line(self.id, self.c_mapinfo,
-                                              self.c_points, self.c_cats)
+        self.id, ftype, c_points, c_cats = c_read_line(self.id, self.c_mapinfo,
+                                                       self.c_points,
+                                                       self.c_cats)
 
 
 class Point(Geo):
@@ -330,7 +333,7 @@
 
     def __init__(self, x=0, y=0, z=None, **kargs):
         super(Point, self).__init__(**kargs)
-        if self.id is not None:
+        if self.id and self.c_mapinfo:
             self.read()
         else:
             self.is2D = True if z is None else False
@@ -378,11 +381,27 @@
         return "Point(%s)" % ', '.join(['%f' % coor for coor in self.coords()])
 
     def __eq__(self, pnt):
+        """Return True if the coordinates are the same.
+
+        >>> p0 = Point()
+        >>> p1 = Point()
+        >>> p2 = Point(1, 1)
+        >>> p0 == p1
+        True
+        >>> p1 == p2
+        False
+        """
         if isinstance(pnt, Point):
             return pnt.coords() == self.coords()
 
         return Point(*pnt).coords() == self.coords()
 
+    def __ne__(self, other):
+        return not self == other
+
+    # Restore Python 2 hashing beaviour on Python 3
+    __hash__ = object.__hash__
+
     def coords(self):
         """Return a tuple with the point coordinates. ::
 
@@ -619,7 +638,7 @@
     def append(self, pnt):
         """Appends one point to the end of a line, using the
         ``Vect_append_point`` C function.
-        
+
         :param pnt: the point to add to line
         :type pnt: a Point object or a tuple with the coordinates
 
@@ -739,11 +758,11 @@
         return libvect.Vect_line_geodesic_length(self.c_points)
 
     def distance(self, pnt):
-        """Calculate the distance between line and a point. 
-        
+        """Calculate the distance between line and a point.
+
         :param pnt: the point to calculate distance
         :type pnt: a Point object or a tuple with the coordinates
-        
+
         Return a tuple with:
 
             * the closest point on the line,
@@ -847,7 +866,7 @@
     def prune_thresh(self, threshold):
         """Remove points in threshold, using the ``Vect_line_prune_thresh``
         C funtion.
-        
+
         :param threshold: the threshold value where prune points
         :type threshold: num
 
@@ -867,7 +886,7 @@
     def remove(self, pnt):
         """Delete point at given index and move all points above down, using
         `Vect_line_delete_point` C function.
-        
+
         :param pnt: the point to remove
         :type pnt: a Point object or a tuple with the coordinates
 
@@ -1042,11 +1061,76 @@
         """
         libvect.Vect_reset_line(self.c_points)
 
+    def nodes(self):
+        """Return the nodes in the line"""
+        if self.is_with_topology():
+            n1 = ctypes.c_int()
+            n2 = ctypes.c_int()
+            libvect.Vect_get_line_nodes(self.c_mapinfo, self.id,
+                                        ctypes.byref(n1),
+                                        ctypes.byref(n2))
+            return (Node(n1.value, self.c_mapinfo),
+                    Node(n2.value, self.c_mapinfo))
 
+
 class Node(object):
-    pass
+    def __init__(self, v_id, c_mapinfo):
+        self.id = v_id  # vector id
+        self.c_mapinfo = c_mapinfo
+        self.is2D = bool(libvect.Vect_is_3d(self.c_mapinfo) != 1)
+        self.nlines = libvect.Vect_get_node_n_lines(self.c_mapinfo, self.id)
 
+    def __len__(self):
+        return self.nlines
 
+    def __iter__(self):
+        return self.ilines()
+
+    def __repr__(self):
+        return "Node(%d)" % self.id
+
+    def coords(self):
+        """Return a tuple with the node coordinates."""
+        x = ctypes.c_double()
+        y = ctypes.c_double()
+        z = ctypes.c_double()
+        libvect.Vect_get_node_coor(self.c_mapinfo, self.id, ctypes.byref(x),
+                                   ctypes.byref(y), ctypes.byref(z))
+        return (x.value, y.value) if self.is2D else (x.value, y.value, z.value)
+
+    def ilines(self, only_in=False, only_out=False):
+        """Return a generator with all lines id connected to a node.
+        The line id is negative if line is ending on the node and positive if
+        starting from the node.
+
+        :param only_in: Return only the lines that are ending in the node
+        :type only_in: bool
+        :param only_out: Return only the lines that are starting in the node
+        :type only_out: bool
+        """
+        for iline in range(self.nlines):
+            lid = libvect.Vect_get_node_line(self.c_mapinfo, self.id, iline)
+            if (not only_in and lid > 0) or (not only_out and lid < 0):
+                yield lid
+
+    def lines(self, only_in=False, only_out=False):
+        """Return a generator with all lines connected to a node.
+
+        :param only_in: Return only the lines that are ending in the node
+        :type only_in: bool
+        :param only_out: Return only the lines that are starting in the node
+        :type only_out: bool
+        """
+        for iline in self.ilines(only_in, only_out):
+            yield Line(id=abs(iline), c_mapinfo=self.c_mapinfo)
+
+    def angles(self):
+        """Return a generator with all lines angles in a node."""
+        for iline in range(self.nlines):
+            yield libvect.Vect_get_node_line_angle(self.c_mapinfo,
+                                                   self.id, iline)
+
+
 class Boundary(Line):
     """
     """
@@ -1087,7 +1171,7 @@
         """Return left value
 
         :param idonly: True to return only the cat of feature
-        :type idonly: bool        
+        :type idonly: bool
         """
         return self._get_centroid(self.left_id, idonly)
 
@@ -1131,7 +1215,7 @@
         Centoid(0.000000, 10.000000)
         >>> from grass.pygrass.vector import VectorTopo
         >>> geo = VectorTopo('geology')
-        >>> geo.open()
+        >>> geo.open(mode='r')
         >>> centroid = Centroid(v_id=1, c_mapinfo=geo.c_mapinfo)
         >>> centroid
         Centoid(893202.874416, 297339.312795)
@@ -1235,9 +1319,6 @@
 
     def perimeter(self):
         """Return the perimeter value of an Isle.
-        ::
-            double Vect_area_perimeter()
-
         """
         border = self.points()
         return libvect.Vect_area_perimeter(border.c_points)
@@ -1280,30 +1361,31 @@
         .. warning::
 
             Not implemented yet.
+
         """
         pass
 
 
 class Area(Geo):
     """
-     'Vect_build_line_area',
-     'Vect_find_area',
-     'Vect_get_area_box',
-     'Vect_get_area_points_geos',
-     'Vect_get_centroid_area',
+    Vect_build_line_area,
+    Vect_find_area,
+    Vect_get_area_box,
+    Vect_get_area_points_geos,
+    Vect_get_centroid_area,
 
-     'Vect_get_isle_area',
-     'Vect_get_line_areas',
-     'Vect_get_num_areas',
-     'Vect_get_point_in_area',
-     'Vect_isle_find_area',
-     'Vect_point_in_area',
-     'Vect_point_in_area_outer_ring',
+    Vect_get_isle_area,
+    Vect_get_line_areas,
+    Vect_get_num_areas,
+    Vect_get_point_in_area,
+    Vect_isle_find_area,
+    Vect_point_in_area,
+    Vect_point_in_area_outer_ring,
 
-     'Vect_read_area_geos',
-     'Vect_remove_small_areas',
-     'Vect_select_areas_by_box',
-     'Vect_select_areas_by_polygon']
+    Vect_read_area_geos,
+    Vect_remove_small_areas,
+    Vect_select_areas_by_box,
+    Vect_select_areas_by_polygon
     """
     # geometry type
     gtype = libvect.GV_AREA
@@ -1352,12 +1434,13 @@
         :type centroid: a Centroid object
         """
         centroid_id = libvect.Vect_get_area_centroid(self.c_mapinfo, self.id)
-        if centroid:
-            centroid.id = centroid_id
-            centroid.read()
-            return centroid
-        return Centroid(v_id=centroid_id, c_mapinfo=self.c_mapinfo,
-                        area_id=self.id)
+        if centroid_id:
+            if centroid:
+                centroid.id = centroid_id
+                centroid.read()
+                return centroid
+            return Centroid(v_id=centroid_id, c_mapinfo=self.c_mapinfo,
+                            area_id=self.id)
 
     def num_isles(self):
         return libvect.Vect_get_area_num_isles(self.c_mapinfo, self.id)
@@ -1371,8 +1454,7 @@
 
     def area(self):
         """Returns area of area without areas of isles.
-        double Vect_get_area_area (const struct Map_info *Map, int area)
-
+        double Vect_get_area_area (const struct Map_info \*Map, int area)
         """
         return libvect.Vect_get_area_area(self.c_mapinfo, self.id)
 
@@ -1433,8 +1515,8 @@
     def boundaries(self, ilist=False):
         """Creates list of boundaries for given area.
 
-        int Vect_get_area_boundaries(const struct Map_info *Map,
-                                     int area, struct ilist *List)
+        int Vect_get_area_boundaries(const struct Map_info \*Map,
+                                     int area, struct ilist \*List)
         """
         ilst = Ilist()
         libvect.Vect_get_area_boundaries(self.c_mapinfo, self.id,
@@ -1456,14 +1538,14 @@
     def get_first_cat(self):
         """Find FIRST category of given field and area.
 
-        int Vect_get_area_cat(const struct Map_info *Map, int area, int field)
+        int Vect_get_area_cat(const struct Map_info \*Map, int area, int field)
         """
         pass
 
     def contain_pnt(self, pnt, bbox=None):
         """Check if point is in area.
 
-        :param pnt: the point to analyze 
+        :param pnt: the point to analyze
         :type pnt: a Point object or a tuple with the coordinates
         :param bbox: the bounding box where run the analysis
         :type bbox: a Bbox object
@@ -1476,7 +1558,7 @@
     def perimeter(self):
         """Calculate area perimeter.
 
-        double Vect_area_perimeter (const struct line_pnts *Points)
+        :return: double Vect_area_perimeter (const struct line_pnts \*Points)
 
         """
         border = self.get_points()
@@ -1486,8 +1568,9 @@
         self.boundary = self.get_points(line)
         self.centroid = self.get_centroid(centroid)
         #self.isles = self.get_isles(isles)
-        libvect.Vect_read_line(self.c_mapinfo, None, self.c_cats,
-                               self.centroid.id)
+        if self.centroid:
+            libvect.Vect_read_line(self.c_mapinfo, None, self.c_cats,
+                                   self.centroid.id)
 
 
 #
@@ -1547,7 +1630,7 @@
         raise IndexError('Index out of range')
     if feature_id > 0:
         ftype = libvect.Vect_read_line(c_mapinfo, c_points, c_cats, feature_id)
-        return ftype, c_points, c_cats
+        return feature_id, ftype, c_points, c_cats
     else:
         raise ValueError('The index must be >0, %r given.' % feature_id)
 
@@ -1558,8 +1641,8 @@
     """
     c_points = c_points if c_points else ctypes.pointer(libvect.line_pnts())
     c_cats = c_cats if c_cats else ctypes.pointer(libvect.line_cats())
-    ftype, c_points, c_cats = c_read_line(feature_id, c_mapinfo,
-                                          c_points, c_cats)
+    feature_id, ftype, c_points, c_cats = c_read_line(feature_id, c_mapinfo,
+                                                      c_points, c_cats)
     if GV_TYPE[ftype]['obj'] is not None:
         return GV_TYPE[ftype]['obj'](v_id=feature_id, c_mapinfo=c_mapinfo,
                                      c_points=c_points, c_cats=c_cats,

Modified: grass/branches/releasebranch_7_0/lib/python/pygrass/vector/table.py
===================================================================
--- grass/branches/releasebranch_7_0/lib/python/pygrass/vector/table.py	2014-10-26 16:12:08 UTC (rev 62381)
+++ grass/branches/releasebranch_7_0/lib/python/pygrass/vector/table.py	2014-10-26 16:42:33 UTC (rev 62382)
@@ -4,8 +4,6 @@
 
 @author: pietro
 
-
-
 """
 from __future__ import (nested_scopes, generators, division, absolute_import,
                         with_statement, print_function, unicode_literals)
@@ -38,7 +36,7 @@
 
 def get_path(path):
     """Return the full path to the database; replacing environment variable
-    with real values ::
+    with real values
 
     >>> path = '$GISDBASE/$LOCATION_NAME/$MAPSET/sqlite/sqlite.db'
     >>> new_path = get_path(path)
@@ -60,19 +58,18 @@
 
 
 class Filters(object):
-    """Help user to build a simple sql query. ::
+    """Help user to build a simple sql query.
 
-        >>> filter = Filters('table')
-        >>> filter.get_sql()
-        u'SELECT * FROM table;'
-        >>> filter.where("area<10000").get_sql()
-        u'SELECT * FROM table WHERE area<10000;'
-        >>> filter.select("cat", "area").get_sql()
-        u'SELECT cat, area FROM table WHERE area<10000;'
-        >>> filter.order_by("area").limit(10).get_sql()
-        u'SELECT cat, area FROM table WHERE area<10000 ORDER BY area LIMIT 10;'
+    >>> filter = Filters('table')
+    >>> filter.get_sql()
+    u'SELECT * FROM table;'
+    >>> filter.where("area<10000").get_sql()
+    u'SELECT * FROM table WHERE area<10000;'
+    >>> filter.select("cat", "area").get_sql()
+    u'SELECT cat, area FROM table WHERE area<10000;'
+    >>> filter.order_by("area").limit(10).get_sql()
+    u'SELECT cat, area FROM table WHERE area<10000 ORDER BY area LIMIT 10;'
 
-    ..
     """
     def __init__(self, tname):
         self.tname = tname
@@ -168,24 +165,23 @@
     It is possible to instantiate a Columns object given the table name and
     the database connection.
 
-    For a sqlite table: ::
+    For a sqlite table:
 
-        >>> import sqlite3
-        >>> path = '$GISDBASE/$LOCATION_NAME/PERMANENT/sqlite/sqlite.db'
-        >>> cols_sqlite = Columns('census',
-        ...                       sqlite3.connect(get_path(path)))
-        >>> cols_sqlite.tname
-        u'census'
+    >>> import sqlite3
+    >>> path = '$GISDBASE/$LOCATION_NAME/PERMANENT/sqlite/sqlite.db'
+    >>> cols_sqlite = Columns('census',
+    ...                       sqlite3.connect(get_path(path)))
+    >>> cols_sqlite.tname
+    u'census'
 
-    For a postgreSQL table: ::
+    For a postgreSQL table:
 
-        >>> import psycopg2 as pg                              #doctest: +SKIP
-        >>> cols_pg = Columns('boundary_municp_pg',
-        ...                   pg.connect('host=localhost dbname=grassdb')) #doctest: +SKIP
-        >>> cols_pg.tname #doctest: +SKIP
-        'boundary_municp_pg'                                   #doctest: +SKIP
+    >>> import psycopg2 as pg                              #doctest: +SKIP
+    >>> cols_pg = Columns('boundary_municp_pg',
+    ...                   pg.connect('host=localhost dbname=grassdb')) #doctest: +SKIP
+    >>> cols_pg.tname #doctest: +SKIP
+    'boundary_municp_pg'                                   #doctest: +SKIP
 
-    ..
     """
     def __init__(self, tname, connection, key='cat'):
         self.tname = tname
@@ -214,24 +210,42 @@
         return self.odict.__len__()
 
     def __eq__(self, obj):
+        """Return True if two table have the same columns.
+
+        >>> import sqlite3
+        >>> path = '$GISDBASE/$LOCATION_NAME/PERMANENT/sqlite/sqlite.db'
+        >>> connection = sqlite3.connect(get_path(path))
+        >>> cols0 = Columns('census', connection)
+        >>> cols1 = Columns('census', connection)
+        >>> cols2 = Columns('hospitals', connection)
+        >>> cols0 == cols1
+        True
+        >>> cols1 == cols2
+        False
+        """
         return obj.tname == self.tname and obj.odict == self.odict
 
+    def __ne__(self, other):
+        return not self == other
+
+    # Restore Python 2 hashing beaviour on Python 3
+    __hash__ = object.__hash__
+
     def is_pg(self):
-        """Return True if is a psycopg connection. ::
+        """Return True if is a psycopg connection.
 
-            >>> import sqlite3
-            >>> path = '$GISDBASE/$LOCATION_NAME/PERMANENT/sqlite/sqlite.db'
-            >>> cols_sqlite = Columns('census',
-            ...                       sqlite3.connect(get_path(path)))
-            >>> cols_sqlite.is_pg()
-            False
-            >>> import psycopg2 as pg #doctest: +SKIP
-            >>> cols_pg = Columns('boundary_municp_pg',
-            ...                   pg.connect('host=localhost dbname=grassdb')) #doctest: +SKIP
-            >>> cols_pg.is_pg() #doctest: +SKIP
-            True
+        >>> import sqlite3
+        >>> path = '$GISDBASE/$LOCATION_NAME/PERMANENT/sqlite/sqlite.db'
+        >>> cols_sqlite = Columns('census',
+        ...                       sqlite3.connect(get_path(path)))
+        >>> cols_sqlite.is_pg()
+        False
+        >>> import psycopg2 as pg #doctest: +SKIP
+        >>> cols_pg = Columns('boundary_municp_pg',
+        ...                   pg.connect('host=localhost dbname=grassdb')) #doctest: +SKIP
+        >>> cols_pg.is_pg() #doctest: +SKIP
+        True
 
-        ..
         """
         return hasattr(self.conn, 'xid')
 
@@ -266,7 +280,7 @@
                 odict[name] = ctype
             self.odict = odict
         values = ','.join(['?', ] * self.__len__())
-        kv = ','.join(['%s=?' % k for k in self.odict.keys()])
+        kv = ','.join(['%s=?' % k for k in self.odict.keys() if k != self.key])
         where = "%s=?" % self.key
         self.insert_str = sql.INSERT.format(tname=self.tname, values=values)
         self.update_str = sql.UPDATE_WHERE.format(tname=self.tname,
@@ -274,19 +288,19 @@
 
     def sql_descr(self, remove=None):
         """Return a string with description of columns.
-           Remove it is used to remove a columns.::
+        Remove it is used to remove a columns.
 
-            >>> import sqlite3
-            >>> path = '$GISDBASE/$LOCATION_NAME/PERMANENT/sqlite/sqlite.db'
-            >>> cols_sqlite = Columns('census',
-            ...                       sqlite3.connect(get_path(path)))
-            >>> cols_sqlite.sql_descr()                   # doctest: +ELLIPSIS
-            u'cat integer, OBJECTID integer, AREA double precision, ...'
-            >>> import psycopg2 as pg                         # doctest: +SKIP
-            >>> cols_pg = Columns('boundary_municp_pg',
-            ...                   pg.connect('host=localhost dbname=grassdb')) # doctest: +SKIP
-            >>> cols_pg.sql_descr()                 # doctest: +ELLIPSIS +SKIP
-            'cat int4, objectid int4, area float8, perimeter float8, ...'
+        >>> import sqlite3
+        >>> path = '$GISDBASE/$LOCATION_NAME/PERMANENT/sqlite/sqlite.db'
+        >>> cols_sqlite = Columns('census',
+        ...                       sqlite3.connect(get_path(path)))
+        >>> cols_sqlite.sql_descr()                   # doctest: +ELLIPSIS
+        u'cat integer, OBJECTID integer, AREA double precision, ...'
+        >>> import psycopg2 as pg                         # doctest: +SKIP
+        >>> cols_pg = Columns('boundary_municp_pg',
+        ...                   pg.connect('host=localhost dbname=grassdb')) # doctest: +SKIP
+        >>> cols_pg.sql_descr()                 # doctest: +ELLIPSIS +SKIP
+        'cat int4, objectid int4, area float8, perimeter float8, ...'
         """
         if remove:
             return ', '.join(['%s %s' % (key, val) for key, val in self.items()
@@ -296,43 +310,39 @@
                               for key, val in self.items()])
 
     def types(self):
-        """Return a list with the column types. ::
+        """Return a list with the column types.
 
-            >>> import sqlite3
-            >>> path = '$GISDBASE/$LOCATION_NAME/PERMANENT/sqlite/sqlite.db'
-            >>> cols_sqlite = Columns('census',
-            ...                       sqlite3.connect(get_path(path)))
-            >>> cols_sqlite.types()                       # doctest: +ELLIPSIS
-            [u'integer', u'integer', ...]
-            >>> import psycopg2 as pg                         # doctest: +SKIP
-            >>> cols_pg = Columns('boundary_municp_pg',
-            ...                   pg.connect('host=localhost dbname=grassdb')) # doctest: +SKIP
-            >>> cols_pg.types()                     # doctest: +ELLIPSIS +SKIP
-            ['int4', 'int4', 'float8', 'float8', 'float8', ...]
+        >>> import sqlite3
+        >>> path = '$GISDBASE/$LOCATION_NAME/PERMANENT/sqlite/sqlite.db'
+        >>> cols_sqlite = Columns('census',
+        ...                       sqlite3.connect(get_path(path)))
+        >>> cols_sqlite.types()                       # doctest: +ELLIPSIS
+        [u'integer', u'integer', ...]
+        >>> import psycopg2 as pg                         # doctest: +SKIP
+        >>> cols_pg = Columns('boundary_municp_pg',
+        ...                   pg.connect('host=localhost dbname=grassdb')) # doctest: +SKIP
+        >>> cols_pg.types()                     # doctest: +ELLIPSIS +SKIP
+        ['int4', 'int4', 'float8', 'float8', 'float8', ...]
 
-
-        ..
         """
         return self.odict.values()
 
     def names(self, remove=None, unicod=True):
         """Return a list with the column names.
-           Remove it is used to remove a columns.::
+        Remove it is used to remove a columns.
 
-            >>> import sqlite3
-            >>> path = '$GISDBASE/$LOCATION_NAME/PERMANENT/sqlite/sqlite.db'
-            >>> cols_sqlite = Columns('census',
-            ...                       sqlite3.connect(get_path(path)))
-            >>> cols_sqlite.names()                      # doctest: +ELLIPSIS
-            [u'cat', u'OBJECTID', u'AREA', u'PERIMETER', ...]
-            >>> import psycopg2 as pg                         # doctest: +SKIP
-            >>> cols_pg = Columns('boundary_municp_pg',       # doctest: +SKIP
-            ...                   pg.connect('host=localhost dbname=grassdb'))
-            >>> cols_pg.names()                     # doctest: +ELLIPSIS +SKIP
-            ['cat', 'objectid', 'area', 'perimeter', ...]
+        >>> import sqlite3
+        >>> path = '$GISDBASE/$LOCATION_NAME/PERMANENT/sqlite/sqlite.db'
+        >>> cols_sqlite = Columns('census',
+        ...                       sqlite3.connect(get_path(path)))
+        >>> cols_sqlite.names()                      # doctest: +ELLIPSIS
+        [u'cat', u'OBJECTID', u'AREA', u'PERIMETER', ...]
+        >>> import psycopg2 as pg                         # doctest: +SKIP
+        >>> cols_pg = Columns('boundary_municp_pg',       # doctest: +SKIP
+        ...                   pg.connect('host=localhost dbname=grassdb'))
+        >>> cols_pg.names()                     # doctest: +ELLIPSIS +SKIP
+        ['cat', 'objectid', 'area', 'perimeter', ...]
 
-
-        ..
         """
         if remove:
             nams = self.odict.keys()
@@ -345,21 +355,20 @@
             return [str(name) for name in nams]
 
     def items(self):
-        """Return a list of tuple with column name and column type.  ::
+        """Return a list of tuple with column name and column type.
 
-            >>> import sqlite3
-            >>> path = '$GISDBASE/$LOCATION_NAME/PERMANENT/sqlite/sqlite.db'
-            >>> cols_sqlite = Columns('census',
-            ...                       sqlite3.connect(get_path(path)))
-            >>> cols_sqlite.items()                       # doctest: +ELLIPSIS
-            [(u'cat', u'integer'), ...]
-            >>> import psycopg2 as pg                         # doctest: +SKIP
-            >>> cols_pg = Columns('boundary_municp_pg',
-            ...                   pg.connect('host=localhost dbname=grassdb')) # doctest: +SKIP
-            >>> cols_pg.items()                     # doctest: +ELLIPSIS +SKIP
-            [('cat', 'int4'), ('objectid', 'int4'), ('area', 'float8'), ...]
+        >>> import sqlite3
+        >>> path = '$GISDBASE/$LOCATION_NAME/PERMANENT/sqlite/sqlite.db'
+        >>> cols_sqlite = Columns('census',
+        ...                       sqlite3.connect(get_path(path)))
+        >>> cols_sqlite.items()                       # doctest: +ELLIPSIS
+        [(u'cat', u'integer'), ...]
+        >>> import psycopg2 as pg                         # doctest: +SKIP
+        >>> cols_pg = Columns('boundary_municp_pg',
+        ...                   pg.connect('host=localhost dbname=grassdb')) # doctest: +SKIP
+        >>> cols_pg.items()                     # doctest: +ELLIPSIS +SKIP
+        [('cat', 'int4'), ('objectid', 'int4'), ('area', 'float8'), ...]
 
-        ..
         """
         return self.odict.items()
 
@@ -370,26 +379,24 @@
         :type col_name: str
         :param col_type: the tipe of column to add
         :type col_type: str
-        ::
 
-            >>> import sqlite3
-            >>> path = '$GISDBASE/$LOCATION_NAME/$MAPSET/sqlite/sqlite.db'
-            >>> from grass.pygrass.functions import copy, remove
-            >>> copy('census','mycensus','vect')
-            >>> cols_sqlite = Columns('mycensus',
-            ...                       sqlite3.connect(get_path(path)))
-            >>> cols_sqlite.add(['n_pizza'], ['INT'])
-            >>> 'n_pizza' in cols_sqlite
-            True
-            >>> import psycopg2 as pg                         # doctest: +SKIP
-            >>> cols_pg = Columns('boundary_municp_pg',
-            ...                   pg.connect('host=localhost dbname=grassdb'))  #doctest: +SKIP
-            >>> cols_pg.add('n_pizza', 'INT')                 # doctest: +SKIP
-            >>> 'n_pizza' in cols_pg                          # doctest: +SKIP
-            True
-            >>> remove('mycensus', 'vect')
+        >>> import sqlite3
+        >>> path = '$GISDBASE/$LOCATION_NAME/$MAPSET/sqlite/sqlite.db'
+        >>> from grass.pygrass.functions import copy, remove
+        >>> copy('census','mycensus','vect')
+        >>> cols_sqlite = Columns('mycensus',
+        ...                       sqlite3.connect(get_path(path)))
+        >>> cols_sqlite.add(['n_pizza'], ['INT'])
+        >>> 'n_pizza' in cols_sqlite
+        True
+        >>> import psycopg2 as pg                         # doctest: +SKIP
+        >>> cols_pg = Columns('boundary_municp_pg',
+        ...                   pg.connect('host=localhost dbname=grassdb'))  #doctest: +SKIP
+        >>> cols_pg.add('n_pizza', 'INT')                 # doctest: +SKIP
+        >>> 'n_pizza' in cols_pg                          # doctest: +SKIP
+        True
+        >>> remove('mycensus', 'vect')
 
-        ..
         """
         def check_col(col_type):
             """Check the column type if it is supported by GRASS
@@ -431,34 +438,32 @@
         :type old_name: str
         :param new_name: the name of new column
         :type new_name: str
-        ::
 
-            >>> import sqlite3
-            >>> path = '$GISDBASE/$LOCATION_NAME/$MAPSET/sqlite/sqlite.db'
-            >>> from grass.pygrass.functions import copy, remove
-            >>> copy('census','mycensus','vect')
-            >>> cols_sqlite = Columns('mycensus',
-            ...                       sqlite3.connect(get_path(path)))
-            >>> cols_sqlite.add(['n_pizza'], ['INT'])
-            >>> 'n_pizza' in cols_sqlite
-            True
-            >>> cols_sqlite.rename('n_pizza', 'n_pizzas')  # doctest: +ELLIPSIS
-            >>> 'n_pizza' in cols_sqlite
-            False
-            >>> 'n_pizzas' in cols_sqlite
-            True
+        >>> import sqlite3
+        >>> path = '$GISDBASE/$LOCATION_NAME/$MAPSET/sqlite/sqlite.db'
+        >>> from grass.pygrass.functions import copy, remove
+        >>> copy('census','mycensus','vect')
+        >>> cols_sqlite = Columns('mycensus',
+        ...                       sqlite3.connect(get_path(path)))
+        >>> cols_sqlite.add(['n_pizza'], ['INT'])
+        >>> 'n_pizza' in cols_sqlite
+        True
+        >>> cols_sqlite.rename('n_pizza', 'n_pizzas')  # doctest: +ELLIPSIS
+        >>> 'n_pizza' in cols_sqlite
+        False
+        >>> 'n_pizzas' in cols_sqlite
+        True
 
-            >>> import psycopg2 as pg                         # doctest: +SKIP
-            >>> cols_pg = Columns('boundary_municp_pg',
-            ...                   pg.connect('host=localhost dbname=grassdb')) # doctest: +SKIP
-            >>> cols_pg.rename('n_pizza', 'n_pizzas')         # doctest: +SKIP
-            >>> 'n_pizza' in cols_pg                          # doctest: +SKIP
-            False
-            >>> 'n_pizzas' in cols_pg                         # doctest: +SKIP
-            True
-            >>> remove('mycensus', 'vect')
+        >>> import psycopg2 as pg                         # doctest: +SKIP
+        >>> cols_pg = Columns('boundary_municp_pg',
+        ...                   pg.connect('host=localhost dbname=grassdb')) # doctest: +SKIP
+        >>> cols_pg.rename('n_pizza', 'n_pizzas')         # doctest: +SKIP
+        >>> 'n_pizza' in cols_pg                          # doctest: +SKIP
+        False
+        >>> 'n_pizzas' in cols_pg                         # doctest: +SKIP
+        True
+        >>> remove('mycensus', 'vect')
 
-        ..
         """
         cur = self.conn.cursor()
         if self.is_pg():
@@ -487,31 +492,30 @@
         :type col_name: str
         :param new_type: the new type of column
         :type new_type: str
-        ::
 
-            >>> import sqlite3
-            >>> path = '$GISDBASE/$LOCATION_NAME/$MAPSET/sqlite/sqlite.db'
-            >>> from grass.pygrass.functions import copy, remove
-            >>> copy('census','mycensus','vect')
-            >>> cols_sqlite = Columns('mycensus',
-            ...                       sqlite3.connect(get_path(path)))
-            >>> cols_sqlite.add(['n_pizzas'], ['INT'])
-            >>> cols_sqlite.cast('n_pizzas', 'float8')  # doctest: +ELLIPSIS
-            Traceback (most recent call last):
-              ...
-            DBError: u'SQLite does not support to cast columns.'
-            >>> import psycopg2 as pg                         # doctest: +SKIP
-            >>> cols_pg = Columns('boundary_municp_pg',
-            ...                   pg.connect('host=localhost dbname=grassdb')) # doctest: +SKIP
-            >>> cols_pg.cast('n_pizzas', 'float8')            # doctest: +SKIP
-            >>> cols_pg['n_pizzas']                           # doctest: +SKIP
-            'float8'
-            >>> remove('mycensus', 'vect')
+        >>> import sqlite3
+        >>> path = '$GISDBASE/$LOCATION_NAME/$MAPSET/sqlite/sqlite.db'
+        >>> from grass.pygrass.functions import copy, remove
+        >>> copy('census','mycensus','vect')
+        >>> cols_sqlite = Columns('mycensus',
+        ...                       sqlite3.connect(get_path(path)))
+        >>> cols_sqlite.add(['n_pizzas'], ['INT'])
+        >>> cols_sqlite.cast('n_pizzas', 'float8')  # doctest: +ELLIPSIS
+        Traceback (most recent call last):
+          ...
+        DBError: SQLite does not support to cast columns.
+        >>> import psycopg2 as pg                         # doctest: +SKIP
+        >>> cols_pg = Columns('boundary_municp_pg',
+        ...                   pg.connect('host=localhost dbname=grassdb')) # doctest: +SKIP
+        >>> cols_pg.cast('n_pizzas', 'float8')            # doctest: +SKIP
+        >>> cols_pg['n_pizzas']                           # doctest: +SKIP
+        'float8'
+        >>> remove('mycensus', 'vect')
 
-            .. warning ::
+        .. warning ::
 
-               It is not possible to cast a column with sqlite
-            ..
+           It is not possible to cast a column with sqlite
+
         """
         if self.is_pg():
             cur = self.conn.cursor()
@@ -529,27 +533,25 @@
 
         :param col_name: the name of column to remove
         :type col_name: str
-        ::
 
-            >>> import sqlite3
-            >>> path = '$GISDBASE/$LOCATION_NAME/$MAPSET/sqlite/sqlite.db'
-            >>> from grass.pygrass.functions import copy, remove
-            >>> copy('census','mycensus','vect')
-            >>> cols_sqlite = Columns('mycensus',
-            ...                       sqlite3.connect(get_path(path)))
-            >>> cols_sqlite.drop('CHILD')                 # doctest: +ELLIPSIS
-            >>> 'CHILD' in cols_sqlite
-            False
+        >>> import sqlite3
+        >>> path = '$GISDBASE/$LOCATION_NAME/$MAPSET/sqlite/sqlite.db'
+        >>> from grass.pygrass.functions import copy, remove
+        >>> copy('census','mycensus','vect')
+        >>> cols_sqlite = Columns('mycensus',
+        ...                       sqlite3.connect(get_path(path)))
+        >>> cols_sqlite.drop('CHILD')                 # doctest: +ELLIPSIS
+        >>> 'CHILD' in cols_sqlite
+        False
 
-            >>> import psycopg2 as pg                         # doctest: +SKIP
-            >>> cols_pg = Columns('boundary_municp_pg',
-            ...                   pg.connect('host=localhost dbname=grassdb')) # doctest: +SKIP
-            >>> cols_pg.drop('CHILD') # doctest: +SKIP
-            >>> 'CHILD' in cols_pg # doctest: +SKIP
-            False
-            >>> remove('mycensus','vect')
+        >>> import psycopg2 as pg                         # doctest: +SKIP
+        >>> cols_pg = Columns('boundary_municp_pg',
+        ...                   pg.connect('host=localhost dbname=grassdb')) # doctest: +SKIP
+        >>> cols_pg.drop('CHILD') # doctest: +SKIP
+        >>> 'CHILD' in cols_pg # doctest: +SKIP
+        False
+        >>> remove('mycensus','vect')
 
-            ..
         """
         cur = self.conn.cursor()
         if self.is_pg():
@@ -573,50 +575,48 @@
     """Define a Link between vector map and the attributes table.
 
     It is possible to define a Link object or given all the information
-    (layer, name, table name, key, database, driver): ::
+    (layer, name, table name, key, database, driver):
 
-        >>> link = Link(1, 'link0', 'census', 'cat',
-        ...             '$GISDBASE/$LOCATION_NAME/PERMANENT/sqlite/sqlite.db', 'sqlite')
-        >>> link.number                                       # doctest: +SKIP
-        1
-        >>> link.name
-        'link0'
-        >>> link.table_name
-        'census'
-        >>> link.key
-        'cat'
-        >>> link.database
-        '$GISDBASE/$LOCATION_NAME/PERMANENT/sqlite/sqlite.db'
-        >>> link.driver
-        'sqlite'
-        >>> link
-        Link(1, link0, sqlite)
+    >>> link = Link(1, 'link0', 'census', 'cat',
+    ...             '$GISDBASE/$LOCATION_NAME/PERMANENT/sqlite/sqlite.db', 'sqlite')
+    >>> link.layer
+    1
+    >>> link.name
+    'link0'
+    >>> link.table_name
+    'census'
+    >>> link.key
+    'cat'
+    >>> link.database
+    '$GISDBASE/$LOCATION_NAME/PERMANENT/sqlite/sqlite.db'
+    >>> link.driver
+    'sqlite'
+    >>> link
+    Link(1, link0, sqlite)
 
 
-    It is possible to change parameters with: ::
+    It is possible to change parameters with:
 
-        >>> link.driver = 'pg'                                # doctest: +SKIP
-        >>> link.driver                                       # doctest: +SKIP
-        'pg'
-        >>> link.driver = 'postgres'                # doctest: +ELLIPSIS +SKIP
-        Traceback (most recent call last):
-          ...
-        TypeError: Driver not supported, use: sqlite, pg.
-        >>> link.driver                                       # doctest: +SKIP
-        'pg'
-        >>> link.number = 0                         # doctest: +ELLIPSIS +SKIP
-        Traceback (most recent call last):
-          ...
-        TypeError: Number must be positive and greater than 0.
+    >>> link.driver = 'pg'                                # doctest: +SKIP
+    >>> link.driver                                       # doctest: +SKIP
+    'pg'
+    >>> link.driver = 'postgres'                # doctest: +ELLIPSIS +SKIP
+    Traceback (most recent call last):
+      ...
+    TypeError: Driver not supported, use: sqlite, pg.
+    >>> link.driver                                       # doctest: +SKIP
+    'pg'
+    >>> link.number = 0                         # doctest: +ELLIPSIS +SKIP
+    Traceback (most recent call last):
+      ...
+    TypeError: Number must be positive and greater than 0.
 
 
     Or given a c_fieldinfo object that is a ctypes pointer to the field_info C
     struct. ::
 
-        >>> link = Link(c_fieldinfo = ctypes.pointer(libvect.field_info()))
+    >>> link = Link(c_fieldinfo = ctypes.pointer(libvect.field_info()))
 
-
-    ..
     """
     def _get_layer(self):
         return self.c_fieldinfo.contents.number
@@ -697,29 +697,47 @@
         return "Link(%d, %s, %s)" % (self.layer, self.name, self.driver)
 
     def __eq__(self, link):
+        """Return True if two Link instance have the same parameters.
+
+        >>> l0 = Link(1, 'link0', 'census', 'cat',
+        ...           '$GISDBASE/$LOCATION_NAME/PERMANENT/sqlite/sqlite.db', 'sqlite')
+        >>> l1 = Link(1, 'link0', 'census', 'cat',
+        ...           '$GISDBASE/$LOCATION_NAME/PERMANENT/sqlite/sqlite.db', 'sqlite')
+        >>> l2 = Link(2, 'link0', 'census', 'cat',
+        ...           '$GISDBASE/$LOCATION_NAME/PERMANENT/sqlite/sqlite.db', 'sqlite')
+        >>> l0 == l1
+        True
+        >>> l1 == l2
+        False
+        """
         attrs = ['layer', 'name', 'table_name', 'key', 'driver']
         for attr in attrs:
             if getattr(self, attr) != getattr(link, attr):
                 return False
         return True
 
+    def __ne__(self, other):
+        return not self == other
+
+    # Restore Python 2 hashing beaviour on Python 3
+    __hash__ = object.__hash__
+
     def connection(self):
-        """Return a connection object. ::
+        """Return a connection object.
 
-            >>> link = Link(1, 'link0', 'census', 'cat',
-            ...             '$GISDBASE/$LOCATION_NAME/PERMANENT/sqlite/sqlite.db',
-            ...             'sqlite')
-            >>> conn = link.connection()
-            >>> cur = conn.cursor()
-            >>> cur.execute("SELECT cat,TOTAL_POP,PERIMETER FROM %s" %
-            ...             link.table_name)              # doctest: +ELLIPSIS
-            <sqlite3.Cursor object at ...>
-            >>> cur.fetchone()
-            (1, 44, 757.669)
-            >>> cur.close()
-            >>> conn.close()
+        >>> link = Link(1, 'link0', 'census', 'cat',
+        ...             '$GISDBASE/$LOCATION_NAME/PERMANENT/sqlite/sqlite.db',
+        ...             'sqlite')
+        >>> conn = link.connection()
+        >>> cur = conn.cursor()
+        >>> cur.execute("SELECT cat,TOTAL_POP,PERIMETER FROM %s" %
+        ...             link.table_name)              # doctest: +ELLIPSIS
+        <sqlite3.Cursor object at ...>
+        >>> cur.fetchone()
+        (1, 44, 757.669)
+        >>> cur.close()
+        >>> conn.close()
 
-        ...
         """
         if self.driver == 'sqlite':
             import sqlite3
@@ -748,38 +766,36 @@
             raise TypeError(str_err)
 
     def table(self):
-        """Return a Table object. ::
+        """Return a Table object.
 
-            >>> link = Link(1, 'link0', 'census', 'cat',
-            ...             '$GISDBASE/$LOCATION_NAME/PERMANENT/sqlite/sqlite.db',
-            ...             'sqlite')
-            >>> table = link.table()
-            >>> table.filters.select('cat', 'TOTAL_POP', 'PERIMETER')
-            Filters(u'SELECT cat, TOTAL_POP, PERIMETER FROM census;')
-            >>> cur = table.execute()
-            >>> cur.fetchone()
-            (1, 44, 757.669)
-            >>> cur.close()
+        >>> link = Link(1, 'link0', 'census', 'cat',
+        ...             '$GISDBASE/$LOCATION_NAME/PERMANENT/sqlite/sqlite.db',
+        ...             'sqlite')
+        >>> table = link.table()
+        >>> table.filters.select('cat', 'TOTAL_POP', 'PERIMETER')
+        Filters(u'SELECT cat, TOTAL_POP, PERIMETER FROM census;')
+        >>> cur = table.execute()
+        >>> cur.fetchone()
+        (1, 44, 757.669)
+        >>> cur.close()
 
-        ..
         """
         return Table(self.table_name, self.connection(), self.key)
 
     def info(self):
-        """Print information of the link. ::
+        """Print information of the link.
 
-            >>> link = Link(1, 'link0', 'census', 'cat',
-            ...             '$GISDBASE/$LOCATION_NAME/PERMANENT/sqlite/sqlite.db',
-            ...             'sqlite')
-            >>> link.info()
-            layer:     1
-            name:      link0
-            table:     census
-            key:       cat
-            database:  $GISDBASE/$LOCATION_NAME/PERMANENT/sqlite/sqlite.db
-            driver:    sqlite
+        >>> link = Link(1, 'link0', 'census', 'cat',
+        ...             '$GISDBASE/$LOCATION_NAME/PERMANENT/sqlite/sqlite.db',
+        ...             'sqlite')
+        >>> link.info()
+        layer:     1
+        name:      link0
+        table:     census
+        key:       cat
+        database:  $GISDBASE/$LOCATION_NAME/PERMANENT/sqlite/sqlite.db
+        driver:    sqlite
 
-        ..
         """
         print("layer:    ", self.layer)
         print("name:     ", self.name)
@@ -790,20 +806,20 @@
 
 
 class DBlinks(object):
-    """Interface containing link to the table DB. ::
+    """Interface containing link to the table DB.
 
-        >>> from grass.pygrass.vector import VectorTopo
-        >>> cens = VectorTopo('census')
-        >>> cens.open()
-        >>> dblinks = DBlinks(cens.c_mapinfo)
-        >>> dblinks
-        DBlinks([Link(1, census, sqlite)])
-        >>> dblinks[0]
-        Link(1, census, sqlite)
-        >>> dblinks['census']
-        Link(1, census, sqlite)
+    >>> from grass.pygrass.vector import VectorTopo
+    >>> cens = VectorTopo('census')
+    >>> cens.open(mode='r')
+    >>> dblinks = DBlinks(cens.c_mapinfo)
+    >>> dblinks
+    DBlinks([Link(1, census, sqlite)])
+    >>> dblinks[0]
+    Link(1, census, sqlite)
+    >>> dblinks['census']
+    Link(1, census, sqlite)
+    >>> cens.close()
 
-    ..
     """
     def __init__(self, c_mapinfo):
         self.c_mapinfo = c_mapinfo
@@ -873,7 +889,7 @@
 
         >>> from grass.pygrass.vector import VectorTopo
         >>> municip = VectorTopo('census')
-        >>> municip.open()
+        >>> municip.open(mode='r')
         >>> dblinks = DBlinks(municip.c_mapinfo)
         >>> dblinks
         DBlinks([Link(1, census, sqlite)])
@@ -900,7 +916,7 @@
 
         >>> from grass.pygrass.vector import VectorTopo
         >>> municip = VectorTopo('census')
-        >>> municip.open()
+        >>> municip.open(mode='r')
         >>> dblinks = DBlinks(municip.c_mapinfo)
         >>> dblinks
         DBlinks([Link(1, census, sqlite)])
@@ -926,22 +942,21 @@
 
 
 class Table(object):
-    """::
+    """
 
-        >>> import sqlite3
-        >>> path = '$GISDBASE/$LOCATION_NAME/PERMANENT/sqlite/sqlite.db'
-        >>> tab_sqlite = Table(name='census',
-        ...                    connection=sqlite3.connect(get_path(path)))
-        >>> tab_sqlite.name
-        u'census'
-        >>> import psycopg2                                   # doctest: +SKIP
-        >>> tab_pg = Table('boundary_municp_pg',
-        ...                psycopg2.connect('host=localhost dbname=grassdb',
-        ...                                 'pg'))            # doctest: +SKIP
-        >>> tab_pg.columns                          # doctest: +ELLIPSIS +SKIP
-        Columns([('cat', 'int4'), ...])
+    >>> import sqlite3
+    >>> path = '$GISDBASE/$LOCATION_NAME/PERMANENT/sqlite/sqlite.db'
+    >>> tab_sqlite = Table(name='census',
+    ...                    connection=sqlite3.connect(get_path(path)))
+    >>> tab_sqlite.name
+    u'census'
+    >>> import psycopg2                                   # doctest: +SKIP
+    >>> tab_pg = Table('boundary_municp_pg',
+    ...                psycopg2.connect('host=localhost dbname=grassdb',
+    ...                                 'pg'))            # doctest: +SKIP
+    >>> tab_pg.columns                          # doctest: +ELLIPSIS +SKIP
+    Columns([('cat', 'int4'), ...])
 
-    ..
     """
     def _get_name(self):
         """Private method to return the name of table"""
@@ -973,16 +988,15 @@
         self.filters = Filters(self.name)
 
     def __repr__(self):
-        """::
+        """
 
-            >>> import sqlite3
-            >>> path = '$GISDBASE/$LOCATION_NAME/PERMANENT/sqlite/sqlite.db'
-            >>> tab_sqlite = Table(name='census',
-            ...                    connection=sqlite3.connect(get_path(path)))
-            >>> tab_sqlite
-            Table(u'census')
+        >>> import sqlite3
+        >>> path = '$GISDBASE/$LOCATION_NAME/PERMANENT/sqlite/sqlite.db'
+        >>> tab_sqlite = Table(name='census',
+        ...                    connection=sqlite3.connect(get_path(path)))
+        >>> tab_sqlite
+        Table(u'census')
 
-        ..
         """
         return "Table(%r)" % (self.name)
 
@@ -1072,9 +1086,9 @@
 
     def exist(self, cursor=None):
         """Return True if the table already exist in the DB, False otherwise
+
         :param cursor: the cursor to connect, if None it use the cursor
-                     of connection table object
-        :type cursor: Cursor object
+                       of connection table object
         """
         cur = cursor if cursor else self.conn.cursor()
         return table_exist(cur, self.name)
@@ -1086,7 +1100,7 @@
                        more rows using a list of tuple and paramater `many`
         :type values: tuple
         :param cursor: the cursor to connect, if None it use the cursor
-                     of connection table object
+                       of connection table object
         :type cursor: Cursor object
         :param many: True to run executemany function
         :type many: bool
@@ -1096,22 +1110,23 @@
             return cur.executemany(self.columns.insert_str, values)
         return cur.execute(self.columns.insert_str, values)
 
-    def update(self, key, values, cursor=None, many=False):
-        """Update a column for each row
+    def update(self, key, values, cursor=None):
+        """Update a table row
 
-        :param key: the name of column
-        :param values: the values to insert
-        :type values: str
+        :param key: the rowid
+        :type key: int
+        :param values: the values to insert without row id.
+                       For example if we have a table with four columns:
+                       cat, c0, c1, c2 the values list should
+                       containing only c0, c1, c2 values.
+        :type values: list
         :param cursor: the cursor to connect, if None it use the cursor
-                     of connection table object
+                       of connection table object
         :type cursor: Cursor object
-        :param many: True to run executemany function
-        :type many: bool
         """
         cur = cursor if cursor else self.conn.cursor()
-        vals = list(values)
-        vals.append(key)
-        return cur.execute(self.columns.update_str, vals)
+        values.append(key)
+        return cur.execute(self.columns.update_str, values)
 
     def create(self, cols, name=None, overwrite=False, cursor=None):
         """Create a new table

Modified: grass/branches/releasebranch_7_0/lib/python/pygrass/vector/testsuite/test_geometry.py
===================================================================
--- grass/branches/releasebranch_7_0/lib/python/pygrass/vector/testsuite/test_geometry.py	2014-10-26 16:12:08 UTC (rev 62381)
+++ grass/branches/releasebranch_7_0/lib/python/pygrass/vector/testsuite/test_geometry.py	2014-10-26 16:42:33 UTC (rev 62382)
@@ -8,12 +8,15 @@
 import unittest
 import numpy as np
 
+from grass.gunittest import TestCase, test
+
 import grass.lib.vector as libvect
 
-from grass.pygrass.vector.geometry import Point, Line
+from grass.pygrass.vector import VectorTopo
+from grass.pygrass.vector.geometry import Point, Line, Node
 
 
-class PointTestCase(unittest.TestCase):
+class PointTestCase(TestCase):
 
     def test_empty_init(self):
         """Test Point()"""
@@ -83,7 +86,7 @@
         pass
 
 
-class LineTestCase(unittest.TestCase):
+class LineTestCase(TestCase):
 
     def test_len(self):
         """Test __len__ magic method"""
@@ -122,9 +125,68 @@
         self.assertTupleEqual(line.get_pnt(1).coords(), vals)
 
     def test_bbox(self):
-        line = Line([(0, 0), (0, 1), (2, 1), (2, 0)])
+        """Test bbox method"""
+        line = Line([(0, 10), (0, 11), (1, 11), (1, 10)])
         bbox = line.bbox()
+        self.assertEqual(11, bbox.north)
+        self.assertEqual(10, bbox.south)
+        self.assertEqual(1, bbox.east)
+        self.assertEqual(0, bbox.west)
 
+    def test_nodes(self):
+        """Test inodes method"""
+        def nodes2tuple(nodes):
+            """Convert an iterable of nodes to a tuple of nodes id"""
+            return tuple(n.id for n in nodes)
 
+        with VectorTopo("roadsmajor", mode='r') as vect:
+            self.assertTupleEqual((206, 172), nodes2tuple(vect[284].nodes()))
+            self.assertTupleEqual((208, 206), nodes2tuple(vect[287].nodes()))
+            self.assertTupleEqual((206, 209), nodes2tuple(vect[288].nodes()))
+            self.assertTupleEqual((218, 206), nodes2tuple(vect[301].nodes()))
+
+
+
+class NodeTestCase(TestCase):
+    @classmethod
+    def setUpClass(cls):
+        cls.vect = None
+        cls.vect = VectorTopo("roadsmajor")
+        cls.vect.open('r')
+        cls.c_mapinfo = cls.vect.c_mapinfo
+
+    @classmethod
+    def tearDownClass(cls):
+        if cls.vect is not None:
+            cls.vect.close()
+            cls.c_mapinfo = None
+
+    def test_init(self):
+        """Test Node __init__"""
+        node = Node(v_id=206, c_mapinfo=self.c_mapinfo)
+        self.assertEqual(206, node.id)
+        self.assertTrue(node.is2D)
+        self.assertEqual(4, node.nlines)
+
+    def test_coords(self):
+        """Test Node coordinates"""
+        node = Node(v_id=206, c_mapinfo=self.c_mapinfo)
+        self.assertTupleEqual((620906.5786131569, 221685.65913128198),
+                              node.coords())
+
+    def test_ilines(self):
+        """Test Node coordinates"""
+        node = Node(v_id=206, c_mapinfo=self.c_mapinfo)
+        self.assertTupleEqual((288, -301, -287, 284), tuple(node.ilines()))
+        self.assertTupleEqual((-301, -287), tuple(node.ilines(only_in=True)))
+        self.assertTupleEqual((288, 284), tuple(node.ilines(only_out=True)))
+
+    def test_angles(self):
+        """Test Node angles"""
+        node = Node(v_id=206, c_mapinfo=self.c_mapinfo)
+        angles = (-3.044905185699463, -1.026218056678772,
+                  0.10362745821475983, 2.2236430644989014)
+        self.assertTupleEqual(angles, tuple(node.angles()))
+
 if __name__ == '__main__':
-    unittest.main()
+    test()

Modified: grass/branches/releasebranch_7_0/lib/python/pygrass/vector/testsuite/test_vector3d.py
===================================================================
--- grass/branches/releasebranch_7_0/lib/python/pygrass/vector/testsuite/test_vector3d.py	2014-10-26 16:12:08 UTC (rev 62381)
+++ grass/branches/releasebranch_7_0/lib/python/pygrass/vector/testsuite/test_vector3d.py	2014-10-26 16:42:33 UTC (rev 62382)
@@ -4,9 +4,10 @@
 
 @author: pietro
 """
-import unittest
 import numpy as np
 
+from grass.gunittest import TestCase, test
+
 from grass.script.core import run_command
 
 from grass.pygrass.vector import VectorTopo
@@ -26,7 +27,7 @@
     return x, y
 
 
-class VectorTopo3DTestCase(unittest.TestCase):
+class VectorTopo3DTestCase(TestCase):
 
     npoints = 10
     tmpname = "tmp_vect3d"
@@ -60,8 +61,8 @@
         """Remove the generated vector map, if exist"""
         mset = get_mapset_vector(cls.tmpname, mapset='')
         if mset:
-            run_command("g.remove", vect="%s@%s" % (cls.tmpname, mset))
+            run_command("g.remove", flags='f', type='vect', pattern=cls.tmpname)
 
 
 if __name__ == '__main__':
-    unittest.main()
+    test()



More information about the grass-commit mailing list