This is an automated email from the git hooks/post-receive script.

johanvdw-guest pushed a commit to branch master
in repository owslib.

commit 58865a94d623537109b1d21e42529bee68e40a6d
Author: Johan Van de Wauw <johan.vandew...@gmail.com>
Date:   Tue Dec 23 22:09:23 2014 +0100

    Imported Upstream version 0.8.12
---
 OWSLib.egg-info/PKG-INFO         |   2 +-
 OWSLib.egg-info/requires.txt     |   2 +-
 PKG-INFO                         |   2 +-
 VERSION.txt                      |   2 +-
 owslib/__init__.py               |   2 +-
 owslib/feature/__init__.py       |   8 +-
 owslib/feature/wfs100.py         |  20 ++-
 owslib/feature/wfs110.py         |  21 ++-
 owslib/feature/wfs200.py         |  29 ++--
 owslib/fes.py                    |  28 +++-
 owslib/swe/observation/sos100.py |  27 ++--
 owslib/swe/observation/sos200.py |  31 ++--
 owslib/util.py                   |  29 +++-
 owslib/wfs.py                    |  13 +-
 owslib/wms.py                    |   5 +-
 owslib/wmts.py                   | 333 +++++++++++++++++++++++++--------------
 owslib/wps.py                    |   5 +-
 requirements-dev.txt             |   1 +
 18 files changed, 356 insertions(+), 204 deletions(-)

diff --git a/OWSLib.egg-info/PKG-INFO b/OWSLib.egg-info/PKG-INFO
index 9227ad9..c16916a 100644
--- a/OWSLib.egg-info/PKG-INFO
+++ b/OWSLib.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: OWSLib
-Version: 0.8.10
+Version: 0.8.12
 Summary: OGC Web Service utility library
 Home-page: http://geopython.github.io/OWSLib
 Author: Tom Kralidis
diff --git a/OWSLib.egg-info/requires.txt b/OWSLib.egg-info/requires.txt
index 306406a..8b6b3b8 100644
--- a/OWSLib.egg-info/requires.txt
+++ b/OWSLib.egg-info/requires.txt
@@ -1,2 +1,2 @@
 python-dateutil>=1.5
-pytz
\ No newline at end of file
+pytz
diff --git a/PKG-INFO b/PKG-INFO
index 9227ad9..c16916a 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: OWSLib
-Version: 0.8.10
+Version: 0.8.12
 Summary: OGC Web Service utility library
 Home-page: http://geopython.github.io/OWSLib
 Author: Tom Kralidis
diff --git a/VERSION.txt b/VERSION.txt
index ef50561..7eff8ab 100644
--- a/VERSION.txt
+++ b/VERSION.txt
@@ -1 +1 @@
-0.8.10
+0.8.12
diff --git a/owslib/__init__.py b/owslib/__init__.py
index c2c9176..0a5ca3c 100644
--- a/owslib/__init__.py
+++ b/owslib/__init__.py
@@ -1 +1 @@
-__version__ = '0.8.10'
+__version__ = '0.8.12'
diff --git a/owslib/feature/__init__.py b/owslib/feature/__init__.py
index 5a9b224..91030d1 100644
--- a/owslib/feature/__init__.py
+++ b/owslib/feature/__init__.py
@@ -11,7 +11,7 @@ from urllib import urlencode
 import logging
 from owslib.util import log
 
-class WebFeatureService_:
+class WebFeatureService_(object):
     """Base class for WebFeatureService implementations"""
 
     def getBBOXKVP (self,bbox,typename):
@@ -76,7 +76,7 @@ class WebFeatureService_:
 
     def getGETGetFeatureRequest(self, typename=None, filter=None, bbox=None, 
featureid=None,
                    featureversion=None, propertyname=None, 
maxfeatures=None,storedQueryID=None, storedQueryParams={},
-                   outputFormat=None, method='Get'):
+                   outputFormat=None, method='Get', startindex=None):
         """Formulate proper GetFeature request using KVP encoding
         ----------
         typename : list
@@ -97,6 +97,8 @@ class WebFeatureService_:
             Qualified name of the HTTP DCP method to use.
         outputFormat: string (optional)
             Requested response format of the request.
+        startindex: int (optional)
+            Start position to return feature set (paging in combination with 
maxfeatures)
 
         There are 3 different modes of use
 
@@ -126,6 +128,8 @@ class WebFeatureService_:
             request['featureversion'] = str(featureversion)
         if maxfeatures: 
             request['maxfeatures'] = str(maxfeatures)
+        if startindex:
+            request['startindex'] = str(startindex)
         if storedQueryID: 
             request['storedQuery_id']=str(storedQueryID)
             for param in storedQueryParams:
diff --git a/owslib/feature/wfs100.py b/owslib/feature/wfs100.py
index 9fb25e5..13975f7 100644
--- a/owslib/feature/wfs100.py
+++ b/owslib/feature/wfs100.py
@@ -49,7 +49,7 @@ class WebFeatureService_1_0_0(object):
 
     Implements IWebFeatureService.
     """
-    def __new__(self,url, version, xml, parse_remote_metadata=False):
+    def __new__(self,url, version, xml, parse_remote_metadata=False, 
timeout=30):
         """ overridden __new__ method 
         
         @type url: string
@@ -58,10 +58,11 @@ class WebFeatureService_1_0_0(object):
         @param xml: elementtree object
         @type parse_remote_metadata: boolean
         @param parse_remote_metadata: whether to fully process MetadataURL 
elements
+        @param timeout: time (in seconds) after which requests should timeout
         @return: initialized WebFeatureService_1_0_0 object
         """
         obj=object.__new__(self)
-        obj.__init__(url, version, xml, parse_remote_metadata)
+        obj.__init__(url, version, xml, parse_remote_metadata, timeout)
         return obj
     
     def __getitem__(self,name):
@@ -72,10 +73,11 @@ class WebFeatureService_1_0_0(object):
             raise KeyError, "No content named %s" % name
     
     
-    def __init__(self, url, version, xml=None, parse_remote_metadata=False):
+    def __init__(self, url, version, xml=None, parse_remote_metadata=False, 
timeout=30):
         """Initialize."""
         self.url = url
         self.version = version
+        self.timeout = timeout
         self._capabilities = None
         reader = WFSCapabilitiesReader(self.version)
         if xml:
@@ -113,12 +115,12 @@ class WebFeatureService_1_0_0(object):
         self.exceptions = [f.text for f \
                 in self._capabilities.findall('Capability/Exception/Format')]
       
-    def getcapabilities(self, timeout=30):
+    def getcapabilities(self):
         """Request and return capabilities document from the WFS as a 
         file-like object.
         NOTE: this is effectively redundant now"""
         reader = WFSCapabilitiesReader(self.version)
-        return urlopen(reader.capabilities_url(self.url), timeout=timeout)
+        return urlopen(reader.capabilities_url(self.url), timeout=self.timeout)
     
     def items(self):
         '''supports dict-like items() access'''
@@ -129,7 +131,8 @@ class WebFeatureService_1_0_0(object):
     
     def getfeature(self, typename=None, filter=None, bbox=None, featureid=None,
                    featureversion=None, propertyname=['*'], maxfeatures=None,
-                   srsname=None, outputFormat=None, 
method='{http://www.opengis.net/wfs}Get'):
+                   srsname=None, outputFormat=None, 
method='{http://www.opengis.net/wfs}Get',
+                   startindex=None):
         """Request and return feature data as a file-like object.
         
         Parameters
@@ -154,6 +157,8 @@ class WebFeatureService_1_0_0(object):
             EPSG code to request the data in
         outputFormat: string (optional)
             Requested response format of the request.
+        startindex: int (optional)
+            Start position to return feature set (paging in combination with 
maxfeatures)
 
             
         There are 3 different modes of use
@@ -186,13 +191,14 @@ class WebFeatureService_1_0_0(object):
             request['propertyname'] = ','.join(propertyname)
         if featureversion: request['featureversion'] = str(featureversion)
         if maxfeatures: request['maxfeatures'] = str(maxfeatures)
+        if startindex: request['startindex'] = str(startindex)
 
         if outputFormat is not None:
             request["outputFormat"] = outputFormat
 
         data = urlencode(request)
         log.debug("Making request: %s?%s" % (base_url, data))
-        u = openURL(base_url, data, method)
+        u = openURL(base_url, data, method, timeout=self.timeout)
         
         
         # check for service exceptions, rewrap, and return
diff --git a/owslib/feature/wfs110.py b/owslib/feature/wfs110.py
index 590a8da..8f30c03 100644
--- a/owslib/feature/wfs110.py
+++ b/owslib/feature/wfs110.py
@@ -32,7 +32,7 @@ class WebFeatureService_1_1_0(WebFeatureService_):
 
     Implements IWebFeatureService.
     """
-    def __new__(self,url, version, xml, parse_remote_metadata=False):
+    def __new__(self,url, version, xml, parse_remote_metadata=False, 
timeout=30):
         """ overridden __new__ method
 
         @type url: string
@@ -41,10 +41,11 @@ class WebFeatureService_1_1_0(WebFeatureService_):
         @param xml: elementtree object
         @type parse_remote_metadata: boolean
         @param parse_remote_metadata: whether to fully process MetadataURL 
elements
+        @param timeout: time (in seconds) after which requests should timeout
         @return: initialized WebFeatureService_1_1_0 object
         """
         obj=object.__new__(self)
-        obj.__init__(url, version, xml, parse_remote_metadata)
+        obj.__init__(url, version, xml, parse_remote_metadata, timeout)
         return obj
 
     def __getitem__(self,name):
@@ -55,10 +56,11 @@ class WebFeatureService_1_1_0(WebFeatureService_):
             raise KeyError, "No content named %s" % name
 
 
-    def __init__(self, url, version, xml=None, parse_remote_metadata=False):
+    def __init__(self, url, version, xml=None, parse_remote_metadata=False, 
timeout=30):
         """Initialize."""
         self.url = url
         self.version = version
+        self.timeout = timeout
         self._capabilities = None
         self.owscommon = OwsCommon('1.0.0')
         reader = WFSCapabilitiesReader(self.version)
@@ -99,12 +101,12 @@ class WebFeatureService_1_1_0(WebFeatureService_):
         self.exceptions = [f.text for f \
                 in self._capabilities.findall('Capability/Exception/Format')]
 
-    def getcapabilities(self, timeout=30):
+    def getcapabilities(self):
         """Request and return capabilities document from the WFS as a
         file-like object.
         NOTE: this is effectively redundant now"""
         reader = WFSCapabilitiesReader(self.version)
-        return urlopen(reader.capabilities_url(self.url), timeout=timeout)
+        return urlopen(reader.capabilities_url(self.url), timeout=self.timeout)
 
     def items(self):
         '''supports dict-like items() access'''
@@ -115,7 +117,8 @@ class WebFeatureService_1_1_0(WebFeatureService_):
 
     def getfeature(self, typename=None, filter=None, bbox=None, featureid=None,
                    featureversion=None, propertyname=['*'], maxfeatures=None,
-                   srsname=None, outputFormat=None, method='Get'):
+                   srsname=None, outputFormat=None, method='Get',
+                   startindex=None):
         """Request and return feature data as a file-like object.
 
         Parameters
@@ -140,6 +143,8 @@ class WebFeatureService_1_1_0(WebFeatureService_):
             EPSG code to request the data in
         outputFormat: string (optional)
             Requested response format of the request.
+        startindex: int (optional)
+            Start position to return feature set (paging in combination with 
maxfeatures)
 
         There are 3 different modes of use
 
@@ -194,12 +199,14 @@ class WebFeatureService_1_1_0(WebFeatureService_):
             request['featureversion'] = str(featureversion)
         if maxfeatures is not None:
             request['maxfeatures'] = str(maxfeatures)
+        if startindex is not None:
+            request['startindex'] = str(startindex)
         if outputFormat is not None:
             request["outputFormat"] = outputFormat
 
         data = urlencode(request)
         log.debug("Making request: %s?%s" % (base_url, data))
-        u = openURL(base_url, data, method)
+        u = openURL(base_url, data, method, timeout=self.timeout)
 
         # check for service exceptions, rewrap, and return
         # We're going to assume that anything with a content-length > 32k
diff --git a/owslib/feature/wfs200.py b/owslib/feature/wfs200.py
index 2659d38..b4175ae 100644
--- a/owslib/feature/wfs200.py
+++ b/owslib/feature/wfs200.py
@@ -40,7 +40,7 @@ class WebFeatureService_2_0_0(WebFeatureService_):
 
     Implements IWebFeatureService.
     """
-    def __new__(self,url, version, xml, parse_remote_metadata=False):
+    def __new__(self,url, version, xml, parse_remote_metadata=False, 
timeout=30):
         """ overridden __new__ method 
         
         @type url: string
@@ -49,10 +49,11 @@ class WebFeatureService_2_0_0(WebFeatureService_):
         @param xml: elementtree object
         @type parse_remote_metadata: boolean
         @param parse_remote_metadata: whether to fully process MetadataURL 
elements
+        @param timeout: time (in seconds) after which requests should timeout
         @return: initialized WebFeatureService_2_0_0 object
         """
         obj=object.__new__(self)
-        obj.__init__(url, version, xml, parse_remote_metadata)
+        obj.__init__(url, version, xml, parse_remote_metadata, timeout)
         return obj
     
     def __getitem__(self,name):
@@ -63,12 +64,13 @@ class WebFeatureService_2_0_0(WebFeatureService_):
             raise KeyError, "No content named %s" % name
     
     
-    def __init__(self, url,  version, xml=None, parse_remote_metadata=False):
+    def __init__(self, url,  version, xml=None, parse_remote_metadata=False, 
timeout=30):
         """Initialize."""
         if log.isEnabledFor(logging.DEBUG):
             log.debug('building WFS %s'%url)
         self.url = url
         self.version = version
+        self.timeout = timeout
         self._capabilities = None
         reader = WFSCapabilitiesReader(self.version)
         if xml:
@@ -119,12 +121,12 @@ class WebFeatureService_2_0_0(WebFeatureService_):
         self.exceptions = [f.text for f \
                 in self._capabilities.findall('Capability/Exception/Format')]
       
-    def getcapabilities(self, timeout=30):
+    def getcapabilities(self):
         """Request and return capabilities document from the WFS as a 
         file-like object.
         NOTE: this is effectively redundant now"""
         reader = WFSCapabilitiesReader(self.version)
-        return urlopen(reader.capabilities_url(self.url), timeout=timeout)
+        return urlopen(reader.capabilities_url(self.url), timeout=self.timeout)
     
     def items(self):
         '''supports dict-like items() access'''
@@ -135,7 +137,7 @@ class WebFeatureService_2_0_0(WebFeatureService_):
     
     def getfeature(self, typename=None, filter=None, bbox=None, featureid=None,
                    featureversion=None, propertyname=None, 
maxfeatures=None,storedQueryID=None, storedQueryParams={},
-                   method='Get', timeout=30, outputFormat=None):
+                   method='Get', outputFormat=None, startindex=None):
         """Request and return feature data as a file-like object.
         #TODO: NOTE: have changed property name from ['*'] to None - check the 
use of this in WFS 2.0
         Parameters
@@ -156,10 +158,10 @@ class WebFeatureService_2_0_0(WebFeatureService_):
             Maximum number of features to be returned.
         method : string
             Qualified name of the HTTP DCP method to use.
-        timeout : number
-            A timeout value (in seconds) for the request.
         outputFormat: string (optional)
             Requested response format of the request.
+        startindex: int (optional)
+            Start position to return feature set (paging in combination with 
maxfeatures)
 
         There are 3 different modes of use
 
@@ -167,7 +169,6 @@ class WebFeatureService_2_0_0(WebFeatureService_):
         2) typename and filter (==query) (more expressive)
         3) featureid (direct access to known features)
         """
-
         url = data = None
         if typename and type(typename) == type(""):
             typename = [typename]
@@ -175,7 +176,7 @@ class WebFeatureService_2_0_0(WebFeatureService_):
             (url) = self.getGETGetFeatureRequest(typename, filter, bbox, 
featureid,
                                                  featureversion, propertyname,
                                                  maxfeatures, storedQueryID,
-                                                 storedQueryParams, 
outputFormat)
+                                                 storedQueryParams, 
outputFormat, 'Get', startindex)
             if log.isEnabledFor(logging.DEBUG):
                 log.debug('GetFeature WFS GET url %s'% url)
         else:
@@ -183,7 +184,7 @@ class WebFeatureService_2_0_0(WebFeatureService_):
 
 
         # If method is 'Post', data will be None here
-        u = urlopen(url, data, timeout)
+        u = urlopen(url, data, self.timeout)
         
         # check for service exceptions, rewrap, and return
         # We're going to assume that anything with a content-length > 32k
@@ -240,7 +241,7 @@ class WebFeatureService_2_0_0(WebFeatureService_):
         return u.read()
         
         
-    def _getStoredQueries(self, timeout=30):
+    def _getStoredQueries(self):
         ''' gets descriptions of the stored queries available on the server '''
         sqs=[]
         #This method makes two calls to the WFS - one ListStoredQueries, and 
one DescribeStoredQueries. The information is then
@@ -255,7 +256,7 @@ class WebFeatureService_2_0_0(WebFeatureService_):
 
         request = {'service': 'WFS', 'version': self.version, 'request': 
'ListStoredQueries'}
         encoded_request = urlencode(request)
-        u = urlopen(base_url, data=encoded_request, timeout=timeout)
+        u = urlopen(base_url, data=encoded_request, timeout=self.timeout)
         tree=etree.fromstring(u.read())
         tempdict={}       
         for sqelem in tree[:]:
@@ -275,7 +276,7 @@ class WebFeatureService_2_0_0(WebFeatureService_):
             base_url = self.url
         request = {'service': 'WFS', 'version': self.version, 'request': 
'DescribeStoredQueries'}
         encoded_request = urlencode(request)
-        u = urlopen(base_url, data=encoded_request, timeout=timeout)
+        u = urlopen(base_url, data=encoded_request, timeout=to)
         tree=etree.fromstring(u.read())
         tempdict2={} 
         for sqelem in tree[:]:
diff --git a/owslib/fes.py b/owslib/fes.py
index 5fb9816..d69b095 100644
--- a/owslib/fes.py
+++ b/owslib/fes.py
@@ -104,7 +104,7 @@ class FilterRequest(object):
 
         return self._root
        
-    def setConstraint(self, constraint):
+    def setConstraint(self, constraint, tostring=False):
         """
         Construct and process a  GetRecords request
     
@@ -112,12 +112,17 @@ class FilterRequest(object):
         ----------
 
         - constraint: An OgcExpression object
+        - tostring (optional): return as string
 
         """
         self._root.append(constraint.toXML())
+
+        if tostring:
+            return util.element_to_string(self._root, xml_declaration=False)
+
         return self._root
 
-    def setConstraintList(self, constraints):
+    def setConstraintList(self, constraints, tostring=False):
         """
         Construct and process a  GetRecords request
     
@@ -135,15 +140,20 @@ class FilterRequest(object):
 
                        [[a,b],[c],[d],[e]] or [[a,b],c,d,e]
                        (a && b) || c || d || e
+        - tostring (optional): return as string
 
         """
         ors = []
         if len(constraints) == 1:
             if isinstance(constraints[0], OgcExpression):
-                return self.setConstraint(constraints[0])
+                flt = self.setConstraint(constraints[0])
             else:
                 self._root.append(And(operations=constraints[0]).toXML())
-                return self._root
+                flt = self._root
+            if tostring:
+                return util.element_to_string(flt, xml_declaration=False)
+            else:
+                return flt
 
         for c in constraints:
             if isinstance(c, OgcExpression):
@@ -159,6 +169,10 @@ class FilterRequest(object):
                     ors.append(And(operations=ands))
 
         self._root.append(Or(operations=ors).toXML())
+
+        if tostring:
+            return util.element_to_string(self._root, xml_declaration=False)
+
         return self._root
 
 
@@ -340,12 +354,15 @@ class PropertyIsBetween(OgcExpression):
         
 class BBox(OgcExpression):
     """Construct a BBox, two pairs of coordinates (west-south and 
east-north)"""
-    def __init__(self, bbox):
+    def __init__(self, bbox, crs=None):
         self.bbox = bbox
+        self.crs = crs
     def toXML(self):
         tmp = etree.Element(util.nspath_eval('ogc:BBOX', namespaces))
         etree.SubElement(tmp, util.nspath_eval('ogc:PropertyName', 
namespaces)).text = 'ows:BoundingBox'
         tmp2 = etree.SubElement(tmp, util.nspath_eval('gml:Envelope', 
namespaces))
+        if self.crs is not None:
+            tmp2.set('srsName', self.crs)
         etree.SubElement(tmp2, util.nspath_eval('gml:lowerCorner', 
namespaces)).text = '%s %s' % (self.bbox[0], self.bbox[1])
         etree.SubElement(tmp2, util.nspath_eval('gml:upperCorner', 
namespaces)).text = '%s %s' % (self.bbox[2], self.bbox[3])
         return tmp
@@ -390,3 +407,4 @@ class Not(UnaryLogicOpType):
     def __init__(self, operations):
         super(Not,self).__init__('ogc:Not', operations)
 
+
diff --git a/owslib/swe/observation/sos100.py b/owslib/swe/observation/sos100.py
index cc070df..698c53a 100644
--- a/owslib/swe/observation/sos100.py
+++ b/owslib/swe/observation/sos100.py
@@ -30,7 +30,7 @@ class SensorObservationService_1_0_0(object):
 
     def __getitem__(self,id):
         ''' check contents dictionary to allow dict like access to service 
observational offerings'''
-        if name in self.__getattribute__('contents').keys():
+        if id in self.__getattribute__('contents').keys():
             return self.__getattribute__('contents')[id]
         else:
             raise KeyError, "No Observational Offering with id: %s" % id
@@ -67,17 +67,17 @@ class SensorObservationService_1_0_0(object):
         raise KeyError("No operation named %s" % name)
 
     def _build_metadata(self):
-        """ 
+        """
             Set up capabilities metadata objects
         """
         # ows:ServiceIdentification metadata
         service_id_element = 
self._capabilities.find(nspath_eval('ows:ServiceIdentification', namespaces))
         self.identification = ows.ServiceIdentification(service_id_element)
-        
+
         # ows:ServiceProvider metadata
         service_provider_element = 
self._capabilities.find(nspath_eval('ows:ServiceProvider', namespaces))
         self.provider = ows.ServiceProvider(service_provider_element)
-            
+
         # ows:OperationsMetadata metadata
         self.operations=[]
         for elem in 
self._capabilities.findall(nspath_eval('ows:OperationsMetadata/ows:Operation', 
namespaces)):
@@ -115,7 +115,7 @@ class SensorObservationService_1_0_0(object):
 
         assert isinstance(procedure, str)
         request['procedure'] = procedure
-        
+
         url_kwargs = {}
         if 'timeout' in kwargs:
             url_kwargs['timeout'] = kwargs.pop('timeout') # Client specified 
timeout value
@@ -124,13 +124,13 @@ class SensorObservationService_1_0_0(object):
         if kwargs:
             for kw in kwargs:
                 request[kw]=kwargs[kw]
-       
-        data = urlencode(request)        
+
+        data = urlencode(request)
 
 
         response = openURL(base_url, data, method, username=self.username, 
password=self.password, **url_kwargs).read()
 
-            
+
 
         tr = etree.fromstring(response)
 
@@ -176,7 +176,7 @@ class SensorObservationService_1_0_0(object):
         # Optional Fields
         if eventTime is not None:
             request['eventTime'] = eventTime
-        
+
         url_kwargs = {}
         if 'timeout' in kwargs:
             url_kwargs['timeout'] = kwargs.pop('timeout') # Client specified 
timeout value
@@ -185,7 +185,7 @@ class SensorObservationService_1_0_0(object):
             for kw in kwargs:
                 request[kw]=kwargs[kw]
 
-        data = urlencode(request)        
+        data = urlencode(request)
 
         response = openURL(base_url, data, method, username=self.username, 
password=self.password, **kwargs).read()
         try:
@@ -193,13 +193,13 @@ class SensorObservationService_1_0_0(object):
             if tr.tag == nspath_eval("ows:ExceptionReport", namespaces):
                 raise ows.ExceptionReport(tr)
             else:
-                return response                
+                return response
         except ows.ExceptionReport:
             raise
         except BaseException:
             return response
 
-    def get_operation_by_name(self, name): 
+    def get_operation_by_name(self, name):
         """
             Return a Operation item by name, case insensitive
         """
@@ -266,7 +266,7 @@ class SosObservationOffering(object):
 
     def __str__(self):
         return 'Offering id: %s, name: %s' % (self.id, self.name)
-        
+
 class SosCapabilitiesReader(object):
     def __init__(self, version="1.0.0", url=None, username=None, 
password=None):
         self.version = version
@@ -316,4 +316,3 @@ class SosCapabilitiesReader(object):
         if not isinstance(st, str):
             raise ValueError("String must be of type string, not %s" % 
type(st))
         return etree.fromstring(st)
-
diff --git a/owslib/swe/observation/sos200.py b/owslib/swe/observation/sos200.py
index 0cf2bb2..a306dcc 100644
--- a/owslib/swe/observation/sos200.py
+++ b/owslib/swe/observation/sos200.py
@@ -32,7 +32,7 @@ class SensorObservationService_2_0_0(object):
 
     def __getitem__(self,id):
         ''' check contents dictionary to allow dict like access to service 
observational offerings'''
-        if name in self.__getattribute__('contents').keys():
+        if id in self.__getattribute__('contents').keys():
             return self.__getattribute__('contents')[id]
         else:
             raise KeyError, "No Observational Offering with id: %s" % id
@@ -56,8 +56,8 @@ class SensorObservationService_2_0_0(object):
 
         # Avoid building metadata if the response is an Exception
         se = self._capabilities.find(nspath_eval('ows:ExceptionReport', 
namespaces))
-        if se is not None: 
-            raise ows.ExceptionReport(se) 
+        if se is not None:
+            raise ows.ExceptionReport(se)
 
         # build metadata objects
         self._build_metadata()
@@ -70,17 +70,17 @@ class SensorObservationService_2_0_0(object):
         raise KeyError("No operation named %s" % name)
 
     def _build_metadata(self):
-        """ 
+        """
             Set up capabilities metadata objects
         """
         # ows:ServiceIdentification metadata
         service_id_element = 
self._capabilities.find(nspath_eval('ows:ServiceIdentification', namespaces))
         self.identification = ows.ServiceIdentification(service_id_element)
-        
+
         # ows:ServiceProvider metadata
         service_provider_element = 
self._capabilities.find(nspath_eval('ows:ServiceProvider', namespaces))
         self.provider = ows.ServiceProvider(service_provider_element)
-            
+
         # ows:OperationsMetadata metadata
         self.operations= []
         for elem in 
self._capabilities.findall(nspath_eval('ows:OperationsMetadata/ows:Operation', 
namespaces)):
@@ -118,7 +118,7 @@ class SensorObservationService_2_0_0(object):
 
         assert isinstance(procedure, str)
         request['procedure'] = procedure
-        
+
         url_kwargs = {}
         if 'timeout' in kwargs:
             url_kwargs['timeout'] = kwargs.pop('timeout') # Client specified 
timeout value
@@ -127,8 +127,8 @@ class SensorObservationService_2_0_0(object):
         if kwargs:
             for kw in kwargs:
                 request[kw]=kwargs[kw]
-       
-        data = urlencode(request)        
+
+        data = urlencode(request)
 
         response = openURL(base_url, data, method, username=self.username, 
password=self.password, **url_kwargs).read()
         tr = etree.fromstring(response)
@@ -155,7 +155,7 @@ class SensorObservationService_2_0_0(object):
             anything else e.g. vendor specific parameters
         """
 
-        base_url = 
self.get_operation_by_name('GetObservation').methods[method]['url']        
+        base_url = 
self.get_operation_by_name('GetObservation').methods[method]['url']
 
         request = {'service': 'SOS', 'version': self.version, 'request': 
'GetObservation'}
 
@@ -172,7 +172,7 @@ class SensorObservationService_2_0_0(object):
         # Optional Fields
         if eventTime is not None:
             request['temporalFilter'] = eventTime
-        
+
         url_kwargs = {}
         if 'timeout' in kwargs:
             url_kwargs['timeout'] = kwargs.pop('timeout') # Client specified 
timeout value
@@ -181,7 +181,7 @@ class SensorObservationService_2_0_0(object):
             for kw in kwargs:
                 request[kw]=kwargs[kw]
 
-        data = urlencode(request)        
+        data = urlencode(request)
 
         response = openURL(base_url, data, method, username=self.username, 
password=self.password, **url_kwargs).read()
         try:
@@ -189,13 +189,13 @@ class SensorObservationService_2_0_0(object):
             if tr.tag == nspath_eval("ows:ExceptionReport", namespaces):
                 raise ows.ExceptionReport(tr)
             else:
-                return response                
+                return response
         except ows.ExceptionReport:
             raise
         except BaseException:
             return response
 
-    def get_operation_by_name(self, name): 
+    def get_operation_by_name(self, name):
         """
             Return a Operation item by name, case insensitive
         """
@@ -262,7 +262,7 @@ class SosObservationOffering(object):
 
     def __str__(self):
         return 'Offering id: %s, name: %s' % (self.id, self.name)
-        
+
 class SosCapabilitiesReader(object):
     def __init__(self, version="2.0.0", url=None, username=None, 
password=None):
         self.version = version
@@ -312,4 +312,3 @@ class SosCapabilitiesReader(object):
         if not isinstance(st, str):
             raise ValueError("String must be of type string, not %s" % 
type(st))
         return etree.fromstring(st)
-
diff --git a/owslib/util.py b/owslib/util.py
index d700892..1b36730 100644
--- a/owslib/util.py
+++ b/owslib/util.py
@@ -390,28 +390,40 @@ def http_post(url=None, request=None, lang='en-US', 
timeout=10, username=None, p
         return response
 
 
-def element_to_string(element, encoding=None):
+def element_to_string(element, encoding=None, xml_declaration=False):
     """
     Returns a string from a XML object
 
     Parameters
     ----------
-    - xml:                 etree Element
+    - element: etree Element
     - encoding (optional): encoding in string form. 'utf-8', 'ISO-8859-1', etc.
+    - xml_declaration (optional): whether to include xml declaration
 
     """
+
+    output = None
+
     if encoding is None:
         encoding = "ISO-8859-1"
 
     if etree.__name__ == 'lxml.etree':
-        if encoding in ['unicode', 'utf-8']:
-            return '<?xml version="1.0" encoding="utf-8" 
standalone="no"?>\n%s' % \
-                   etree.tostring(element, encoding='unicode')
+        if xml_declaration:
+            if encoding in ['unicode', 'utf-8']:
+                output = '<?xml version="1.0" encoding="utf-8" 
standalone="no"?>\n%s' % \
+                       etree.tostring(element, encoding='unicode')
+            else:
+                output = etree.tostring(element, encoding=encoding, 
xml_declaration=True)
         else:
-            return etree.tostring(element, encoding=encoding, 
xml_declaration=True)
+                output = etree.tostring(element)
     else:
-        return '<?xml version="1.0" encoding="%s" standalone="no"?>\n%s' % 
(encoding,
-               etree.tostring(element, encoding=encoding))
+        if xml_declaration:
+            output = '<?xml version="1.0" encoding="%s" standalone="no"?>\n%s' 
% (encoding,
+                   etree.tostring(element, encoding=encoding))
+        else:
+            output = etree.tostring(element)
+
+    return output
 
 
 def xml2string(xml):
@@ -576,3 +588,4 @@ try:  # 2.7
 except:  # 2.6
     from ordereddict import OrderedDict
 
+
diff --git a/owslib/wfs.py b/owslib/wfs.py
index 3c4fc4b..3f2b6cd 100644
--- a/owslib/wfs.py
+++ b/owslib/wfs.py
@@ -14,7 +14,8 @@ Web Feature Server (WFS) methods and metadata. Factory 
function.
 """
 
 from feature import wfs100, wfs110, wfs200 
-def WebFeatureService(url, version='1.0.0', xml=None, 
parse_remote_metadata=False):
+def WebFeatureService(url, version='1.0.0', xml=None, 
parse_remote_metadata=False,
+                      timeout=30):
     ''' wfs factory function, returns a version specific WebFeatureService 
object
     
     @type url: string
@@ -23,12 +24,16 @@ def WebFeatureService(url, version='1.0.0', xml=None, 
parse_remote_metadata=Fals
     @param xml: elementtree object
     @type parse_remote_metadata: boolean
     @param parse_remote_metadata: whether to fully process MetadataURL elements
+    @param timeout: time (in seconds) after which requests should timeout
     @return: initialized WebFeatureService_2_0_0 object
     '''
     if version in  ['1.0', '1.0.0']:
-        return wfs100.WebFeatureService_1_0_0(url, version, xml, 
parse_remote_metadata)
+        return wfs100.WebFeatureService_1_0_0(url, version, xml, 
parse_remote_metadata, 
+                                              timeout=timeout)
     elif version in  ['1.1', '1.1.0']:
-        return wfs110.WebFeatureService_1_1_0(url, version, xml, 
parse_remote_metadata)
+        return wfs110.WebFeatureService_1_1_0(url, version, xml, 
parse_remote_metadata,
+                                              timeout=timeout)
     elif version in ['2.0', '2.0.0']:
-        return wfs200.WebFeatureService_2_0_0(url,  version, xml, 
parse_remote_metadata)
+        return wfs200.WebFeatureService_2_0_0(url,  version, xml, 
parse_remote_metadata,
+                                              timeout=timeout)
 
diff --git a/owslib/wms.py b/owslib/wms.py
index 045c388..e726b5c 100644
--- a/owslib/wms.py
+++ b/owslib/wms.py
@@ -1,4 +1,4 @@
-# -*- coding: ISO-8859-15 -*-
+# -*- coding: iso-8859-15 -*-
 # =============================================================================
 # Copyright (c) 2004, 2006 Sean C. Gillies
 # Copyright (c) 2005 Nuxeo SARL <http://nuxeo.com>
@@ -366,7 +366,8 @@ class ContentMetadata:
         sh = elem.find('ScaleHint') 
         self.scaleHint = None 
         if sh is not None: 
-            self.scaleHint = {'min': sh.attrib['min'], 'max': 
sh.attrib['max']} 
+            if 'min' in sh.attrib and 'max' in sh.attrib:
+                self.scaleHint = {'min': sh.attrib['min'], 'max': 
sh.attrib['max']} 
 
         attribution = elem.find('Attribution')
         if attribution is not None:
diff --git a/owslib/wmts.py b/owslib/wmts.py
index a402334..eff4e04 100644
--- a/owslib/wmts.py
+++ b/owslib/wmts.py
@@ -16,13 +16,16 @@
 
 Abstract
 --------
-The wmts module of the OWSlib package provides client-side functionality for 
fetching tiles from an OGC Web Map Tile Service (WMTS)
+The wmts module of the OWSlib package provides client-side functionality
+for fetching tiles from an OGC Web Map Tile Service (WMTS)
 
 
 Disclaimer
 ----------
-PLEASE NOTE: the owslib wmts module should be considered in early-beta state: 
it has been tested against only one WMTS server (NASA EODSIS).
-More extensive testing is needed and feedback (to br...@frogmouth.net) would 
be appreciated.
+PLEASE NOTE: the owslib wmts module should be considered in early-beta
+state: it has been tested against only one WMTS server (NASA EODSIS).
+More extensive testing is needed and feedback (to br...@frogmouth.net)
+would be appreciated.
 
 """
 
@@ -37,16 +40,48 @@ from iso import MD_Metadata
 from ows import ServiceProvider, ServiceIdentification, OperationsMetadata
 
 
+_OWS_NS = '{http://www.opengis.net/ows/1.1}'
 _WMTS_NS = '{http://www.opengis.net/wmts/1.0}'
+_XLINK_NS = '{http://www.w3.org/1999/xlink}'
+
+_ABSTRACT_TAG = _OWS_NS + 'Abstract'
+_IDENTIFIER_TAG = _OWS_NS + 'Identifier'
+_LOWER_CORNER_TAG = _OWS_NS + 'LowerCorner'
+_OPERATIONS_METADATA_TAG = _OWS_NS + 'OperationsMetadata'
+_SERVICE_IDENTIFICATION_TAG = _OWS_NS + 'ServiceIdentification'
+_SERVICE_PROVIDER_TAG = _OWS_NS + 'ServiceProvider'
+_SUPPORTED_CRS_TAG = _OWS_NS + 'SupportedCRS'
+_TITLE_TAG = _OWS_NS + 'Title'
+_UPPER_CORNER_TAG = _OWS_NS + 'UpperCorner'
+_WGS84_BOUNDING_BOX_TAG = _OWS_NS + 'WGS84BoundingBox'
+
+_CONTENTS_TAG = _WMTS_NS + 'Contents'
+_FORMAT_TAG = _WMTS_NS + 'Format'
+_INFO_FORMAT_TAG = _WMTS_NS + 'InfoFormat'
+_LAYER_TAG = _WMTS_NS + 'Layer'
+_LAYER_REF_TAG = _WMTS_NS + 'LayerRef'
+_MATRIX_HEIGHT_TAG = _WMTS_NS + 'MatrixHeight'
+_MATRIX_WIDTH_TAG = _WMTS_NS + 'MatrixWidth'
+_MAX_TILE_COL_TAG = _WMTS_NS + 'MaxTileCol'
+_MAX_TILE_ROW_TAG = _WMTS_NS + 'MaxTileRow'
+_MIN_TILE_COL_TAG = _WMTS_NS + 'MinTileCol'
+_MIN_TILE_ROW_TAG = _WMTS_NS + 'MinTileRow'
+_RESOURCE_URL_TAG = _WMTS_NS + 'ResourceURL'
+_SCALE_DENOMINATOR_TAG = _WMTS_NS + 'ScaleDenominator'
+_SERVICE_METADATA_URL_TAG = _WMTS_NS + 'ServiceMetadataURL'
+_STYLE_TAG = _WMTS_NS + 'Style'
+_THEME_TAG = _WMTS_NS + 'Theme'
+_THEMES_TAG = _WMTS_NS + 'Themes'
+_TILE_HEIGHT_TAG = _WMTS_NS + 'TileHeight'
 _TILE_MATRIX_SET_LINK_TAG = _WMTS_NS + 'TileMatrixSetLink'
 _TILE_MATRIX_SET_TAG = _WMTS_NS + 'TileMatrixSet'
 _TILE_MATRIX_SET_LIMITS_TAG = _WMTS_NS + 'TileMatrixSetLimits'
 _TILE_MATRIX_LIMITS_TAG = _WMTS_NS + 'TileMatrixLimits'
 _TILE_MATRIX_TAG = _WMTS_NS + 'TileMatrix'
-_MIN_TILE_ROW_TAG = _WMTS_NS + 'MinTileRow'
-_MAX_TILE_ROW_TAG = _WMTS_NS + 'MaxTileRow'
-_MIN_TILE_COL_TAG = _WMTS_NS + 'MinTileCol'
-_MAX_TILE_COL_TAG = _WMTS_NS + 'MaxTileCol'
+_TILE_WIDTH_TAG = _WMTS_NS + 'TileWidth'
+_TOP_LEFT_CORNER_TAG = _WMTS_NS + 'TopLeftCorner'
+
+_HREF_TAG = _XLINK_NS + 'href'
 
 
 class ServiceException(Exception):
@@ -75,35 +110,57 @@ class WebMapTileService(object):
     Implements IWebMapService.
     """
 
-    def __getitem__(self,name):
-        ''' check contents dictionary to allow dict like access to service 
layers'''
+    def __getitem__(self, name):
+        '''Check contents dictionary to allow dict like access to
+        service layers'''
         if name in self.__getattribute__('contents').keys():
             return self.__getattribute__('contents')[name]
         else:
-            raise KeyError, "No content named %s" % name
+            raise KeyError("No content named %s" % name)
+
+    def __init__(self, url, version='1.0.0', xml=None, username=None,
+                 password=None, parse_remote_metadata=False,
+                 vendor_kwargs=None):
+        """Initialize.
 
+        Parameters
+        ----------
+        url : string
+            Base URL for the WMTS service.
+        version : string
+            Optional WMTS version. Defaults to '1.0.0'.
+        xml : string
+            Optional XML content to use as the content for the initial
+            GetCapabilities request. Typically only used for testing.
+        username : string
+            Optional user name for authentication.
+        password : string
+            Optional password for authentication.
+        parse_remote_metadata: string
+            Currently unused.
+        vendor_kwargs : dict
+            Optional vendor-specific parameters to be included in all
+            requests.
 
-    def __init__(self, url, version='1.0.0', xml=None,
-                username=None, password=None, parse_remote_metadata=False
-                ):
-        """Initialize."""
+        """
         self.url = url
         self.username = username
         self.password = password
         self.version = version
+        self.vendor_kwargs = vendor_kwargs
         self._capabilities = None
 
         # Authentication handled by Reader
-        reader = WMTSCapabilitiesReader(
-                self.version, url=self.url, un=self.username, pw=self.password
-                )
+        reader = WMTSCapabilitiesReader(self.version, url=self.url,
+                                        un=self.username, pw=self.password)
 
         if xml:  # read from stored xml
             self._capabilities = reader.readString(xml)
         else:  # read from server
-            self._capabilities = reader.read(self.url)
+            self._capabilities = reader.read(self.url, self.vendor_kwargs)
 
-        # avoid building capabilities metadata if the response is a 
ServiceExceptionReport
+        # Avoid building capabilities metadata if the response is a
+        # ServiceExceptionReport.
         # TODO: check if this needs a namespace
         se = self._capabilities.find('ServiceException')
         if se is not None:
@@ -118,70 +175,78 @@ class WebMapTileService(object):
             reader = WMTSCapabilitiesReader(
                 self.version, url=self.url, un=self.username, pw=self.password
                 )
-            self._capabilities = ServiceMetadata(reader.read(self.url))
+            xml = reader.read(self.url, self.vendor_kwargs)
+            self._capabilities = ServiceMetadata(xml)
         return self._capabilities
 
     def _buildMetadata(self, parse_remote_metadata=False):
         ''' set up capabilities metadata objects '''
 
-        #serviceIdentification metadata
-        
serviceident=self._capabilities.find('{http://www.opengis.net/ows/1.1}ServiceIdentification')
-        self.identification=ServiceIdentification(serviceident)
+        # serviceIdentification metadata
+        serviceident = self._capabilities.find(_SERVICE_IDENTIFICATION_TAG)
+        self.identification = ServiceIdentification(serviceident)
 
-        #serviceProvider metadata
-        
serviceprov=self._capabilities.find('{http://www.opengis.net/ows/1.1}ServiceProvider')
-        self.provider=ServiceProvider(serviceprov)
+        # serviceProvider metadata
+        serviceprov = self._capabilities.find(_SERVICE_PROVIDER_TAG)
+        self.provider = ServiceProvider(serviceprov)
 
-        #serviceOperations metadata
-        self.operations=[]
-        for elem in 
self._capabilities.find('{http://www.opengis.net/ows/1.1}OperationsMetadata')[:]:
+        # serviceOperations metadata
+        self.operations = []
+        for elem in self._capabilities.find(_OPERATIONS_METADATA_TAG)[:]:
             self.operations.append(OperationsMetadata(elem))
 
-        #serviceContents metadata: our assumption is that services use a 
top-level
-        #layer as a metadata organizer, nothing more.
-        self.contents={}
-        caps = 
self._capabilities.find('{http://www.opengis.net/wmts/1.0}Contents')
+        # serviceContents metadata: our assumption is that services use
+        # a top-level layer as a metadata organizer, nothing more.
+        self.contents = {}
+        caps = self._capabilities.find(_CONTENTS_TAG)
 
         def gather_layers(parent_elem, parent_metadata):
-            for index, elem in 
enumerate(parent_elem.findall('{http://www.opengis.net/wmts/1.0}Layer')):
-                cm = ContentMetadata(elem, parent=parent_metadata, 
index=index+1, parse_remote_metadata=parse_remote_metadata)
+            for index, elem in enumerate(parent_elem.findall(_LAYER_TAG)):
+                cm = ContentMetadata(
+                    elem, parent=parent_metadata, index=index+1,
+                    parse_remote_metadata=parse_remote_metadata)
                 if cm.id:
                     if cm.id in self.contents:
-                        raise KeyError('Content metadata for layer "%s" 
already exists' % cm.id)
+                        raise KeyError('Content metadata for layer "%s" '
+                                       'already exists' % cm.id)
                     self.contents[cm.id] = cm
                 gather_layers(elem, cm)
         gather_layers(caps, None)
 
         self.tilematrixsets = {}
-        for elem in 
caps.findall('{http://www.opengis.net/wmts/1.0}TileMatrixSet'):
+        for elem in caps.findall(_TILE_MATRIX_SET_TAG):
             tms = TileMatrixSet(elem)
             if tms.identifier:
                 if tms.identifier in self.tilematrixsets:
-                    raise KeyError('TileMatrixSet with identifier "%s" already 
exists' % tms.identifier)
+                    raise KeyError('TileMatrixSet with identifier "%s" '
+                                   'already exists' % tms.identifier)
                 self.tilematrixsets[tms.identifier] = tms
 
         self.themes = {}
-        for elem in 
self._capabilities.findall('{http://www.opengis.net/wmts/1.0}Themes/{http://www.opengis.net/wmts/1.0}Theme'):
+        for elem in self._capabilities.findall(_THEMES_TAG + '/' + _THEME_TAG):
             theme = Theme(elem)
             if theme.identifier:
                 if theme.identifier in self.themes:
-                    raise KeyError('Theme with identifier "%s" already exists' 
% theme.identifier)
+                    raise KeyError('Theme with identifier "%s" already exists'
+                                   % theme.identifier)
                 self.themes[theme.identifier] = theme
 
-        serviceMetadataURL = 
self._capabilities.find('{http://www.opengis.net/wmts/1.0}ServiceMetadataURL')
+        serviceMetadataURL = self._capabilities.find(_SERVICE_METADATA_URL_TAG)
         if serviceMetadataURL is not None:
-            self.serviceMetadataURL = 
serviceMetadataURL.attrib['{http://www.w3.org/1999/xlink}href']
+            self.serviceMetadataURL = serviceMetadataURL.attrib[_HREF_TAG]
         else:
             self.serviceMetadataURL = None
 
     def items(self):
         '''supports dict-like items() access'''
-        items=[]
+        items = []
         for item in self.contents:
-            items.append((item,self.contents[item]))
+            items.append((item, self.contents[item]))
         return items
 
-    def buildTileRequest(self, layer=None, style=None, format=None, 
tilematrixset=None, tilematrix=None, row=None, column=None, **kwargs):
+    def buildTileRequest(self, layer=None, style=None, format=None,
+                         tilematrixset=None, tilematrix=None, row=None,
+                         column=None, **kwargs):
         """Return the URL-encoded parameters for a GetTile request.
 
         Parameters
@@ -252,12 +317,14 @@ TILEMATRIX=6&TILEROW=4&TILECOL=4&FORMAT=image%2Fjpeg'
         request.append(('FORMAT', format))
 
         for key, value in kwargs.iteritems():
-            request.append(key, value)
+            request.append((key, value))
 
         data = urlencode(request, True)
         return data
 
-    def gettile(self, base_url=None, layer=None, style=None, format=None, 
tilematrixset=None, tilematrix=None, row=None, column=None, **kwargs):
+    def gettile(self, base_url=None, layer=None, style=None, format=None,
+                tilematrixset=None, tilematrix=None, row=None, column=None,
+                **kwargs):
         """Return a tile from the WMTS.
 
         Returns the tile image as a file-like object.
@@ -303,15 +370,27 @@ TILEMATRIX=6&TILEROW=4&TILECOL=4&FORMAT=image%2Fjpeg'
             >>> out.close()
 
         """
-        data = self.buildTileRequest(layer, style, format, tilematrixset, 
tilematrix, row, column, **kwargs)
+        vendor_kwargs = self.vendor_kwargs or {}
+        vendor_kwargs.update(kwargs)
+        data = self.buildTileRequest(layer, style, format, tilematrixset,
+                                     tilematrix, row, column, **vendor_kwargs)
 
         if base_url is None:
             base_url = self.url
             try:
-                get_verbs = filter(lambda x: x.get('type').lower() == 'get', 
self.getOperationByName('GetTile').methods)
+                get_verbs = filter(
+                    lambda x: x.get('type').lower() == 'get',
+                    self.getOperationByName('GetTile').methods)
                 if len(get_verbs) > 1:
                     # Filter by constraints
-                    base_url = next(x for x in filter(list, ([pv.get('url') 
for const in pv.get('constraints') if 'kvp' in map(lambda x: x.lower(), 
const.values)] for pv in get_verbs if pv.get('constraints'))))[0]
+                    base_url = next(
+                        x for x in filter(
+                            list,
+                            ([pv.get('url')
+                                for const in pv.get('constraints')
+                                if 'kvp' in map(
+                                    lambda x: x.lower(), const.values)]
+                             for pv in get_verbs if pv.get('constraints'))))[0]
                 elif len(get_verbs) == 1:
                     base_url = get_verbs[0].get('url')
             except StopIteration:
@@ -341,74 +420,80 @@ TILEMATRIX=6&TILEROW=4&TILECOL=4&FORMAT=image%2Fjpeg'
         for item in self.operations:
             if item.name == name:
                 return item
-        raise KeyError, "No operation named %s" % name
+        raise KeyError("No operation named %s" % name)
+
 
 class TileMatrixSet(object):
     '''Holds one TileMatrixSet'''
     def __init__(self, elem):
-        if elem.tag != '{http://www.opengis.net/wmts/1.0}TileMatrixSet':
+        if elem.tag != _TILE_MATRIX_SET_TAG:
             raise ValueError('%s should be a TileMatrixSet' % (elem,))
-        self.identifier = 
testXMLValue(elem.find('{http://www.opengis.net/ows/1.1}Identifier')).strip()
-        self.crs = 
testXMLValue(elem.find('{http://www.opengis.net/ows/1.1}SupportedCRS')).strip()
-        if (self.crs == None) or (self.identifier == None):
+        self.identifier = testXMLValue(elem.find(_IDENTIFIER_TAG)).strip()
+        self.crs = testXMLValue(elem.find(_SUPPORTED_CRS_TAG)).strip()
+        if self.crs is None or self.identifier is None:
             raise ValueError('%s incomplete TileMatrixSet' % (elem,))
         self.tilematrix = {}
-        for tilematrix in 
elem.findall('{http://www.opengis.net/wmts/1.0}TileMatrix'):
+        for tilematrix in elem.findall(_TILE_MATRIX_TAG):
             tm = TileMatrix(tilematrix)
             if tm.identifier:
                 if tm.identifier in self.tilematrix:
-                    raise KeyError('TileMatrix with identifier "%s" already 
exists' % tm.identifier)
+                    raise KeyError('TileMatrix with identifier "%s" '
+                                   'already exists' % tm.identifier)
                 self.tilematrix[tm.identifier] = tm
 
+
 class TileMatrix(object):
     '''Holds one TileMatrix'''
     def __init__(self, elem):
-        if elem.tag != '{http://www.opengis.net/wmts/1.0}TileMatrix':
+        if elem.tag != _TILE_MATRIX_TAG:
             raise ValueError('%s should be a TileMatrix' % (elem,))
-        self.identifier = 
testXMLValue(elem.find('{http://www.opengis.net/ows/1.1}Identifier')).strip()
-        sd = 
testXMLValue(elem.find('{http://www.opengis.net/wmts/1.0}ScaleDenominator'))
+        self.identifier = testXMLValue(elem.find(_IDENTIFIER_TAG)).strip()
+        sd = testXMLValue(elem.find(_SCALE_DENOMINATOR_TAG))
         if sd is None:
             raise ValueError('%s is missing ScaleDenominator' % (elem,))
         self.scaledenominator = float(sd)
-        tl = 
testXMLValue(elem.find('{http://www.opengis.net/wmts/1.0}TopLeftCorner'))
+        tl = testXMLValue(elem.find(_TOP_LEFT_CORNER_TAG))
         if tl is None:
             raise ValueError('%s is missing TopLeftCorner' % (elem,))
         (lon, lat) = tl.split(" ")
         self.topleftcorner = (float(lon), float(lat))
-        width = 
testXMLValue(elem.find('{http://www.opengis.net/wmts/1.0}TileWidth'))
-        height = 
testXMLValue(elem.find('{http://www.opengis.net/wmts/1.0}TileHeight'))
+        width = testXMLValue(elem.find(_TILE_WIDTH_TAG))
+        height = testXMLValue(elem.find(_TILE_HEIGHT_TAG))
         if (width is None) or (height is None):
-            raise ValueError('%s is missing TileWidth and/or TileHeight' % 
(elem,))
+            msg = '%s is missing TileWidth and/or TileHeight' % (elem,)
+            raise ValueError(msg)
         self.tilewidth = int(width)
         self.tileheight = int(height)
-        mw = 
testXMLValue(elem.find('{http://www.opengis.net/wmts/1.0}MatrixWidth'))
-        mh = 
testXMLValue(elem.find('{http://www.opengis.net/wmts/1.0}MatrixHeight'))
+        mw = testXMLValue(elem.find(_MATRIX_WIDTH_TAG))
+        mh = testXMLValue(elem.find(_MATRIX_HEIGHT_TAG))
         if (mw is None) or (mh is None):
-            raise ValueError('%s is missing MatrixWidth and/or MatrixHeight' % 
(elem,))
+            msg = '%s is missing MatrixWidth and/or MatrixHeight' % (elem,)
+            raise ValueError(msg)
         self.matrixwidth = int(mw)
         self.matrixheight = int(mh)
 
+
 class Theme:
     """
     Abstraction for a WMTS theme
     """
     def __init__(self, elem):
-        if elem.tag != '{http://www.opengis.net/wmts/1.0}Theme':
+        if elem.tag != _THEME_TAG:
             raise ValueError('%s should be a Theme' % (elem,))
-        self.identifier = 
testXMLValue(elem.find('{http://www.opengis.net/ows/1.1}Identifier')).strip()
-        title = 
testXMLValue(elem.find('{http://www.opengis.net/ows/1.1}Title'))
+        self.identifier = testXMLValue(elem.find(_IDENTIFIER_TAG)).strip()
+        title = testXMLValue(elem.find(_TITLE_TAG))
         if title is not None:
             self.title = title.strip()
         else:
             self.title = None
-        abstract = 
testXMLValue(elem.find('{http://www.opengis.net/ows/1.1}Abstract'))
+        abstract = testXMLValue(elem.find(_ABSTRACT_TAG))
         if abstract is not None:
             self.abstract = abstract.strip()
         else:
             self.abstract = None
 
         self.layerRefs = []
-        layerRefs = elem.findall('{http://www.opengis.net/wmts/1.0}LayerRef')
+        layerRefs = elem.findall(_LAYER_REF_TAG)
         for layerRef in layerRefs:
             if layerRef.text is not None:
                 self.layerRefs.append(layerRef.text)
@@ -465,9 +550,9 @@ class TileMatrixSetLink(object):
                 set_limits_elements = link_element.findall(
                     _TILE_MATRIX_SET_LIMITS_TAG)
                 if set_limits_elements:
-                   raise ValueError('Multiple instances of TileMatrixSet'
-                                    ' plus TileMatrixSetLimits in %s' %
-                                    link_element)
+                    raise ValueError('Multiple instances of TileMatrixSet'
+                                     ' plus TileMatrixSetLimits in %s' %
+                                     link_element)
                 for matrix_set_element in matrix_set_elements:
                     uri = matrix_set_element.text.strip()
                     links.append(TileMatrixSetLink(uri))
@@ -475,12 +560,15 @@ class TileMatrixSetLink(object):
                 uri = matrix_set_elements[0].text.strip()
 
                 tilematrixlimits = {}
-                path = '%s/%s' % (_TILE_MATRIX_SET_LIMITS_TAG, 
_TILE_MATRIX_LIMITS_TAG)
+                path = '%s/%s' % (_TILE_MATRIX_SET_LIMITS_TAG,
+                                  _TILE_MATRIX_LIMITS_TAG)
                 for limits_element in link_element.findall(path):
                     tml = TileMatrixLimits(limits_element)
                     if tml.tilematrix:
                         if tml.tilematrix in tilematrixlimits:
-                            raise KeyError('TileMatrixLimits with tileMatrix 
"%s" already exists' % tml.tilematrix)
+                            msg = ('TileMatrixLimits with tileMatrix "%s" '
+                                   'already exists' % tml.tilematrix)
+                            raise KeyError(msg)
                         tilematrixlimits[tml.tilematrix] = tml
 
                 links.append(TileMatrixSetLink(uri, tilematrixlimits))
@@ -506,8 +594,9 @@ class ContentMetadata:
 
     Implements IContentMetadata.
     """
-    def __init__(self, elem, parent=None, index=0, 
parse_remote_metadata=False):
-        if elem.tag != '{http://www.opengis.net/wmts/1.0}Layer':
+    def __init__(self, elem, parent=None, index=0,
+                 parse_remote_metadata=False):
+        if elem.tag != _LAYER_TAG:
             raise ValueError('%s should be a Layer' % (elem,))
 
         self.parent = parent
@@ -516,25 +605,26 @@ class ContentMetadata:
         else:
             self.index = str(index)
 
-        self.id = self.name = 
testXMLValue(elem.find('{http://www.opengis.net/ows/1.1}Identifier'))
+        self.id = self.name = testXMLValue(elem.find(_IDENTIFIER_TAG))
         # title is mandatory property
         self.title = None
-        title = 
testXMLValue(elem.find('{http://www.opengis.net/ows/1.1}Title'))
+        title = testXMLValue(elem.find(_TITLE_TAG))
         if title is not None:
             self.title = title.strip()
 
-        self.abstract = 
testXMLValue(elem.find('{http://www.opengis.net/ows/1.1}Abstract'))
+        self.abstract = testXMLValue(elem.find(_ABSTRACT_TAG))
 
         # bboxes
-        b = elem.find('{http://www.opengis.net/ows/1.1}WGS84BoundingBox')
+        b = elem.find(_WGS84_BOUNDING_BOX_TAG)
         self.boundingBox = None
         if b is not None:
-            lc = b.find("{http://www.opengis.net/ows/1.1}LowerCorner";)
-            uc = b.find("{http://www.opengis.net/ows/1.1}UpperCorner";)
+            lc = b.find(_LOWER_CORNER_TAG)
+            uc = b.find(_UPPER_CORNER_TAG)
             ll = [float(s) for s in lc.text.split()]
             ur = [float(s) for s in uc.text.split()]
-            self.boundingBoxWGS84 = (ll[0],ll[1],ur[0],ur[1])
-        # TODO: there is probably some more logic here, and it should probably 
be shared code
+            self.boundingBoxWGS84 = (ll[0], ll[1], ur[0], ur[1])
+        # TODO: there is probably some more logic here, and it should
+        # probably be shared code
 
         self._tilematrixsets = [f.text.strip() for f in
                                 elem.findall(_TILE_MATRIX_SET_LINK_TAG + '/' +
@@ -552,32 +642,32 @@ class ContentMetadata:
                 self.tilematrixsetlinks[tmsl.tilematrixset] = tmsl
 
         self.resourceURLs = []
-        for resourceURL in 
elem.findall('{http://www.opengis.net/wmts/1.0}ResourceURL'):
+        for resourceURL in elem.findall(_RESOURCE_URL_TAG):
             resource = {}
             for attrib in ['format', 'resourceType', 'template']:
                 resource[attrib] = resourceURL.attrib[attrib]
             self.resourceURLs.append(resource)
 
-        #Styles
+        # Styles
         self.styles = {}
-        for s in elem.findall('{http://www.opengis.net/wmts/1.0}Style'):
+        for s in elem.findall(_STYLE_TAG):
             style = {}
             isdefaulttext = s.attrib.get('isDefault')
             style['isDefault'] = (isdefaulttext == "true")
-            identifier = s.find('{http://www.opengis.net/ows/1.1}Identifier')
+            identifier = s.find(_IDENTIFIER_TAG)
             if identifier is None:
                 raise ValueError('%s missing identifier' % (s,))
-            title = s.find('{http://www.opengis.net/ows/1.1}Title')
+            title = s.find(_TITLE_TAG)
             if title is not None:
                 style['title'] = title.text
             self.styles[identifier.text] = style
 
-        self.formats = [f.text for f in 
elem.findall('{http://www.opengis.net/wmts/1.0}Format')]
+        self.formats = [f.text for f in elem.findall(_FORMAT_TAG)]
 
-        self.infoformats = [f.text for f in 
elem.findall('{http://www.opengis.net/wmts/1.0}InfoFormat')]
+        self.infoformats = [f.text for f in elem.findall(_INFO_FORMAT_TAG)]
 
         self.layers = []
-        for child in elem.findall('{http://www.opengis.net/wmts/1.0}Layer'):
+        for child in elem.findall(_LAYER_TAG):
             self.layers.append(ContentMetadata(child, self))
 
     @property
@@ -606,37 +696,41 @@ class WMTSCapabilitiesReader:
         self.username = un
         self.password = pw
 
-    def capabilities_url(self, service_url):
+    def capabilities_url(self, service_url, vendor_kwargs=None):
         """Return a capabilities url
         """
-        qs = []
-        if service_url.find('?') != -1:
-            qs = urlparse.parse_qsl(service_url.split('?')[1])
-
-        params = [x[0] for x in qs]
-
-        if 'service' not in params:
-            qs.append(('service', 'WMTS'))
-        if 'request' not in params:
-            qs.append(('request', 'GetCapabilities'))
-        if 'version' not in params:
-            qs.append(('version', self.version))
-
-        urlqs = urlencode(tuple(qs))
-        return service_url.split('?')[0] + '?' + urlqs
-
-    def read(self, service_url):
+        # Ensure the 'service', 'request', and 'version' parameters,
+        # and any vendor-specific parameters are included in the URL.
+        pieces = urlparse.urlparse(service_url)
+        args = urlparse.parse_qs(pieces.query)
+        if 'service' not in args:
+            args['service'] = 'WMTS'
+        if 'request' not in args:
+            args['request'] = 'GetCapabilities'
+        if 'version' not in args:
+            args['version'] = self.version
+        if vendor_kwargs:
+            args.update(vendor_kwargs)
+        query = urlencode(args, doseq=True)
+        pieces = urlparse.ParseResult(pieces.scheme, pieces.netloc,
+                                      pieces.path, pieces.params,
+                                      query, pieces.fragment)
+        return urlparse.urlunparse(pieces)
+
+    def read(self, service_url, vendor_kwargs=None):
         """Get and parse a WMTS capabilities document, returning an
         elementtree instance
 
         service_url is the base url, to which is appended the service,
-        version, and request parameters
+        version, and request parameters. Optional vendor-specific
+        parameters can also be supplied as a dict.
         """
-        getcaprequest = self.capabilities_url(service_url)
+        getcaprequest = self.capabilities_url(service_url, vendor_kwargs)
 
-        #now split it up again to use the generic openURL function...
-        spliturl=getcaprequest.split('?')
-        u = openURL(spliturl[0], spliturl[1], method='Get', username = 
self.username, password = self.password)
+        # now split it up again to use the generic openURL function...
+        spliturl = getcaprequest.split('?')
+        u = openURL(spliturl[0], spliturl[1], method='Get',
+                    username=self.username, password=self.password)
         return etree.fromstring(u.read())
 
     def readString(self, st):
@@ -645,5 +739,6 @@ class WMTSCapabilitiesReader:
         string should be an XML capabilities document
         """
         if not isinstance(st, str):
-            raise ValueError("String must be of type string, not %s" % 
type(st))
+            msg = 'String must be of type string, not %s' % type(st)
+            raise ValueError(msg)
         return etree.fromstring(st)
diff --git a/owslib/wps.py b/owslib/wps.py
index 3479fab..0929c31 100644
--- a/owslib/wps.py
+++ b/owslib/wps.py
@@ -808,6 +808,7 @@ class InputOutput(object):
         self.supportedValues = []
         self.defaultValue = None
         self.dataType = None
+        self.anyValue = False
         
     def _parseData(self, element):
         """
@@ -856,7 +857,7 @@ class InputOutput(object):
                 elif subElement.tag.endswith('DefaultValue'):
                     self.defaultValue = getTypedValue(self.dataType, 
subElement.text)
                 elif subElement.tag.endswith('AnyValue'):
-                    self.allowedValues.append( getTypedValue(self.dataType, 
'AnyValue') )
+                    self.anyValue = True
                     
 
     def _parseComplexData(self, element, complexDataElementName):
@@ -1364,6 +1365,8 @@ def printInputOutput(value, indent=''):
     print '%s identifier=%s, title=%s, abstract=%s, data type=%s' % (indent, 
value.identifier, value.title, value.abstract, value.dataType)
     for val in value.allowedValues:
         print '%s Allowed Value: %s' % (indent, printValue(val))
+    if value.anyValue:
+        print ' Any value allowed'
     for val in value.supportedValues:
         print '%s Supported Value: %s' % (indent, printValue(val))
     print '%s Default Value: %s ' % (indent, printValue(value.defaultValue))
diff --git a/requirements-dev.txt b/requirements-dev.txt
index e2ad8c1..d51e4fc 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -1,3 +1,4 @@
+pep8
 pytest
 pytest-cov
 Pillow

-- 
Alioth's /usr/local/bin/git-commit-notice on 
/srv/git.debian.org/git/pkg-grass/owslib.git

_______________________________________________
Pkg-grass-devel mailing list
Pkg-grass-devel@lists.alioth.debian.org
http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/pkg-grass-devel

Reply via email to