[owslib] 01/04: Imported Upstream version 0.9.0
Johan Van de Wauw
johanvdw-guest at moszumanska.debian.org
Tue Jun 16 19:00:53 UTC 2015
This is an automated email from the git hooks/post-receive script.
johanvdw-guest pushed a commit to branch master
in repository owslib.
commit 0a6a7e9c30417711d465bc8c4c652eb80a536a71
Author: Johan Van de Wauw <johan.vandewauw at gmail.com>
Date: Tue Jun 16 20:38:44 2015 +0200
Imported Upstream version 0.9.0
---
OWSLib.egg-info/PKG-INFO | 2 +-
OWSLib.egg-info/SOURCES.txt | 1 +
OWSLib.egg-info/requires.txt | 1 +
PKG-INFO | 2 +-
VERSION.txt | 2 +-
owslib/__init__.py | 2 +-
owslib/coverage/wcs100.py | 5 +-
owslib/coverage/wcs110.py | 163 ++++++++++++++++-----------
owslib/coverage/wcs111.py | 31 +++++
owslib/coverage/wcsBase.py | 29 ++---
owslib/crs.py | 18 +++
owslib/csw.py | 97 +++++++++-------
owslib/etree.py | 25 +++-
owslib/feature/__init__.py | 8 +-
owslib/feature/wfs100.py | 51 ++++++---
owslib/feature/wfs110.py | 42 ++++---
owslib/feature/wfs200.py | 59 ++++++----
owslib/fes.py | 2 +-
owslib/iso.py | 37 +++++-
owslib/namespaces.py | 8 +-
owslib/swe/common.py | 28 ++---
owslib/swe/observation/sos100.py | 18 ++-
owslib/swe/observation/sos200.py | 17 ++-
owslib/swe/sensor/sml.py | 2 +-
owslib/tms.py | 27 +++--
owslib/util.py | 238 +++++++++++++++++++--------------------
owslib/waterml/wml10.py | 8 +-
owslib/waterml/wml11.py | 8 +-
owslib/wcs.py | 18 ++-
owslib/wms.py | 48 ++++----
owslib/wmts.py | 46 ++++----
owslib/wps.py | 2 +-
requirements.txt | 1 +
setup.py | 1 +
34 files changed, 627 insertions(+), 420 deletions(-)
diff --git a/OWSLib.egg-info/PKG-INFO b/OWSLib.egg-info/PKG-INFO
index 5fac362..a6acc29 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.13
+Version: 0.9.0
Summary: OGC Web Service utility library
Home-page: http://geopython.github.io/OWSLib
Author: Tom Kralidis
diff --git a/OWSLib.egg-info/SOURCES.txt b/OWSLib.egg-info/SOURCES.txt
index 92df1f4..81b7e47 100644
--- a/OWSLib.egg-info/SOURCES.txt
+++ b/OWSLib.egg-info/SOURCES.txt
@@ -40,6 +40,7 @@ owslib/wps.py
owslib/coverage/__init__.py
owslib/coverage/wcs100.py
owslib/coverage/wcs110.py
+owslib/coverage/wcs111.py
owslib/coverage/wcsBase.py
owslib/coverage/wcsdecoder.py
owslib/feature/__init__.py
diff --git a/OWSLib.egg-info/requires.txt b/OWSLib.egg-info/requires.txt
index 8b6b3b8..508d303 100644
--- a/OWSLib.egg-info/requires.txt
+++ b/OWSLib.egg-info/requires.txt
@@ -1,2 +1,3 @@
python-dateutil>=1.5
pytz
+requests>=2.7
diff --git a/PKG-INFO b/PKG-INFO
index 5fac362..a6acc29 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: OWSLib
-Version: 0.8.13
+Version: 0.9.0
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 c2f73c6..ac39a10 100644
--- a/VERSION.txt
+++ b/VERSION.txt
@@ -1 +1 @@
-0.8.13
+0.9.0
diff --git a/owslib/__init__.py b/owslib/__init__.py
index 442ef8c..05339c5 100644
--- a/owslib/__init__.py
+++ b/owslib/__init__.py
@@ -1,3 +1,3 @@
from __future__ import (absolute_import, division, print_function)
-__version__ = '0.8.13'
+__version__ = '0.9.0'
diff --git a/owslib/coverage/wcs100.py b/owslib/coverage/wcs100.py
index 110b118..d9ec1f4 100644
--- a/owslib/coverage/wcs100.py
+++ b/owslib/coverage/wcs100.py
@@ -12,7 +12,10 @@
from __future__ import (absolute_import, division, print_function)
from owslib.coverage.wcsBase import WCSBase, WCSCapabilitiesReader, ServiceException
-from urllib import urlencode
+try:
+ from urllib import urlencode
+except ImportError:
+ from urllib.parse import urlencode
from owslib.util import openURL, testXMLValue
from owslib.etree import etree
from owslib.crs import Crs
diff --git a/owslib/coverage/wcs110.py b/owslib/coverage/wcs110.py
index 01a212b..9e2591b 100644
--- a/owslib/coverage/wcs110.py
+++ b/owslib/coverage/wcs110.py
@@ -15,8 +15,10 @@ from __future__ import (absolute_import, division, print_function)
from .wcsBase import WCSBase, WCSCapabilitiesReader, ServiceException
from owslib.util import openURL, testXMLValue
-from urllib import urlencode
-from urllib2 import urlopen
+try:
+ from urllib import urlencode
+except ImportError:
+ from urllib.parse import urlencode
from owslib.etree import etree
import os, errno
from owslib.coverage import wcsdecoder
@@ -25,14 +27,26 @@ from owslib.crs import Crs
import logging
from owslib.util import log
-def ns(tag):
- return '{http://www.opengis.net/wcs/1.1}'+tag
+class Namespaces_1_1_0():
+
+ def WCS(self, tag):
+ return '{http://www.opengis.net/wcs/1.1}'+tag
+
+ def WCS_OWS(self, tag):
+ return '{http://www.opengis.net/wcs/1.1/ows}'+tag
+
+ def OWS(self, tag):
+ return '{http://www.opengis.net/ows/1.1}'+tag
+
class WebCoverageService_1_1_0(WCSBase):
"""Abstraction for OGC Web Coverage Service (WCS), version 1.1.0
Implements IWebCoverageService.
"""
+ version='1.1.0'
+ ns = Namespaces_1_1_0()
+
def __getitem__(self, name):
''' check contents dictionary to allow dict like access to service layers'''
if name in self.__getattribute__('contents').keys():
@@ -41,7 +55,7 @@ class WebCoverageService_1_1_0(WCSBase):
raise KeyError("No content named %s" % name)
def __init__(self,url,xml, cookies):
- self.version='1.1.0'
+
self.url = url
self.cookies=cookies
# initialize from saved capability document or access the server
@@ -52,7 +66,7 @@ class WebCoverageService_1_1_0(WCSBase):
self._capabilities = reader.read(self.url)
# check for exceptions
- se = self._capabilities.find('{http://www.opengis.net/ows/1.1}Exception')
+ se = self._capabilities.find(self.ns.OWS('Exception'))
if se is not None:
err_message = str(se.text).strip()
@@ -61,19 +75,19 @@ class WebCoverageService_1_1_0(WCSBase):
#build metadata objects:
#serviceIdentification metadata
- elem=self._capabilities.find('{http://www.opengis.net/wcs/1.1/ows}ServiceIdentification')
+ elem=self._capabilities.find(self.ns.WCS_OWS('ServiceIdentification'))
if elem is None:
- elem=self._capabilities.find('{http://www.opengis.net/ows/1.1}ServiceIdentification')
- self.identification=ServiceIdentification(elem)
+ elem=self._capabilities.find(self.ns.OWS('ServiceIdentification'))
+ self.identification=ServiceIdentification(elem, self.ns)
#serviceProvider
- elem=self._capabilities.find('{http://www.opengis.net/ows/1.1}ServiceProvider')
- self.provider=ServiceProvider(elem)
+ elem=self._capabilities.find(self.ns.OWS('ServiceProvider')) or self._capabilities.find(self.ns.OWS('ServiceProvider'))
+ self.provider=ServiceProvider(elem, self.ns)
#serviceOperations
self.operations = []
- for elem in self._capabilities.findall('{http://www.opengis.net/wcs/1.1/ows}OperationsMetadata/{http://www.opengis.net/wcs/1.1/ows}Operation/'):
- self.operations.append(Operation(elem))
+ for elem in self._capabilities.findall(self.ns.WCS_OWS('OperationsMetadata') + '/' + self.ns.WCS_OWS('Operation') + '/'):
+ self.operations.append(Operation(elem, self.ns))
# exceptions - ***********TO DO *************
self.exceptions = [f.text for f \
@@ -82,16 +96,16 @@ class WebCoverageService_1_1_0(WCSBase):
# serviceContents: our assumption is that services use a top-level layer
# as a metadata organizer, nothing more.
self.contents = {}
- top = self._capabilities.find('{http://www.opengis.net/wcs/1.1}Contents/{http://www.opengis.net/wcs/1.1}CoverageSummary')
- for elem in self._capabilities.findall('{http://www.opengis.net/wcs/1.1}Contents/{http://www.opengis.net/wcs/1.1}CoverageSummary/{http://www.opengis.net/wcs/1.1}CoverageSummary'):
- cm=ContentMetadata(elem, top, self)
+ top = self._capabilities.find(self.ns.WCS('Contents') + '/' + self.ns.WCS('CoverageSummary'))
+ for elem in self._capabilities.findall(self.ns.WCS('Contents') + '/' + self.ns.WCS('CoverageSummary') + '/' + self.ns.WCS('CoverageSummary')):
+ cm=ContentMetadata(elem, top, self, self.ns)
self.contents[cm.id]=cm
if self.contents=={}:
#non-hierarchical.
top=None
- for elem in self._capabilities.findall('{http://www.opengis.net/wcs/1.1}Contents/{http://www.opengis.net/wcs/1.1}CoverageSummary'):
- cm=ContentMetadata(elem, top, self)
+ for elem in self._capabilities.findall(self.ns.WCS('Contents') + '/' + self.ns.WCS('CoverageSummary')):
+ cm=ContentMetadata(elem, top, self, self.ns)
#make the describeCoverage requests to populate the supported formats/crs attributes
self.contents[cm.id]=cm
@@ -138,10 +152,10 @@ class WebCoverageService_1_1_0(WCSBase):
if store = false, returns a multipart mime
"""
if log.isEnabledFor(logging.DEBUG):
- log.debug('WCS 1.1.0 DEBUG: Parameters passed to GetCoverage: identifier=%s, bbox=%s, time=%s, format=%s, rangesubset=%s, gridbaseCRS=%s, gridtype=%s, gridCS=%s, gridorigin=%s, gridoffsets=%s, method=%s, other_arguments=%s'%(identifier, bbox, time, format, rangesubset, gridbaseCRS, gridtype, gridCS, gridorigin, gridoffsets, method, str(kwargs)))
+ log.debug('WCS 1.1.0 DEBUG: Parameters passed to GetCoverage: identifier=%s, bbox=%s, time=%s, format=%s, rangesubset=%s, gridbaseCRS=%s, gridtype=%s, gridCS=%s, gridorigin=%s, gridoffsets=%s, method=%s, other_arguments=%s'%(identifier, bbox, time, format, rangesubset, gridbaseCRS, gridtype, gridCS, gridorigin, gridoffsets, method, str(kwargs)))
if method == 'Get':
- method='{http://www.opengis.net/wcs/1.1/ows}Get'
+ method=self.ns.WCS_OWS('Get')
try:
base_url = next((m.get('url') for m in self.getOperationByName('GetCoverage').methods if m.get('type').lower() == method.lower()))
except StopIteration:
@@ -199,11 +213,14 @@ class Operation(object):
"""Abstraction for operation metadata
Implements IOperationMetadata.
"""
- def __init__(self, elem):
+ ns = Namespaces_1_1_0()
+
+ def __init__(self, elem, nmSpc):
+
self.name = elem.get('name')
- self.formatOptions = [f.text for f in elem.findall('{http://www.opengis.net/wcs/1.1/ows}Parameter/{http://www.opengis.net/wcs/1.1/ows}AllowedValues/{http://www.opengis.net/wcs/1.1/ows}Value')]
+ self.formatOptions = [f.text for f in elem.findall(nmSpc.WCS_OWS('Parameter') + '/' + nmSpc.WCS_OWS('AllowedValues') + '/' + nmSpc.WCS_OWS('Value'))]
methods = []
- for verb in elem.findall('{http://www.opengis.net/wcs/1.1/ows}DCP/{http://www.opengis.net/wcs/1.1/ows}HTTP/*'):
+ for verb in elem.findall(nmSpc.WCS_OWS('DCP') + '/' + nmSpc.WCS_OWS('HTTP/*')):
url = verb.attrib['{http://www.w3.org/1999/xlink}href']
methods.append((verb.tag, {'url': url}))
self.methods = dict(methods)
@@ -211,86 +228,94 @@ class Operation(object):
class ServiceIdentification(object):
""" Abstraction for ServiceIdentification Metadata
implements IServiceIdentificationMetadata"""
- def __init__(self,elem):
+
+ def __init__(self, elem, nmSpc):
self.service="WCS"
- self.version="1.1.0"
- self.title=testXMLValue(elem.find('{http://www.opengis.net/ows}Title'))
+
+ self.title=testXMLValue(elem.find(nmSpc.OWS('Title')))
if self.title is None: #may have used the wcs ows namespace:
- self.title=testXMLValue(elem.find('{http://www.opengis.net/wcs/1.1/ows}Title'))
+ self.title=testXMLValue(elem.find(nmSpc.WCS_OWS('Title')))
+ if self.title is None: #may have used the other wcs ows namespace:
+ self.title=testXMLValue(elem.find(nmSpc.OWS('Title')))
- self.abstract=testXMLValue(elem.find('{http://www.opengis.net/ows}Abstract'))
+ self.abstract=testXMLValue(elem.find(nmSpc.OWS('Abstract')))
if self.abstract is None:#may have used the wcs ows namespace:
- self.abstract=testXMLValue(elem.find('{http://www.opengis.net/wcs/1.1/ows}Abstract'))
- if elem.find('{http://www.opengis.net/ows}Abstract') is not None:
- self.abstract=elem.find('{http://www.opengis.net/ows}Abstract').text
+ self.abstract=testXMLValue(elem.find(nmSpc.WCS_OWS('Abstract')))
+ if self.title is None: #may have used the other wcs ows namespace:
+ self.title=testXMLValue(elem.find(nmSpc.OWS('Abstract')))
+ if elem.find(nmSpc.OWS('Abstract')) is not None:
+ self.abstract=elem.find(nmSpc.OWS('Abstract')).text
else:
self.abstract = None
- self.keywords = [f.text for f in elem.findall('{http://www.opengis.net/ows}Keywords/{http://www.opengis.net/ows}Keyword')]
- #self.link = elem.find('{http://www.opengis.net/wcs/1.1}Service/{http://www.opengis.net/wcs/1.1}OnlineResource').attrib.get('{http://www.w3.org/1999/xlink}href', '')
+ self.keywords = [f.text for f in elem.findall(nmSpc.OWS('Keywords') + '/' + nmSpc.OWS('Keyword'))]
+ #self.link = elem.find(nmSpc.WCS('Service') + '/' + nmSpc.WCS('OnlineResource')).attrib.get('{http://www.w3.org/1999/xlink}href', '')
- if elem.find('{http://www.opengis.net/wcs/1.1/ows}Fees') is not None:
- self.fees=elem.find('{http://www.opengis.net/wcs/1.1/ows}Fees').text
+ if elem.find(nmSpc.WCS_OWS('Fees')) is not None:
+ self.fees=elem.find(nmSpc.WCS_OWS('Fees')).text
else:
self.fees=None
- if elem.find('{http://www.opengis.net/wcs/1.1/ows}AccessConstraints') is not None:
- self.accessConstraints=elem.find('{http://www.opengis.net/wcs/1.1/ows}AccessConstraints').text
+ if elem.find(nmSpc.WCS_OWS('AccessConstraints')) is not None:
+ self.accessConstraints=elem.find(nmSpc.WCS_OWS('AccessConstraints')).text
else:
- self.accessConstraints=None
-
+ self.accessConstraints=None
class ServiceProvider(object):
""" Abstraction for ServiceProvider metadata
implements IServiceProviderMetadata """
- def __init__(self,elem):
- name=elem.find('{http://www.opengis.net/ows}ProviderName')
+
+ def __init__(self, elem, nmSpc):
+ name=elem.find(nmSpc.OWS('ProviderName'))
+
if name is not None:
self.name=name.text
else:
self.name=None
- #self.contact=ServiceContact(elem.find('{http://www.opengis.net/ows}ServiceContact'))
- self.contact =ContactMetadata(elem)
+
+ #self.contact=ServiceContact(elem.find(nmSpc.OWS('ServiceContact')))
+ self.contact =ContactMetadata(elem, nmSpc)
self.url=self.name # no obvious definitive place for url in wcs, repeat provider name?
class ContactMetadata(object):
''' implements IContactMetadata'''
- def __init__(self, elem):
+
+ def __init__(self, elem, nmSpc):
try:
- self.name = elem.find('{http://www.opengis.net/ows}ServiceContact/{http://www.opengis.net/ows}IndividualName').text
+ self.name = elem.find(nmSpc.OWS('ServiceContact') + '/' + nmSpc.OWS('IndividualName')).text
except AttributeError:
self.name = None
try:
- self.organization=elem.find('{http://www.opengis.net/ows}ProviderName').text
+ self.organization=elem.find(nmSpc.OWS('ProviderName')).text
except AttributeError:
self.organization = None
try:
- self.address = elem.find('{http://www.opengis.net/ows}ServiceContact/{http://www.opengis.net/ows}ContactInfo/{http://www.opengis.net/ows}Address/{http://www.opengis.net/ows}DeliveryPoint').text
+ self.address = elem.find(nmSpc.OWS('ServiceContact') + '/' + nmSpc.OWS('ContactInfo') + '/' + nmSpc.OWS('Address') + '/' + nmSpc.OWS('DeliveryPoint')).text
except AttributeError:
self.address = None
try:
- self.city= elem.find('{http://www.opengis.net/ows}ServiceContact/{http://www.opengis.net/ows}ContactInfo/{http://www.opengis.net/ows}Address/{http://www.opengis.net/ows}City').text
+ self.city= elem.find(nmSpc.OWS('ServiceContact') + '/' + nmSpc.OWS('ContactInfo') + '/' + nmSpc.OWS('Address') + '/' + nmSpc.OWS('City')).text
except AttributeError:
self.city = None
try:
- self.region= elem.find('{http://www.opengis.net/ows}ServiceContact/{http://www.opengis.net/ows}ContactInfo/{http://www.opengis.net/ows}Address/{http://www.opengis.net/ows}AdministrativeArea').text
+ self.region= elem.find(nmSpc.OWS('ServiceContact') + '/' + nmSpc.OWS('ContactInfo') + '/' + nmSpc.OWS('Address') + '/' + nmSpc.OWS('AdministrativeArea')).text
except AttributeError:
self.region = None
try:
- self.postcode= elem.find('{http://www.opengis.net/ows}ServiceContact/{http://www.opengis.net/ows}ContactInfo/{http://www.opengis.net/ows}Address/{http://www.opengis.net/ows}PostalCode').text
+ self.postcode= elem.find(nmSpc.OWS('ServiceContact') + '/' + nmSpc.OWS('ContactInfo') + '/' + nmSpc.OWS('Address') + '/' + nmSpc.OWS('PostalCode')).text
except AttributeError:
self.postcode = None
try:
- self.country= elem.find('{http://www.opengis.net/ows}ServiceContact/{http://www.opengis.net/ows}ContactInfo/{http://www.opengis.net/ows}Address/{http://www.opengis.net/ows}Country').text
+ self.country= elem.find(nmSpc.OWS('ServiceContact') + '/' + nmSpc.OWS('ContactInfo') + '/' + nmSpc.OWS('Address') + '/' + nmSpc.OWS('Country')).text
except AttributeError:
self.country = None
try:
- self.email = elem.find('{http://www.opengis.net/ows}ServiceContact/{http://www.opengis.net/ows}ContactInfo/{http://www.opengis.net/ows}Address/{http://www.opengis.net/ows}ElectronicMailAddress').text
+ self.email = elem.find(nmSpc.OWS('ServiceContact') + '/' + nmSpc.OWS('ContactInfo') + '/' + nmSpc.OWS('Address') + '/' + nmSpc.OWS('ElectronicMailAddress')).text
except AttributeError:
self.email = None
@@ -298,36 +323,37 @@ class ContentMetadata(object):
"""Abstraction for WCS ContentMetadata
Implements IContentMetadata
"""
- def __init__(self, elem, parent, service):
+
+ def __init__(self, elem, parent, service, nmSpc):
"""Initialize."""
#TODO - examine the parent for bounding box info.
-
+
self._service=service
self._elem=elem
self._parent=parent
- self.id=self._checkChildAndParent('{http://www.opengis.net/wcs/1.1}Identifier')
- self.description =self._checkChildAndParent('{http://www.opengis.net/wcs/1.1}Description')
- self.title =self._checkChildAndParent('{http://www.opengis.net/ows}Title')
- self.abstract =self._checkChildAndParent('{http://www.opengis.net/ows}Abstract')
+ self.id=self._checkChildAndParent(nmSpc.WCS('Identifier'))
+ self.description =self._checkChildAndParent(nmSpc.WCS('Description'))
+ self.title =self._checkChildAndParent(nmSpc.OWS('Title'))
+ self.abstract =self._checkChildAndParent(nmSpc.OWS('Abstract'))
#keywords.
self.keywords=[]
- for kw in elem.findall('{http://www.opengis.net/ows}Keywords/{http://www.opengis.net/ows}Keyword'):
+ for kw in elem.findall(nmSpc.OWS('Keywords') + '/' + nmSpc.OWS('Keyword')):
if kw is not None:
self.keywords.append(kw.text)
#also inherit any keywords from parent coverage summary (if there is one)
if parent is not None:
- for kw in parent.findall('{http://www.opengis.net/ows}Keywords/{http://www.opengis.net/ows}Keyword'):
+ for kw in parent.findall(nmSpc.OWS('Keywords') + '/' + nmSpc.OWS('Keyword')):
if kw is not None:
self.keywords.append(kw.text)
self.boundingBox=None #needed for iContentMetadata harmonisation
self.boundingBoxWGS84 = None
- b = elem.find('{http://www.opengis.net/ows}WGS84BoundingBox')
+ b = elem.find(nmSpc.OWS('WGS84BoundingBox'))
if b is not None:
- lc=b.find('{http://www.opengis.net/ows}LowerCorner').text
- uc=b.find('{http://www.opengis.net/ows}UpperCorner').text
+ lc=b.find(nmSpc.OWS('LowerCorner')).text
+ uc=b.find(nmSpc.OWS('UpperCorner')).text
self.boundingBoxWGS84 = (
float(lc.split()[0]),float(lc.split()[1]),
float(uc.split()[0]), float(uc.split()[1]),
@@ -335,11 +361,11 @@ class ContentMetadata(object):
# bboxes - other CRS
self.boundingboxes = []
- for bbox in elem.findall('{http://www.opengis.net/ows}BoundingBox'):
+ for bbox in elem.findall(nmSpc.OWS('BoundingBox')):
if bbox is not None:
try:
- lc=b.find('{http://www.opengis.net/ows}LowerCorner').text
- uc=b.find('{http://www.opengis.net/ows}UpperCorner').text
+ lc=b.find(nmSpc.OWS('LowerCorner')).text
+ uc=b.find(nmSpc.OWS('UpperCorner')).text
boundingBox = (
float(lc.split()[0]),float(lc.split()[1]),
float(uc.split()[0]), float(uc.split()[1]),
@@ -354,13 +380,13 @@ class ContentMetadata(object):
#SupportedCRS
self.supportedCRS=[]
- for crs in elem.findall('{http://www.opengis.net/wcs/1.1}SupportedCRS'):
+ for crs in elem.findall(nmSpc.WCS('SupportedCRS')):
self.supportedCRS.append(Crs(crs.text))
#SupportedFormats
self.supportedFormats=[]
- for format in elem.findall('{http://www.opengis.net/wcs/1.1}SupportedFormat'):
+ for format in elem.findall(nmSpc.WCS('SupportedFormat')):
self.supportedFormats.append(format.text)
#grid is either a gml:Grid or a gml:RectifiedGrid if supplied as part of the DescribeCoverage response.
@@ -404,3 +430,4 @@ class ContentMetadata(object):
except:
value = None
return value
+
diff --git a/owslib/coverage/wcs111.py b/owslib/coverage/wcs111.py
new file mode 100644
index 0000000..63034c5
--- /dev/null
+++ b/owslib/coverage/wcs111.py
@@ -0,0 +1,31 @@
+# -*- coding: ISO-8859-15 -*-
+# =============================================================================
+# Copyright (c) 2015 Luís de Sousa
+#
+# Authors :
+# Luís de Sousa <luis.a.de.sousa at gmail.com>
+#
+# Contact email: luis.a.de.sousa at gmail.com
+# =============================================================================
+
+from owslib.coverage import wcs110
+
+class Namespaces_1_1_1():
+
+ def WCS(self, tag):
+ return '{http://www.opengis.net/wcs/1.1.1}'+tag
+
+ def WCS_OWS(self, tag):
+ return '{http://www.opengis.net/wcs/1.1.1/ows}'+tag
+
+ def OWS(self, tag):
+ return '{http://www.opengis.net/ows/1.1}'+tag
+
+
+class WebCoverageService_1_1_1(wcs110.WebCoverageService_1_1_0):
+ """Abstraction for OGC Web Coverage Service (WCS), version 1.1.1
+ Implements IWebCoverageService.
+ """
+ version='1.1.1'
+ ns = Namespaces_1_1_1()
+
diff --git a/owslib/coverage/wcsBase.py b/owslib/coverage/wcsBase.py
index f49c640..29ea2d3 100644
--- a/owslib/coverage/wcsBase.py
+++ b/owslib/coverage/wcsBase.py
@@ -11,11 +11,15 @@
from __future__ import (absolute_import, division, print_function)
-from urllib import urlencode
-from urllib2 import urlopen, Request
+try:
+ from urllib import urlencode
+except ImportError:
+ from urllib.parse import urlencode
from owslib.etree import etree
import cgi
-from StringIO import StringIO
+from six.moves import cStringIO as StringIO
+import six
+from owslib.util import openURL
class ServiceException(Exception):
@@ -108,19 +112,14 @@ class WCSCapabilitiesReader(object):
@return: An elementtree tree representation of the capabilities document
"""
request = self.capabilities_url(service_url)
- req = Request(request)
- if self.cookies is not None:
- req.add_header('Cookie', self.cookies)
- u = urlopen(req, timeout=timeout)
+ u = openURL(request, timeout=timeout, cookies=self.cookies)
return etree.fromstring(u.read())
-
+
def readString(self, st):
"""Parse a WCS capabilities document, returning an
instance of WCSCapabilitiesInfoset
string should be an XML capabilities document
"""
- if not isinstance(st, str):
- raise ValueError("String must be of type string, not %s" % type(st))
return etree.fromstring(st)
class DescribeCoverageReader(object):
@@ -159,7 +158,7 @@ class DescribeCoverageReader(object):
if self.version == '1.0.0':
if 'coverage' not in params:
qs.append(('coverage', self.identifier))
- elif self.version == '1.1.0':
+ elif self.version == '1.1.0' or self.version == '1.1.1':
#NOTE: WCS 1.1.0 is ambigous about whether it should be identifier
#or identifiers (see tables 9, 10 of specification)
if 'identifiers' not in params:
@@ -181,10 +180,6 @@ class DescribeCoverageReader(object):
@return: An elementtree tree representation of the capabilities document
"""
request = self.descCov_url(service_url)
- req = Request(request)
- if self.cookies is not None:
- req.add_header('Cookie', self.cookies)
- u = urlopen(req, timeout=timeout)
+ u = openURL(request, cookies=self.cookies, timeout=timeout)
return etree.fromstring(u.read())
-
-
+
diff --git a/owslib/crs.py b/owslib/crs.py
index cd55358..c99f7a6 100644
--- a/owslib/crs.py
+++ b/owslib/crs.py
@@ -1815,6 +1815,24 @@ class Crs(object):
(self.version or ""),
(self.code or ""))
+ def getcodeuri1(self):
+ """Create for example "http://www.opengis.net/def/crs/EPSG/0/4326"
+ string and return back
+
+ :returns: String code formated in "http://www.opengis.net/def/crs/EPSG/0/code"
+ """
+
+ return 'http://www.opengis.net/def/crs/EPSG/0/%s' % self.code
+
+ def getcodeuri2(self):
+ """Create for example "http://www.opengis.net/gml/srs/epsg.xml#4326"
+ string and return back
+
+ :returns: String code formated in "http://www.opengis.net/gml/srs/epsg.xml#code"
+ """
+
+ return 'http://www.opengis.net/gml/srs/epsg.xml#%s' % self.code
+
def __eq__(self, other):
if isinstance(other, self.__class__):
return self.getcodeurn() == other.getcodeurn()
diff --git a/owslib/csw.py b/owslib/csw.py
index 0d800c8..ed52686 100644
--- a/owslib/csw.py
+++ b/owslib/csw.py
@@ -11,13 +11,18 @@
from __future__ import (absolute_import, division, print_function)
-import base64
import inspect
import warnings
-import StringIO
+import six
+try:
+ from StringIO import StringIO as BytesIO # Python 2
+except ImportError:
+ from io import BytesIO # Python 3
import random
-from urllib import urlencode
-from urllib2 import Request, urlopen
+try: # Python 3
+ from urllib.parse import urlencode
+except ImportError: # Python 2
+ from urllib import urlencode
from owslib.util import OrderedDict
@@ -29,7 +34,7 @@ from owslib.iso import MD_Metadata
from owslib.fgdc import Metadata
from owslib.dif import DIF
from owslib.namespaces import Namespaces
-from owslib.util import cleanup_namespaces, bind_url, add_namespaces
+from owslib.util import cleanup_namespaces, bind_url, add_namespaces, openURL
# default variables
outputformat = 'application/xml'
@@ -41,7 +46,7 @@ namespaces = get_namespaces()
schema = 'http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd'
schema_location = '%s %s' % (namespaces['csw'], schema)
-class CatalogueServiceWeb:
+class CatalogueServiceWeb(object):
""" csw request class """
def __init__(self, url, lang='en-US', version='2.0.2', timeout=10, skip_caps=False,
username=None, password=None):
@@ -77,25 +82,31 @@ class CatalogueServiceWeb:
data = {'service': self.service, 'version': self.version, 'request': 'GetCapabilities'}
- self.request = '%s%s' % (bind_url(self.url), urlencode(data))
+ self.request = urlencode(data)
self._invoke()
if self.exceptionreport is None:
# ServiceIdentification
val = self._exml.find(util.nspath_eval('ows:ServiceIdentification', namespaces))
- self.identification=ows.ServiceIdentification(val,self.owscommon.namespace)
+ if val is not None:
+ self.identification = ows.ServiceIdentification(val,self.owscommon.namespace)
+ else:
+ self.identification = None
# ServiceProvider
val = self._exml.find(util.nspath_eval('ows:ServiceProvider', namespaces))
- self.provider=ows.ServiceProvider(val,self.owscommon.namespace)
+ if val is not None:
+ self.provider = ows.ServiceProvider(val,self.owscommon.namespace)
+ else:
+ self.provider = None
# ServiceOperations metadata
- self.operations=[]
+ self.operations = []
for elem in self._exml.findall(util.nspath_eval('ows:OperationsMetadata/ows:Operation', namespaces)):
self.operations.append(ows.OperationsMetadata(elem, self.owscommon.namespace))
# FilterCapabilities
val = self._exml.find(util.nspath_eval('ogc:Filter_Capabilities', namespaces))
- self.filters=fes.FilterCapabilities(val)
+ self.filters = fes.FilterCapabilities(val)
def describerecord(self, typename='csw:Record', format=outputformat):
"""
@@ -276,7 +287,7 @@ class CatalogueServiceWeb:
'id': ','.join(id),
}
- self.request = '%s%s' % (bind_url(self.url), urlencode(data))
+ self.request = urlencode(data)
self._invoke()
@@ -504,7 +515,7 @@ class CatalogueServiceWeb:
"""
urls=[]
- for key,rec in self.records.iteritems():
+ for key,rec in six.iteritems(self.records):
#create a generator object, and iterate through it until the match is found
#if not found, gets the default value (here "none")
url = next((d['url'] for d in rec.references if d['scheme'] == service_string), None)
@@ -592,35 +603,43 @@ class CatalogueServiceWeb:
def _invoke(self):
# do HTTP request
- if isinstance(self.request, basestring): # GET KVP
- req = Request(self.request)
- if self.username is not None and self.password is not None:
- base64string = base64.encodestring('%s:%s' % (self.username, self.password))[:-1]
- req.add_header('Authorization', 'Basic %s' % base64string)
- self.response = urlopen(req, timeout=self.timeout).read()
- else:
- xml_post_url = self.url
- # Get correct POST URL based on Operation list.
- # If skip_caps=True, then self.operations has not been set, so use
- # default URL.
- if hasattr(self, 'operations'):
- caller = inspect.stack()[1][3]
- if caller == 'getrecords2': caller = 'getrecords'
- try:
- op = self.get_operation_by_name(caller)
- post_verbs = filter(lambda x: x.get('type').lower() == 'post', op.methods)
+ request_url = self.url
+
+ # Get correct URL based on Operation list.
+
+ # If skip_caps=True, then self.operations has not been set, so use
+ # default URL.
+ if hasattr(self, 'operations'):
+ caller = inspect.stack()[1][3]
+ if caller == 'getrecords2': caller = 'getrecords'
+ try:
+ op = self.get_operation_by_name(caller)
+ if isinstance(self.request, six.string_types): # GET KVP
+ get_verbs = [x for x in op.methods if x.get('type').lower() == 'get']
+ request_url = get_verbs[0].get('url')
+ else:
+ post_verbs = [x for x in op.methods if x.get('type').lower() == 'post']
if len(post_verbs) > 1:
# Filter by constraints. We must match a PostEncoding of "XML"
- try:
- xml_post_url = next(x for x in filter(list, ([pv.get('url') for const in pv.get('constraints') if const.name.lower() == "postencoding" and 'xml' in map(lambda x: x.lower(), const.values)] for pv in post_verbs)))[0]
- except StopIteration:
+ for pv in post_verbs:
+ for const in pv.get('constraints'):
+ if const.name.lower() == 'postencoding':
+ values = [v.lower() for v in const.values]
+ if 'xml' in values:
+ request_url = pv.get('url')
+ break
+ else:
# Well, just use the first one.
- xml_post_url = post_verbs[0].get('url')
+ request_url = post_verbs[0].get('url')
elif len(post_verbs) == 1:
- xml_post_url = post_verbs[0].get('url')
- except: # no such luck, just go with xml_post_url
- pass
+ request_post_url = post_verbs[0].get('url')
+ except: # no such luck, just go with request_url
+ pass
+ if isinstance(self.request, six.string_types): # GET KVP
+ self.request = '%s%s' % (bind_url(request_url), self.request)
+ self.response = openURL(self.request, None, 'Get', username=self.username, password=self.password, timeout=self.timeout).read()
+ else:
self.request = cleanup_namespaces(self.request)
# Add any namespaces used in the "typeNames" attribute of the
# csw:Query element to the query's xml namespaces.
@@ -634,10 +653,10 @@ class CatalogueServiceWeb:
self.request = util.element_to_string(self.request, encoding='utf-8')
- self.response = util.http_post(xml_post_url, self.request, self.lang, self.timeout, self.username, self.password)
+ self.response = util.http_post(request_url, self.request, self.lang, self.timeout, self.username, self.password)
# parse result see if it's XML
- self._exml = etree.parse(StringIO.StringIO(self.response))
+ self._exml = etree.parse(BytesIO(self.response))
# it's XML. Attempt to decipher whether the XML response is CSW-ish """
valid_xpaths = [
diff --git a/owslib/etree.py b/owslib/etree.py
index 70d766d..74bd835 100644
--- a/owslib/etree.py
+++ b/owslib/etree.py
@@ -5,7 +5,8 @@
# =============================================================================
from __future__ import (absolute_import, division, print_function)
-
+import six
+import inspect
def patch_well_known_namespaces(etree_module):
@@ -28,20 +29,36 @@ def patch_well_known_namespaces(etree_module):
pass
warnings.warn("Only 'lxml.etree' >= 2.3 and 'xml.etree.ElementTree' >= 1.3 are fully supported!")
- for k, v in ns.get_namespaces().iteritems():
+ for k, v in six.iteritems(ns.get_namespaces()):
register_namespace(k, v)
# try to find lxml or elementtree
try:
- from lxml import etree
+ from lxml import etree, ParseError
+ ElementType = etree._Element
except ImportError:
try:
- # Python 2.5 with ElementTree included
+ # Python 2.x/3.x with ElementTree included
import xml.etree.ElementTree as etree
+
+ try:
+ from xml.etree.ElementTree import ParseError
+ except ImportError:
+ from xml.parsers.expat import ExpatError as ParseError
+
+ if hasattr(etree, 'Element') and inspect.isclass(etree.Element):
+ # python 3.4, 3.3, 2.7
+ ElementType = etree.Element
+ else:
+ # python 2.6
+ ElementType = etree._ElementInterface
+
except ImportError:
try:
# Python < 2.5 with ElementTree installed
import elementtree.ElementTree as etree
+ ParseError = StandardError # i can't find a ParseError related item in elementtree docs!
+ ElementType = etree.Element
except ImportError:
raise RuntimeError('You need either lxml or ElementTree to use OWSLib!')
diff --git a/owslib/feature/__init__.py b/owslib/feature/__init__.py
index b43a11a..2082e3f 100644
--- a/owslib/feature/__init__.py
+++ b/owslib/feature/__init__.py
@@ -9,7 +9,10 @@ from __future__ import (absolute_import, division, print_function)
from owslib.crs import Crs
-from urllib import urlencode
+try:
+ from urllib import urlencode
+except ImportError:
+ from urllib.parse import urlencode
import logging
from owslib.util import log
@@ -77,7 +80,7 @@ class WebFeatureService_(object):
return None
def getGETGetFeatureRequest(self, typename=None, filter=None, bbox=None, featureid=None,
- featureversion=None, propertyname=None, maxfeatures=None,storedQueryID=None, storedQueryParams={},
+ featureversion=None, propertyname=None, maxfeatures=None,storedQueryID=None, storedQueryParams=None,
outputFormat=None, method='Get', startindex=None):
"""Formulate proper GetFeature request using KVP encoding
----------
@@ -108,6 +111,7 @@ class WebFeatureService_(object):
2) typename and filter (==query) (more expressive)
3) featureid (direct access to known features)
"""
+ storedQueryParams = storedQueryParams or {}
base_url = next((m.get('url') for m in self.getOperationByName('GetFeature').methods if m.get('type').lower() == method.lower()))
base_url = base_url if base_url.endswith("?") else base_url+"?"
diff --git a/owslib/feature/wfs100.py b/owslib/feature/wfs100.py
index 619834d..03b6ae0 100644
--- a/owslib/feature/wfs100.py
+++ b/owslib/feature/wfs100.py
@@ -9,9 +9,12 @@
from __future__ import (absolute_import, division, print_function)
import cgi
-from cStringIO import StringIO
-from urllib import urlencode
-from urllib2 import urlopen
+from six import PY2
+from six.moves import cStringIO as StringIO
+try:
+ from urllib import urlencode
+except ImportError:
+ from urllib.parse import urlencode
from owslib.util import openURL, testXMLValue, extract_xml_list, ServiceException, xmltag_split
from owslib.etree import etree
from owslib.fgdc import Metadata
@@ -122,7 +125,7 @@ class WebFeatureService_1_0_0(object):
file-like object.
NOTE: this is effectively redundant now"""
reader = WFSCapabilitiesReader(self.version)
- return urlopen(reader.capabilities_url(self.url), timeout=self.timeout)
+ return openURL(reader.capabilities_url(self.url), timeout=self.timeout)
def items(self):
'''supports dict-like items() access'''
@@ -130,9 +133,20 @@ class WebFeatureService_1_0_0(object):
for item in self.contents:
items.append((item,self.contents[item]))
return items
-
+
+ def _makeStringIO(self, strval):
+ """
+ Helper method to make sure the StringIO being returned will work.
+
+ Differences between Python 2.6/2.7/3.x mean we have a lot of cases to handle.
+ """
+ if PY2:
+ return StringIO(strval)
+
+ return StringIO(strval.decode())
+
def getfeature(self, typename=None, filter=None, bbox=None, featureid=None,
- featureversion=None, propertyname=['*'], maxfeatures=None,
+ featureversion=None, propertyname='*', maxfeatures=None,
srsname=None, outputFormat=None, method='{http://www.opengis.net/wfs}Get',
startindex=None):
"""Request and return feature data as a file-like object.
@@ -189,8 +203,11 @@ class WebFeatureService_1_0_0(object):
assert len(typename) > 0
request['typename'] = ','.join(typename)
- if propertyname:
+ if propertyname is not None:
+ if not isinstance(propertyname, list):
+ propertyname = [propertyname]
request['propertyname'] = ','.join(propertyname)
+
if featureversion: request['featureversion'] = str(featureversion)
if maxfeatures: request['maxfeatures'] = str(maxfeatures)
if startindex: request['startindex'] = str(startindex)
@@ -207,14 +224,14 @@ class WebFeatureService_1_0_0(object):
# We're going to assume that anything with a content-length > 32k
# is data. We'll check anything smaller.
- try:
+ if 'Content-Length' in u.info():
length = int(u.info()['Content-Length'])
have_read = False
- except (KeyError, AttributeError):
+ else:
data = u.read()
have_read = True
length = len(data)
-
+
if length < 32000:
if not have_read:
data = u.read()
@@ -223,16 +240,16 @@ class WebFeatureService_1_0_0(object):
tree = etree.fromstring(data)
except BaseException:
# Not XML
- return StringIO(data)
+ return self._makeStringIO(data)
else:
if tree.tag == "{%s}ServiceExceptionReport" % OGC_NAMESPACE:
se = tree.find(nspath('ServiceException', OGC_NAMESPACE))
raise ServiceException(str(se.text).strip())
else:
- return StringIO(data)
+ return self._makeStringIO(data)
else:
if have_read:
- return StringIO(data)
+ return self._makeStringIO(data)
return u
def getOperationByName(self, name):
@@ -316,7 +333,7 @@ class ContentMetadata:
if metadataUrl['url'] is not None and parse_remote_metadata: # download URL
try:
- content = urlopen(metadataUrl['url'], timeout=timeout)
+ content = openURL(metadataUrl['url'], timeout=timeout)
doc = etree.parse(content)
if metadataUrl['type'] is not None:
if metadataUrl['type'] == 'FGDC':
@@ -385,7 +402,7 @@ class WFSCapabilitiesReader(object):
A timeout value (in seconds) for the request.
"""
request = self.capabilities_url(url)
- u = urlopen(request, timeout=timeout)
+ u = openURL(request, timeout=timeout)
return etree.fromstring(u.read())
def readString(self, st):
@@ -394,7 +411,7 @@ class WFSCapabilitiesReader(object):
string should be an XML capabilities document
"""
- if not isinstance(st, str):
- raise ValueError("String must be of type string, not %s" % type(st))
+ if not isinstance(st, str) and not isinstance(st, bytes):
+ raise ValueError("String must be of type string or bytes, not %s" % type(st))
return etree.fromstring(st)
diff --git a/owslib/feature/wfs110.py b/owslib/feature/wfs110.py
index b5f693a..5dd9e4e 100644
--- a/owslib/feature/wfs110.py
+++ b/owslib/feature/wfs110.py
@@ -10,9 +10,12 @@
from __future__ import (absolute_import, division, print_function)
import cgi
-from cStringIO import StringIO
-from urllib import urlencode
-from urllib2 import urlopen
+from six import PY2
+from six.moves import cStringIO as StringIO
+try:
+ from urllib import urlencode
+except ImportError:
+ from urllib.parse import urlencode
from owslib.util import openURL, testXMLValue, nspath_eval, ServiceException
from owslib.etree import etree
from owslib.fgdc import Metadata
@@ -108,7 +111,7 @@ class WebFeatureService_1_1_0(WebFeatureService_):
file-like object.
NOTE: this is effectively redundant now"""
reader = WFSCapabilitiesReader(self.version)
- return urlopen(reader.capabilities_url(self.url), timeout=self.timeout)
+ return openURL(reader.capabilities_url(self.url), timeout=self.timeout)
def items(self):
'''supports dict-like items() access'''
@@ -117,8 +120,19 @@ class WebFeatureService_1_1_0(WebFeatureService_):
items.append((item,self.contents[item]))
return items
+ def _makeStringIO(self, strval):
+ """
+ Helper method to make sure the StringIO being returned will work.
+
+ Differences between Python 2.6/2.7/3.x mean we have a lot of cases to handle.
+ """
+ if PY2:
+ return StringIO(strval)
+
+ return StringIO(strval.decode())
+
def getfeature(self, typename=None, filter=None, bbox=None, featureid=None,
- featureversion=None, propertyname=['*'], maxfeatures=None,
+ featureversion=None, propertyname='*', maxfeatures=None,
srsname=None, outputFormat=None, method='Get',
startindex=None):
"""Request and return feature data as a file-like object.
@@ -213,10 +227,10 @@ class WebFeatureService_1_1_0(WebFeatureService_):
# check for service exceptions, rewrap, and return
# We're going to assume that anything with a content-length > 32k
# is data. We'll check anything smaller.
- try:
+ if 'Content-Length' in u.info():
length = int(u.info()['Content-Length'])
have_read = False
- except (KeyError, AttributeError):
+ else:
data = u.read()
have_read = True
length = len(data)
@@ -229,16 +243,16 @@ class WebFeatureService_1_1_0(WebFeatureService_):
tree = etree.fromstring(data)
except BaseException:
# Not XML
- return StringIO(data)
+ return self._makeStringIO(data)
else:
if tree.tag == "{%s}ServiceExceptionReport" % namespaces["ogc"]:
se = tree.find(nspath_eval('ServiceException', namespaces["ogc"]))
raise ServiceException(str(se.text).strip())
else:
- return StringIO(data)
+ return self._makeStringIO(data)
else:
if have_read:
- return StringIO(data)
+ return self._makeStringIO(data)
return u
def getOperationByName(self, name):
@@ -294,7 +308,7 @@ class ContentMetadata:
if metadataUrl['url'] is not None and parse_remote_metadata: # download URL
try:
- content = urlopen(metadataUrl['url'], timeout=timeout)
+ content = openURL(metadataUrl['url'], timeout=timeout)
doc = etree.parse(content)
if metadataUrl['type'] is not None:
if metadataUrl['type'] == 'FGDC':
@@ -351,7 +365,7 @@ class WFSCapabilitiesReader(object):
A timeout value (in seconds) for the request.
"""
request = self.capabilities_url(url)
- u = urlopen(request, timeout=timeout)
+ u = openURL(request, timeout=timeout)
return etree.fromstring(u.read())
def readString(self, st):
@@ -360,7 +374,7 @@ class WFSCapabilitiesReader(object):
string should be an XML capabilities document
"""
- if not isinstance(st, str):
- raise ValueError("String must be of type string, not %s" % type(st))
+ if not isinstance(st, str) and not isinstance(st, bytes):
+ raise ValueError("String must be of type string or bytes, not %s" % type(st))
return etree.fromstring(st)
diff --git a/owslib/feature/wfs200.py b/owslib/feature/wfs200.py
index b6d9c1e..9a39966 100644
--- a/owslib/feature/wfs200.py
+++ b/owslib/feature/wfs200.py
@@ -11,16 +11,19 @@ from __future__ import (absolute_import, division, print_function)
#owslib imports:
from owslib.ows import ServiceIdentification, ServiceProvider, OperationsMetadata
from owslib.etree import etree
-from owslib.util import nspath, testXMLValue
+from owslib.util import nspath, testXMLValue, openURL
from owslib.crs import Crs
from owslib.feature import WebFeatureService_
from owslib.namespaces import Namespaces
#other imports
import cgi
-from cStringIO import StringIO
-from urllib import urlencode
-from urllib2 import urlopen
+from six import PY2
+from six.moves import cStringIO as StringIO
+try:
+ from urllib import urlencode
+except ImportError:
+ from urllib.parse import urlencode
import logging
from owslib.util import log
@@ -128,7 +131,7 @@ class WebFeatureService_2_0_0(WebFeatureService_):
file-like object.
NOTE: this is effectively redundant now"""
reader = WFSCapabilitiesReader(self.version)
- return urlopen(reader.capabilities_url(self.url), timeout=self.timeout)
+ return openURL(reader.capabilities_url(self.url), timeout=self.timeout)
def items(self):
'''supports dict-like items() access'''
@@ -136,9 +139,20 @@ class WebFeatureService_2_0_0(WebFeatureService_):
for item in self.contents:
items.append((item,self.contents[item]))
return items
-
+
+ def _makeStringIO(self, strval):
+ """
+ Helper method to make sure the StringIO being returned will work.
+
+ Differences between Python 2.6/2.7/3.x mean we have a lot of cases to handle.
+ """
+ if PY2:
+ return StringIO(strval)
+
+ return StringIO(strval.decode())
+
def getfeature(self, typename=None, filter=None, bbox=None, featureid=None,
- featureversion=None, propertyname=None, maxfeatures=None,storedQueryID=None, storedQueryParams={},
+ featureversion=None, propertyname=None, maxfeatures=None,storedQueryID=None, storedQueryParams=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
@@ -171,6 +185,7 @@ class WebFeatureService_2_0_0(WebFeatureService_):
2) typename and filter (==query) (more expressive)
3) featureid (direct access to known features)
"""
+ storedQueryParams = storedQueryParams or {}
url = data = None
if typename and type(typename) == type(""):
typename = [typename]
@@ -186,19 +201,19 @@ class WebFeatureService_2_0_0(WebFeatureService_):
# If method is 'Post', data will be None here
- u = urlopen(url, data, self.timeout)
-
+ u = openURL(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
# is data. We'll check anything smaller.
- try:
+ if 'Content-Length' in u.info():
length = int(u.info()['Content-Length'])
have_read = False
- except KeyError:
+ else:
data = u.read()
have_read = True
length = len(data)
-
+
if length < 32000:
if not have_read:
data = u.read()
@@ -207,16 +222,16 @@ class WebFeatureService_2_0_0(WebFeatureService_):
tree = etree.fromstring(data)
except BaseException:
# Not XML
- return StringIO(data)
+ return self._makeStringIO(data)
else:
if tree.tag == "{%s}ServiceExceptionReport" % OGC_NAMESPACE:
se = tree.find(nspath('ServiceException', OGC_NAMESPACE))
raise ServiceException(str(se.text).strip())
else:
- return StringIO(data)
+ return self._makeStringIO(data)
else:
if have_read:
- return StringIO(data)
+ return self._makeStringIO(data)
return u
@@ -239,7 +254,7 @@ class WebFeatureService_2_0_0(WebFeatureService_):
for kw in kwargs.keys():
request[kw]=str(kwargs[kw])
encoded_request=urlencode(request)
- u = urlopen(base_url + encoded_request)
+ u = openURL(base_url + encoded_request)
return u.read()
@@ -258,7 +273,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=self.timeout)
+ u = openURL(base_url, data=encoded_request, timeout=self.timeout)
tree=etree.fromstring(u.read())
tempdict={}
for sqelem in tree[:]:
@@ -278,7 +293,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=to)
+ u = openURL(base_url, data=encoded_request, timeout=to)
tree=etree.fromstring(u.read())
tempdict2={}
for sqelem in tree[:]:
@@ -386,7 +401,7 @@ class ContentMetadata:
if metadataUrl['url'] is not None and parse_remote_metadata: # download URL
try:
- content = urllib2.urlopen(metadataUrl['url'], timeout=timeout)
+ content = openURL(metadataUrl['url'], timeout=timeout)
doc = etree.parse(content)
try: # FGDC
metadataUrl['metadata'] = Metadata(doc)
@@ -438,7 +453,7 @@ class WFSCapabilitiesReader(object):
A timeout value (in seconds) for the request.
"""
request = self.capabilities_url(url)
- u = urlopen(request, timeout=timeout)
+ u = openURL(request, timeout=timeout)
return etree.fromstring(u.read())
def readString(self, st):
@@ -447,6 +462,6 @@ class WFSCapabilitiesReader(object):
string should be an XML capabilities document
"""
- if not isinstance(st, str):
- raise ValueError("String must be of type string, not %s" % type(st))
+ if not isinstance(st, str) and not isinstance(st, bytes):
+ raise ValueError("String must be of type string or bytes, not %s" % type(st))
return etree.fromstring(st)
diff --git a/owslib/fes.py b/owslib/fes.py
index 9caed02..e944635 100644
--- a/owslib/fes.py
+++ b/owslib/fes.py
@@ -98,7 +98,7 @@ class FilterRequest(object):
# And together filters if more than one exists
- filters = filter(None,[keyword_filter, bbox_filter, dc_type_equals_filter])
+ filters = [_f for _f in [keyword_filter, bbox_filter, dc_type_equals_filter] if _f]
if len(filters) == 1:
self._root.append(filters[0].toXML())
elif len(filters) > 1:
diff --git a/owslib/iso.py b/owslib/iso.py
index 6c294d0..adc7e96 100644
--- a/owslib/iso.py
+++ b/owslib/iso.py
@@ -541,6 +541,9 @@ class SV_ServiceIdentification(object):
""" process SV_ServiceIdentification """
def __init__(self, md=None):
if md is None:
+ self.title = None
+ self.abstract = None
+ self.contact = None
self.identtype = 'service'
self.type = None
self.version = None
@@ -552,6 +555,15 @@ class SV_ServiceIdentification(object):
else:
val=md.find(util.nspath_eval('gmd:citation/gmd:CI_Citation/gmd:title/gco:CharacterString', namespaces))
self.title=util.testXMLValue(val)
+
+ val=md.find(util.nspath_eval('gmd:abstract/gco:CharacterString', namespaces))
+ self.abstract=util.testXMLValue(val)
+
+ self.contact = None
+ val = md.find(util.nspath_eval('gmd:citation/gmd:CI_Citation/gmd:citedResponsibleParty/gmd:CI_ResponsibleParty', namespaces))
+ if val is not None:
+ self.contact = CI_ResponsibleParty(val)
+
self.identtype = 'service'
val = md.find(util.nspath_eval('srv:serviceType/gco:LocalName', namespaces))
self.type = util.testXMLValue(val)
@@ -705,12 +717,29 @@ class EX_Extent(object):
class MD_ReferenceSystem(object):
""" process MD_ReferenceSystem """
- def __init__(self, md):
+ def __init__(self, md=None):
if md is None:
- pass
+ self.code = None
+ self.codeSpace = None
+ self.version = None
else:
val = md.find(util.nspath_eval('gmd:referenceSystemIdentifier/gmd:RS_Identifier/gmd:code/gco:CharacterString', namespaces))
- self.code = util.testXMLValue(val)
+ if val is not None:
+ self.code = util.testXMLValue(val)
+ else:
+ self.code = None
+
+ val = md.find(util.nspath_eval('gmd:referenceSystemIdentifier/gmd:RS_Identifier/gmd:codeSpace/gco:CharacterString', namespaces))
+ if val is not None:
+ self.codeSpace = util.testXMLValue(val)
+ else:
+ self.codeSpace = None
+
+ val = md.find(util.nspath_eval('gmd:referenceSystemIdentifier/gmd:RS_Identifier/gmd:version/gco:CharacterString', namespaces))
+ if val is not None:
+ self.version = util.testXMLValue(val)
+ else:
+ self.version = None
def _testCodeListValue(elpath):
""" get gco:CodeListValue_Type attribute, else get text content """
@@ -761,7 +790,7 @@ class CodelistCatalogue(object):
self.dictionaries[id]['entries'][id2]['codespace'] = util.testXMLValue(val, True)
def getcodelistdictionaries(self):
- return self.dictionaries.keys()
+ return list(self.dictionaries.keys())
def getcodedefinitionidentifiers(self, cdl):
if cdl in self.dictionaries:
diff --git a/owslib/namespaces.py b/owslib/namespaces.py
index 5ee49b1..2ab248f 100644
--- a/owslib/namespaces.py
+++ b/owslib/namespaces.py
@@ -1,4 +1,5 @@
from __future__ import (absolute_import, division, print_function)
+import six
class Namespaces(object):
@@ -35,6 +36,7 @@ class Namespaces(object):
'ows200': 'http://www.opengis.net/ows/2.0',
'rim' : 'urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0',
'rdf' : 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
+ 'sa' : 'http://www.opengis.net/sampling/1.0',
'sml' : 'http://www.opengis.net/sensorML/1.0.1',
'sml101': 'http://www.opengis.net/sensorML/1.0.1',
'sos' : 'http://www.opengis.net/sos/1.0',
@@ -72,7 +74,7 @@ class Namespaces(object):
'http://www.opengis.net/wfs/2.0'
"""
retval = None
- if key in self.namespace_dict.keys():
+ if key in self.namespace_dict:
retval = self.namespace_dict[key]
return retval
@@ -102,7 +104,7 @@ class Namespaces(object):
key += version
retval = None
- if key in self.namespace_dict.keys():
+ if key in self.namespace_dict:
retval = self.namespace_dict[key]
return retval
@@ -131,7 +133,7 @@ class Namespaces(object):
if keys is None or len(keys) == 0:
return self.namespace_dict
- if isinstance(keys, unicode) or isinstance(keys, str):
+ if isinstance(keys, six.string_types):
return { keys: self.get_namespace(keys) }
retval = {}
diff --git a/owslib/swe/common.py b/owslib/swe/common.py
index bee32ac..ea11064 100644
--- a/owslib/swe/common.py
+++ b/owslib/swe/common.py
@@ -24,7 +24,7 @@ def make_pair(string, cast=None):
string = string.split(" ")
if cast is not None:
try:
- string = map(lambda x: cast(x), string)
+ string = [cast(x) for x in string]
except:
print("Could not cast pair to correct type. Setting to an empty tuple!")
string = ""
@@ -60,9 +60,9 @@ def get_float(value):
except:
return None
-AnyScalar = map(lambda x: nspv(x), ["swe20:Boolean", "swe20:Count", "swe20:Quantity", "swe20:Time", "swe20:Category", "swe20:Text"])
-AnyNumerical = map(lambda x: nspv(x), ["swe20:Count", "swe20:Quantity", "swe20:Time"])
-AnyRange = map(lambda x: nspv(x), ["swe20:QuantityRange", "swe20:TimeRange", "swe20:CountRange", "swe20:CategoryRange"])
+AnyScalar = [nspv(x) for x in ["swe20:Boolean", "swe20:Count", "swe20:Quantity", "swe20:Time", "swe20:Category", "swe20:Text"]]
+AnyNumerical = [nspv(x) for x in ["swe20:Count", "swe20:Quantity", "swe20:Time"]]
+AnyRange = [nspv(x) for x in ["swe20:QuantityRange", "swe20:TimeRange", "swe20:CountRange", "swe20:CategoryRange"]]
class NamedObject(object):
def __init__(self, element):
@@ -111,7 +111,7 @@ class AbstractSimpleComponent(AbstractDataComponent):
self.axisID = testXMLAttribute(element,"axisID") # string, optional
# Elements
- self.quality = filter(None, [Quality(q) for q in [e.find('*') for e in element.findall(nspv("swe20:quality"))] if q is not None])
+ self.quality = [_f for _f in [Quality(q) for q in [e.find('*') for e in element.findall(nspv("swe20:quality"))] if q is not None] if _f]
try:
self.nilValues = NilValues(element.find(nspv("swe20:nilValues")))
except:
@@ -134,7 +134,7 @@ class Quality(object):
class NilValues(AbstractSWE):
def __init__(self, element):
super(NilValues, self).__init__(element)
- self.nilValue = filter(None, [nilValue(x) for x in element.findall(nspv("swe20:nilValue"))]) # string, min=0, max=X
+ self.nilValue = [_f for _f in [nilValue(x) for x in element.findall(nspv("swe20:nilValue"))] if _f] # string, min=0, max=X
class nilValue(object):
def __init__(self, element):
@@ -144,21 +144,21 @@ class nilValue(object):
class AllowedTokens(AbstractSWE):
def __init__(self, element):
super(AllowedTokens, self).__init__(element)
- self.value = filter(None, [testXMLValue(x) for x in element.findall(nspv("swe20:value"))]) # string, min=0, max=X
+ self.value = [_f for _f in [testXMLValue(x) for x in element.findall(nspv("swe20:value"))] if _f] # string, min=0, max=X
self.pattern = testXMLValue(element.find(nspv("swe20:pattern"))) # string (Unicode Technical Standard #18, Version 13), min=0
class AllowedValues(AbstractSWE):
def __init__(self, element):
super(AllowedValues, self).__init__(element)
- self.value = filter(None, map(lambda x: get_float(x), [testXMLValue(x) for x in element.findall(nspv("swe20:value"))]))
- self.interval = filter(None, [make_pair(testXMLValue(x)) for x in element.findall(nspv("swe20:interval"))])
+ self.value = [_f for _f in [get_float(x) for x in [testXMLValue(x) for x in element.findall(nspv("swe20:value"))]] if _f]
+ self.interval = [_f for _f in [make_pair(testXMLValue(x)) for x in element.findall(nspv("swe20:interval"))] if _f]
self.significantFigures = get_int(testXMLValue(element.find(nspv("swe20:significantFigures")))) # integer, min=0
class AllowedTimes(AbstractSWE):
def __init__(self, element):
super(AllowedTimes, self).__init__(element)
- self.value = filter(None, [testXMLValue(x) for x in element.findall(nspv("swe20:value"))])
- self.interval = filter(None, [make_pair(testXMLValue(x)) for x in element.findall(nspv("swe20:interval"))])
+ self.value = [_f for _f in [testXMLValue(x) for x in element.findall(nspv("swe20:value"))] if _f]
+ self.interval = [_f for _f in [make_pair(testXMLValue(x)) for x in element.findall(nspv("swe20:interval"))] if _f]
self.significantFigures = get_int(testXMLValue(element.find(nspv("swe20:significantFigures")))) # integer, min=0
class Boolean(AbstractSimpleComponent):
@@ -390,11 +390,11 @@ class AbstractEncoding(object):
def __new__(cls, element):
t = element[-1].tag.split("}")[-1]
if t == "TextEncoding":
- return super(AbstractEncoding, cls).__new__(TextEncoding, element)
+ return super(AbstractEncoding, cls).__new__(TextEncoding)
elif t == "XMLEncoding":
- return super(AbstractEncoding, cls).__new__(XMLEncoding, element)
+ return super(AbstractEncoding, cls).__new__(XMLEncoding)
elif t == "BinaryEncoding":
- return super(AbstractEncoding, cls).__new__(BinaryEncoding, element)
+ return super(AbstractEncoding, cls).__new__(BinaryEncoding)
class TextEncoding(AbstractEncoding):
def __init__(self, element):
diff --git a/owslib/swe/observation/sos100.py b/owslib/swe/observation/sos100.py
index 06e5b1d..21bbc4e 100644
--- a/owslib/swe/observation/sos100.py
+++ b/owslib/swe/observation/sos100.py
@@ -3,7 +3,10 @@ from __future__ import (absolute_import, division, print_function)
import cgi
from owslib.etree import etree
from datetime import datetime
-from urllib import urlencode
+try: # Python 3
+ from urllib.parse import urlencode
+except ImportError: # Python 2
+ from urllib import urlencode
from owslib import ows
from owslib.crs import Crs
from owslib.fes import FilterCapabilities
@@ -12,7 +15,7 @@ from owslib.namespaces import Namespaces
def get_namespaces():
n = Namespaces()
- ns = n.get_namespaces(["ogc","sml","gml","om","sos","swe","xlink"])
+ ns = n.get_namespaces(["ogc","sa","sml","gml","sos","swe","xlink"])
ns["ows"] = n.get_namespace("ows110")
return ns
namespaces = get_namespaces()
@@ -145,6 +148,7 @@ class SensorObservationService_1_0_0(object):
offerings=None,
observedProperties=None,
eventTime=None,
+ procedure=None,
method='Get',
**kwargs):
"""
@@ -183,6 +187,9 @@ class SensorObservationService_1_0_0(object):
if 'timeout' in kwargs:
url_kwargs['timeout'] = kwargs.pop('timeout') # Client specified timeout value
+ if procedure is not None:
+ request['procedure'] = procedure
+
if kwargs:
for kw in kwargs:
request[kw]=kwargs[kw]
@@ -270,6 +277,9 @@ class SosObservationOffering(object):
def __str__(self):
return 'Offering id: %s, name: %s' % (self.id, self.name)
+ def __repr__(self):
+ return "<SosObservationOffering '%s'>" % self.name
+
class SosCapabilitiesReader(object):
def __init__(self, version="1.0.0", url=None, username=None, password=None):
self.version = version
@@ -316,6 +326,6 @@ class SosCapabilitiesReader(object):
st should be an XML capabilities document
"""
- if not isinstance(st, str):
- raise ValueError("String must be of type string, not %s" % type(st))
+ if not isinstance(st, bytes):
+ raise ValueError("String must be of type bytes, not %s" % type(st))
return etree.fromstring(st)
diff --git a/owslib/swe/observation/sos200.py b/owslib/swe/observation/sos200.py
index 5a0d45f..4894908 100644
--- a/owslib/swe/observation/sos200.py
+++ b/owslib/swe/observation/sos200.py
@@ -3,7 +3,10 @@ from __future__ import (absolute_import, division, print_function)
import cgi
from owslib.etree import etree
from datetime import datetime
-from urllib import urlencode
+try: # Python 3
+ from urllib.parse import urlencode
+except ImportError: # Python 2
+ from urllib import urlencode
from owslib import ows
from owslib.crs import Crs
from owslib.fes import FilterCapabilities200
@@ -12,7 +15,7 @@ from owslib.namespaces import Namespaces
def get_namespaces():
n = Namespaces()
- ns = n.get_namespaces(["fes","ogc","om","gml32","sml","swe20","swes","xlink"])
+ ns = n.get_namespaces(["fes","ogc","om","gml32","sa","sml","swe20","swes","xlink"])
ns["ows"] = n.get_namespace("ows110")
ns["sos"] = n.get_namespace("sos20")
return ns
@@ -179,6 +182,9 @@ class SensorObservationService_2_0_0(object):
if 'timeout' in kwargs:
url_kwargs['timeout'] = kwargs.pop('timeout') # Client specified timeout value
+ if procedure is not None:
+ request['procedure'] = procedure
+
if kwargs:
for kw in kwargs:
request[kw]=kwargs[kw]
@@ -264,6 +270,9 @@ class SosObservationOffering(object):
def __str__(self):
return 'Offering id: %s, name: %s' % (self.id, self.name)
+
+ def __repr__(self):
+ return "<SosObservationOffering '%s'>" % self.name
class SosCapabilitiesReader(object):
def __init__(self, version="2.0.0", url=None, username=None, password=None):
@@ -311,6 +320,6 @@ class SosCapabilitiesReader(object):
st should be an XML capabilities document
"""
- if not isinstance(st, str):
- raise ValueError("String must be of type string, not %s" % type(st))
+ if not isinstance(st, str) and not isinstance(st, bytes):
+ raise ValueError("String must be of type string or bytes, not %s" % type(st))
return etree.fromstring(st)
diff --git a/owslib/swe/sensor/sml.py b/owslib/swe/sensor/sml.py
index 28e199e..61c7ed9 100644
--- a/owslib/swe/sensor/sml.py
+++ b/owslib/swe/sensor/sml.py
@@ -19,7 +19,7 @@ def nsp(path):
class SensorML(object):
def __init__(self, element):
- if isinstance(element, str):
+ if isinstance(element, str) or isinstance(element, bytes):
self._root = etree.fromstring(element)
else:
self._root = element
diff --git a/owslib/tms.py b/owslib/tms.py
index c5eff4b..9bfa59c 100644
--- a/owslib/tms.py
+++ b/owslib/tms.py
@@ -18,7 +18,7 @@
from __future__ import (absolute_import, division, print_function)
from .etree import etree
-from .util import openURL, testXMLValue
+from .util import openURL, testXMLValue, ServiceException
FORCE900913 = False
@@ -40,14 +40,13 @@ class TileMapService(object):
Implements IWebMapService.
"""
- def __init__(self, url, version='1.0.0', xml=None,
- username=None, password=None, parse_remote_metadata=False
- ):
+ def __init__(self, url, version='1.0.0', xml=None, username=None, password=None, parse_remote_metadata=False, timeout=30):
"""Initialize."""
self.url = url
self.username = username
self.password = password
self.version = version
+ self.timeout = timeout
self.services = None
self._capabilities = None
self.contents={}
@@ -59,7 +58,7 @@ class TileMapService(object):
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, timeout=self.timeout)
# build metadata objects
self._buildMetadata(parse_remote_metadata)
@@ -117,22 +116,22 @@ class TileMapService(object):
items.append((item,self.contents[item]))
return items
- def _gettilefromset(self, tilesets, x, y,z, ext):
+ def _gettilefromset(self, tilesets, x, y,z, ext, timeout=None):
for tileset in tilesets:
if tileset['order'] == z:
url = tileset['href'] + '/' + str(x) +'/' + str(y) + '.' + ext
u = openURL(url, '', username = self.username,
- password = self.password)
+ password = self.password, timeout=timeout or self.timeout)
return u
else:
raise ValueError('cannot find zoomlevel %i for TileMap' % z)
- def gettile(self, x,y,z, id=None, title=None, srs=None, mimetype=None):
+ def gettile(self, x,y,z, id=None, title=None, srs=None, mimetype=None, timeout=None):
if not id and not title and not srs:
raise ValueError('either id or title and srs must be specified')
if id:
return self._gettilefromset(self.contents[id].tilemap.tilesets,
- x, y, z, self.contents[id].tilemap.extension)
+ x, y, z, self.contents[id].tilemap.extension, timeout=timeout)
elif title and srs:
for tm in self.contents.values():
@@ -140,12 +139,12 @@ class TileMapService(object):
if mimetype:
if tm.tilemap.mimetype == mimetype:
return self._gettilefromset(tm.tilemap.tilesets,
- x, y, z, tm.tilemap.extension)
+ x, y, z, tm.tilemap.extension, timeout=timeout)
else:
#if no format is given we return the tile from the
# first tilemap that matches name and srs
return self._gettilefromset(tm.tilemap.tilesets,
- x, y,z, tm.tilemap.extension)
+ x, y,z, tm.tilemap.extension, timeout=timeout)
else:
raise ValueError('cannot find %s with projection %s for zoomlevel %i'
%(title, srs, z) )
@@ -161,7 +160,7 @@ class ServiceIdentification(object):
def __init__(self, infoset, version):
self._root=infoset
if self._root.tag != 'TileMapService':
- raise ServiceException
+ raise ServiceException("Expected TileMapService tag, got %s" % self._root.tag)
self.version = version
self.title = testXMLValue(self._root.find('Title'))
self.abstract = testXMLValue(self._root.find('Abstract'))
@@ -318,11 +317,11 @@ class TMSCapabilitiesReader(object):
self.password = pw
- def read(self, service_url):
+ def read(self, service_url, timeout=30):
"""Get and parse a TMS capabilities document, returning an
elementtree instance
"""
- u = openURL(service_url, '', method='Get', username = self.username, password = self.password)
+ u = openURL(service_url, '', method='Get', username=self.username, password=self.password, timeout=timeout)
return etree.fromstring(u.read())
def readString(self, st):
diff --git a/owslib/util.py b/owslib/util.py
index cc586c0..a4b023c 100644
--- a/owslib/util.py
+++ b/owslib/util.py
@@ -9,59 +9,36 @@
from __future__ import (absolute_import, division, print_function)
-import base64
import sys
from dateutil import parser
from datetime import datetime
import pytz
-from owslib.etree import etree
+from owslib.etree import etree, ParseError
from owslib.namespaces import Namespaces
-import urlparse, urllib2
-from urllib2 import urlopen, HTTPError, Request
-from urllib2 import HTTPPasswordMgrWithDefaultRealm
-from urllib2 import HTTPBasicAuthHandler
-from StringIO import StringIO
+try: # Python 3
+ from urllib.parse import urlsplit, urlencode
+except ImportError: # Python 2
+ from urlparse import urlsplit
+ from urllib import urlencode
+
+try:
+ from StringIO import StringIO # Python 2
+ BytesIO = StringIO
+except ImportError:
+ from io import StringIO, BytesIO # Python 3
+
import cgi
-from urllib import urlencode
import re
from copy import deepcopy
import warnings
import time
-
+import six
+import requests
"""
Utility functions and classes
"""
-class RereadableURL(StringIO,object):
- """ Class that acts like a combination of StringIO and url - has seek method and url headers etc """
- def __init__(self, u):
- #get url headers etc from url
- self.headers = u.headers
- #get file like seek, read methods from StringIO
- content=u.read()
- #Due to race conditions the XML file might be empty. In that case the parsing method would
- #throw an exception. This issue can be fixed by requesting the url again and again
- #until the content is non-empty. To avoid an endless loop a time limit is hardcoded.
- timelimit = 10.0#sleep for an accumulated maximum of 10 seconds before giving up.
- timestep = 0.25
- timecur = 0.0
- while content == "":
- page = urllib2.urlopen(u.url)
- text = page.read()
- #The header line with <?xml... should not be in content.
- if "<?xml" == text.strip()[:5]:
- content = "\n".join(text.split("\n")[1:])
- else:
- content = text
- if timecur > timelimit:
- break#The parsing with se_tree = etree.fromstring(se_xml) will throw an exception.
- if content == "":
- time.sleep(timestep)
- timecur += timestep
- super(RereadableURL, self).__init__(content)
-
-
class ServiceException(Exception):
#TODO: this should go in ows common module when refactored.
pass
@@ -69,7 +46,7 @@ class ServiceException(Exception):
# http://stackoverflow.com/questions/6256183/combine-two-dictionaries-of-dictionaries-python
dict_union = lambda d1,d2: dict((x,(dict_union(d1.get(x,{}),d2[x]) if
isinstance(d2.get(x),dict) else d2.get(x,d1.get(x)))) for x in
- set(d1.keys()+d2.keys()))
+ set(list(d1.keys())+list(d2.keys())))
# Infinite DateTimes for Python. Used in SWE 2.0 and other OGC specs as "INF" and "-INF"
@@ -140,64 +117,85 @@ def xml_to_dict(root, prefix=None, depth=1, diction=None):
return ret
-def openURL(url_base, data, method='Get', cookies=None, username=None, password=None, timeout=30):
- ''' function to open urls - wrapper around urllib2.urlopen but with additional checks for OGC service exceptions and url formatting, also handles cookies and simple user password authentication'''
- url_base.strip()
- lastchar = url_base[-1]
- if lastchar not in ['?', '&']:
- if url_base.find('?') == -1:
- url_base = url_base + '?'
- else:
- url_base = url_base + '&'
-
+class ResponseWrapper(object):
+ """
+ Return object type from openURL.
+
+ Provides a thin shim around requests response object to maintain code compatibility.
+ """
+ def __init__(self, response):
+ self._response = response
+
+ def info(self):
+ return self._response.headers
+
+ def read(self):
+ if not self._response.encoding:
+ return self._response.content # bytes
+
+ return self._response.text.encode('utf-8') # str
+
+ def geturl(self):
+ return self._response.url
+
+ # @TODO: __getattribute__ for poking at response
+
+def openURL(url_base, data=None, method='Get', cookies=None, username=None, password=None, timeout=30):
+ """
+ Function to open URLs.
+
+ Uses requests library but with additional checks for OGC service exceptions and url formatting.
+ Also handles cookies and simple user password authentication.
+ """
+ auth = None
if username and password:
- # Provide login information in order to use the WMS server
- # Create an OpenerDirector with support for Basic HTTP
- # Authentication...
- passman = HTTPPasswordMgrWithDefaultRealm()
- passman.add_password(None, url_base, username, password)
- auth_handler = HTTPBasicAuthHandler(passman)
- opener = urllib2.build_opener(auth_handler)
- openit = opener.open
+ auth = (username, password)
+
+ headers = {}
+ rkwargs = {'timeout':timeout}
+
+ # FIXUP for WFS in particular, remove xml style namespace
+ # @TODO does this belong here?
+ method = method.split("}")[-1]
+
+ if method.lower() == 'post':
+ try:
+ xml = etree.fromstring(data)
+ headers['Content-Type'] = "text/xml"
+ except (ParseError, UnicodeEncodeError):
+ pass
+
+ rkwargs['data'] = data
+
+ elif method.lower() == 'get':
+ rkwargs['params'] = data
else:
- # NOTE: optionally set debuglevel>0 to debug HTTP connection
- #opener = urllib2.build_opener(urllib2.HTTPHandler(debuglevel=0))
- #openit = opener.open
- openit = urlopen
-
- try:
- if method == 'Post':
- req = Request(url_base, data)
- # set appropriate header if posting XML
- try:
- xml = etree.fromstring(data)
- req.add_header('Content-Type', "text/xml")
- except:
- pass
- else:
- req=Request(url_base + data)
- if cookies is not None:
- req.add_header('Cookie', cookies)
- u = openit(req, timeout=timeout)
- except HTTPError as e: #Some servers may set the http header to 400 if returning an OGC service exception or 401 if unauthorised.
- if e.code in [400, 401]:
- raise ServiceException(e.read())
- else:
- raise e
+ raise ValueError("Unknown method ('%s'), expected 'get' or 'post'" % method)
+
+ if cookies is not None:
+ rkwargs['cookies'] = cookies
+
+ req = requests.request(method.upper(),
+ url_base,
+ **rkwargs)
+
+ if req.status_code in [400, 401]:
+ raise ServiceException(req.text)
+
+ if req.status_code in [404]: # add more if needed
+ req.raise_for_status()
+
# check for service exceptions without the http header set
- if 'Content-Type' in u.info() and u.info()['Content-Type'] in ['text/xml', 'application/xml']:
+ if 'Content-Type' in req.headers and req.headers['Content-Type'] in ['text/xml', 'application/xml']:
#just in case 400 headers were not set, going to have to read the xml to see if it's an exception report.
- #wrap the url stram in a extended StringIO object so it's re-readable
- u=RereadableURL(u)
- se_xml= u.read()
- se_tree = etree.fromstring(se_xml)
+ se_tree = etree.fromstring(req.content)
serviceException=se_tree.find('{http://www.opengis.net/ows}Exception')
if serviceException is None:
serviceException=se_tree.find('ServiceException')
if serviceException is not None:
raise ServiceException(str(serviceException.text).strip())
- u.seek(0) #return cursor to start of u
- return u
+
+ return ResponseWrapper(req)
#default namespace for nspath is OWS common
OWS_NAMESPACE = 'http://www.opengis.net/ows/1.1'
@@ -243,12 +241,12 @@ def cleanup_namespaces(element):
def add_namespaces(root, ns_keys):
- if isinstance(ns_keys, basestring):
+ if isinstance(ns_keys, six.string_types):
ns_keys = [ns_keys]
namespaces = Namespaces()
- ns_keys = map(lambda x: (x, namespaces.get_namespace(x)), ns_keys)
+ ns_keys = [(x, namespaces.get_namespace(x)) for x in ns_keys]
if etree.__name__ != 'lxml.etree':
# We can just add more namespaces when not using lxml.
@@ -273,7 +271,7 @@ def add_namespaces(root, ns_keys):
# Recreate the root element with updated nsmap
new_root = etree.Element(root.tag, nsmap=new_map)
# Carry over attributes
- for a, v in root.items():
+ for a, v in list(root.items()):
new_root.set(a, v)
# Carry over children
for child in root:
@@ -355,41 +353,31 @@ def http_post(url=None, request=None, lang='en-US', timeout=10, username=None, p
"""
- if url is not None:
- u = urlparse.urlsplit(url)
- r = urllib2.Request(url, request)
- r.add_header('User-Agent', 'OWSLib (https://geopython.github.io/OWSLib)')
- r.add_header('Content-type', 'text/xml')
- r.add_header('Content-length', '%d' % len(request))
- r.add_header('Accept', 'text/xml')
- r.add_header('Accept-Language', lang)
- r.add_header('Accept-Encoding', 'gzip,deflate')
- r.add_header('Host', u.netloc)
-
- if username is not None and password is not None:
- base64string = base64.encodestring('%s:%s' % (username, password))[:-1]
- r.add_header('Authorization', 'Basic %s' % base64string)
- try:
- up = urllib2.urlopen(r,timeout=timeout);
- except TypeError:
- import socket
- socket.setdefaulttimeout(timeout)
- up = urllib2.urlopen(r)
+ if url is None:
+ raise ValueError("URL required")
+
+ u = urlsplit(url)
+
+ headers = {
+ 'User-Agent' : 'OWSLib (https://geopython.github.io/OWSLib)',
+ 'Content-type' : 'text/xml',
+ 'Content-length' : '%d' % len(request),
+ 'Accept' : 'text/xml',
+ 'Accept-Language' : lang,
+ 'Accept-Encoding' : 'gzip,deflate',
+ 'Host' : u.netloc,
+ }
- ui = up.info() # headers
- response = up.read()
- up.close()
+ rkwargs = {}
- # check if response is gzip compressed
- if 'Content-Encoding' in ui:
- if ui['Content-Encoding'] == 'gzip': # uncompress response
- import gzip
- cds = StringIO(response)
- gz = gzip.GzipFile(fileobj=cds)
- response = gz.read()
+ if username is not None and password is not None:
+ rkwargs['auth'] = (username, password)
- return response
+ up = requests.post(url, request, headers=headers, **rkwargs)
+ if not up.encoding:
+ return up.content # bytes
+ return up.text.encode('utf-8') # str
def element_to_string(element, encoding=None, xml_declaration=False):
"""
@@ -484,7 +472,7 @@ def build_get_url(base_url, params):
pars = [x[0] for x in qs]
- for key,value in params.iteritems():
+ for key,value in six.iteritems(params):
if key not in pars:
qs.append( (key,value) )
@@ -494,7 +482,7 @@ def build_get_url(base_url, params):
def dump(obj, prefix=''):
'''Utility function to print to standard output a generic object with all its attributes.'''
- print("%s %s : %s" % (prefix, obj.__class__, obj.__dict__))
+ print("%s %s.%s : %s" % (prefix, obj.__module__, obj.__class__.__name__, obj.__dict__))
def getTypedValue(type, value):
''' Utility function to cast a string value to the appropriate XSD type. '''
@@ -546,7 +534,7 @@ a newline. This will extract out all of the keywords correctly.
"""
keywords = [re.split(r'[\n\r]+',f.text) for f in elements if f.text]
flattened = [item.strip() for sublist in keywords for item in sublist]
- remove_blank = filter(None, flattened)
+ remove_blank = [_f for _f in flattened if _f]
return remove_blank
diff --git a/owslib/waterml/wml10.py b/owslib/waterml/wml10.py
index f90dfe4..0f0c0f5 100644
--- a/owslib/waterml/wml10.py
+++ b/owslib/waterml/wml10.py
@@ -1,7 +1,7 @@
from __future__ import (absolute_import, division, print_function)
from owslib.waterml.wml import SitesResponse, TimeSeriesResponse, VariablesResponse, namespaces
-from owslib.etree import etree
+from owslib.etree import etree, ElementType
def ns(namespace):
return namespaces.get(namespace)
@@ -9,10 +9,10 @@ def ns(namespace):
class WaterML_1_0(object):
def __init__(self, element):
- if isinstance(element, str) or isinstance(element, unicode):
- self._root = etree.fromstring(str(element))
- else:
+ if isinstance(element, ElementType):
self._root = element
+ else:
+ self._root = etree.fromstring(element)
if hasattr(self._root, 'getroot'):
self._root = self._root.getroot()
diff --git a/owslib/waterml/wml11.py b/owslib/waterml/wml11.py
index f95a27b..ac8ef47 100644
--- a/owslib/waterml/wml11.py
+++ b/owslib/waterml/wml11.py
@@ -1,7 +1,7 @@
from __future__ import (absolute_import, division, print_function)
from owslib.waterml.wml import SitesResponse, TimeSeriesResponse, VariablesResponse, namespaces
-from owslib.etree import etree
+from owslib.etree import etree, ElementType
def ns(namespace):
return namespaces.get(namespace)
@@ -9,10 +9,10 @@ def ns(namespace):
class WaterML_1_1(object):
def __init__(self, element):
- if isinstance(element, str) or isinstance(element, unicode):
- self._root = etree.fromstring(str(element))
- else:
+ if isinstance(element, ElementType):
self._root = element
+ else:
+ self._root = etree.fromstring(element)
if hasattr(self._root, 'getroot'):
self._root = self._root.getroot()
diff --git a/owslib/wcs.py b/owslib/wcs.py
index 572c87a..b67e771 100644
--- a/owslib/wcs.py
+++ b/owslib/wcs.py
@@ -15,29 +15,27 @@ Web Coverage Server (WCS) methods and metadata. Factory function.
from __future__ import (absolute_import, division, print_function)
-import urllib2
from . import etree
-from .coverage import wcs100, wcs110, wcsBase
+from .coverage import wcs100, wcs110, wcs111, wcsBase
+from owslib.util import openURL
def WebCoverageService(url, version=None, xml=None, cookies=None, timeout=30):
''' wcs factory function, returns a version specific WebCoverageService object '''
-
+
if version is None:
if xml is None:
reader = wcsBase.WCSCapabilitiesReader()
request = reader.capabilities_url(url)
- if cookies is None:
- xml = urllib2.urlopen(request, timeout=timeout).read()
- else:
- req = urllib2.Request(request)
- req.add_header('Cookie', cookies)
- xml=urllib2.urlopen(req, timeout=timeout)
+ xml = openURL(request, cookies=cookies, timeout=timeout).read()
+
capabilities = etree.etree.fromstring(xml)
version = capabilities.get('version')
del capabilities
-
+
if version == '1.0.0':
return wcs100.WebCoverageService_1_0_0.__new__(wcs100.WebCoverageService_1_0_0, url, xml, cookies)
elif version == '1.1.0':
return wcs110.WebCoverageService_1_1_0.__new__(wcs110.WebCoverageService_1_1_0,url, xml, cookies)
+ elif version == '1.1.1':
+ return wcs111.WebCoverageService_1_1_1.__new__(wcs111.WebCoverageService_1_1_1,url, xml, cookies)
diff --git a/owslib/wms.py b/owslib/wms.py
index bfe7597..238635b 100644
--- a/owslib/wms.py
+++ b/owslib/wms.py
@@ -18,9 +18,15 @@ Currently supports only version 1.1.1 of the WMS protocol.
from __future__ import (absolute_import, division, print_function)
import cgi
-import urllib2
-from urllib import urlencode
+try: # Python 3
+ from urllib.parse import urlencode
+except ImportError: # Python 2
+ from urllib import urlencode
+
import warnings
+
+import six
+
from .etree import etree
from .util import openURL, testXMLValue, extract_xml_list, xmltag_split
from .fgdc import Metadata
@@ -55,30 +61,27 @@ class WebMapService(object):
def __getitem__(self,name):
''' check contents dictionary to allow dict like access to service layers'''
- if name in self.__getattribute__('contents').keys():
+ if name in self.__getattribute__('contents'):
return self.__getattribute__('contents')[name]
else:
raise KeyError("No content named %s" % name)
- def __init__(self, url, version='1.1.1', xml=None,
- username=None, password=None, parse_remote_metadata=False
- ):
+ def __init__(self, url, version='1.1.1', xml=None, username=None, password=None, parse_remote_metadata=False, timeout=30):
"""Initialize."""
self.url = url
self.username = username
self.password = password
self.version = version
+ self.timeout = timeout
self._capabilities = None
-
+
# Authentication handled by Reader
- reader = WMSCapabilitiesReader(
- self.version, url=self.url, un=self.username, pw=self.password
- )
+ reader = WMSCapabilitiesReader(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, timeout=self.timeout)
# avoid building capabilities metadata if the response is a ServiceExceptionReport
se = self._capabilities.find('ServiceException')
@@ -150,7 +153,7 @@ class WebMapService(object):
)
u = self._open(reader.capabilities_url(self.url))
# check for service exceptions, and return
- if u.info().gettype() == 'application/vnd.ogc.se_xml':
+ if u.info()['Content-Type'] == 'application/vnd.ogc.se_xml':
se_xml = u.read()
se_tree = etree.fromstring(se_xml)
err_message = str(se_tree.find('ServiceException').text).strip()
@@ -162,6 +165,7 @@ class WebMapService(object):
bgcolor='#FFFFFF',
exceptions='application/vnd.ogc.se_xml',
method='Get',
+ timeout=None,
**kwargs
):
"""Request and return an image from the WMS as a file-like object.
@@ -200,8 +204,8 @@ class WebMapService(object):
size=(300, 300),\
format='image/jpeg',\
transparent=True)
- >>> out = open('example.jpg.jpg', 'wb')
- >>> out.write(img.read())
+ >>> out = open('example.jpg', 'wb')
+ >>> bytes_written = out.write(img.read())
>>> out.close()
"""
@@ -240,13 +244,13 @@ class WebMapService(object):
data = urlencode(request)
- u = openURL(base_url, data, method, username = self.username, password = self.password)
+ u = openURL(base_url, data, method, username=self.username, password=self.password, timeout=timeout or self.timeout)
# check for service exceptions, and return
if u.info()['Content-Type'] == 'application/vnd.ogc.se_xml':
se_xml = u.read()
se_tree = etree.fromstring(se_xml)
- err_message = unicode(se_tree.find('ServiceException').text).strip()
+ err_message = six.text_type(se_tree.find('ServiceException').text).strip()
raise ServiceException(err_message, se_xml)
return u
@@ -411,7 +415,7 @@ class ContentMetadata:
## some servers found in the wild use a single SRS
## tag containing a whitespace separated list of SRIDs
## instead of several SRS tags. hence the inner loop
- for srslist in map(lambda x: x.text, elem.findall('SRS')):
+ for srslist in [x.text for x in elem.findall('SRS')]:
if srslist:
for srs in srslist.split():
self.crsOptions.append(srs)
@@ -481,7 +485,7 @@ class ContentMetadata:
if metadataUrl['url'] is not None and parse_remote_metadata: # download URL
try:
- content = urllib2.urlopen(metadataUrl['url'], timeout=timeout)
+ content = openURL(metadataUrl['url'], timeout=timeout)
doc = etree.parse(content)
if metadataUrl['type'] is not None:
if metadataUrl['type'] == 'FGDC':
@@ -610,7 +614,7 @@ class WMSCapabilitiesReader:
urlqs = urlencode(tuple(qs))
return service_url.split('?')[0] + '?' + urlqs
- def read(self, service_url):
+ def read(self, service_url, timeout=30):
"""Get and parse a WMS capabilities document, returning an
elementtree instance
@@ -621,7 +625,7 @@ class WMSCapabilitiesReader:
#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)
+ u = openURL(spliturl[0], spliturl[1], method='Get', username=self.username, password=self.password, timeout=timeout)
return etree.fromstring(u.read())
def readString(self, st):
@@ -629,6 +633,6 @@ class WMSCapabilitiesReader:
string should be an XML capabilities document
"""
- if not isinstance(st, str):
- raise ValueError("String must be of type string, not %s" % type(st))
+ if not isinstance(st, str) and not isinstance(st, bytes):
+ raise ValueError("String must be of type string or bytes, not %s" % type(st))
return etree.fromstring(st)
diff --git a/owslib/wmts.py b/owslib/wmts.py
index 9b4d78d..f915f6f 100644
--- a/owslib/wmts.py
+++ b/owslib/wmts.py
@@ -32,9 +32,14 @@ would be appreciated.
from __future__ import (absolute_import, division, print_function)
import warnings
-import urlparse
-import urllib2
-from urllib import urlencode
+import six
+from six.moves import filter
+try: # Python 3
+ from urllib.parse import (urlencode, urlparse, urlunparse, parse_qs,
+ ParseResult)
+except ImportError: # Python 2
+ from urllib import urlencode
+ from urlparse import urlparse, urlunparse, parse_qs, ParseResult
from .etree import etree
from .util import openURL, testXMLValue, getXMLInteger
from .fgdc import Metadata
@@ -115,7 +120,7 @@ class WebMapTileService(object):
def __getitem__(self, name):
'''Check contents dictionary to allow dict like access to
service layers'''
- if name in self.__getattribute__('contents').keys():
+ if name in self.__getattribute__('contents'):
return self.__getattribute__('contents')[name]
else:
raise KeyError("No content named %s" % name)
@@ -293,7 +298,7 @@ TILEMATRIX=6&TILEROW=4&TILECOL=4&FORMAT=image%2Fjpeg'
if (layer is None):
raise ValueError("layer is mandatory (cannot be None)")
if style is None:
- style = self[layer].styles.keys()[0]
+ style = list(self[layer].styles.keys())[0]
if format is None:
format = self[layer].formats[0]
if tilematrixset is None:
@@ -318,7 +323,7 @@ TILEMATRIX=6&TILEROW=4&TILECOL=4&FORMAT=image%2Fjpeg'
request.append(('TILECOL', str(column)))
request.append(('FORMAT', format))
- for key, value in kwargs.iteritems():
+ for key, value in six.iteritems(kwargs):
request.append((key, value))
data = urlencode(request, True)
@@ -368,7 +373,7 @@ TILEMATRIX=6&TILEROW=4&TILECOL=4&FORMAT=image%2Fjpeg'
tilematrix='6',\
row=4, column=4)
>>> out = open('tile.jpg', 'wb')
- >>> out.write(img.read())
+ >>> bytes_written = out.write(img.read())
>>> out.close()
"""
@@ -380,9 +385,9 @@ TILEMATRIX=6&TILEROW=4&TILECOL=4&FORMAT=image%2Fjpeg'
if base_url is None:
base_url = self.url
try:
- get_verbs = filter(
- lambda x: x.get('type').lower() == 'get',
- self.getOperationByName('GetTile').methods)
+ methods = self.getOperationByName('GetTile').methods
+ get_verbs = [x for x in methods
+ if x.get('type').lower() == 'get']
if len(get_verbs) > 1:
# Filter by constraints
base_url = next(
@@ -390,8 +395,7 @@ TILEMATRIX=6&TILEROW=4&TILECOL=4&FORMAT=image%2Fjpeg'
list,
([pv.get('url')
for const in pv.get('constraints')
- if 'kvp' in map(
- lambda x: x.lower(), const.values)]
+ if 'kvp' in [x.lower() for x in const.values]]
for pv in get_verbs if pv.get('constraints'))))[0]
elif len(get_verbs) == 1:
base_url = get_verbs[0].get('url')
@@ -404,7 +408,7 @@ TILEMATRIX=6&TILEROW=4&TILECOL=4&FORMAT=image%2Fjpeg'
if u.info()['Content-Type'] == 'application/vnd.ogc.se_xml':
se_xml = u.read()
se_tree = etree.fromstring(se_xml)
- err_message = unicode(se_tree.find('ServiceException').text)
+ err_message = six.text_type(se_tree.find('ServiceException').text)
raise ServiceException(err_message.strip(), se_xml)
return u
@@ -703,8 +707,8 @@ class WMTSCapabilitiesReader:
"""
# 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)
+ pieces = urlparse(service_url)
+ args = parse_qs(pieces.query)
if 'service' not in args:
args['service'] = 'WMTS'
if 'request' not in args:
@@ -714,10 +718,10 @@ class WMTSCapabilitiesReader:
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)
+ pieces = ParseResult(pieces.scheme, pieces.netloc,
+ pieces.path, pieces.params,
+ query, pieces.fragment)
+ return urlunparse(pieces)
def read(self, service_url, vendor_kwargs=None):
"""Get and parse a WMTS capabilities document, returning an
@@ -740,7 +744,7 @@ class WMTSCapabilitiesReader:
string should be an XML capabilities document
"""
- if not isinstance(st, str):
- msg = 'String must be of type string, not %s' % type(st)
+ if not isinstance(st, str) and not isinstance(st, bytes):
+ msg = 'String must be of type string or bytes, not %s' % type(st)
raise ValueError(msg)
return etree.fromstring(st)
diff --git a/owslib/wps.py b/owslib/wps.py
index 0463407..43de1b0 100644
--- a/owslib/wps.py
+++ b/owslib/wps.py
@@ -377,7 +377,7 @@ class WPSReader(object):
Method to read a WPS GetCapabilities document from an XML string.
"""
- if not isinstance(string, str):
+ if not isinstance(string, str) and not isinstance(string, bytes):
raise ValueError("Input must be of type string, not %s" % type(string))
return etree.fromstring(string)
diff --git a/requirements.txt b/requirements.txt
index 8b6b3b8..508d303 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,2 +1,3 @@
python-dateutil>=1.5
pytz
+requests>=2.7
diff --git a/setup.py b/setup.py
index 7ba54c1..a316d64 100644
--- a/setup.py
+++ b/setup.py
@@ -1,3 +1,4 @@
+from __future__ import absolute_import
from setuptools import setup, find_packages
import owslib
from setuptools.command.test import test as TestCommand
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-grass/owslib.git
More information about the Pkg-grass-devel
mailing list