From 466117c60f7a7db97cc582872c621a5d8542c2d8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ma=C3=ABl=20M=C3=A9liani?= <m.meliani@neogeo-online.net>
Date: Thu, 10 Oct 2013 16:52:57 +0200
Subject: [PATCH] Cleaned up the source code and added few comments and
 docstrings.

---
 docs/mra_reference.md |   6 +-
 src/extensions.py     |   9 +-
 src/metadata.py       |  19 +++-
 src/mra.py            | 104 ++++++++---------
 src/mralogs.py        |  16 ++-
 src/pyhtml.py         |   9 +-
 src/pyxml.py          |  22 +++-
 src/server.py         | 253 +++++++++++++++++++++++++++++++-----------
 src/stores.py         |  89 +++++++--------
 src/tools.py          |  33 +++---
 src/webapp.py         |  87 +++++++--------
 11 files changed, 403 insertions(+), 244 deletions(-)

diff --git a/docs/mra_reference.md b/docs/mra_reference.md
index 6adbfc2..43e9c0d 100644
--- a/docs/mra_reference.md
+++ b/docs/mra_reference.md
@@ -1,3 +1,7 @@
 # MapServer Rest API - Rest Configuration API Reference
 
-It is in process of writing...
\ No newline at end of file
+It is in process of writing...
+
+Nevertheless, you could refer (in part) to the GeoServer Rest API documentation cause MRA was designed to offer compatibility with this model.
+
+You will also find few *docstrings* by reading the code, especially the module `server.py` which defines the URL mapping infrastructure and HTTP methods used by the REST API.
\ No newline at end of file
diff --git a/src/extensions.py b/src/extensions.py
index 562f251..e521179 100644
--- a/src/extensions.py
+++ b/src/extensions.py
@@ -24,6 +24,11 @@
 #                                                                       #
 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 
+"""
+    Module for managing add-ons (experimental).
+
+"""
+
 import sys
 import os.path
 import logging
@@ -47,9 +52,9 @@ class ExtensionManager(object):
         try:
             self.load_plugins(pkg)
         except ImportError:
-            logging.error("Could not load plugin package '%s' from %s" % (pkg, path))
+            logging.error("Could not load plugin package \"%s\" from %s" % (pkg, path))
         else:
-            logging.info("Loaded plugin package '%s' from %s" % (pkg, path))
+            logging.info("Loaded plugin package \"%s\" from %s" % (pkg, path))
         sys.path.remove(path)
 
     def extend(self, name, *args, **kwargs):
diff --git a/src/metadata.py b/src/metadata.py
index 5baaa62..f5dc65f 100644
--- a/src/metadata.py
+++ b/src/metadata.py
@@ -24,6 +24,14 @@
 #                                                                       #
 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 
+"""
+    Wrapper for managing metadata of mapfiles.
+
+    MRA requires the use of its own metadata, which appear as "mra" in mapfiles.
+    This module is to simplify their use.
+
+"""
+
 import yaml
 import contextlib
 from mapscript import MapServerError
@@ -33,8 +41,8 @@ METADATA_NAME="mra"
 def get_metadata(obj, key, *args):
     """Returns Metadata for a mapObj or a layerObj.
     get_metadata(obj, key, [default]) -> value
-    """
 
+    """
     if len(args) > 1:
         return TypeError("get_metadata expected at most 3 arguments, got %d" % (2 + len(args)))
 
@@ -87,6 +95,7 @@ def del_metadata(obj, key):
 def metadata(obj, key, *args):
     """Context manager that exposes the metadata and saves it again.
     Warning: This is only usefull if the metadata is mutable!
+
     """
     metadata = get_metadata(obj, key, *args)
     yield metadata
@@ -100,16 +109,14 @@ def __get_mra(obj):
     try:
         metadata = yaml.load(text)
     except yaml.parser.ParserError:
-        raise IOError("File has corrupted MRA metadata for entry '%s'." % key)
+        raise IOError("File has corrupted MRA metadata for entry \"%s\"." % key)
     return metadata
 
 def __save_mra(obj, mra_metadata):
     set_metadata(obj, METADATA_NAME, yaml.safe_dump(mra_metadata))
 
 def get_mra_metadata(obj, key, *args):
-    """
-    get_metadata(obj, key, [default]) -> value
-    """
+    """get_metadata(obj, key, [default]) -> value"""
 
     if len(args) > 1:
         return TypeError("get_mra_metadata expected at most 3 arguments, got %d" % (2 + len(args)))
@@ -150,8 +157,8 @@ def mra_metadata(obj, *args):
     Warning: This is only usefull if the metadata is mutable!
     If no key is provided the metadata dict itobj is exposed.
     Usage: with mra_metadata(obj, [key, [dflt, setdefault] ]) as metadata
-    """
 
+    """
     if not args:
         mra_metadata = __get_mra(obj)
         yield mra_metadata
diff --git a/src/mra.py b/src/mra.py
index 11dc06f..476d5e4 100644
--- a/src/mra.py
+++ b/src/mra.py
@@ -24,22 +24,30 @@
 #                                                                       #
 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 
+"""
+    Module for managing MapFiles in MRA conceptual model.
+
+    It deliberately follows a conceptual model close to GeoServer REST
+    API in order to ensure as much compatibility as possible. And thus 
+    allows to switch from first to second.
+    
+    But, the next should be (will be) more consistent with MapServer
+    while maintaining a certain proximity with GeoServer...
+
+"""
+
 import os
 import os.path
 import string
 import urlparse
 import functools
-
 import web
 import yaml
 import mapscript
-
 import tools
 from extensions import plugins
-
 import webapp
 from webapp import KeyExists
-
 import stores
 import metadata
 
@@ -48,10 +56,9 @@ class MetadataMixin(object):
     def __getattr__(self, attr):
         if hasattr(self, "ms") and hasattr(metadata, attr):
             return functools.partial(getattr(metadata, attr), self.ms)
-        raise AttributeError("'%s' object has no attribute '%s'" %
+        raise AttributeError("\"%s\" object has no attribute \"%s\"." %
                              (type(self).__name__, attr))
 
-
 class Layer(MetadataMixin):
     def __init__(self, backend):
         self.ms = backend
@@ -59,14 +66,13 @@ class Layer(MetadataMixin):
     def update(self, name, enabled, metadata):
         self.ms.name = name
         self.ms.template = "foo.html" # TODO: Support html format response
-
         self.enable(enabled)
         self.update_metadatas(metadata)
 
     def enable(self, enabled=True):
         requests = ["GetCapabilities", "GetMap", "GetFeatureInfo", "GetLegendGraphic"]
         self.ms.status = mapscript.MS_ON if enabled else mapscript.MS_OFF
-        self.set_metadata("wms_enable_request", " ".join(('%s' if enabled else "!%s") % c for c in requests))
+        self.set_metadata("wms_enable_request", " ".join(("%s" if enabled else "!%s") % c for c in requests))
 
     def get_type_name(self):
         return {
@@ -99,7 +105,7 @@ class Layer(MetadataMixin):
     def get_latlon_extent(self):
         rect = mapscript.rectObj(*self.get_extent())
         res = rect.project(mapscript.projectionObj(self.get_proj4()),
-                           mapscript.projectionObj('+init=epsg:4326'))
+                           mapscript.projectionObj("+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs"))
         return stores.Extent(rect.minx, rect.miny, rect.maxx, rect.maxy)
 
     def get_fields(self):
@@ -130,9 +136,7 @@ class Layer(MetadataMixin):
     def get_SLD(self):
         return self.ms.generateSLD().decode("LATIN1").encode("UTF8")
 
-
     def add_style_sld(self, mf, s_name, new_sld):
-
         # Because we do not want to apply the sld to real layers by mistake
         # we need to rename it to something we are sure is not used.
         sld_layer_name = "__mra_tmp_template"
@@ -171,19 +175,18 @@ class Layer(MetadataMixin):
         mf.ms.removeLayer(ms_template_layer.index)
 
     def set_default_style(self, mf):
-
         if self.ms.type == mapscript.MS_LAYER_POINT:
             self.ms.tolerance = 8
             self.ms.toleranceunits = 6
-            s_name = 'default_point'
+            s_name = "default_point"
         elif self.ms.type == mapscript.MS_LAYER_LINE:
             self.ms.tolerance = 8
             self.ms.toleranceunits = 6
-            s_name = 'default_line'
+            s_name = "default_line"
         elif self.ms.type == mapscript.MS_LAYER_POLYGON:
             self.ms.tolerance = 0
             self.ms.toleranceunits = 6
-            s_name = 'default_polygon'
+            s_name = "default_polygon"
         else:
             return
 
@@ -204,7 +207,6 @@ class Layer(MetadataMixin):
         else:
             raise KeyError(s_name)
 
-
 class LayerGroup(object):
 
     def __init__(self, name, mapfile):
@@ -212,7 +214,7 @@ class LayerGroup(object):
         self.mapfile = mapfile
 
     def iter_layers(self):
-        return self.mapfile.iter_layers(meta={"wms_group_name":self.name})
+        return self.mapfile.iter_layers(meta={"wms_group_name": self.name})
 
     def get_layers(self):
         return list(self.iter_layers())
@@ -261,7 +263,6 @@ class LayerGroup(object):
 
         return extent
 
-
 class Mapfile(MetadataMixin):
 
     def __init__(self, path, create=False, needed=False):
@@ -334,6 +335,7 @@ class Mapfile(MetadataMixin):
         # Create the layer.
         layer = Layer(mapscript.layerObj(self.ms))
 
+        # Add some default metadata.
         dflt_metadata = {
             "wms_title": l_name,
             "wms_abstract": l_name,
@@ -366,7 +368,6 @@ class Mapfile(MetadataMixin):
         layer = self.get_layer(l_name)
         self.ms.moveLayerDown(layer.ms.index)
 
-
     # Layergroups
 
     def create_layergroup(self, lg_name, mra_metadata={}):
@@ -401,13 +402,9 @@ class Mapfile(MetadataMixin):
         with self.mra_metadata("layergroups", {}) as layergroups:
             del layergroups[lg_name]
 
-
-
-
 # Workspaces are special Mapfiles that are composed of LayerModels
 # which are layers that can be used to configure other layers.
 
-
 class LayerModel(Layer):
     def __init__(self, ws, *args, **kwargs):
         Layer.__init__(self, *args, **kwargs)
@@ -415,7 +412,6 @@ class LayerModel(Layer):
         self.name = self.get_mra_metadata("name", None)
 
 class FeatureTypeModel(LayerModel):
-
     def update(self, ds_name, ft_name, metadata):
         ws = self.ws
 
@@ -463,7 +459,6 @@ class FeatureTypeModel(LayerModel):
         self.update_mra_metadatas(metadata)
         self.update_mra_metadatas({"name": ft_name, "type": "featuretype", "storage": ds_name})
 
-
     def configure_layer(self, layer, enabled=True):
         ws = self.ws
 
@@ -471,7 +466,6 @@ class FeatureTypeModel(LayerModel):
 
         # We must also update all our personal attributes (type, ...)
         # because we might not have been cloned.
-
         layer.ms.type = self.ms.type
         layer.ms.setProjection(self.ms.getProjection())
         layer.ms.setExtent(self.ms.extent.minx, self.ms.extent.miny,
@@ -495,7 +489,7 @@ class FeatureTypeModel(LayerModel):
 
         if enabled:
             layer.set_metadata("wfs_enable_request",
-                               "GetCapabilities GetFeature DescribeFeatureType")
+                               "GetCapabilities DescribeFeatureType GetFeature")
 
         # Configure the layer based on information from the store.
         ds = ws.get_datastore(self.get_mra_metadata("storage"))
@@ -532,7 +526,6 @@ class FeatureTypeModel(LayerModel):
 
         plugins.extend("post_configure_vector_layer", self, ws, ds, ft, layer)
 
-
 class CoverageModel(LayerModel):
 
     def update(self, cs_name, c_name, metadata):
@@ -547,7 +540,7 @@ class CoverageModel(LayerModel):
         self.ms.type = mapscript.MS_LAYER_RASTER
         self.ms.setProjection(cs.get_proj4())
         self.ms.setExtent(*cs.get_extent())
-        self.ms.setProcessingKey("RESAMPLE","AVERAGE")
+        self.ms.setProcessingKey("RESAMPLE", "AVERAGE")
 
         # Configure the connection to the store.
         # This is a little hacky as we have to translate stuff...
@@ -560,7 +553,7 @@ class CoverageModel(LayerModel):
         self.ms.data = self.ws.mra.get_file_path(url.path)
             # TODO: strip extention.
         #else:
-        #    raise ValueError("Unhandled type '%s'" % cparam["dbtype"])
+        #    raise ValueError("Unhandled type \"%s\"." % cparam["dbtype"])
 
         # Deactivate wms and wcs requests, because we are a virtual layer.
         self.set_metadatas({
@@ -598,19 +591,16 @@ class CoverageModel(LayerModel):
                 })
 
         layer.set_metadatas({
-                "wfs_name": layer.get_metadata("wms_name"),
-                "wfs_title": layer.get_metadata("wms_title"),
-                "wfs_abstract": layer.get_metadata("wms_abstract"),
-                # TODO: wfs_keywordlist, wcs_srs, wcs_getfeature_formatlist...
+                "wcs_name": layer.get_metadata("wms_name"),
+                "wcs_label": layer.get_metadata("wms_title"),
+                "wcs_description": layer.get_metadata("wms_abstract")
                 })
 
         if enabled:
-            layer.set_metadata("wcs_enable_request", "GetCapabilities GetCoverage DescribeCoverage")
+            layer.set_metadata("wcs_enable_request", "GetCapabilities DescribeCoverage GetCoverage")
 
         plugins.extend("post_configure_raster_layer", self, ws, layer)
 
-
-
 class Workspace(Mapfile):
 
     def __init__(self, mra, *args, **kwargs):
@@ -633,7 +623,7 @@ class Workspace(Mapfile):
         elif st_type == "coveragestore":
             return stores.Coveragestore(cparam)
         else:
-            raise AssertionError("Unknown st_type '%s'." % st_type)
+            raise AssertionError("Unknown st_type \"%s\"." % st_type)
 
     def get_store_info(self, st_type, name):
         info = self.get_mra_metadata("%ss" % st_type, {})[name].copy()
@@ -664,72 +654,86 @@ class Workspace(Mapfile):
 
     def get_datastore(self, name):
         """Returns a store.Datastore object from the workspace."""
+
         return self.get_store("datastore", name)
 
     def get_datastore_info(self, name):
         """Returns info for a datastore from the workspace."""
+
         return self.get_store_info("datastore", name)
 
     def iter_datastore_names(self):
         """Return an iterator over the datastore names."""
+
         return self.iter_store_names("datastore")
 
     def iter_datastores(self):
         """Return an iterator over the datastore (names, configuration)."""
+
         return self.iter_stores("datastore")
 
     def create_datastore(self, name, configuration):
         """Creates a new datastore."""
+
         return self.create_store("datastore", name, configuration)
 
     def update_datastore(self, name, configuration):
         """Update a datastore."""
+
         return self.update_store("datastore", name, configuration)
 
     def delete_datastore(self, name):
         """Delete a datastore."""
+
         try:
             next(self.iter_featuretypemodels(name))
         except StopIteration:
             pass # No layers use our store, all OK.
         else:
-            raise ValueError("The datastore '%s' can't be delete because it is used." % name)
+            raise ValueError("The datastore \"%s\" can't be delete because it is used." % name)
         return self.delete_store("datastore", name)
 
     # Coveragestores (this is c/p from datastores):
 
     def get_coveragestore(self, name):
         """Returns a store.Coveragestore object from the workspace."""
+
         return self.get_store("coveragestore", name)
 
     def get_coveragestore_info(self, name):
         """Returns info for a coveragestore from the workspace."""
+
         return self.get_store_info("coveragestore", name)
 
     def iter_coveragestore_names(self):
         """Return an iterator over the coveragestore names."""
+
         return self.iter_store_names("coveragestore")
 
     def iter_coveragestores(self):
         """Return an iterator over the coveragestore (names, configuration)."""
+
         return self.iter_stores("coveragestore")
 
     def create_coveragestore(self, name, configuration):
         """Creates a new coveragestore."""
+
         return self.create_store("coveragestore", name, configuration)
 
     def update_coveragestore(self, name, configuration):
         """Update a coveragestore."""
+
         return self.update_store("coveragestore", name, configuration)
 
     def delete_coveragestore(self, name):
         """Delete a coveragestore."""
+
         try:
             next(self.iter_coveragemodels(name))
         except StopIteration:
             pass # No layers use our store, all OK.
         else:
-            raise ValueError("The coveragestore '%s' can't be delete because it is used." % name)
+            raise ValueError("The coveragestore \"%s\" can't be delete because it is used." % name)
         return self.delete_store("coveragestore", name)
 
     # LayerModels:
@@ -740,7 +744,7 @@ class Workspace(Mapfile):
         elif st_type == "coverage":
             prefix = "c"
         else:
-            raise ValueError("Unknown layer model type '%s'." % st_type)
+            raise ValueError("Unknown layer model type \"%s\"." % st_type)
         return "%s:%s:%s" % (prefix, store, name)
 
     def __ms2model(self, ms_layer, st_type=None):
@@ -749,7 +753,7 @@ class Workspace(Mapfile):
         elif st_type == "coverage" or not st_type and ms_layer.name.startswith("c:"):
             return CoverageModel(self, ms_layer)
         else:
-            raise ValueError("Badly named Layer Model '%s'." % ms_layer.name)
+            raise ValueError("Badly named Layer Model \"%s\"." % ms_layer.name)
 
     def iter_layermodels(self, st_type=None, store=None, **kwargs):
         if st_type:
@@ -761,7 +765,7 @@ class Workspace(Mapfile):
 
     def get_layermodel(self, st_type, store, name):
         try:
-            return next(self.iter_layermodels(attr={"name":self.__model_name(st_type, store, name)}))
+            return next(self.iter_layermodels(attr={"name": self.__model_name(st_type, store, name)}))
         except StopIteration:
             raise KeyError((st_type, store, name))
 
@@ -788,10 +792,9 @@ class Workspace(Mapfile):
     def delete_layermodel(self, st_type, ds_name, ft_name):
         model = self.get_layermodel(st_type, ds_name, ft_name)
         if model.get_mra_metadata("layers", []):
-            raise ValueError("The %s '%s' can't be delete because it is used." % (st_type, ft_name))
+            raise ValueError("The %s \"%s\" can't be delete because it is used." % (st_type, ft_name))
         self.ms.removeLayer(model.ms.index)
 
-
     # Featuretypes
 
     def iter_featuretypemodels(self, ds_name=None, **kwargs):
@@ -832,7 +835,6 @@ class Workspace(Mapfile):
     def delete_coveragemodel(self, ds_name, ft_name):
         return self.delete_layermodel("coverage", ds_name, ft_name)
 
-
 # Finaly the global context:
 
 class MRA(object):
@@ -845,7 +847,7 @@ class MRA(object):
     def safe_path_join(self, root, *args):
         full_path = os.path.realpath(os.path.join(root, *args))
         if not full_path.startswith(os.path.realpath(root)):
-            raise webapp.Forbidden(message="path '%s' outside root directory." % (args))
+            raise webapp.Forbidden(message="Path \"%s\" outside root directory." % (args))
         return full_path
 
     def mk_path(self, path):
@@ -881,7 +883,7 @@ class MRA(object):
         todo = ["default_point.sld", "default_line.sld", "default_polygon.sld"]
         for (_, _, files) in os.walk(self.get_style_path()):
             for f in files:
-                if f.endswith(".sld") and not f.startswith('.'):
+                if f.endswith(".sld") and not f.startswith("."):
                     yield f[:-4]
                 if f in todo:
                     todo.remove(f)
@@ -889,7 +891,6 @@ class MRA(object):
         for f in todo:
             yield f[:-4]
 
-
     def create_style(self, name, data):
         path = self.get_style_path("%s.sld" % name)
         with open(self.mk_path(path), "w") as f:
@@ -945,7 +946,7 @@ class MRA(object):
     def list_workspaces(self):
         for (_, _, files) in os.walk(self.get_available_path()):
             for f in files:
-                if f.endswith(".ws.map") and not f.startswith('.'):
+                if f.endswith(".ws.map") and not f.startswith("."):
                     yield f[:-7]
 
     def create_workspace(self, name):
@@ -962,7 +963,6 @@ class MRA(object):
     def delete_workspace(self, name):
         path = self.get_available_path("%s.ws.map" % name)
 
-
     # Services:
 
     def get_service_path(self, *args):
@@ -1004,4 +1004,4 @@ class MRA(object):
                 raise ValueError("Only local files are suported.")
             return self.get_file_path(url.path)
         else:
-            raise ValueError("Unhandled type '%s'" % cparam.get("dbtype", "<unknown>"))
+            raise ValueError("Unhandled type \"%s\"." % cparam.get("dbtype", "<unknown>"))
diff --git a/src/mralogs.py b/src/mralogs.py
index 98580c9..d1cc8cb 100644
--- a/src/mralogs.py
+++ b/src/mralogs.py
@@ -24,6 +24,11 @@
 #                                                                       #
 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 
+"""
+    Module for managing the logs.
+
+"""
+
 import sys
 import string
 import inspect
@@ -44,14 +49,14 @@ def setup(log_level, log_file=None, format="%(asctime)s %(levelname)7s: %(messag
 class Reccord(logging.Handler):
     """A logging.Handler class which stores the records.
     You'll probably want to iterate on this class to get the results.
-    """
 
+    """
     def __init__(self, level=logging.DEBUG, logger=""):
         """Sets up a logging.Handler class which stores the records.
         level is the logging level and defaults to loggong.DEBUG, logger
         is by default the root loger.
-        """
 
+        """
         logging.Handler.__init__(self)
         self.level = level
         self.records = []
@@ -82,6 +87,7 @@ def short_str(obj, length=15, delta=5, tail="..."):
     """Returns a short version of str(obj) of length +/- delta.
     It tries to cut on characters from string.punctuation.
     If cut tail is appended. (defaults to '...')
+
     """
     s = str(obj)
     if len(s) < length:
@@ -95,13 +101,14 @@ def logIn(level="debug", filter=(lambda *a, **kw:True)):
     filter can be used to specify a function that should be used to
     check if we want to log or not. It will be passed the same arguments
     as the decorated function.
+
     """
     def decorator(f):
         @functools.wraps(f)
         def wrapper(*args, **kwargs):
             if filter(*args, **kwargs):
                 arguments = inspect.getcallargs(getattr(f, "original_function", f), *args, **kwargs)
-                getattr(logging, level, "error")("function '%s' was called with args: %s" %
+                getattr(logging, level, "error")("function \"%s\" was called with args: %s" %
                                                  (f.__name__, dict([(a, short_str(v)) for a, v
                                                                     in arguments.iteritems()])))
             return f(*args, **kwargs)
@@ -120,13 +127,14 @@ def logOut(level="debug", filter=(lambda *a, **kw:True)):
     check if we want to log or not. It will be passed the same arguments
     as the decorated function, except for the fact the return value shall
     be inserted as first argument.
+
     """
     def decorator(f):
         @functools.wraps(f)
         def wrapper(*args, **kwargs):
             ret = f(*args, **kwargs)
             if filter(ret, *args, **kwargs):
-                getattr(logging, level, "error")("function '%s' returned: %s" % (f.__name__, short_str(ret)))
+                getattr(logging, level, "error")("function \"%s\" returned: %s" % (f.__name__, short_str(ret)))
             return ret
         wrapper.original_function = f
         return wrapper
diff --git a/src/pyhtml.py b/src/pyhtml.py
index c777160..cc1b36e 100644
--- a/src/pyhtml.py
+++ b/src/pyhtml.py
@@ -24,6 +24,11 @@
 #                                                                       #
 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 
+"""
+    HTML interface of the REST API.
+
+"""
+
 import pyxml
 import StringIO
 import urlparse
@@ -34,6 +39,7 @@ from xml.sax.saxutils import unescape
 def should_be_url(s):
     """This is used to find out if it might be a good idea to
     consider a string is a URL.
+
     """
     parsed = urlparse.urlparse(s)
     return parsed.scheme and parsed.netloc
@@ -43,8 +49,8 @@ def dump_xml(xml, fp, indent=0, indent_depth=2, reinit=True):
     """Recursive function that transforms an ElementTree to html
     written to the file like stream fp.
     indent can be used to specify the amount of indentation wanted.
-    """
 
+    """
     def new_id():
         """Gets a new unique element ID."""
         global __dump_xml_max_element_id
@@ -95,6 +101,7 @@ def dump(obj, fp, indent=0, *args, **kwargs):
     """Writes the html represention of obj to the file-like object fp.
     This uses pyxml to first transform the object into xml.
     *args and **kwargs are forwarded to pyxml.xml()
+    
     """
     xml = pyxml.xml(obj, *args, **kwargs)
     dump_xml(xml, fp, indent)
diff --git a/src/pyxml.py b/src/pyxml.py
index e9e38be..c48ceb3 100644
--- a/src/pyxml.py
+++ b/src/pyxml.py
@@ -24,6 +24,11 @@
 #                                                                       #
 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 
+"""
+    XML interface of the REST API.
+
+"""
+
 import xml.etree.ElementTree as etree
 from xml.etree.ElementTree import Element
 from xml.sax.saxutils import escape
@@ -85,6 +90,7 @@ def default_xml_dict_mapper(obj_name, key_name="key"):
     """Maps to xml_dict and tries to deduce the entry naming from obj_name.
     If obj_name is plural then entries's tag will the singular and a key_name
     attribute will hold the key, otherwises the tag will be the key.
+
     """
     singular_name = singular(obj_name)
     if singular_name != obj_name:
@@ -95,6 +101,7 @@ def default_xml_dict_mapper(obj_name, key_name="key"):
 def default_xml_list_mapper(obj_name, entry_name="entry"):
     """Always maps to xml_list but tries to name its entries after the
     singular of obj_name if possible. If not they are named after entry_name.
+
     """
     singular_name = singular(obj_name)
     if singular_name != obj_name:
@@ -116,6 +123,7 @@ def default_xml_mapper(obj, obj_name,
 
     Otherwise the name is checked for known special cases such as "href",
     and otherwise it is assumed to be a string.
+
     """
     if obj == None:
         return None, None
@@ -146,8 +154,8 @@ def xml(obj, obj_name=None, parent=None,
     appends the element that would normaly be returned to the parent and
     returns the parent. If a new element is returned it's tag is set to
     obj_name. The mapping is done according to the xxx_mappers.
-    """
 
+    """
     # Findout the object's name.
     if obj_name == None:
         obj_name = parent.tag if parent != None else "object"
@@ -177,7 +185,9 @@ def xml_string(parent, obj, _=None, xml_mapper=default_xml_mapper,
 def xml_dict(parent, obj, hint=None, xml_mapper=default_xml_mapper,
              dict_mapper=default_xml_dict_mapper, list_mapper=default_xml_list_mapper):
     """Adds obj to parent as if it is a dictionary.
+
     The entries are of the form: <key>value</key> or <hint[0] hint[1]=key>value</hint[0]>
+
     """
     for k, v in obj.iteritems():
         if hint:
@@ -190,7 +200,9 @@ def xml_dict(parent, obj, hint=None, xml_mapper=default_xml_mapper,
 def xml_list(parent, obj, hint, xml_mapper=default_xml_mapper,
              dict_mapper=default_xml_dict_mapper, list_mapper=default_xml_list_mapper):
     """Adds obj to parent as if it is a list.
+
     The entries are of the form: <hint>value</hint>
+
     """
     for v in obj:
         child = etree.Element(tag=hint, attrib={})
@@ -215,14 +227,13 @@ def obj(xml):
     Otherwise if all the tags of the children are unique then it is also considered
     a dictionary but mapping the tags and the conntents.
     If none of the above the object is considered to be a list.
-    """
 
+    """
     def trans(tag):
-        """
-        This function is pure bullshit.
+        """This function is pure bullshit.
         Stupid ugly hack to acomodate geoserver/mra.
-        """
 
+        """
         if tag.startswith("{") and tag.endswith("}link"):
             return "href"
         if tag == "published":
@@ -277,6 +288,7 @@ def loads(s, retname=False, *args, **kwargs):
 def load(fp, retname=False, *args, **kwargs):
     """Returns an object coresponding to what is described in the xml
     read from the file-like object fp.
+    
     """
     try:
         xml = etree.parse(fp)
diff --git a/src/server.py b/src/server.py
index bdac1ed..97b7400 100755
--- a/src/server.py
+++ b/src/server.py
@@ -24,32 +24,28 @@
 #                                                                       #
 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 
-import os.path
+"""
+    URL mapping infrastructure and HTTP methods used by the REST API.
 
-import sys
+    MRA is following the model: <container> --- <element>
 
+"""
+
+import os.path
+import sys
 import web
 import json
 import urlparse
-
-import mralogs
 import logging
-
+import mralogs
 from mra import MRA
-
-
 import webapp
 from webapp import HTTPCompatible, urlmap, get_data
-
 import tools
 from tools import href, assert_is_empty
-
 from pyxml import Entries
-
-
 from extensions import plugins
 
-
 # Some helper functions first.
 def get_workspace(ws_name):
     with webapp.mightNotFound():
@@ -67,10 +63,16 @@ class index(object):
             "layergroups": href("layergroups/"),
             }
 
-
 class workspaces(object):
+    """Workspaces container.
+
+    See 'workspace' class documentation for definition of a 'workspace'.
+
+    """
     @HTTPCompatible()
     def GET(self, format, *args, **kwargs):
+        """List all workspaces."""
+
         return {"workspaces": [{
                     "name": ws_name,
                     "href": "%s/workspaces/%s.%s" % (web.ctx.home, ws_name, format)
@@ -79,6 +81,8 @@ class workspaces(object):
 
     @HTTPCompatible()
     def POST(self, format):
+        """Create a new workspace."""
+
         data = get_data(name="workspace", mandatory=["name"], authorized=["name"])
         ws_name = data.pop("name")
 
@@ -87,10 +91,24 @@ class workspaces(object):
 
         webapp.Created("%s/workspaces/%s.%s" % (web.ctx.home, ws_name, format))
 
-
 class workspace(object):
+    """A workspace is a grouping of data stores and coverage stores.
+    
+    In fact, a workspace is assimilated to one mapfile (<workspace_name>.ws.map) 
+    which contains some unactivated layers. These layers allows to configure 
+    connections to data (data store or coverage store) then data 
+    itself (featuretype for vector type or coverage for raster type).
+
+    These layers should not be published as OGC service as such. 
+    However, in the near future (TODO), it should be possible to publish 
+    a workspace as permitted GeoServer.
+    And this workspace should be identified as a usual MapFile.
+
+    """
     @HTTPCompatible()
     def GET(self, ws_name, format):
+        """Return workspace <ws>."""
+
         ws = get_workspace(ws_name)
         return {"workspace": ({
                     "name": ws.name,
@@ -104,8 +122,15 @@ class workspace(object):
 
 
 class datastores(object):
+    """Data stores container.
+
+    See 'datastore' class documentation for definition of a 'datastore'.
+
+    """
     @HTTPCompatible()
     def GET(self, ws_name, format):
+        """List all data stores in workspace <ws>."""
+
         ws = get_workspace(ws_name)
         return {"dataStores": [{
                     "name": ds_name,
@@ -116,8 +141,9 @@ class datastores(object):
 
     @HTTPCompatible()
     def POST(self, ws_name, format):
-        ws = get_workspace(ws_name)
+        """Create a new data store."""
 
+        ws = get_workspace(ws_name)
 
         data = get_data(name="dataStore", mandatory=["name"], authorized=["name", "title", "abstract", "connectionParameters"])
         ds_name = data.pop("name")
@@ -132,8 +158,16 @@ class datastores(object):
 
 
 class datastore(object):
+    """A data store is a source of spatial data that is vector based.
+
+    A data store is a connection to a data source as implied by OGR library.
+    It could be a shapefile, a PostGIS database or any other data type supported by OGR.
+
+    """
     @HTTPCompatible()
     def GET(self, ws_name, ds_name, format):
+        """Return data store <ds>."""
+
         ws = get_workspace(ws_name)
 
         with webapp.mightNotFound("dataStore", workspace=ws_name):
@@ -158,6 +192,8 @@ class datastore(object):
 
     @HTTPCompatible()
     def PUT(self, ws_name, ds_name, format):
+        """Modify data store <ds>."""
+
         ws = get_workspace(ws_name)
 
         data = get_data(name="dataStore", mandatory=["name"], authorized=["name", "title", "abstract", "connectionParameters"])
@@ -171,6 +207,8 @@ class datastore(object):
 
     @HTTPCompatible()
     def DELETE(self, ws_name, ds_name, format):
+        """Delete data store <ds>."""
+
         ws = get_workspace(ws_name)
 
         # TODO: check, this is not consistent between ds/cs.
@@ -183,10 +221,16 @@ class datastore(object):
 
 
 class featuretypes(object):
+    """Feature types container.
+
+    See 'featuretype' class documentation for definition of a 'featuretype'.
+
+    """
     @HTTPCompatible()
     def GET(self, ws_name, ds_name, format):
-        ws = get_workspace(ws_name)
+        """List all feature types in selected data store <ds>."""
 
+        ws = get_workspace(ws_name)
         return {"featureTypes": [{
                     "name": ft.name,
                     "href": "%s/workspaces/%s/datastores/%s/featuretypes/%s.%s" % (
@@ -196,8 +240,11 @@ class featuretypes(object):
 
     @HTTPCompatible()
     def POST(self, ws_name, ds_name, format):
-        ws = get_workspace(ws_name)
+        """Create a new feature type. It create the associated layer by defaut.
+        This layer is added in the mapfile: <layer.map>
 
+        """
+        ws = get_workspace(ws_name)
         data = get_data(name="featureType",
                         mandatory=["name"],
                         authorized=["name", "title", "abstract"])
@@ -209,11 +256,8 @@ class featuretypes(object):
         ws.save()
 
         # Then creates the associated layer by default:
-
         model = ws.get_featuretypemodel(ds_name, data["name"])
-
         mf = mra.get_available()
-
         with webapp.mightConflict():
             mf.create_layer(model, data["name"], True)
         mf.save()
@@ -223,8 +267,15 @@ class featuretypes(object):
 
 
 class featuretype(object):
+    """A feature type is a data set that originates from a data store.
+    
+    A feature type is considered as a layer under MapServer which is still unactivated.
+
+    """
     @HTTPCompatible()
     def GET(self, ws_name, ds_name, ft_name, format):
+        """Return feature type <ft>."""
+
         ws = get_workspace(ws_name)
 
         ds = ws.get_datastore(ds_name)
@@ -283,6 +334,8 @@ class featuretype(object):
 
     @HTTPCompatible()
     def PUT(self, ws_name, ds_name, ft_name, format):
+        """Modify feature type <ft>."""
+
         ws = get_workspace(ws_name)
 
         data = get_data(name="featureType", mandatory=["name"], authorized=["name", "title", "abstract"])
@@ -297,6 +350,8 @@ class featuretype(object):
 
     @HTTPCompatible()
     def DELETE(self, ws_name, ds_name, ft_name, format):
+        """Delete feature type <ft>."""
+
         ws = get_workspace(ws_name)
 
         # We need to check if there are any layers using this.
@@ -309,10 +364,16 @@ class featuretype(object):
 
 
 class coveragestores(object):
+    """Coverage stores container.
+
+    See 'coveragestore' class documentation for definition of a 'coveragestore'.
+
+    """
     @HTTPCompatible()
     def GET(self, ws_name, format):
-        ws = get_workspace(ws_name)
+        """List all coverage stores in workspace."""
 
+        ws = get_workspace(ws_name)
         return {"coverageStores": [{
                     "name": cs_name,
                     "href": "%s/workspaces/%s/coveragestores/%s.%s" % (
@@ -322,8 +383,9 @@ class coveragestores(object):
 
     @HTTPCompatible()
     def POST(self, ws_name, format):
-        ws = get_workspace(ws_name)
+        """Create new coverage store.""" 
 
+        ws = get_workspace(ws_name)
         data = get_data(name="coverageStore", mandatory=["name"], authorized=["name", "title", "abstract", "connectionParameters"])
         cs_name = data.pop("name")
 
@@ -331,15 +393,28 @@ class coveragestores(object):
             ws.create_coveragestore(cs_name, data)
         ws.save()
 
+        # Then creates the associated layer by default:
+        # model = ws.get_coveragetypemodel(cs_name, data["name"])
+        # mf = mra.get_available()
+        # with webapp.mightConflict():
+        #     mf.create_layer(model, data["name"], True)
+        # mf.save()
+
         webapp.Created("%s/workspaces/%s/coveragestores/%s.%s" % (
                 web.ctx.home, ws_name, cs_name, format))
 
 
 class coveragestore(object):
+    """A coverage store is a source of spatial data that is raster based.
+
+    A coverage store is a connection to a raster data source as implied by GDAL library.
+
+    """
     @HTTPCompatible()
     def GET(self, ws_name, cs_name, format):
-        ws = get_workspace(ws_name)
+        """Return coverage store <cs>."""
 
+        ws = get_workspace(ws_name)
         with webapp.mightNotFound("coverageStore", workspace=ws_name):
             info = ws.get_coveragestore_info(cs_name)
         connectionParameters = info.get("connectionParameters", {})
@@ -367,8 +442,9 @@ class coveragestore(object):
 
     @HTTPCompatible()
     def PUT(self, ws_name, cs_name, format):
+        """Modify coverage store <ds>."""
+        
         ws = get_workspace(ws_name)
-
         data = get_data(name="coverageStore", mandatory=["name"], authorized=["name", "title", "abstract", "connectionParameters"])
         if cs_name != data.pop("name"):
             raise webapp.Forbidden("Can't change the name of a coverage store.")
@@ -379,8 +455,9 @@ class coveragestore(object):
 
     @HTTPCompatible()
     def DELETE(self, ws_name, cs_name, format):
-        ws = get_workspace(ws_name)
+        """Delete coverage store <ds>."""
 
+        ws = get_workspace(ws_name)
         # TODO: check, this is not consistent between ds/cs.
         # We need to check if this datastore is empty.
         assert_is_empty(ws.iter_coverages(cs_name=cs_name), "coveragestore", ds_name)
@@ -391,10 +468,16 @@ class coveragestore(object):
 
 
 class coverages(object):
+    """Coverages container.
+
+    See 'coverage' class documentation for definition of a 'coverage'.
+
+    """
     @HTTPCompatible()
     def GET(self, ws_name, cs_name, format):
-        ws = get_workspace(ws_name)
+        """List all coverages in selected coverages store <cs>."""
 
+        ws = get_workspace(ws_name)
         return {"coverages": [{
                     "name": c.name,
                     "href": "%s/workspaces/%s/coveragestores/%s/coverages/%s.%s" % (
@@ -404,10 +487,10 @@ class coverages(object):
 
     @HTTPCompatible()
     def POST(self, ws_name, cs_name, format):
-        ws = get_workspace(ws_name)
+        """Create a new coverage."""
 
+        ws = get_workspace(ws_name)
         data = get_data(name="coverage", mandatory=["name"], authorized=["name", "title", "abstract"])
-
         with webapp.mightConflict("coverage", coveragestore=cs_name):
             with webapp.mightNotFound("coverage", coveragestore=cs_name):
                 ws.create_coveragemodel(data["name"], cs_name, data)
@@ -418,13 +501,18 @@ class coverages(object):
 
 
 class coverage(object):
+    """A coverage is a raster based data set that originates from a coverage store.
+
+    A coverage is considered as a layer under MapServer which is still unactivated.
+
+    """
     @HTTPCompatible()
     def GET(self, ws_name, cs_name, c_name, format):
-        ws = get_workspace(ws_name)
+        """Return coverage <c>."""
 
+        ws = get_workspace(ws_name)
         # with webapp.mightNotFound("coveragestore", workspace=ws_name):
         #     cs = ws.get_coveragestore(cs_name)
-
         with webapp.mightNotFound("coverage", coveragestore=cs_name):
             c = ws.get_coveragemodel(c_name, cs_name)
 
@@ -489,6 +577,8 @@ class coverage(object):
 
     @HTTPCompatible()
     def PUT(self, ws_name, cs_name, c_name, format):
+        """Modify coverage <c>."""
+        
         ws = get_workspace(ws_name)
 
         data = get_data(name="coverage", mandatory=["name"], authorized=["name", "title", "abstract"])
@@ -504,8 +594,9 @@ class coverage(object):
 
     @HTTPCompatible()
     def DELETE(self, ws_name, cs_name, c_name, format):
-        ws = get_workspace(ws_name)
+        """Delete coverage <c>."""
 
+        ws = get_workspace(ws_name)
         # We need to check if there are any layers using this.
         assert_is_empty(ws.iter_layers(mra={"name":c_name, "workspace":ws_name, "storage":cs_name,
                                             "type":"coverage"}), "coverage", ft_name)
@@ -516,9 +607,11 @@ class coverage(object):
 
 
 class files(object):
+    """Uploads a file from a local source. The body of the request is the file itself."""
 
     @HTTPCompatible(allow_all=True)
     def PUT(self, ws_name, st_type, st_name, f_type, format):
+
         import zipfile
 
         # TODO: According to geoserv's examples we might have to handle
@@ -585,8 +678,12 @@ class files(object):
 
 
 class styles(object):
+    """SLD styles container."""
+
     @HTTPCompatible()
     def GET(self, format):
+        """List all SLD styles."""
+
         return {"styles": [{
                     "name": s_name,
                     "href": "%s/styles/%s.%s" % (web.ctx.home, s_name, format)
@@ -595,6 +692,10 @@ class styles(object):
 
     @HTTPCompatible()
     def POST(self, format):
+        """Create a new SLD style. Add the 'name' parameter in order to specify 
+        the name to be given to the new style.
+
+        """
         params = web.input(name=None)
         name = params.name
         if name == None:
@@ -607,8 +708,19 @@ class styles(object):
 
 
 class style(object):
+    """A style describes how a resource (a feature type or a coverage) should be 
+    symbolized or rendered by a Web Map Service.
+    
+    Styles are specified with SLD and translated into the mapfile (with CLASS and
+    STYLE blocs) to be applied.
+    An extension may be considered to manage all style possibilities offered by MapServer.
+    This should be made with the module 'extensions.py'.
+
+    """
     @HTTPCompatible(authorize=["sld"])
     def GET(self, s_name, format):
+        """Return style <s>."""
+
         with webapp.mightNotFound():
             style = mra.get_style(s_name)
 
@@ -624,6 +736,8 @@ class style(object):
 
     @HTTPCompatible()
     def PUT(self, s_name, format):
+        """Modify style <s>."""
+
         #TODO: Also update layers using this style.
         with webapp.mightNotFound():
             mra.delete_style(s_name)
@@ -632,14 +746,20 @@ class style(object):
 
     @HTTPCompatible()
     def DELETE(self, s_name, format):
+        """Delete style <s>."""
+        
         #TODO: Maybe check for layers using this style?
         with webapp.mightNotFound():
             mra.delete_style(s_name)
 
 
 class layers(object):
+    """Layers container."""
+
     @HTTPCompatible()
     def GET(self, format):
+        """List all layers."""
+
         mf = mra.get_available()
         return {"layers": [{
                     "name": layer.ms.name,
@@ -649,6 +769,8 @@ class layers(object):
 
     @HTTPCompatible()
     def POST(self, format):
+        """Create a new layers."""
+
         data = get_data(name="layer",
                         mandatory=["name", "resource"],
                         authorized=["name", "title", "abstract", "resource", "enabled", "defaultStyle"])
@@ -689,8 +811,15 @@ class layers(object):
 
 
 class layer(object):
+    """A layer is a published resource (feature type or coverage) from a MapFile.
+
+    All layers are added in one single MapFile which should be activate as OGC service.
+
+    """
     @HTTPCompatible()
     def GET(self, l_name, format):
+        """Return layer <l>."""
+
         mf = mra.get_available()
         with webapp.mightNotFound():
             layer = mf.get_layer(l_name)
@@ -728,6 +857,8 @@ class layer(object):
 
     @HTTPCompatible()
     def PUT(self, l_name, format):
+        """Modify layer <l>."""
+
         mf = mra.get_available()
 
         data = get_data(name="layer", mandatory=[],
@@ -775,6 +906,8 @@ class layer(object):
 
     @HTTPCompatible()
     def DELETE(self, l_name, format):
+        """Delete layer <l>."""
+
         mf = mra.get_available()
         with webapp.mightNotFound():
             mf.delete_layer(l_name)
@@ -782,8 +915,12 @@ class layer(object):
 
 
 class layerstyles(object):
+    """Styles container associated to one layer."""
+
     @HTTPCompatible()
     def GET(self, l_name, format):
+        """Return all style from layer <l>."""
+
         mf = mra.get_available()
         with webapp.mightNotFound():
             layer = mf.get_layer(l_name)
@@ -797,33 +934,13 @@ class layerstyles(object):
                     } for s_name in layer.iter_styles()],
                 }
 
-    # @HTTPCompatible()
-    # def POST(self, l_name, format):
-    #     data = get_data(name="style", mandatory=["resource"],
-    #                     authorized=["name", "title", "abstract", "resource"])
-
-    #     href = data["resource"]["href"]
-    #     try:
-    #         _, s_name = mra.href_parse(href, 2)
-    #     except ValueError:
-    #         raise webapp.NotFound(message="style '%s' was not found." % href)
-
-    #     with webapp.mightNotFound():
-    #         style = mra.get_style(s_name)
-
-    #     mf = mra.get_available()
-    #     with webapp.mightNotFound():
-    #         layer = mf.get_layer(l_name)
-
-    #     layer.add_style_sld(mf, s_name, style)
-    #     mf.save()
-
-    #     webapp.Created("%s/layers/%s/layerstyles/%s.%s" % (web.ctx.home, l_name, s_name, format))
-
-
 class layerstyle(object):
+    """Style associated to layer <l>."""
+
     @HTTPCompatible()
     def DELETE(self, l_name, s_name, format):
+        """Remove style <s> from layer <l>."""
+
         mf = mra.get_available()
         with webapp.mightNotFound():
             layer = mf.get_layer(l_name)
@@ -833,6 +950,8 @@ class layerstyle(object):
 
 
 class layerfields(object):
+    """Attributes (Fields) container associated to layer <l>."""
+
     @HTTPCompatible()
     def GET(self, l_name, format):
         mf = mra.get_available()
@@ -847,8 +966,12 @@ class layerfields(object):
 
 
 class layergroups(object):
+    """Layergroups container."""
+
     @HTTPCompatible()
     def GET(self, format):
+        """List all layer groups."""
+
         mf = mra.get_available()
 
         return {"layerGroups" : [{
@@ -859,6 +982,8 @@ class layergroups(object):
 
     @HTTPCompatible()
     def POST(self, format):
+        """Create a new layer group."""
+
         mf = mra.get_available()
 
         data = get_data(name="layerGroup", mandatory=["name"], authorized=["name", "title", "abstract", "layers"])
@@ -876,9 +1001,14 @@ class layergroups(object):
 
 
 class layergroup(object):
+    """A layergroup is a grouping of layers and styles that can be accessed 
+    as a single layer in a WMS GetMap request.
 
+    """
     @HTTPCompatible()
     def GET(self, lg_name, format):
+        """Return layergroup <lg>."""
+
         mf = mra.get_available()
         with webapp.mightNotFound():
             lg = mf.get_layergroup(lg_name)
@@ -905,6 +1035,8 @@ class layergroup(object):
 
     @HTTPCompatible()
     def PUT(self, lg_name, format):
+        """Modify layergroup <lg>."""
+
         mf = mra.get_available()
 
         with webapp.mightNotFound():
@@ -925,6 +1057,7 @@ class layergroup(object):
 
     @HTTPCompatible()
     def DELETE(self, lg_name, format):
+        """Delete layergroup <lg>."""
 
         mf = mra.get_available()
         with webapp.mightNotFound():
@@ -1001,43 +1134,35 @@ class OWSSettings(object):
 
 # Index:
 urlmap(index, "")
-
 # Workspaces:
 urlmap(workspaces, "workspaces")
 urlmap(workspace, "workspaces", ())
-
 # Datastores:
 urlmap(datastores, "workspaces", (), "datastores")
 urlmap(datastore, "workspaces", (), "datastores", ())
 # Featuretypes:
 urlmap(featuretypes, "workspaces", (), "datastores", (), "featuretypes")
 urlmap(featuretype, "workspaces", (), "datastores", (), "featuretypes", ())
-
 # Coveragestores:
 urlmap(coveragestores, "workspaces", (), "coveragestores")
 urlmap(coveragestore, "workspaces", (), "coveragestores", ())
 # Coverages:
 urlmap(coverages, "workspaces", (), "coveragestores", (), "coverages")
 urlmap(coverage, "workspaces", (), "coveragestores", (), "coverages", ())
-
 # Files:
 urlmap(files, "workspaces", (), "(datastores|coveragestores)", (), "(file|url|external)")
-
 # Styles:
 urlmap(styles, "styles")
 urlmap(style, "styles", ())
-
 # Layers, layer styles and data fields:
 urlmap(layers, "layers")
 urlmap(layer, "layers", ())
 urlmap(layerstyles, "layers", (), "styles")
 urlmap(layerstyle, "layers", (), "styles", ())
 urlmap(layerfields, "layers", (), "fields")
-
 # Layergroups:
 urlmap(layergroups, "layergroups")
 urlmap(layergroup, "layergroups", ())
-
 # OGC Web Services:
 urlmap(OWSGlobalSettings, "services", "(wms|wfs|wcs)", "settings")
 urlmap(OWSSettings, "services", "(wms|wfs|wcs)", (), "settings")
@@ -1061,4 +1186,4 @@ app = web.application(urls, globals())
 if __name__ == "__main__":
     app.run()
 
-application = app.wsgifunc()
+application = app.wsgifunc()
\ No newline at end of file
diff --git a/src/stores.py b/src/stores.py
index b0a5cad..8aa64f0 100644
--- a/src/stores.py
+++ b/src/stores.py
@@ -24,6 +24,12 @@
 #                                                                       #
 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 
+"""
+    A field implementation of georeferenced data (both vector and raster)
+    backed by the GDAL/OGR library.
+
+"""
+
 from osgeo import ogr, osr, gdal
 import mapscript
 import tools
@@ -57,7 +63,6 @@ class Extent(list):
             self[1] = min(self[1], y)
             self[3] = max(self[3], y)
 
-
 class Field(object):
     """A Field implementation backed by ogr."""
 
@@ -68,8 +73,7 @@ class Field(object):
         ogr.FieldDefn.GetFieldTypeName(ogr.FieldDefn(), 0)
 
     def __init__(self, backend, layer):
-        """backend should be a ogr.FieldDefn object which will be used to retrieve data.
-        """
+        """Backend should be a ogr.FieldDefn object which will be used to retrieve data."""
 
         self.backend = backend
         self.layer = layer
@@ -93,17 +97,17 @@ class Field(object):
     def get_type_gml(self):
         type = self.get_type()
         if type in (0, 1):
-            return 'Integer'
+            return "Integer"
         if type in (2, 3):
-            return 'Real'
+            return "Real"
         if type in (4, 5):
-            return 'Character'
+            return "Character"
         if type in (6, 7):
-            return 'Unknown' #:)
+            return "Unknown" #:)
         if type in (9, 10):
-            return 'Date'
+            return "Date"
         else:
-            return 'Unknown'
+            return "Unknown"
 
     def get_width(self):
         return self.backend.GetWidth()
@@ -111,13 +115,11 @@ class Field(object):
     def is_nullable(self):
         return self.nullable
 
-
 class Feature(object):
     """A Feature implementation backed by ogr."""
 
     def __init__(self, backend, layer):
-        """backend should be a ogr.Feature object which will be used to retrieve data.
-        """
+        """Backend should be a ogr.Feature object which will be used to retrieve data."""
 
         self.backend = backend
         self.layer
@@ -137,7 +139,6 @@ class Feature(object):
     def get_field(self):
         return Field(self.backend.GetFieldDefn(), layer)
 
-
 class Featuretype(object):
     """A featuretype implementation backed by ogr."""
 
@@ -146,8 +147,7 @@ class Featuretype(object):
         return ogr.GeometryTypeToName(i)
 
     def __init__(self, backend, ds, no_aditional_info=False):
-        """backend should be a ogr.Layer object which will be used to retrieve data.
-        """
+        """Backend should be a ogr.Layer object which will be used to retrieve data."""
 
         self.backend = backend
         self.ds = ds
@@ -156,7 +156,6 @@ class Featuretype(object):
         if not no_aditional_info:
             self.get_aditional_info()
 
-
     def __len__(self):
         return self.nbfeatures()
 
@@ -195,31 +194,31 @@ class Featuretype(object):
 
     def get_geomtype_mapscript(self):
         ogr_geometry = self.get_geomtype_name()
-        if ogr_geometry in ('POINT', 'MULTIPOINT'):
+        if ogr_geometry in ("POINT", "MULTIPOINT"):
             return mapscript.MS_LAYER_POINT
-        if ogr_geometry in ('LINESTRING','MULTILINESTRING'):
+        if ogr_geometry in ("LINESTRING", "MULTILINESTRING"):
             return mapscript.MS_LAYER_LINE
-        if ogr_geometry in ('MULTIPOLYGON','POLYGON'):
+        if ogr_geometry in ("MULTIPOLYGON", "POLYGON"):
             return mapscript.MS_LAYER_POLYGON
         else:
-            raise KeyError('Unrecognized geometry \'%s\'' % ogr_geometry)
+            raise KeyError("Unrecognized geometry: \"%s\"" % ogr_geometry)
 
     def get_geomtype_gml(self):
         ogr_geometry = self.get_geomtype_name()
-        if ogr_geometry == 'POINT':
+        if ogr_geometry == "POINT":
             return ogr_geometry.lower()
-        if ogr_geometry == 'MULTIPOINT':
+        if ogr_geometry == "MULTIPOINT":
             return ogr_geometry.lower()
-        if ogr_geometry == 'LINESTRING':
-            return 'line'
-        if ogr_geometry == 'MULTILINESTRING':
-            return 'multiline'
-        if ogr_geometry == 'POLYGON':
+        if ogr_geometry == "LINESTRING":
+            return "line"
+        if ogr_geometry == "MULTILINESTRING":
+            return "multiline"
+        if ogr_geometry == "POLYGON":
             return ogr_geometry.lower()
-        if ogr_geometry == 'MULTIPOLYGON':
+        if ogr_geometry == "MULTIPOLYGON":
             return ogr_geometry.lower()
         else:
-            raise KeyError('Unrecognized geometry \'%s\'' % ogr_geometry)
+            raise KeyError("Unrecognized geometry: \"%s\"" % ogr_geometry)
 
     def get_geomtype_wkb(self):
         return Featuretype.geomtype_name(self.get_geomtype())
@@ -248,8 +247,7 @@ class Featuretype(object):
     def get_latlon_extent(self):
         rect = mapscript.rectObj(*self.get_extent())
         res = rect.project(mapscript.projectionObj(self.get_proj4()),
-                           mapscript.projectionObj('+init=epsg:4326'))
-
+                           mapscript.projectionObj("+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs"))
         return Extent(rect.minx, rect.miny, rect.maxx, rect.maxy)
 
     def get_native(self):
@@ -276,21 +274,19 @@ class Featuretype(object):
 
     def iterfeatures(self, what=[], when={}):
         if what != [] or when != {}:
-            raise NotImplementedError("iterfeature doesn't support filters yet.")
+            raise NotImplementedError("Iterfeature doesn't support filters yet.")
         for i in xrange(self.backend.GetFeatureCount()):
             yield Feature(self.backend.GetFeature(i), self)
 
     def get_aditional_info(self):
-
-        tokens = self.get_name().split('.', 2)
+        tokens = self.get_name().split(".", 2)
         if len(tokens) < 2:
-            tokens.insert(0, 'public')
+            tokens.insert(0, "public")
         schema, table = tokens
 
         result = self.ds.backend.ExecuteSQL("SELECT column_name, is_nullable FROM INFORMATION_SCHEMA.COLUMNS "
                                          "WHERE table_schema = '%s' AND table_name = '%s'" %
                                          (schema, table))
-
         if not result: return
 
         for i in xrange(result.GetFeatureCount()):
@@ -298,7 +294,6 @@ class Featuretype(object):
             name, nullable = feature.GetField(0), feature.GetField(1)
             self.nullables[name] = nullable
 
-
 class Datastore(object):
     """A datastore implementation backed by ogr."""
 
@@ -307,11 +302,12 @@ class Datastore(object):
         or something more complex used by gdal/ogr to access databases for example.
 
         The first argument to __init__ can also directly be a gdal/ogr object.
+
         """
         self.schema = schema
         self.backend = path if isinstance(path, ogr.DataSource) else ogr.Open(path)
         if self.backend == None:
-            raise ValueError("Datastore backend could not be opened using '%s'." % path)
+            raise ValueError("Datastore backend could not be opened using \"%s\"." % path)
 
     def __len__(self):
         return self.nblayers()
@@ -329,11 +325,11 @@ class Datastore(object):
     def __getitem__(self, key):
         if isinstance(key, int):
             item = self.backend.GetLayerByIndex(key)
-            if item == None: raise IndexError("No layer '%s'" % key)
+            if item == None: raise IndexError("No layer \"%s\"" % key)
         else:
             if self.schema:
                 key = "%s.%s" % (self.schema, key)
-            item = self.backend.GetLayerByName(key.encode('ascii', 'ignore'))
+            item = self.backend.GetLayerByName(key.encode("ascii", "ignore"))
             if item == None: raise KeyError(key)
         return Featuretype(item, self)
 
@@ -344,30 +340,27 @@ class Datastore(object):
         for i in xrange(self.backend.GetLayerCount()):
             yield Featuretype(self.backend.GetLayerByIndex(i), self)
 
-
 class Band(object):
     """A band immplementation backed by gdal."""
 
     def __init__(self, backend):
-        """backend should be a gdal.Band object which will be used to retrieve data.
-        """
+        """backend should be a gdal.Band object which will be used to retrieve data."""
 
         self.backend = backend
 
-
 class Coveragestore(object):
     """A coveragestore implementation backed by gdal."""
 
     def __init__(self, path):
         """Path will be used to open the store, it can be a simple filesystem path
         or something more complex used by gdal/ogr to access databases for example.
-
+        
         The first argument to __init__ can also directly be a gdal/ogr object.
+        
         """
-
         self.backend = path if isinstance(path, gdal.Dataset) else gdal.Open(path)
         if self.backend == None:
-            raise ValueError("Coveragestore backend could not be opened. '%s'." % path)
+            raise ValueError("Coveragestore backend could not be opened. \"%s\"." % path)
 
     def __len__(self):
         return self.nbbands()
@@ -406,7 +399,7 @@ class Coveragestore(object):
     def get_latlon_extent(self):
         rect = mapscript.rectObj(*self.get_extent())
         res = rect.project(mapscript.projectionObj(self.get_proj4()),
-                           mapscript.projectionObj('+init=epsg:4326'))
+                           mapscript.projectionObj("+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs"))
 
         return Extent(rect.minx, rect.miny, rect.maxx, rect.maxy)
 
diff --git a/src/tools.py b/src/tools.py
index 14932d9..8891a5b 100644
--- a/src/tools.py
+++ b/src/tools.py
@@ -24,14 +24,16 @@
 #                                                                       #
 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 
+"""
+    Miscellaneous functions.
+
+"""
+
 import os
 import yaml
 import sys
-
 import pyxml
-
 import webapp
-
 import xml.etree.ElementTree as etree
 from osgeo import osr
 
@@ -43,15 +45,15 @@ def assert_is_empty(generator, tname, iname):
     except StopIteration:
         pass # Everything is ok.
     else:
-        raise webapp.Forbidden(message="Can't remove '%s' because it is an non-empty %s." % (iname, tname))
+        raise webapp.Forbidden(message="Can't remove \"%s\" because it is an non-empty %s." % (iname, tname))
 
 def href(url):
-    return pyxml.Entries({'href': url})
+    return pyxml.Entries({"href": url})
 
 def safe_path_join(root, *args):
     full_path = os.path.realpath(os.path.join(root, *args))
     if not full_path.startswith(os.path.realpath(root)):
-        raise webapp.Forbidden(message="path '%s' outside root directory." % (args))
+        raise webapp.Forbidden(message="Path \"%s\" outside root directory." % (args))
     return full_path
 
 def is_hidden(path):
@@ -60,24 +62,29 @@ def is_hidden(path):
     return os.path.basename(path).startswith(".")
 
 def wkt_to_proj4(wkt):
+    """Return Proj4 definition from WKT definition."""
+
     srs = osr.SpatialReference()
     srs.ImportFromWkt(wkt)
     return srs.ExportToProj4()
 
 def proj4_to_wkt(proj4):
+    """Return WKT definition from Proj4 definition."""
+
     srs = osr.SpatialReference()
     srs.ImportFromProj4(proj4)
     return srs.ExportToWkt()
 
 def wkt_to_authority(wkt):
+    """Return authority name and authority code from WKT definition."""
+
     srs = osr.SpatialReference()
     srs.ImportFromWkt(wkt)
 
-    # Are there really no other with osgeo? Oo
-
-    if srs.GetAuthorityCode('PROJCS') != None:
-        return srs.GetAuthorityName('PROJCS'), srs.GetAuthorityCode('PROJCS')
-    elif srs.GetAuthorityCode('GEOGCS') != None :
-        return srs.GetAuthorityName('GEOGCS'), srs.GetAuthorityCode('GEOGCS')
+    # Are there really no other way with osgeo?
+    if srs.GetAuthorityCode("PROJCS") != None:
+        return srs.GetAuthorityName("PROJCS"), srs.GetAuthorityCode("PROJCS")
+    elif srs.GetAuthorityCode("GEOGCS") != None :
+        return srs.GetAuthorityName("GEOGCS"), srs.GetAuthorityCode("GEOGCS")
     else:
-        return "Unknown", "Unknown"
+        return "Unknown", "Unknown" # :s it could be improved... (TODO)
diff --git a/src/webapp.py b/src/webapp.py
index cec215b..3b13ad1 100644
--- a/src/webapp.py
+++ b/src/webapp.py
@@ -24,21 +24,21 @@
 #                                                                       #
 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 
-import web
+"""
+    ...
+
+"""
 
+import web
 import json
 import pyxml
 import pyhtml
-
 import inspect
 import functools
-
 import os.path
 import itertools
-
 import mralogs
 
-
 class KeyExists(KeyError):
     pass
 
@@ -49,21 +49,20 @@ def Created(location):
     web.ctx.status = "201 Created"
     web.header("Location", location)
 
-
 class BadRequest(web.webapi.HTTPError):
     """`400 Bad Request` error."""
     def __init__(self, message="bad request"):
         self.message = message
-        status = '400 Bad Request'
-        headers = {'Content-Type': 'text/html'}
+        status = "400 Bad Request"
+        headers = {"Content-Type": "text/html"}
         web.webapi.HTTPError.__init__(self, status, headers, message)
 
 class NotFound(web.webapi.HTTPError):
     """`404 Not Found` error."""
     def __init__(self, message="not found"):
         self.message = message
-        status = '404 Not Found'
-        headers = {'Content-Type': 'text/html'}
+        status = "404 Not Found"
+        headers = {"Content-Type": "text/html"}
         web.webapi.HTTPError.__init__(self, status, headers, message)
 
 class Unauthorized(web.webapi.HTTPError):
@@ -71,34 +70,31 @@ class Unauthorized(web.webapi.HTTPError):
     def __init__(self, message="unauthorized"):
         self.message = message
         status = "401 Unauthorized"
-        headers = {'Content-Type': 'text/html'}
+        headers = {"Content-Type": "text/html"}
         web.webapi.HTTPError.__init__(self, status, headers, message)
 
-
 class Forbidden(web.webapi.HTTPError):
     """`403 Forbidden` error."""
     def __init__(self, message="forbidden"):
         self.message = message
         status = "403 Forbidden"
-        headers = {'Content-Type': 'text/html'}
+        headers = {"Content-Type": "text/html"}
         web.webapi.HTTPError.__init__(self, status, headers, message)
 
-
 class Conflict(web.webapi.HTTPError):
     """`409 Conflict` error."""
     def __init__(self, message="conflict"):
         self.message = message
         status = "409 Conflict"
-        headers = {'Content-Type': 'text/html'}
+        headers = {"Content-Type": "text/html"}
         web.webapi.HTTPError.__init__(self, status, headers, message)
 
-
 class NotAcceptable(web.webapi.HTTPError):
     """`406 Not Acceptable` error."""
     def __init__(self, message="not acceptable"):
         self.message = message
         status = "406 Not Acceptable"
-        headers = {'Content-Type': 'text/html'}
+        headers = {"Content-Type": "text/html"}
         web.webapi.HTTPError.__init__(self, status, headers, message)
 
 class NotImplemented(web.webapi.HTTPError):
@@ -106,10 +102,9 @@ class NotImplemented(web.webapi.HTTPError):
     def __init__(self, message="not implemented"):
         self.message = message
         status = "501 Not Implemented"
-        headers = {'Content-Type': 'text/html'}
+        headers = {"Content-Type": "text/html"}
         web.webapi.HTTPError.__init__(self, status, headers, message)
 
-
 # The folowing helpers are for managing exceptions and transforming them into http errors:
 
 class exceptionManager(object):
@@ -125,7 +120,6 @@ class exceptionManager(object):
         if not self.raise_all and exc_type in self.exceptions:
             return not self.handle(exc_type, exc_value, traceback)
 
-
 class exceptionsToHTTPError(exceptionManager):
     def __init__(self, message=None, exceptions=None, **kwargs):
         if message != None:
@@ -138,12 +132,11 @@ class exceptionsToHTTPError(exceptionManager):
         raise self.HTTPError(message=msg)
 
 class nargString(list):
-    """
-    This object only implements format, which it redirects to one
+    """This object only implements format, which it redirects to one
     of the strings given to its __init__ according to how many
     arguments are given to format.
-    """
 
+    """
     def __init__(self, *args):
         list.__init__(self, args)
 
@@ -152,7 +145,6 @@ class nargString(list):
             raise TypeError("To many arguments for string formatting.")
         return self[len(args) + len(kwargs)].format(*args, **kwargs)
 
-
 class mightFailLookup(exceptionsToHTTPError):
     def __init__(self, name=None, message=None, exceptions=None, **kwargs):
         if len(kwargs) == 1:
@@ -161,7 +153,6 @@ class mightFailLookup(exceptionsToHTTPError):
             kwargs["name"] = name
         exceptionsToHTTPError.__init__(self, message, exceptions, **kwargs)
 
-
 class mightNotFound(mightFailLookup):
     exceptions = (KeyError, IndexError)
     HTTPError = NotFound
@@ -194,12 +185,13 @@ class URLMap(object):
 
     *args defines the path to be used. See help(URLMap.__call__) for
     further information.
-    """
 
+    """
     def __init__(self, var="/([^/]+)", endvar="/((?:[^.^/]+\.?(?=.*\..*[^/]$))*[^.^/]+)"):
         """Instanciates a URLMap object.
         The argument var is used instead of () when it is passed to the
         mapper. It should be a regex defining the default 'variable' component.
+
         """
         self.map = []
         self.var = var
@@ -213,8 +205,8 @@ class URLMap(object):
         a list then a regex is generated to allow for any of the values in the
         list. Else the value is used. The final values of the components are
         always separated by a "/" in the final path.
-        """
 
+        """
         last_is_var = False
 
         components = []
@@ -234,7 +226,6 @@ class URLMap(object):
             else:
                 components.append("/%s" % arg)
 
-
         # format = kwargs.get("format", True)
         # if format:
         #     components.append(format if isinstance(format, basestring) else "(?:(\.[^/^.]+)?|/$)")
@@ -249,6 +240,7 @@ class URLMap(object):
     def __getattr__(self, name):
         """Maps all attributes to a wrapper function calling self(name, *args, **kwargs),
         see help(URLMap.__call__).
+
         """
         def wrapper(*args, **kwargs):
             return self(name, *args, **kwargs)
@@ -259,6 +251,7 @@ class URLMap(object):
         Also cleans that list because it should only be called once
         anyway, if you don't want it to be cleared you can simply
         use iter(self.map).
+
         """
         map = self.map
         self.map = []
@@ -276,8 +269,8 @@ def default_renderer(format, authorized, content, name_hint):
         url = web.ctx.path + web.ctx.query
         if url.endswith(".html"):
             url = url[:-5]
-        urls = [(x, "%s.%s" % (url, x)) for x in authorized if x != 'html']
-        templates = os.path.join(os.path.dirname(__file__), 'templates/')
+        urls = [(x, "%s.%s" % (url, x)) for x in authorized if x != "html"]
+        templates = os.path.join(os.path.dirname(__file__), "templates/")
         render = web.template.render(templates)
         return render.response(web.ctx.home, web.ctx.path.split("/"), urls, pyhtml.dumps(content, obj_name=name_hint, indent=4))
     elif format == "json":
@@ -285,7 +278,6 @@ def default_renderer(format, authorized, content, name_hint):
     else:
         return str(content)
 
-
 class HTTPCompatible(object):
     """Decorator factory used to transform the output of a backend function
     to be suited for the web.
@@ -293,8 +285,8 @@ class HTTPCompatible(object):
     Renders the ouput according to a renderer function.
     Sets correct Content-Type according to the format.
     Maps exceptions to coresponding HTTP error codes.
-    """
 
+    """
     return_logs = False
 
     known_mimes = {
@@ -326,8 +318,8 @@ class HTTPCompatible(object):
 
         name_hint is used if the output format requires the outer-most level to have a name.
         If it set to None it will be expected the return is a one element dict.
-        """
 
+        """
         self.default = default
         self.renderer = renderer
 
@@ -353,8 +345,8 @@ class HTTPCompatible(object):
     def __call__(self, f):
         """Returns a wrapper around f in order to make its return value suitable
         for the web.
-        """
 
+        """
         if self.render == None:
             # We must guess if we want to render or not.
             self.render = f.__name__ in ["GET"]
@@ -450,40 +442,39 @@ class HTTPCompatible(object):
         self.original_function = f
         return wrapper
 
-
 def get_data(name=None, mandatory=[], authorized=[], forbidden=[]):
     data = web.data()
 
     if not data:
-        raise web.badrequest('You must suply some data. (mandatory: %s, authorized: %s)' % (mandatory, authorized))
+        raise web.badrequest("You must suply some data. (mandatory: %s, authorized: %s)" % (mandatory, authorized))
 
-    if not 'CONTENT_TYPE' in web.ctx.env:
-        raise web.badrequest('You must specify a Content-Type.')
+    if not "CONTENT_TYPE" in web.ctx.env:
+        raise web.badrequest("You must specify a Content-Type.")
 
-    ctype = web.ctx.env.get('CONTENT_TYPE')
+    ctype = web.ctx.env.get("CONTENT_TYPE")
 
     try:
-        if 'text/xml' in ctype or  'application/xml' in ctype:
+        if "text/xml" in ctype or  "application/xml" in ctype:
             data, dname = pyxml.loads(data, retname=True)
-            print "received '%s'" % dname
+            print "received \"%s\"" % dname
             print data
             if name and dname != name: data = None
-        elif 'application/json' in ctype:
+        elif "application/json" in ctype:
             data = json.loads(data)
             if name: data = data.get(name, None)
         else:
-            raise web.badrequest('Content-type \'%s\' is not allowed.' % ctype)
+            raise web.badrequest("Content-type \"%s\" is not allowed." % ctype)
     except (AttributeError, ValueError):
-        raise web.badrequest('Could not decode input data (%s).' % data)
+        raise web.badrequest("Could not decode input data (%s)." % data)
 
     if name and data == None:
-        raise web.badrequest('The object you are sending does not contain a \'%s\' entry.' % name)
+        raise web.badrequest("The object you are sending does not contain a \"%s\" entry." % name)
 
     if not all(x in data for x in mandatory):
-        raise web.badrequest('The following elements are missing, %s' % [x for x in mandatory if x not in data])
+        raise web.badrequest("The following elements are missing, \"%s\"" % [x for x in mandatory if x not in data])
     if any(x in data for x in forbidden):
-        raise web.badrequest('You are not allowed to send any of %s' % [x for x in forbidden if x in data])
+        raise web.badrequest("You are not allowed to send any of \"%s\"" % [x for x in forbidden if x in data])
     if authorized and any(x not in authorized for x in data):
-        raise web.badrequest('You are not allowed to send any of %s' % ([x for x in data if x not in authorized]))
+        raise web.badrequest("You are not allowed to send any of \"%s\"" % ([x for x in data if x not in authorized]))
 
     return data
-- 
GitLab