Ok, I have prepared a more thorough implementation of this idea. Although
some unit tests were failing for me (looks like some servers have been
updated/reconfigured since the tests were last updated) I have verified that
this patch doesn't break any tests that were already passing, and that it
works properly in GeoNode (ie no more session proliferation).
I don't believe I have an account on the OWSLib trac and self-registration
does not seem to be enabled, but the patch is attached to this email (I can
make it available via HTTP as well if that would be more useful; it wasn't
clear if my last attachment to this list made it through or not.)
There's a lot changed internally (everything is standardized on going
through local "opener" variables now) but for the factory methods used there
are just a few new optional arguments when setting up the client object
(these should work for WebFeatureService, WebMapService, WebCoverageService,
and CatalogueServiceWeb):
*opener* = a urllib2.OpenerDirector. if the other parameters listed here
are provided then they will be added to the opener.
*cookies* = a cookielib.CookieJar implementation (compatibility note! where
cookies were allowed before they were expected as a literal string, I have
used a CookieJar as this is the only option to work with the builtin
urllib2.HTTPCookieProcessor. However, when I tried to test the previous
cookie code I couldn't get it to work due to an apparent bug in the code so
I would be surprised if client code is relying on that functionality. I can
elaborate more on the bug I think I found but that code is replaced in the
patch.)
*username* = a username for http basic authentication (only used if a
password is also provided)
*password* = a password for http basic authentication (only used if a
username is also provided)
--
David Winslow
OpenGeo - http://opengeo.org/
On Wed, Apr 27, 2011 at 10:15 AM, David Winslow <[email protected]>wrote:
> The patch I have does not modify the existing password support - so when
> you pass in username/password to the openURL function, a new OpenerDirector
> is created, configured and used for that one request and immediately thrown
> away, ignoring the new "opener" field I have added to the various OWS client
> classes. I'll try and unify these for the final patch, now that I've gotten
> some feedback on the approach I've chosen.
>
> --
> David Winslow
> OpenGeo - http://opengeo.org/
>
>
> On Tue, Apr 26, 2011 at 10:45 AM, Dominic Lowe <[email protected]>wrote:
>
>> Hi David,
>>
>> I'd be very happy to see that patch - I'm sure there are other
>> circumstances when cookies would be useful in OWSLib.
>>
>> Am I right in thinking your current patch does not support http
>> password/username logon? I think that's pretty important to maintain as that
>> was a requirement from some other users.
>>
>> Otherwise I'm pretty happy for you to suggest a patch. Tom Kralidis may
>> have more comments as he has been most active on OWSLib recently.
>>
>> Regards,
>> Dominic
>>
>>
>>
>> On 25/04/11 19:07, [email protected] wrote:
>>
>>> Hi all,
>>>
>>> I work on GeoNode (http://geonode.org/). We're using OWSLib to fetch
>>> data from GeoNetwork. Recently I've discovered that due to the way
>>> GeoNetwork handles sessions, it makes a big difference for us to include
>>> HTTP cookies on our service requests (without them, GeoNetwork creates a new
>>> session for each service request and eventually locks up after running out
>>> of RAM.)
>>>
>>> Therefore I would like to modify owslib to use a (persistent) instance of
>>> urllib2.HTTPCookieProcessor when opening URLs, one which I can get a
>>> reference to so that I can log in to GeoNetwork and have owslib use the
>>> session cookie. Ideally, there would be a way to provide my own instance of
>>> urllib2.OpenerDirector which I can customize as needed.
>>>
>>> I am currently using a monkey patch to fix this behavior, but it's not
>>> the way I'd like to do things (you can see the diff in GeoNode here:
>>> https://github.com/dwins/geonode/compare/master...csw-cookies ).
>>>
>>> For a deeper fix I see basically 3 options:
>>>
>>> 1) Use Python's urllib2.install_opener to set a default opener instance
>>> to use, and be careful not to pass arguments into owslib methods that would
>>> cause them to create their own openers (for example, including a username
>>> and password in a call to owslib.util.openURL).
>>> 2) Create a module-level variable (owslib.util.opener) and modify
>>> owslib.util.openURL and owslib.util.http_post to reference it exclusively.
>>> Then client code which needs to modify the HTTP handling behavior can
>>> simply replace this variable.
>>> 3) Make each client class (ie, owslib.csw.CatalogueServiceWeb) use its
>>> own opener instance which may be provided by client code.
>>>
>>> The attached patch is a partial implementation of approach #3. (In each
>>> of these cases there is the question of how to handle the username/password
>>> arguments to owslib.util.openURL, I haven't addressed this.)
>>>
>>> Is this sort of thing something the OWSLib community would be interested
>>> in incorporating? If so I'm happy to update this patch to incorporate
>>> feedback etc.
>>>
>>> --
>>> David Winslow
>>> OpenGeo - http://opengeo.org/
>>>
>>>
>> --
>> Scanned by iCritical.
>> _______________________________________________
>> Community mailing list
>> [email protected]
>> http://lists.gispython.org/mailman/listinfo/community
>>
>
>
diff --git a/owslib/coverage/wcs100.py b/owslib/coverage/wcs100.py
index a0fd70b..fdb2d5c 100644
--- a/owslib/coverage/wcs100.py
+++ b/owslib/coverage/wcs100.py
@@ -11,7 +11,7 @@
from wcsBase import WCSBase, WCSCapabilitiesReader
from urllib import urlencode
-from owslib.util import openURL, testXMLValue
+from owslib.util import testXMLValue
from owslib.etree import etree
import os, errno
@@ -34,7 +34,8 @@ class WebCoverageService_1_0_0(WCSBase):
else:
raise KeyError, "No content named %s" % name
- def __init__(self,url,xml, cookies):
+ def __init__(self,url,xml, opener=None, cookies=None, username=None, password=None, url_base=None):
+ super(WebCoverageService_1_0_0, self).__init__(opener, cookies, username, password, url_base)
self.version='1.0.0'
self.url = url
self.cookies=cookies
@@ -135,9 +136,7 @@ class WebCoverageService_1_0_0(WCSBase):
#encode and request
data = urlencode(request)
self.log.debug('WCS 1.0.0 DEBUG: Second part of URL: %s'%data)
-
-
- u=openURL(base_url, data, method, self.cookies)
+ u=self.openURL(base_url, data, method)
return u
diff --git a/owslib/coverage/wcs110.py b/owslib/coverage/wcs110.py
index d69ed6c..7edcddc 100644
--- a/owslib/coverage/wcs110.py
+++ b/owslib/coverage/wcs110.py
@@ -12,9 +12,8 @@
##########NOTE: Does not conform to new interfaces yet #################
from wcsBase import WCSBase, WCSCapabilitiesReader
-from owslib.util import openURL, testXMLValue
+from owslib.util import testXMLValue
from urllib import urlencode
-from urllib2 import urlopen
from owslib.etree import etree
import os, errno
from owslib.coverage import wcsdecoder
@@ -169,7 +168,7 @@ class WebCoverageService_1_1_0(WCSBase):
#encode and request
data = urlencode(request)
- u=openURL(base_url, data, method, self.cookies)
+ u=self.openURL(base_url, data, method)
return u
diff --git a/owslib/coverage/wcsBase.py b/owslib/coverage/wcsBase.py
index 147d1ab..2793202 100644
--- a/owslib/coverage/wcsBase.py
+++ b/owslib/coverage/wcsBase.py
@@ -10,8 +10,9 @@
# =============================================================================
from urllib import urlencode
-from urllib2 import urlopen, Request
+from urllib2 import Request
from owslib.etree import etree
+from owslib.util import Client
import cgi
from StringIO import StringIO
@@ -19,9 +20,9 @@ from StringIO import StringIO
import logging
-class WCSBase(object):
+class WCSBase(Client):
"""Base class to be subclassed by version dependent WCS classes. Provides 'high-level' version independent methods"""
- def __new__(self,url, xml, cookies):
+ def __new__(self, url, xml, opener, cookies, username, password):
""" overridden __new__ method
@type url: string
@@ -31,7 +32,7 @@ class WCSBase(object):
@return: inititalised WCSBase object
"""
obj=object.__new__(self)
- obj.__init__(url, xml, cookies)
+ obj.__init__(url, xml, opener, cookies, username, password)
self.cookies=cookies
self.log = logging.getLogger(__name__)
consoleh = logging.StreamHandler()
@@ -39,8 +40,8 @@ class WCSBase(object):
self._describeCoverage = {} #cache for DescribeCoverage responses
return obj
- def __init__(self):
- pass
+ def __init__(self, url, opener, cookies, username, password):
+ super(WCSBase, self).__init__(opener, cookies, username, password, url)
def getDescribeCoverage(self, identifier):
''' returns a describe coverage document - checks the internal cache to see if it has been fetched before '''
@@ -63,15 +64,16 @@ class WCSBase(object):
elif level=='CRITICAL':
self.log.setLevel(logging.CRITICAL)
-class WCSCapabilitiesReader(object):
+class WCSCapabilitiesReader(Client):
"""Read and parses WCS capabilities document into a lxml.etree infoset
"""
- def __init__(self, version=None, cookies = None):
+ def __init__(self, version=None, cookies=None, opener=None, username=None, password=None, url_base=None):
"""Initialize
@type version: string
@param version: WCS Version parameter e.g '1.0.0'
"""
+ super(WCSCapabilitiesReader, self).__init__(opener, cookies, username, password, url_base)
self.version = version
self._infoset = None
self.cookies = cookies
@@ -109,11 +111,7 @@ class WCSCapabilitiesReader(object):
@rtype: elementtree tree
@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)
+ u = self.openURL(self.capabilities_url(service_url), '')
return etree.fromstring(u.read())
def readString(self, st):
@@ -125,15 +123,16 @@ class WCSCapabilitiesReader(object):
raise ValueError("String must be of type string, not %s" % type(st))
return etree.fromstring(st)
-class DescribeCoverageReader(object):
+class DescribeCoverageReader(Client):
"""Read and parses WCS DescribeCoverage document into a lxml.etree infoset
"""
- def __init__(self, version, identifier, cookies):
+ def __init__(self, version, identifier, cookies, opener=None, username=None, password=None, url_base=None):
"""Initialize
@type version: string
@param version: WCS Version parameter e.g '1.0.0'
"""
+ super(DescribeCoverageReader, self).__init__(opener, cookies, username, password, url_base)
self.version = version
self._infoset = None
self.identifier=identifier
@@ -182,11 +181,5 @@ class DescribeCoverageReader(object):
@rtype: elementtree tree
@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)
+ u = self.opener.open(self.descCov_url(service_url))
return etree.fromstring(u.read())
-
-
diff --git a/owslib/csw.py b/owslib/csw.py
index 213f049..8474cd9 100644
--- a/owslib/csw.py
+++ b/owslib/csw.py
@@ -15,6 +15,7 @@ import random
from owslib.etree import etree
from owslib.filter import *
from owslib import util
+from owslib.util import Client
from owslib.ows import *
from owslib.iso import *
from owslib.fgdc import *
@@ -45,9 +46,9 @@ namespaces = {
schema_location = '%s %s' % (namespaces['csw'], schema)
-class CatalogueServiceWeb:
+class CatalogueServiceWeb(Client):
""" csw request class """
- def __init__(self, url, lang='en-US', version='2.0.2', timeout=10):
+ def __init__(self, url, lang='en-US', version='2.0.2', timeout=10, opener=None, cookies=None, username=None, password=None):
"""
Construct and process a GetCapabilities request
@@ -61,6 +62,7 @@ class CatalogueServiceWeb:
- timeout: timeout in seconds
"""
+ super(CatalogueServiceWeb, self).__init__(opener, cookies, username, password, url)
self.url = url
self.lang = lang
@@ -454,7 +456,7 @@ class CatalogueServiceWeb:
def _invoke(self):
# do HTTP request
- self.response = util.http_post(self.url, self.request, self.lang, self.timeout)
+ self.response = self.http_post(self.url, self.request, self.lang, self.timeout)
# parse result see if it's XML
self._exml = etree.parse(StringIO.StringIO(self.response))
diff --git a/owslib/feature/wfs100.py b/owslib/feature/wfs100.py
index 952d604..354432c 100644
--- a/owslib/feature/wfs100.py
+++ b/owslib/feature/wfs100.py
@@ -8,8 +8,8 @@
import cgi
from cStringIO import StringIO
+from owslib.util import Client
from urllib import urlencode
-from urllib2 import urlopen
import logging
from owslib.etree import etree
@@ -43,12 +43,12 @@ class ServiceException(Exception):
pass
-class WebFeatureService_1_0_0(object):
+class WebFeatureService_1_0_0(Client):
"""Abstraction for OGC Web Feature Service (WFS).
Implements IWebFeatureService.
"""
- def __new__(self,url, version, xml):
+ def __new__(self,url, version, xml, opener, cookies, username, password):
""" overridden __new__ method
@type url: string
@@ -58,7 +58,7 @@ class WebFeatureService_1_0_0(object):
@return: initialized WebFeatureService_1_0_0 object
"""
obj=object.__new__(self)
- obj.__init__(url, version, xml)
+ obj.__init__(url, version, xml, opener, cookies, username, password)
self.log = logging.getLogger()
consoleh = logging.StreamHandler()
self.log.addHandler(consoleh)
@@ -72,8 +72,9 @@ class WebFeatureService_1_0_0(object):
raise KeyError, "No content named %s" % name
- def __init__(self, url, version, xml=None):
+ def __init__(self, url, version, xml, opener, cookies, username, password):
"""Initialize."""
+ super(WebFeatureService_1_0_0, self).__init__(opener, cookies, username, password, url)
self.url = url
self.version = version
self._capabilities = None
@@ -117,8 +118,8 @@ class WebFeatureService_1_0_0(object):
"""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))
+ reader = WFSCapabilitiesReader(self.version, self.opener)
+ return self.opener.open(reader.capabilities_url(self.url))
def items(self):
'''supports dict-like items() access'''
@@ -183,9 +184,9 @@ class WebFeatureService_1_0_0(object):
data = urlencode(request)
if method == 'Post':
- u = urlopen(base_url, data=data)
+ u = self.opener.open(base_url, data=data)
else:
- u = urlopen(base_url + data)
+ u = self.opener.open(base_url + data)
# check for service exceptions, rewrap, and return
# We're going to assume that anything with a content-length > 32k
@@ -363,12 +364,13 @@ class OperationMetadata:
self.methods = dict(methods)
-class WFSCapabilitiesReader(object):
+class WFSCapabilitiesReader(Client):
"""Read and parse capabilities document into a lxml.etree infoset
"""
- def __init__(self, version='1.0'):
+ def __init__(self, version='1.0', opener=None):
"""Initialize"""
+ super(WFSCapabilitiesReader, self).__init__(opener)
self.version = version
self._infoset = None
@@ -401,7 +403,7 @@ class WFSCapabilitiesReader(object):
The URL to the WFS capabilities document.
"""
request = self.capabilities_url(url)
- u = urlopen(request)
+ u = self.opener.open(request)
return etree.fromstring(u.read())
def readString(self, st):
diff --git a/owslib/feature/wfs200.py b/owslib/feature/wfs200.py
index 70b1bda..bdf2dd9 100644
--- a/owslib/feature/wfs200.py
+++ b/owslib/feature/wfs200.py
@@ -9,13 +9,12 @@
#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, Client
#other imports
import cgi
from cStringIO import StringIO
from urllib import urlencode
-from urllib2 import urlopen
import logging
@@ -43,12 +42,12 @@ class ServiceException(Exception):
pass
-class WebFeatureService_2_0_0(object):
+class WebFeatureService_2_0_0(Client):
"""Abstraction for OGC Web Feature Service (WFS).
Implements IWebFeatureService.
"""
- def __new__(self,url, version, xml):
+ def __new__(self,url, version, xml, opener, cookies, username, password):
""" overridden __new__ method
@type url: string
@@ -58,7 +57,7 @@ class WebFeatureService_2_0_0(object):
@return: initialized WebFeatureService_2_0_0 object
"""
obj=object.__new__(self)
- obj.__init__(url, version, xml)
+ obj.__init__(url, version, xml, opener, cookies, username, password)
self.log = logging.getLogger()
consoleh = logging.StreamHandler()
self.log.addHandler(consoleh)
@@ -72,13 +71,14 @@ class WebFeatureService_2_0_0(object):
raise KeyError, "No content named %s" % name
- def __init__(self, url, version, xml=None):
+ def __init__(self, url, version, xml, opener, cookies, username, password):
"""Initialize."""
+ super(WebFeatureService_2_0_0, self).__init__(opener, cookies, username, password)
log.debug('building WFS %s'%url)
self.url = url
self.version = version
self._capabilities = None
- reader = WFSCapabilitiesReader(self.version)
+ reader = WFSCapabilitiesReader(self.version, self.opener)
if xml:
self._capabilities = reader.readString(xml)
else:
@@ -132,7 +132,7 @@ class WebFeatureService_2_0_0(object):
file-like object.
NOTE: this is effectively redundant now"""
reader = WFSCapabilitiesReader(self.version)
- return urlopen(reader.capabilities_url(self.url))
+ return self.opener.open(reader.capabilities_url(self.url))
def items(self):
'''supports dict-like items() access'''
@@ -200,9 +200,9 @@ class WebFeatureService_2_0_0(object):
data = urlencode(request)
if method == 'Post':
- u = urlopen(base_url, data=data)
+ u = self.opener.open(base_url, data=data)
else:
- u = urlopen(base_url + data)
+ u = self.opener.open(base_url + data)
# check for service exceptions, rewrap, and return
# We're going to assume that anything with a content-length > 32k
@@ -245,7 +245,7 @@ class WebFeatureService_2_0_0(object):
for kw in kwargs.keys():
request[kw]=str(kwargs[kw])
data=urlencode(request)
- u = urlopen(base_url + data)
+ u = self.opener.open(base_url + data)
return u.read()
@@ -260,7 +260,7 @@ class WebFeatureService_2_0_0(object):
base_url = self.getOperationByName('ListStoredQueries').methods[method]['url']
request = {'service': 'WFS', 'version': self.version, 'request': 'ListStoredQueries'}
data = urlencode(request)
- u = urlopen(base_url + data)
+ u = self.opener.open(base_url + data)
tree=etree.fromstring(u.read())
base_url = self.getOperationByName('ListStoredQueries').methods[method]['url']
tempdict={}
@@ -278,7 +278,7 @@ class WebFeatureService_2_0_0(object):
base_url = self.getOperationByName('DescribeStoredQueries').methods[method]['url']
request = {'service': 'WFS', 'version': self.version, 'request': 'DescribeStoredQueries'}
data = urlencode(request)
- u = urlopen(base_url + data)
+ u = self.opener.open(base_url + data)
tree=etree.fromstring(u.read())
tempdict2={}
for sqelem in tree[:]:
@@ -360,12 +360,13 @@ class ContentMetadata:
self.styles=None
self.timepositions=None
-class WFSCapabilitiesReader(object):
+class WFSCapabilitiesReader(Client):
"""Read and parse capabilities document into a lxml.etree infoset
"""
- def __init__(self, version='2.0.0'):
+ def __init__(self, version, opener):
"""Initialize"""
+ super(WFSCapabilitiesReader, self).__init__(opener)
self.version = version
self._infoset = None
@@ -398,7 +399,7 @@ class WFSCapabilitiesReader(object):
The URL to the WFS capabilities document.
"""
request = self.capabilities_url(url)
- u = urlopen(request)
+ u = self.opener.open(request)
return etree.fromstring(u.read())
def readString(self, st):
diff --git a/owslib/util.py b/owslib/util.py
index 9419826..4504ee9 100644
--- a/owslib/util.py
+++ b/owslib/util.py
@@ -11,9 +11,11 @@
import sys
from owslib.etree import etree
import urlparse, urllib2
-from urllib2 import urlopen, HTTPError, Request
-from urllib2 import HTTPPasswordMgrWithDefaultRealm
from urllib2 import HTTPBasicAuthHandler
+from urllib2 import HTTPCookieProcessor
+from urllib2 import HTTPError
+from urllib2 import HTTPPasswordMgrWithDefaultRealm
+from urllib2 import Request
from StringIO import StringIO
"""
@@ -34,56 +36,108 @@ class ServiceException(Exception):
#TODO: this should go in ows common module when refactored.
pass
-def openURL(url_base, data, method='Get', cookies=None, username=None, password=None):
- ''' 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 + '?'
+class Client(object):
+ def __init__(self, opener=None, cookies=None, username=None, password=None, url_base=None):
+ if opener is None:
+ self.opener = urllib2.build_opener()
else:
- url_base = url_base + '&'
-
- 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
- else:
- openit = urlopen
-
- try:
- if method == 'Post':
- req = Request(url_base, data)
- else:
- req=Request(url_base + data)
+ self.opener = opener
+
if cookies is not None:
- req.add_header('Cookie', cookies)
- u = openit(req)
- except HTTPError, 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
- # check for service exceptions without the http header set
- if u.info()['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)
- 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
+ self.opener.add_handler(HTTPCookieProcessor(cookies))
+
+ if None not in (username, password, url_base):
+ passman = HTTPPasswordMgrWithDefaultRealm()
+ passman.add_password(None, url_base, username, password)
+ self.opener.add_handler(HTTPBasicAuthHandler(passman))
+
+
+ def openURL(self, url_base, data, method='Get'):
+ ''' 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 + '&'
+
+ try:
+ if method == 'Post':
+ req = Request(url_base, data)
+ else:
+ req = Request(url_base + data)
+ u = self.opener.open(req)
+ except HTTPError, 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
+ # check for service exceptions without the http header set
+ if u.info()['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)
+ 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
+
+
+ def http_post(self, url=None, request=None, lang='en-US', timeout=10):
+ """
+
+ Invoke an HTTP POST request
+
+ Parameters
+ ----------
+
+ - url: the URL of the server
+ - request: the request message
+ - lang: the language
+ - timeout: timeout in seconds
+
+ """
+ assert hasattr(self, 'opener')
+
+ if url is not None:
+ u = urlparse.urlsplit(url)
+ r = urllib2.Request(url, request)
+ r.add_header('User-Agent', 'OWSLib (http://trac.gispython.org/lab/wiki/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)
+
+ try:
+ up = self.opener.open(r, timeout=timeout);
+ except TypeError:
+ import socket
+ socket.setdefaulttimeout(timeout)
+ up = self.opener.open(r)
+
+ ui = up.info() # headers
+ response = up.read()
+ up.close()
+
+ # check if response is gzip compressed
+ if ui.has_key('Content-Encoding'):
+ if ui['Content-Encoding'] == 'gzip': # uncompress response
+ import gzip
+ cds = StringIO(response)
+ gz = gzip.GzipFile(fileobj=cds)
+ response = gz.read()
+
+ return response
+
#default namespace for nspath is OWS common
OWS_NAMESPACE = 'http://www.opengis.net/ows/1.1'
@@ -132,53 +186,6 @@ def testXMLValue(val, attrib=False):
return None
-def http_post(url=None, request=None, lang='en-US', timeout=10):
- """
-
- Invoke an HTTP POST request
-
- Parameters
- ----------
-
- - url: the URL of the server
- - request: the request message
- - lang: the language
- - timeout: timeout in seconds
-
- """
-
- if url is not None:
- u = urlparse.urlsplit(url)
- r = urllib2.Request(url, request)
- r.add_header('User-Agent', 'OWSLib (http://trac.gispython.org/lab/wiki/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)
-
- try:
- up = urllib2.urlopen(r,timeout=timeout);
- except TypeError:
- import socket
- socket.setdefaulttimeout(timeout)
- up = urllib2.urlopen(r)
-
- ui = up.info() # headers
- response = up.read()
- up.close()
-
- # check if response is gzip compressed
- if ui.has_key('Content-Encoding'):
- if ui['Content-Encoding'] == 'gzip': # uncompress response
- import gzip
- cds = StringIO(response)
- gz = gzip.GzipFile(fileobj=cds)
- response = gz.read()
-
- return response
-
def xml2string(xml):
"""
diff --git a/owslib/wcs.py b/owslib/wcs.py
index fec1dbc..cfb5bbb 100644
--- a/owslib/wcs.py
+++ b/owslib/wcs.py
@@ -18,24 +18,26 @@ import urllib2
import etree
from coverage import wcs100, wcs110, wcsBase
-def WebCoverageService(url, version=None, xml=None, cookies=None):
+def WebCoverageService(url, version=None, xml=None, opener=None, cookies=None, username=None, password=None):
''' wcs factory function, returns a version specific WebCoverageService object '''
+ possibly_created_opener = None
if version is None:
if xml is None:
- reader = wcsBase.WCSCapabilitiesReader()
+ reader = wcsBase.WCSCapabilitiesReader(opener=opener, cookies=cookies, username=username, password=password, url_base=url)
+ possibly_created_opener = reader.opener
request = reader.capabilities_url(url)
- if cookies is None:
- xml = urllib2.urlopen(request).read()
- else:
- req = urllib2.Request(request)
- req.add_header('Cookie', cookies)
- xml=urllib2.urlopen(req)
+ xml = reader.opener.open(request).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)
+ service_class = wcs100.WebCoverageService_1_0_0
elif version == '1.1.0':
- return wcs110.WebCoverageService_1_1_0.__new__(wcs110.WebCoverageService_1_1_0,url, xml, cookies)
+ service_class = wcs110.WebCoverageService_1_1_0
+
+ if possibly_created_opener is None:
+ return service_class.__new__(service_class, url, xml, opener, cookies, username, password)
+ else:
+ return service_class.__new__(service_class, url, xml, possibly_created_opener, None, None, None)
diff --git a/owslib/wfs.py b/owslib/wfs.py
index b3c4d94..d16bd33 100644
--- a/owslib/wfs.py
+++ b/owslib/wfs.py
@@ -14,10 +14,12 @@ Web Feature Server (WFS) methods and metadata. Factory function.
"""
from feature import wfs100, wfs200
-def WebFeatureService(url, version='1.0.0', xml=None):
+def WebFeatureService(url, version='1.0.0', xml=None, opener=None, cookies=None, username=None, password=None):
''' wfs factory function, returns a version specific WebFeatureService object '''
if version in ['1.0', '1.0.0']:
- return wfs100.WebFeatureService_1_0_0.__new__(wfs100.WebFeatureService_1_0_0, url, version, xml)
+ return wfs100.WebFeatureService_1_0_0.__new__(wfs100.WebFeatureService_1_0_0,
+ url, version, xml, opener, cookies, username, password)
elif version in ['2.0', '2.0.0']:
- return wfs200.WebFeatureService_2_0_0.__new__(wfs200.WebFeatureService_2_0_0, url, version, xml)
+ return wfs200.WebFeatureService_2_0_0.__new__(wfs200.WebFeatureService_2_0_0,
+ url, version, xml, opener, cookies, username, password)
diff --git a/owslib/wms.py b/owslib/wms.py
index 3d5b0ab..5242116 100644
--- a/owslib/wms.py
+++ b/owslib/wms.py
@@ -18,7 +18,7 @@ Currently supports only version 1.1.1 of the WMS protocol.
import cgi
from urllib import urlencode
from etree import etree
-from .util import openURL
+from .util import Client
class ServiceException(Exception):
@@ -41,7 +41,7 @@ class CapabilitiesError(Exception):
pass
-class WebMapService(object):
+class WebMapService(Client):
"""Abstraction for OGC Web Map Service (WMS).
Implements IWebMapService.
@@ -56,9 +56,10 @@ class WebMapService(object):
def __init__(self, url, version='1.1.1', xml=None,
- username=None, password=None
+ username=None, password=None, opener=None, cookies
):
"""Initialize."""
+ super(WebMapService, self).__init__(opener, cookies = cookies, username=username, password=password, url_base = url)
self.url = url
self.username = username
self.password = password
@@ -220,7 +221,7 @@ class WebMapService(object):
data = urlencode(request)
- u = openURL(base_url, data, method, username = self.username, password = self.password)
+ u = self.openURL(base_url, data, method)
# check for service exceptions, and return
if u.info()['Content-Type'] == 'application/vnd.ogc.se_xml':
@@ -492,28 +493,19 @@ class ContactMetadata:
else: self.position = None
-class WMSCapabilitiesReader:
+class WMSCapabilitiesReader(Client):
"""Read and parse capabilities document into a lxml.etree infoset
"""
- def __init__(self, version='1.1.1', url=None, un=None, pw=None):
+ def __init__(self, version='1.1.1', url=None, un=None, pw=None, client=None):
"""Initialize"""
+ super(WMSCapabilitiesReader, self).__init__(client)
self.version = version
self._infoset = None
self.url = url
self.username = un
self.password = pw
- #if self.username and self.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, self.url, self.username, self.password)
- #auth_handler = HTTPBasicAuthHandler(passman)
- #opener = build_opener(auth_handler)
- #self._open = opener.open
-
def capabilities_url(self, service_url):
"""Return a capabilities url
"""
@@ -544,7 +536,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 = self.openURL(spliturl[0], spliturl[1], method='Get')
return etree.fromstring(u.read())
def readString(self, st):
_______________________________________________
Community mailing list
[email protected]
http://lists.gispython.org/mailman/listinfo/community