[Git][debian-gis-team/owslib][master] 4 commits: New upstream version 0.23.0

Bas Couwenberg gitlab at salsa.debian.org
Sat Feb 6 06:09:21 GMT 2021



Bas Couwenberg pushed to branch master at Debian GIS Project / owslib


Commits:
c9a19efa by Bas Couwenberg at 2021-02-06T06:55:55+01:00
New upstream version 0.23.0
- - - - -
dc812391 by Bas Couwenberg at 2021-02-06T06:56:01+01:00
Update upstream source from tag 'upstream/0.23.0'

Update to upstream version '0.23.0'
with Debian dir f099d42159daa0a41aa1da03d772bd51df45fa6f
- - - - -
3fff354b by Bas Couwenberg at 2021-02-06T06:56:17+01:00
New upstream release.

- - - - -
13415940 by Bas Couwenberg at 2021-02-06T06:57:07+01:00
Set distribution to unstable.

- - - - -


19 changed files:

- CHANGES.rst
- VERSION.txt
- debian/changelog
- owslib/__init__.py
- owslib/coverage/wcs100.py
- owslib/coverage/wcs110.py
- owslib/coverage/wcs200.py
- owslib/coverage/wcs201.py
- owslib/coverage/wcsBase.py
- owslib/iso.py
- owslib/map/wms130.py
- owslib/ows.py
- owslib/util.py
- owslib/wcs.py
- − tests/doctests/gm03_parse.txt
- tests/resources/iso_mi.xml
- + tests/test_gm03.py
- tests/test_iso_parsing.py
- tests/test_wfs_generic.py


Changes:

=====================================
CHANGES.rst
=====================================
@@ -1,6 +1,37 @@
 Changes
 =======
 
+0.23.0 (2021-02-04)
+-------------------
+
+A full list of commits for 0.23.0 can be found at:
+
+https://github.com/geopython/OWSLib/commits/0.23.0
+
+- WMS: Handle empty <DataURL>.<Format> element in GetCapabilities (#739)
+- WCS: Feature/wcs headers integration (#741)
+- Tests: fix WFS tests (#743)
+- Auth: Support requests AuthBase in addition to basic password authentication (#724)
+- WCS: Fix bug with timeout not assigned (#738)
+- Tests: restore ISO GM03 tests as pytests (#734)
+- ISO: OWS/ISO metadata updates (#736)
+- ISO: add support for ISO 19115 keyword thesaurus URLs via gmx:Anchor (#735)
+- ISO: fix reference (#733)
+
+
+0.22.0 (2021-01-21)
+-------------------
+
+A full list of commits for 0.22.0 can be found at:
+
+https://github.com/geopython/OWSLib/commits/0.22.0
+
+- Tests: Fix tests coveralls (#732)
+- WCS, WFS: Add timeout to get capabilities (#730)
+- Tests: migrate to GitHub actions (#727)
+- ISO: add support for ISO 19115-2 (#726)
+
+
 0.21.0 (2020-12-09)
 -------------------
 


=====================================
VERSION.txt
=====================================
@@ -1 +1 @@
-0.22.0
+0.23.0


=====================================
debian/changelog
=====================================
@@ -1,3 +1,10 @@
+owslib (0.23.0-1) unstable; urgency=medium
+
+  * Team upload.
+  * New upstream release.
+
+ -- Bas Couwenberg <sebastic at debian.org>  Sat, 06 Feb 2021 06:56:56 +0100
+
 owslib (0.22.0-1) unstable; urgency=medium
 
   * Team upload.


=====================================
owslib/__init__.py
=====================================
@@ -1 +1 @@
-__version__ = '0.22.0'
+__version__ = '0.23.0'


=====================================
owslib/coverage/wcs100.py
=====================================
@@ -38,14 +38,14 @@ class WebCoverageService_1_0_0(WCSBase):
         else:
             raise KeyError("No content named %s" % name)
 
-    def __init__(self, url, xml, cookies, auth=None, timeout=30):
-        super(WebCoverageService_1_0_0, self).__init__(auth)
+    def __init__(self, url, xml, cookies, auth=None, timeout=30, headers=None):
+        super(WebCoverageService_1_0_0, self).__init__(auth, headers=headers)
         self.version = '1.0.0'
         self.url = url
         self.cookies = cookies
         self.timeout = timeout
         # initialize from saved capability document or access the server
-        reader = WCSCapabilitiesReader(self.version, self.cookies, self.auth)
+        reader = WCSCapabilitiesReader(self.version, self.cookies, self.auth, headers=self.headers)
         if xml:
             self._capabilities = reader.readString(xml)
         else:
@@ -155,7 +155,7 @@ class WebCoverageService_1_0_0(WCSBase):
         data = urlencode(request)
         log.debug('WCS 1.0.0 DEBUG: Second part of URL: %s' % data)
 
-        u = openURL(base_url, data, method, self.cookies, auth=self.auth, timeout=timeout)
+        u = openURL(base_url, data, method, self.cookies, auth=self.auth, timeout=timeout, headers=self.headers)
         return u
 
     def getOperationByName(self, name):


=====================================
owslib/coverage/wcs110.py
=====================================
@@ -51,13 +51,14 @@ class WebCoverageService_1_1_0(WCSBase):
         else:
             raise KeyError("No content named %s" % name)
 
-    def __init__(self, url, xml, cookies, auth=None, timeout=30):
-        super(WebCoverageService_1_1_0, self).__init__(auth=auth)
+    def __init__(self, url, xml, cookies, auth=None, timeout=30, headers=None):
+        super(WebCoverageService_1_1_0, self).__init__(auth=auth, headers=headers)
 
         self.url = url
         self.cookies = cookies
+        self.timeout = timeout
         # initialize from saved capability document or access the server
-        reader = WCSCapabilitiesReader(self.version, self.cookies, self.auth)
+        reader = WCSCapabilitiesReader(self.version, self.cookies, self.auth, headers=self.headers)
         if xml:
             self._capabilities = reader.readString(xml)
         else:
@@ -87,7 +88,7 @@ class WebCoverageService_1_1_0(WCSBase):
         # serviceOperations
         self.operations = []
         for elem in self._capabilities.findall(
-                self.ns.WCS_OWS('OperationsMetadata') + '/' + self.ns.WCS_OWS('Operation') + '/'):
+                self.ns.OWS('OperationsMetadata') + '/' + self.ns.OWS('Operation')):
             self.operations.append(Operation(elem, self.ns))
 
         # exceptions - ***********TO DO *************
@@ -205,7 +206,7 @@ class WebCoverageService_1_1_0(WCSBase):
         # encode and request
         data = urlencode(request)
 
-        u = openURL(base_url, data, method, self.cookies, auth=self.auth, timeout=timeout)
+        u = openURL(base_url, data, method, self.cookies, auth=self.auth, timeout=timeout, headers=self.headers)
         return u
 
     def getOperationByName(self, name):


=====================================
owslib/coverage/wcs200.py
=====================================
@@ -53,14 +53,15 @@ class WebCoverageService_2_0_0(WCSBase):
         else:
             raise KeyError("No content named %s" % name)
 
-    def __init__(self, url, xml, cookies, auth=None, timeout=30):
-        super(WebCoverageService_2_0_0, self).__init__(auth=auth)
+    def __init__(self, url, xml, cookies, auth=None, timeout=30, headers=None):
+        super(WebCoverageService_2_0_0, self).__init__(auth=auth, headers=headers)
         self.version = "2.0.0"
         self.url = url
         self.cookies = cookies
+        self.timeout = timeout
         self.ows_common = OwsCommon(version="2.0.0")
         # initialize from saved capability document or access the server
-        reader = WCSCapabilitiesReader(self.version, self.cookies, self.auth)
+        reader = WCSCapabilitiesReader(self.version, self.cookies, self.auth, headers=self.headers)
         if xml:
             self._capabilities = reader.readString(xml)
         else:
@@ -214,7 +215,7 @@ class WebCoverageService_2_0_0(WCSBase):
             data += param_list_to_url_string(sizes, 'size')
         log.debug("WCS 2.0.0 DEBUG: Second part of URL: %s" % data)
 
-        u = openURL(base_url, data, method, self.cookies, auth=self.auth, timeout=timeout)
+        u = openURL(base_url, data, method, self.cookies, auth=self.auth, timeout=timeout, headers=self.headers)
         return u
 
     def getOperationByName(self, name):


=====================================
owslib/coverage/wcs201.py
=====================================
@@ -53,14 +53,15 @@ class WebCoverageService_2_0_1(WCSBase):
         else:
             raise KeyError("No content named %s" % name)
 
-    def __init__(self, url, xml, cookies, auth=None, timeout=30):
-        super(WebCoverageService_2_0_1, self).__init__(auth=auth)
+    def __init__(self, url, xml, cookies, auth=None, timeout=30, headers=None):
+        super(WebCoverageService_2_0_1, self).__init__(auth=auth, headers=headers)
         self.version = "2.0.1"
         self.url = url
         self.cookies = cookies
+        self.timeout = timeout
         self.ows_common = OwsCommon(version="2.0.1")
         # initialize from saved capability document or access the server
-        reader = WCSCapabilitiesReader(self.version, self.cookies, self.auth)
+        reader = WCSCapabilitiesReader(self.version, self.cookies, self.auth, headers=self.headers)
         if xml:
             self._capabilities = reader.readString(xml)
         else:
@@ -215,7 +216,7 @@ class WebCoverageService_2_0_1(WCSBase):
 
         log.debug("WCS 2.0.1 DEBUG: Second part of URL: %s" % data)
 
-        u = openURL(base_url, data, method, self.cookies, auth=self.auth, timeout=timeout)
+        u = openURL(base_url, data, method, self.cookies, auth=self.auth, timeout=timeout, headers=self.headers)
         return u
 
     def getOperationByName(self, name):


=====================================
owslib/coverage/wcsBase.py
=====================================
@@ -33,7 +33,7 @@ class ServiceException(Exception):
 class WCSBase(object):
     """Base class to be subclassed by version dependent WCS classes. Provides 'high-level'
     version independent methods"""
-    def __new__(self, url, xml, cookies, auth=None):
+    def __new__(self, url, xml, cookies, auth=None, headers=None):
         """ overridden __new__ method
 
         @type url: string
@@ -41,22 +41,25 @@ class WCSBase(object):
         @type xml: string
         @param xml: elementtree object
         @param auth: instance of owslib.util.Authentication
+        @param headers: dict for geoserver's request's headers
         @return: inititalised WCSBase object
         """
         obj = object.__new__(self)
-        obj.__init__(url, xml, cookies, auth=auth)
+        obj.__init__(url, xml, cookies, auth=auth, headers=headers)
         self.cookies = cookies
+        self.headers = headers
         self._describeCoverage = {}  # cache for DescribeCoverage responses
         return obj
 
-    def __init__(self, auth=None):
+    def __init__(self, auth=None, headers=None):
         self.auth = auth or Authentication()
+        self.headers = headers
 
     def getDescribeCoverage(self, identifier):
         ''' returns a describe coverage document - checks the internal cache to see if it has been fetched before '''
         if identifier not in list(self._describeCoverage.keys()):
             reader = DescribeCoverageReader(
-                self.version, identifier, self.cookies, self.auth)
+                self.version, identifier, self.cookies, self.auth, self.headers)
             self._describeCoverage[identifier] = reader.read(self.url)
         return self._describeCoverage[identifier]
 
@@ -65,7 +68,7 @@ class WCSCapabilitiesReader(object):
     """Read and parses WCS capabilities document into a lxml.etree infoset
     """
 
-    def __init__(self, version=None, cookies=None, auth=None):
+    def __init__(self, version=None, cookies=None, auth=None, headers=None):
         """Initialize
         @type version: string
         @param version: WCS Version parameter e.g '1.0.0'
@@ -73,6 +76,7 @@ class WCSCapabilitiesReader(object):
         self.version = version
         self._infoset = None
         self.cookies = cookies
+        self.headers = headers
         self.auth = auth or Authentication()
 
     def capabilities_url(self, service_url):
@@ -109,7 +113,7 @@ class WCSCapabilitiesReader(object):
         @return: An elementtree tree representation of the capabilities document
         """
         request = self.capabilities_url(service_url)
-        u = openURL(request, timeout=timeout, cookies=self.cookies, auth=self.auth)
+        u = openURL(request, timeout=timeout, cookies=self.cookies, auth=self.auth, headers=self.headers)
         return etree.fromstring(u.read())
 
     def readString(self, st):
@@ -124,7 +128,7 @@ class DescribeCoverageReader(object):
     """Read and parses WCS DescribeCoverage document into a lxml.etree infoset
     """
 
-    def __init__(self, version, identifier, cookies, auth=None):
+    def __init__(self, version, identifier, cookies, auth=None, headers=None):
         """Initialize
         @type version: string
         @param version: WCS Version parameter e.g '1.0.0'
@@ -133,6 +137,7 @@ class DescribeCoverageReader(object):
         self._infoset = None
         self.identifier = identifier
         self.cookies = cookies
+        self.headers = headers
         self.auth = auth or Authentication()
 
     def descCov_url(self, service_url):
@@ -186,5 +191,5 @@ class DescribeCoverageReader(object):
         """
 
         request = self.descCov_url(service_url)
-        u = openURL(request, cookies=self.cookies, timeout=timeout, auth=self.auth)
+        u = openURL(request, cookies=self.cookies, timeout=timeout, auth=self.auth, headers=self.headers)
         return etree.fromstring(u.read())


=====================================
owslib/iso.py
=====================================
@@ -171,6 +171,8 @@ class MD_Metadata(object):
             val = md.find(util.nspath_eval('gmi:acquisitionInformation/gmi:MI_AcquisitionInformation', namespaces))
             if val is not None:
                 self.acquisition = MI_AcquisitionInformation(val)
+            else:
+                self.acquisition = None
 
     def get_default_locale(self):
         """ get default gmd:PT_Locale based on gmd:language """
@@ -323,6 +325,13 @@ class MD_Keywords(object):
 
                 thesaurus = val.find(util.nspath_eval('gmd:title/gco:CharacterString', namespaces))
                 self.thesaurus['title'] = util.testXMLValue(thesaurus)
+                self.thesaurus['url'] = None
+
+                if self.thesaurus['title'] is None:  # try gmx:Anchor
+                    t = val.find(util.nspath_eval('gmd:title/gmx:Anchor', namespaces))
+                    if t is not None:
+                        self.thesaurus['title'] = util.testXMLValue(t)
+                        self.thesaurus['url'] = t.attrib.get(util.nspath_eval('xlink:href', namespaces))
 
                 thesaurus = val.find(util.nspath_eval('gmd:date/gmd:CI_Date/gmd:date/gco:Date', namespaces))
                 self.thesaurus['date'] = util.testXMLValue(thesaurus)


=====================================
owslib/map/wms130.py
=====================================
@@ -665,9 +665,11 @@ class ContentMetadata(AbstractContentMetadata):
         self.dataUrls = []
         for m in elem.findall(nspath('DataURL', WMS_NAMESPACE)):
             dataUrl = {
-                'format': m.find(nspath('Format', WMS_NAMESPACE)).text.strip(),
+                'format': m.find(nspath('Format', WMS_NAMESPACE)).text,
                 'url': m.find(nspath('OnlineResource', WMS_NAMESPACE)).attrib['{http://www.w3.org/1999/xlink}href']
             }
+            if dataUrl['format'] is not None:
+                dataUrl['format'] = dataUrl['format'].strip()
             self.dataUrls.append(dataUrl)
 
         # FeatureListURLs


=====================================
owslib/ows.py
=====================================
@@ -62,6 +62,9 @@ class ServiceIdentification(object):
             if f.text is not None:
                 self.keywords.append(f.text)
 
+        val = self._root.find(util.nspath('Keywords/Type', namespace))
+        self.keywords_type = util.testXMLValue(val)
+
         val = self._root.find(util.nspath('AccessConstraints', namespace))
         self.accessconstraints = util.testXMLValue(val)
 


=====================================
owslib/util.py
=====================================
@@ -15,7 +15,7 @@ from datetime import datetime, timedelta
 import pytz
 from owslib.etree import etree, ParseError
 from owslib.namespaces import Namespaces
-from urllib.parse import urlsplit, urlencode, urlparse, parse_qs, urlunparse, parse_qsl
+from urllib.parse import urlsplit, urlencode, urlparse, parse_qs, urlunparse, parse_qsl, unquote
 import copy
 
 from io import StringIO, BytesIO
@@ -24,6 +24,7 @@ import re
 from copy import deepcopy
 import warnings
 import requests
+from requests.auth import AuthBase
 import codecs
 
 """
@@ -173,8 +174,12 @@ def openURL(url_base, data=None, method='Get', cookies=None, username=None, pass
         verify = verify and auth.verify
     else:
         auth = Authentication(username, password, cert, verify)
+
     if auth.username and auth.password:
         rkwargs['auth'] = (auth.username, auth.password)
+    elif auth.auth_delegate is not None:
+        rkwargs['auth'] = auth.auth_delegate
+
     rkwargs['cert'] = auth.cert
     rkwargs['verify'] = verify
 
@@ -192,7 +197,7 @@ def openURL(url_base, data=None, method='Get', cookies=None, username=None, pass
         rkwargs['data'] = data
 
     elif method.lower() == 'get':
-        rkwargs['params'] = data
+        rkwargs['params'] = unquote(data) if data else None
 
     else:
         raise ValueError("Unknown method ('%s'), expected 'get' or 'post'" % method)
@@ -419,6 +424,8 @@ def http_post(url=None, request=None, lang='en-US', timeout=10, username=None, p
         auth = Authentication(username, password)
     if auth.username is not None and auth.password is not None:
         rkwargs['auth'] = (auth.username, auth.password)
+    elif auth.auth_delegate is not None:
+        rkwargs['auth'] = auth.auth_delegate
     rkwargs['verify'] = auth.verify
     rkwargs['cert'] = auth.cert
 
@@ -451,6 +458,8 @@ def http_get(*args, **kwargs):
     # Build keyword args for call to requests.get()
     if auth.username and auth.password:
         rkwargs.setdefault('auth', (auth.username, auth.password))
+    elif auth.auth_delegate is not None:
+        rkwargs['auth'] = auth.auth_delegate
     else:
         rkwargs.setdefault('auth', None)
     rkwargs.setdefault('cert', rkwargs.get('cert'))
@@ -837,11 +846,13 @@ class Authentication(object):
 
     _USERNAME = None
     _PASSWORD = None
+    _AUTH_DELEGATE = None
     _CERT = None
     _VERIFY = None
 
     def __init__(self, username=None, password=None,
-                 cert=None, verify=True, shared=False):
+                 cert=None, verify=True, shared=False,
+                 auth_delegate=None):
         '''
         :param str username=None: Username for basic authentication, None for
             unauthenticated access (or if using cert/verify)
@@ -856,12 +867,16 @@ class Authentication(object):
         :param bool shared=False: Set to True to make the values be class-level
             attributes (shared among instances where shared=True) instead of
             instance-level (shared=False, default)
+        :param AuthBase auth_delegate=None: Instance of requests' AuthBase to
+            allow arbitrary authentication schemes - mutually exclusive with
+            username/password arguments.
         '''
         self.shared = shared
         self.username = username
         self.password = password
         self.cert = cert
         self.verify = verify
+        self.auth_delegate = auth_delegate
 
     @property
     def username(self):
@@ -871,10 +886,15 @@ class Authentication(object):
 
     @username.setter
     def username(self, value):
-        if value is None:
-            pass
-        elif not isinstance(value, str):
-            raise TypeError('Value for "username" must be a str')
+        if value is not None:
+
+            if not isinstance(value, str):
+                raise TypeError('Value for "username" must be a str')
+
+            if self.auth_delegate is not None:
+                raise ValueError('Authentication instances may have username/password or auth_delegate set,'
+                                 ' but not both')
+
         if self.shared:
             self.__class__._USERNAME = value
         else:
@@ -888,10 +908,15 @@ class Authentication(object):
 
     @password.setter
     def password(self, value):
-        if value is None:
-            pass
-        elif not isinstance(value, str):
-            raise TypeError('Value for "password" must be a str')
+        if value is not None:
+
+            if not isinstance(value, str):
+                raise TypeError('Value for "password" must be a str')
+
+            if self.auth_delegate is not None:
+                raise ValueError('Authentication instances may have username/password or auth_delegate set,'
+                                 ' but not both')
+
         if self.shared:
             self.__class__._PASSWORD = value
         else:
@@ -951,8 +976,32 @@ class Authentication(object):
         else:
             self._verify = value
 
+    @property
+    def auth_delegate(self):
+        if self.shared:
+            return self._AUTH_DELEGATE
+        return self._auth_delegate
+
+    @auth_delegate.setter
+    def auth_delegate(self, value):
+        if value is not None:
+            if not isinstance(value, AuthBase):
+                raise TypeError('Value for "auth_delegate" must be an instance of AuthBase')
+
+            if self.username is not None or self.password is not None:
+                raise ValueError('Authentication instances may have username/password or auth_delegate set,'
+                                 ' but not both')
+
+        if self.shared:
+            self.__class__._AUTH_DELEGATE = value
+        else:
+            self._auth_delegate = value
+
     @property
     def urlopen_kwargs(self):
+        if self.auth_delegate is not None:
+            raise NotImplementedError("The urlopen_kwargs property is not supported when auth_delegate is set")
+
         return {
             'username': self.username,
             'password': self.password,
@@ -961,5 +1010,6 @@ class Authentication(object):
         }
 
     def __repr__(self, *args, **kwargs):
-        return '<{} shared={} username={} password={} cert={} verify={}>'.format(
-            self.__class__.__name__, self.shared, self.username, self.password, self.cert, self.verify)
+        return '<{} shared={} username={} password={} cert={} verify={} auth_delegate={}>'.format(
+            self.__class__.__name__, self.shared, self.username, self.password, self.cert, self.verify,
+            self.auth_delegate)


=====================================
owslib/wcs.py
=====================================
@@ -18,7 +18,7 @@ from .coverage import wcs100, wcs110, wcs111, wcsBase, wcs200, wcs201
 from owslib.util import clean_ows_url, Authentication, openURL
 
 
-def WebCoverageService(url, version=None, xml=None, cookies=None, timeout=30, auth=None):
+def WebCoverageService(url, version=None, xml=None, cookies=None, timeout=30, auth=None, headers=None):
     ''' wcs factory function, returns a version specific WebCoverageService object '''
 
     if not auth:
@@ -26,10 +26,10 @@ def WebCoverageService(url, version=None, xml=None, cookies=None, timeout=30, au
 
     if version is None:
         if xml is None:
-            reader = wcsBase.WCSCapabilitiesReader(auth=auth)
+            reader = wcsBase.WCSCapabilitiesReader(auth=auth, headers=headers)
             request = reader.capabilities_url(url)
             xml = openURL(
-                request, cookies=cookies, timeout=timeout, auth=auth).read()
+                request, cookies=cookies, timeout=timeout, auth=auth, headers=headers).read()
 
         capabilities = etree.etree.fromstring(xml)
         version = capabilities.get('version')
@@ -39,16 +39,16 @@ def WebCoverageService(url, version=None, xml=None, cookies=None, timeout=30, au
 
     if version == '1.0.0':
         return wcs100.WebCoverageService_1_0_0.__new__(
-            wcs100.WebCoverageService_1_0_0, clean_url, xml, cookies, auth=auth)
+            wcs100.WebCoverageService_1_0_0, clean_url, xml, cookies, auth=auth, headers=headers)
     elif version == '1.1.0':
         return wcs110.WebCoverageService_1_1_0.__new__(
-            wcs110.WebCoverageService_1_1_0, url, xml, cookies, auth=auth)
+            wcs110.WebCoverageService_1_1_0, url, xml, cookies, auth=auth, headers=headers)
     elif version == '1.1.1':
         return wcs111.WebCoverageService_1_1_1.__new__(
-            wcs111.WebCoverageService_1_1_1, url, xml, cookies, auth=auth)
+            wcs111.WebCoverageService_1_1_1, url, xml, cookies, auth=auth, headers=headers)
     elif version == '2.0.0':
         return wcs200.WebCoverageService_2_0_0.__new__(
-            wcs200.WebCoverageService_2_0_0, url, xml, cookies, auth=auth)
+            wcs200.WebCoverageService_2_0_0, url, xml, cookies, auth=auth, headers=headers)
     elif version == '2.0.1':
         return wcs201.WebCoverageService_2_0_1.__new__(
-            wcs201.WebCoverageService_2_0_1, url, xml, cookies, auth=auth)
+            wcs201.WebCoverageService_2_0_1, url, xml, cookies, auth=auth, headers=headers)


=====================================
tests/doctests/gm03_parse.txt deleted
=====================================
@@ -1,60 +0,0 @@
-
-Imports
-
-    >>> from tests.utils import resource_file
-    >>> from owslib.etree import etree
-    >>> from owslib.gm03 import GM03
-
-Print testing some metadata elements
-
-    >>> e = etree.parse(resource_file('gm03_example1.xml'))
-    >>> gm03 = GM03(e)
-    >>> gm03.header.version
-    '2.3'
-    >>> gm03.header.sender
-    'geocat.ch'
-    >>> hasattr(gm03.data, 'core')
-    False
-    >>> hasattr(gm03.data, 'comprehensive')
-    True
-    >>> len(gm03.data.comprehensive.elements)
-    13
-    >>> sorted(list(gm03.data.comprehensive.elements.keys()))
-    ['address', 'citation', 'contact', 'data_identification', 'date', 'extent', 'extent_geographic_element', 'geographic_bounding_box', 'identification_point_of_contact', 'keywords', 'metadata', 'metadata_point_of_contact', 'responsible_party']
-    >>> isinstance(gm03.data.comprehensive.date, list)
-    True
-    >>> len(gm03.data.comprehensive.date)
-    1
-    >>> gm03.data.comprehensive.metadata.file_identifier
-    '41ac321f632e55cebf0508a2cea5d9023fd12d9ad46edd679f2c275127c88623fb9c9d29726bef7c'
-    >>> gm03.data.comprehensive.metadata.date_stamp
-    '1999-12-31T12:00:00'
-    >>> gm03.data.comprehensive.metadata.language
-    'de'
-
-Test TID searching
-
-    >>> gm03.data.comprehensive.metadata.tid
-    'xN6509077498146737843'
-    >>> search_tid = gm03.data.comprehensive.metadata.tid
-    >>> gm03.data.comprehensive.get_element_by_tid('404') is None
-    True
-    >>> gm03.data.comprehensive.get_element_by_tid(search_tid) is None
-    False
-    >>> search_tid2 = gm03.data.comprehensive.extent.data_identification.ref
-    >>> search_tid2
-    'xN8036063300808707346'
-    >>> gm03.data.comprehensive.get_element_by_tid(search_tid2) is None
-    False
-    >>> e = etree.parse(resource_file('gm03_example2.xml'))
-    >>> gm03 = GM03(e)
-    >>> gm03.data.comprehensive.geographic_bounding_box.extent_type_code
-    'false'
-    >>> gm03.data.comprehensive.geographic_bounding_box.north_bound_latitude
-    '47.1865387201702'
-    >>> gm03.data.comprehensive.geographic_bounding_box.south_bound_latitude
-    '47.1234508676764'
-    >>> gm03.data.comprehensive.geographic_bounding_box.east_bound_longitude
-    '9.10597474389878'
-    >>> gm03.data.comprehensive.geographic_bounding_box.west_bound_longitude
-    '9.23798212070671'


=====================================
tests/resources/iso_mi.xml
=====================================
@@ -1,5 +1,5 @@
 <?xml version="1.0" ?>
-<gmi:MI_Metadata xmlns:gco="http://www.isotc211.org/2005/gco" xmlns:gmd="http://www.isotc211.org/2005/gmd" xmlns:gmi="http://www.isotc211.org/2005/gmi" xmlns:gml="http://www.opengis.net/gml" xmlns:gmx="http://www.isotc211.org/2005/gmx" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.isotc211.org/2005/gmd http://www.isotc211.org/2005/gmd/gmd.xsd http://www.isotc211.org/2005/gmx http://www.isotc211.org/2005/gmx/gmx.xsd http://www.isotc211.org/2005/gmi http://www.isotc211.org/2005/gmx/gmi.xsd">
+<gmi:MI_Metadata xmlns:gco="http://www.isotc211.org/2005/gco" xmlns:gmd="http://www.isotc211.org/2005/gmd" xmlns:gmi="http://www.isotc211.org/2005/gmi" xmlns:gml="http://www.opengis.net/gml" xmlns:gmx="http://www.isotc211.org/2005/gmx" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.isotc211.org/2005/gmd http://www.isotc211.org/2005/gmd/gmd.xsd http://www.isotc211.org/2005/gmx http://www.isotc211.org/2005/gmx/gmx.xsd http://www.isotc211.org/2005/gmi http://www.isotc211.org/2005/gmx/gmi.xsd">
   <gmd:fileIdentifier>
     <gco:CharacterString>3f342f64-9348-11df-ba6a-0014c2c00eab</gco:CharacterString>
   </gmd:fileIdentifier>
@@ -268,6 +268,19 @@
           <gmd:type>
             <gmd:MD_KeywordTypeCode codeList="http://www.isotc211.org/2005/resources/Codelist/gmxCodelists.xml#MD_KeywordTypeCode" codeListValue="theme" codeSpace="ISOTC211/19115">theme</gmd:MD_KeywordTypeCode>
           </gmd:type>
+          <gmd:thesaurusName>
+            <gmd:CI_Citation>
+              <gmd:title>
+                <gmx:Anchor xlink:title="My Vocabulary" xlink:href="https://example.org/my-vocab">My Vocabulary</gmx:Anchor>
+              </gmd:title>
+              <gmd:date>
+                <gmd:CI_Date>
+                  <gmd:date gco:nilReason="missing"/>
+                  <gmd:dateType gco:nilReason="missing"/>
+                </gmd:CI_Date>
+              </gmd:date>
+            </gmd:CI_Citation>
+          </gmd:thesaurusName>
         </gmd:MD_Keywords>
       </gmd:descriptiveKeywords>
       <gmd:descriptiveKeywords>


=====================================
tests/test_gm03.py
=====================================
@@ -0,0 +1,41 @@
+
+from tests.utils import resource_file
+from owslib.etree import etree
+from owslib.gm03 import GM03
+
+
+def test_gm03():
+    """Test GM03 parsing"""
+
+    e = etree.parse(resource_file('gm03_example1.xml'))
+    gm03 = GM03(e)
+    assert gm03.header.version == '2.3'
+    assert gm03.header.sender == 'geocat.ch'
+    assert not hasattr(gm03.data, 'core')
+    assert hasattr(gm03.data, 'comprehensive')
+    assert len(gm03.data.comprehensive.elements) == 13
+    assert sorted(list(gm03.data.comprehensive.elements.keys())) == ['address', 'citation', 'contact', 'data_identification', 'date', 'extent', 'extent_geographic_element', 'geographic_bounding_box', 'identification_point_of_contact', 'keywords', 'metadata', 'metadata_point_of_contact', 'responsible_party']  # noqa
+    assert isinstance(gm03.data.comprehensive.date, list)
+    assert len(gm03.data.comprehensive.date) == 1
+    assert gm03.data.comprehensive.metadata.file_identifier == '41ac321f632e55cebf0508a2cea5d9023fd12d9ad46edd679f2c275127c88623fb9c9d29726bef7c'  # noqa
+    assert gm03.data.comprehensive.metadata.date_stamp == '1999-12-31T12:00:00'
+    assert gm03.data.comprehensive.metadata.language == 'de'
+
+    # Test TID searching
+
+    assert gm03.data.comprehensive.metadata.tid == 'xN6509077498146737843'
+
+    search_tid = gm03.data.comprehensive.metadata.tid
+    assert gm03.data.comprehensive.get_element_by_tid('404') is None
+    assert gm03.data.comprehensive.get_element_by_tid(search_tid) is not None
+    search_tid2 = gm03.data.comprehensive.extent.data_identification.ref
+    assert search_tid2 == 'xN8036063300808707346'
+    assert gm03.data.comprehensive.get_element_by_tid(search_tid2) is not None
+
+    e = etree.parse(resource_file('gm03_example2.xml'))
+    gm03 = GM03(e)
+    assert gm03.data.comprehensive.geographic_bounding_box.extent_type_code == 'false'
+    assert gm03.data.comprehensive.geographic_bounding_box.north_bound_latitude == '47.1865387201702'
+    assert gm03.data.comprehensive.geographic_bounding_box.south_bound_latitude == '47.1234508676764'
+    assert gm03.data.comprehensive.geographic_bounding_box.east_bound_longitude == '9.10597474389878'
+    assert gm03.data.comprehensive.geographic_bounding_box.west_bound_longitude == '9.23798212070671'


=====================================
tests/test_iso_parsing.py
=====================================
@@ -570,6 +570,12 @@ def test_md_parsing_19115_2():
 
     assert md.identifier == '3f342f64-9348-11df-ba6a-0014c2c00eab'
 
+    iden = md.identificationinfo[0]
+
+    assert len(iden.keywords2) == 3
+    assert iden.keywords2[1].thesaurus['title'] == 'My Vocabulary'
+    assert iden.keywords2[1].thesaurus['url'] == 'https://example.org/my-vocab'
+
     ci = md.contentinfo[0]
     assert ci.type == 'image'
     assert ci.cloud_cover == '72'


=====================================
tests/test_wfs_generic.py
=====================================
@@ -1,4 +1,5 @@
 from owslib.wfs import WebFeatureService
+from owslib.util import ServiceException
 from urllib.parse import urlparse
 from tests.utils import resource_file, sorted_url_query, service_ok
 
@@ -58,7 +59,7 @@ def test_outputformat_wfs_100():
                             version='1.0.0')
     feature = wfs.getfeature(
         typename=['sb:Project_Area'], maxfeatures=1, propertyname=None, outputFormat='application/json')
-    assert json.dumps(json.loads(feature.read())) == '{...}'
+    assert len(json.loads(feature.read())['features']) == 1
 
 
 @pytest.mark.online
@@ -70,7 +71,7 @@ def test_outputformat_wfs_110():
                             version='1.1.0')
     feature = wfs.getfeature(
         typename=['sb:Project_Area'], maxfeatures=1, propertyname=None, outputFormat='application/json')
-    assert json.dumps(json.loads(feature.read())) == '{...}'
+    assert len(json.loads(feature.read())['features']) == 1
 
 
 @pytest.mark.online
@@ -82,7 +83,7 @@ def test_outputformat_wfs_200():
                             version='2.0.0')
     feature = wfs.getfeature(
         typename=['sb:Project_Area'], maxfeatures=1, propertyname=None, outputFormat='application/json')
-    assert json.dumps(json.loads(feature.read())) == '{...}'
+    assert len(json.loads(feature.read())['features']) == 1
 
 
 @pytest.mark.online
@@ -92,10 +93,11 @@ def test_srsname_wfs_100():
     import json
     wfs = WebFeatureService('https://www.sciencebase.gov/catalogMaps/mapping/ows/53398e51e4b0db25ad10d288',
                             version='1.0.0')
-    feature = wfs.getfeature(
-        typename=['sb:Project_Area'], maxfeatures=1, propertyname=None, outputFormat='application/json',
-        srsname="EPSG:99999999")
     # ServiceException: Unable to support srsName: EPSG:99999999
+    with pytest.raises(ServiceException):
+        feature = wfs.getfeature(
+            typename=['sb:Project_Area'], maxfeatures=1, propertyname=None, outputFormat='application/json',
+            srsname="EPSG:99999999")
 
     import json
     wfs = WebFeatureService('https://www.sciencebase.gov/catalogMaps/mapping/ows/53398e51e4b0db25ad10d288',
@@ -103,7 +105,7 @@ def test_srsname_wfs_100():
     feature = wfs.getfeature(
         typename=['sb:Project_Area'], maxfeatures=1, propertyname=None, outputFormat='application/json',
         srsname="urn:x-ogc:def:crs:EPSG:4326")
-    assert json.dumps(json.loads(feature.read())) == '{...}'
+    assert len(json.loads(feature.read())['features']) == 1
 
 
 @pytest.mark.online
@@ -114,10 +116,11 @@ def test_srsname_wfs_110():
     wfs = WebFeatureService(
         'https://www.sciencebase.gov/catalogMaps/mapping/ows/53398e51e4b0db25ad10d288',
         version='1.1.0')
-    feature = wfs.getfeature(
-        typename=['sb:Project_Area'], maxfeatures=1, propertyname=None, outputFormat='application/json',
-        srsname="EPSG:99999999")
-    # ServiceException: SRSNAME EPSG:99999999 not supported.  Options: urn:x-ogc:def:crs:EPSG:102715
+    # ServiceException: Unable to support srsName: EPSG:99999999
+    with pytest.raises(ServiceException):
+        feature = wfs.getfeature(
+            typename=['sb:Project_Area'], maxfeatures=1, propertyname=None, outputFormat='application/json',
+            srsname="EPSG:99999999")
 
     import json
     wfs = WebFeatureService(
@@ -126,7 +129,7 @@ def test_srsname_wfs_110():
     feature = wfs.getfeature(
         typename=['sb:Project_Area'], maxfeatures=1, propertyname=None, outputFormat='application/json',
         srsname="urn:x-ogc:def:crs:EPSG:4326")
-    assert json.dumps(json.loads(feature.read())) == '{...}'
+    assert len(json.loads(feature.read())['features']) == 1
 
 
 @pytest.mark.online



View it on GitLab: https://salsa.debian.org/debian-gis-team/owslib/-/compare/6680ab425f343379388c47f384caa0cbcea1fb80...13415940204bd14ef38cc541730b43e897baeb19

-- 
View it on GitLab: https://salsa.debian.org/debian-gis-team/owslib/-/compare/6680ab425f343379388c47f384caa0cbcea1fb80...13415940204bd14ef38cc541730b43e897baeb19
You're receiving this email because of your account on salsa.debian.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-grass-devel/attachments/20210206/94f4efd8/attachment-0001.html>


More information about the Pkg-grass-devel mailing list