[Git][debian-gis-team/owslib][upstream] New upstream version 0.28.0

Bas Couwenberg (@sebastic) gitlab at salsa.debian.org
Mon Feb 20 14:19:52 GMT 2023



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


Commits:
adf95013 by Bas Couwenberg at 2023-02-20T15:10:27+01:00
New upstream version 0.28.0
- - - - -


22 changed files:

- .github/workflows/main.yml
- CHANGES.rst
- VERSION.txt
- docs/en/features.rst
- docs/en/usage.rst
- owslib/__init__.py
- owslib/feature/postrequest.py
- owslib/feature/wfs200.py
- owslib/fgdc.py
- owslib/ogcapi/__init__.py
- owslib/ogcapi/coverages.py
- owslib/ogcapi/features.py
- + owslib/ogcapi/maps.py
- owslib/ogcapi/processes.py
- owslib/ogcapi/records.py
- owslib/util.py
- owslib/wmts.py
- tests/doctests/wfs_MapServerWFSFeature.txt → tests/_broken/doctests/wfs_MapServerWFSFeature.txt
- + tests/test_ogcapi_maps_cubewerx.py
- tests/test_ogcapi_processes_pygeoapi.py
- tests/test_ogcapi_records_pycsw.py
- tests/test_wfs_postrequest.py


Changes:

=====================================
.github/workflows/main.yml
=====================================
@@ -7,7 +7,7 @@ jobs:
     runs-on: ubuntu-latest
     strategy:
       matrix:
-        python-version: [3.6, 3.7, 3.8, 3.9]
+        python-version: ['3.7', '3.8', '3.9']
         lxml: [true, false]
     env:
         LXML: ${{ matrix.lxml }}
@@ -22,7 +22,7 @@ jobs:
     - name: Install packages
       run: |
         sudo apt-get -y install pandoc
-      if: matrix.python-version == 3.6
+      if: matrix.python-version == 3.7
     - name: Install requirements 📦
       run: |
         pip3 install -e .
@@ -34,10 +34,10 @@ jobs:
       run: python3 -m pytest
     - name: run coveralls ⚙️
       run: coveralls
-      if: matrix.python-version == 3.6
+      if: matrix.python-version == 3.7
     - name: build docs 🏗️
       run: cd docs && make html
-      if: matrix.python-version == 3.6
+      if: matrix.python-version == 3.7
     - name: run flake8 ⚙️
       run: flake8 owslib
-      if: matrix.python-version == 3.6
+      if: matrix.python-version == 3.7


=====================================
CHANGES.rst
=====================================
@@ -1,6 +1,11 @@
 Changes
 =======
 
+0.27.2 (unreleased)
+-------------------
+
+- WFS: Fix set_filter for string inputs (#804)
+
 0.27.1 (2022-08-28)
 -------------------
 


=====================================
VERSION.txt
=====================================
@@ -1 +1 @@
-0.27.2
+0.28.0


=====================================
docs/en/features.rst
=====================================
@@ -58,7 +58,9 @@ OGC API Support
 +--------------------------------------------------------------------------------------+------------+
 | `OGC API - Records - Part 1: Core`_                                                  | draft      |
 +--------------------------------------------------------------------------------------+------------+
-| `OGC API - Features - Part 3: Filtering and the Common Query Language (CQL) draft`_  | draft      |
+| `OGC API - Features - Part 3: Filtering and the Common Query Language (CQL)`_        | draft      |
++--------------------------------------------------------------------------------------+------------+
+| `OGC API - Features - Part 4: Create, Replace, Update and Delete`_                   | draft      |
 +--------------------------------------------------------------------------------------+------------+
 | `OGC API - Processes - Part 1: Core`_                                                | 1.0        |
 +--------------------------------------------------------------------------------------+------------+
@@ -89,6 +91,7 @@ OGC API Support
 .. _`OGC API`: https://ogcapi.ogc.org
 .. _`OGC API - Features - Part 1: Core`: https://docs.opengeospatial.org/is/17-069r3/17-069r3.html
 .. _`OGC API - Records - Part 1: Core`: https://github.com/opengeospatial/ogcapi-records
-.. _`OGC API - Features - Part 3: Filtering and the Common Query Language (CQL) draft`: https://docs.ogc.org/DRAFTS/19-079.html
+.. _`OGC API - Features - Part 3: Filtering and the Common Query Language (CQL)`: https://docs.ogc.org/DRAFTS/19-079.html
+.. _`OGC API - Features - Part 4: Create, Replace, Update and Delete`: https://docs.ogc.org/DRAFTS/20-002.html
 .. _`OGC API - Coverages - Part 1: Core`: https://docs.ogc.org/DRAFTS/19-087.html
 .. _`OGC API - Processes - Part 1: Core`: https://docs.ogc.org/is/18-062r2/18-062r2.html


=====================================
docs/en/usage.rst
=====================================
@@ -163,7 +163,7 @@ The `OGC API`_ standards are a clean break from the traditional OGC service arch
 using current design patterns (RESTful, JSON, OpenAPI).  As such, OWSLib the code follows
 the same pattern.
 
-OGC API - Features 1.0 - Part 1: Core
+OGC API - Features - Part 1: Core 1.0
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 .. code-block:: python
@@ -195,7 +195,7 @@ OGC API - Features 1.0 - Part 1: Core
   >>> lakes_query['features'][0]['properties']
   {u'scalerank': 0, u'name_alt': None, u'admin': None, u'featureclass': u'Lake', u'id': 0, u'name': u'Lake Baikal'}
 
-OGC API - Coverages 1.0 - Part 1: Core
+OGC API - Coverages - Part 1: Core 1.0
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 .. code-block:: python
@@ -230,7 +230,7 @@ OGC API - Coverages 1.0 - Part 1: Core
   'float64'
   >> gdps_coverage_query = w.coverage('gdps-temperature', range_subset=[1])
 
-OGC API - Records 1.0 - Part 1: Core
+OGC API - Records - Part 1: Core 1.0
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
   >>> from owslib.ogcapi.records import Records
@@ -273,7 +273,63 @@ OGC API - Records 1.0 - Part 1: Core
   >>> my_catalogue_cql_json_query['features'][0]['properties']['title']
   u'Roadrunner ambush locations'
 
-OGC API - Processes 1.0 - Part 1: Core
+  >>> import json
+
+  >>> record_data = 'sample-geojson-record.json'
+
+  >>> with open(record_data) as fh:
+  ..  data = json.load(fh)
+
+  >>> identifier = data['id']
+
+  >>> w.collection_item_create('my-catalogue', data)
+
+  >>> w.collection_item_update('my-catalogue', identifier, data)
+
+  >>> w.collection_item_delete('my-catalogue', identifier)
+
+
+
+OGC API - Features - Part 4: Create, Replace, Update and Delete
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. note::
+
+   This specification applies to both OGC API - Features and OGC API - Records
+
+
+.. code-block:: python
+
+   import json
+   from owslib.ogcapi.records import Records
+
+   record_data = '/path/to/record.json'
+
+   url = 'http://localhost:8000'
+   collection_id = 'metadata:main'
+
+   r = Records(url)
+
+   cat = r.collection(collection_id)
+
+   with open(record_data) as fh:
+       data = json.load(fh)
+
+   identifier = data['id']
+
+   r.collection_item_delete(collection_id, identifier)
+
+   # insert metadata
+   r.collection_item_create(collection_id, data)
+
+   # update metadata
+   r.collection_item_update(collection_id, identifier, data)
+
+   # delete metadata
+   r.collection_item_delete(collection_id, identifier)
+
+
+OGC API - Processes - Part 1: Core 1.0
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 .. code-block:: python
@@ -289,6 +345,20 @@ OGC API - Processes 1.0 - Part 1: Core
   >>> hello_world['title']
   'Hello World'
 
+
+OGC API - Maps - Part 1: Core 1.0
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: python
+
+  >>> from owslib.ogcapi.maps import Maps
+  >>> m = Maps('http://localhost:5000')
+  >>> lakes = m.collection('lakes')
+  >>> data = m.map('lakes', width=1200, height=800, transparent=False)
+  >>> with open("output.png", "wb") as fh:
+  ...     fh.write(data.getbuffer())
+
+
 WCS
 ---
 


=====================================
owslib/__init__.py
=====================================
@@ -1 +1 @@
-__version__ = '0.27.2'
+__version__ = '0.28.0'


=====================================
owslib/feature/postrequest.py
=====================================
@@ -175,7 +175,11 @@ class PostRequest_2_0_0(PostRequest):
         """
         if isinstance(filter, str):
             f = etree.fromstring(filter)
-            sub_elem = f.find(util.nspath("Filter", FES_NAMESPACE))
+            if f.tag == util.nspath("Filter", FES_NAMESPACE):
+                sub_elem = f
+            else:
+                sub_elem = f.find(util.nspath("Filter", FES_NAMESPACE))
+
         else:
             sub_elem = filter
 


=====================================
owslib/feature/wfs200.py
=====================================
@@ -529,7 +529,7 @@ class ContentMetadata(AbstractContentMetadata):
         else:
             self.abstract = None
         self.keywords = [
-            f.text for f in elem.findall(nspath("Keywords", ns=WFS_NAMESPACE))
+            f.text for f in elem.findall(nspath("Keywords/Keyword", ns=OWS_NAMESPACE))
         ]
 
         # bboxes


=====================================
owslib/fgdc.py
=====================================
@@ -246,11 +246,16 @@ class Keywords(object):
 class Ptcontac(object):
     """ Process ptcontac """
     def __init__(self, md):
-        val = md.find('cntinfo/cntorgp/cntorg')
-        self.cntorg = util.testXMLValue(val)
 
-        val = md.find('cntinfo/cntorgp/cntper')
-        self.cntper = util.testXMLValue(val)
+        ent = md.find('cntinfo/cntorgp')
+        if ent is None:
+            ent = md.find('cntinfo/cntperp')
+
+        if ent is not None:
+            val = ent.find('cntorg')
+            self.cntorg = util.testXMLValue(val)
+            val = ent.find('cntper')
+            self.cntper = util.testXMLValue(val)
 
         val = md.find('cntinfo/cntpos')
         self.cntpos = util.testXMLValue(val)
@@ -323,6 +328,9 @@ class Distinfo(object):
                     digform['name'] = util.testXMLValue(link.find('digtinfo/formname'))
                     digform['url'] = util.testXMLValue(link.find('digtopt/onlinopt/computer/networka/networkr/'))
                     self.stdorder['digform'].append(digform)
+                cnt = val.find('distrib')
+                if cnt is not None:
+                    self.distrib = Ptcontac(cnt)
 
 
 class Metainfo(object):


=====================================
owslib/ogcapi/__init__.py
=====================================
@@ -1,5 +1,5 @@
 # =============================================================================
-# Copyright (c) 2020 Tom Kralidis
+# Copyright (c) 2022 Tom Kralidis
 #
 # Author: Tom Kralidis <tomkralidis at gmail.com>
 #
@@ -15,7 +15,8 @@ import requests
 import yaml
 
 from owslib import __version__
-from owslib.util import Authentication, http_get, http_post
+from owslib.util import (Authentication, http_delete, http_get, http_post,
+                         http_put)
 
 LOGGER = logging.getLogger(__name__)
 
@@ -99,7 +100,7 @@ class API:
             if openapi_format == openapi_json_mimetype:
                 content = response.json()
             elif openapi_format == openapi_yaml_mimetype:
-                content = yaml.load(response.text)
+                content = yaml.safe_load(response.text)
             return content
         else:
             msg = 'Did not find service-desc link'
@@ -114,7 +115,7 @@ class API:
         """
 
         path = 'conformance'
-        return self._request(path)
+        return self._request(path=path)
 
     def _build_url(self, path: str = None, params: dict = {}) -> str:
         """
@@ -141,15 +142,20 @@ class API:
 
         return url
 
-    def _request(self, path: str = None, as_dict: bool = True,
+    def _request(self, method: str = 'GET', path: str = None,
+                 data: str = None, as_dict: bool = True,
                  kwargs: dict = {}) -> dict:
         """
         helper function for request/response patterns against OGC API endpoints
 
         @type path: string
         @param path: path of request
+        @type method: string
+        @param method: HTTP method (default ``GET``)
+        @type data: string
+        @param data: request data payload
         @type as_dict: bool
-        @param as_dict: whether to return JSON dict (default True)
+        @param as_dict: whether to return JSON dict (default ``True``)
         @type kwargs: string
         @param kwargs: ``dict`` of keyword value pair request parameters
 
@@ -159,28 +165,35 @@ class API:
         url = self._build_url(path)
         self.request = url
 
+        LOGGER.debug(f'Method: {method}')
         LOGGER.debug(f'Request: {url}')
+        LOGGER.debug(f'Data: {data}')
         LOGGER.debug(f'Params: {kwargs}')
 
-        if 'cql' not in kwargs:
+        if method == 'GET':
             response = http_get(url, headers=self.headers, auth=self.auth,
                                 params=kwargs)
-        else:
-            LOGGER.debug('CQL query detected')
-            kwargs2 = deepcopy(kwargs)
-            cql = kwargs2.pop('cql')
-            url2 = self._build_url(path, kwargs2)
-            response = http_post(url2, request=cql, auth=self.auth)
+        elif method == 'POST':
+            response = http_post(url, request=data, auth=self.auth)
+        elif method == 'PUT':
+            response = http_put(url, data=data, auth=self.auth)
+        elif method == 'DELETE':
+            response = http_delete(url, auth=self.auth)
 
         LOGGER.debug(f'URL: {response.url}')
+        LOGGER.debug(f'Response status code: {response.status_code}')
 
-        if response.status_code != requests.codes.ok:
+        if not response:
             raise RuntimeError(response.text)
 
         self.request = response.url
 
         if as_dict:
-            return response.json()
+            if len(response.content) == 0:
+                LOGGER.debug('Empty response')
+                return {}
+            else:
+                return response.json()
         else:
             return response.content
 
@@ -199,7 +212,7 @@ class Collections(API):
         """
 
         path = 'collections'
-        return self._request(path)
+        return self._request(path=path)
 
     def collection(self, collection_id: str) -> dict:
         """
@@ -212,7 +225,7 @@ class Collections(API):
         """
 
         path = f'collections/{collection_id}'
-        return self._request(path)
+        return self._request(path=path)
 
     def collection_queryables(self, collection_id: str) -> dict:
         """
@@ -225,4 +238,4 @@ class Collections(API):
         """
 
         path = f'collections/{collection_id}/queryables'
-        return self._request(path)
+        return self._request(path=path)


=====================================
owslib/ogcapi/coverages.py
=====================================
@@ -8,6 +8,7 @@
 
 from io import BytesIO
 import logging
+from typing import BinaryIO
 
 from owslib.ogcapi import Collections
 from owslib.util import Authentication
@@ -23,11 +24,11 @@ class Coverages(Collections):
         __doc__ = Collections.__doc__  # noqa
         super().__init__(url, json_, timeout, headers, auth)
 
-    def coverages(self) -> dict:
+    def coverages(self) -> list:
         """
         implements /collections filtered on coverages
 
-        @returns: `dict` of filtered collections object
+        @returns: `list` of filtered collections object
         """
 
         coverages_ = []
@@ -67,7 +68,7 @@ class Coverages(Collections):
         path = f'collections/{collection_id}/coverage/rangetype'
         return self._request(path=path, kwargs=kwargs)
 
-    def coverage(self, collection_id: str, **kwargs: dict) -> dict:
+    def coverage(self, collection_id: str, **kwargs: dict) -> BinaryIO:
         """
         implements /collection/{collectionId}/coverage/
 


=====================================
owslib/ogcapi/features.py
=====================================
@@ -1,12 +1,14 @@
 # =============================================================================
-# Copyright (c) 2020 Tom Kralidis
+# Copyright (c) 2022 Tom Kralidis
 #
 # Author: Tom Kralidis <tomkralidis at gmail.com>
 #
 # Contact email: tomkralidis at gmail.com
 # =============================================================================
 
+from copy import deepcopy
 import logging
+from urllib.parse import urlencode
 
 from owslib.ogcapi import Collections
 from owslib.util import Authentication
@@ -22,11 +24,11 @@ class Features(Collections):
         __doc__ = Collections.__doc__  # noqa
         super().__init__(url, json_, timeout, headers, auth)
 
-    def feature_collections(self) -> dict:
+    def feature_collections(self) -> list:
         """
         implements /collections filtered on features
 
-        @returns: `dict` of filtered collections object
+        @returns: `list` of filtered collections object
         """
 
         features_ = []
@@ -46,8 +48,8 @@ class Features(Collections):
         @param collection_id: id of collection
         @type bbox: list
         @param bbox: list of minx,miny,maxx,maxy
-        @type datetime: string
-        @param datetime: time extent or time instant
+        @type datetime_: string
+        @param datetime_: time extent or time instant
         @type limit: int
         @param limit: limit number of features
         @type offset: int
@@ -64,9 +66,18 @@ class Features(Collections):
 
         if 'bbox' in kwargs:
             kwargs['bbox'] = ','.join(list(map(str, kwargs['bbox'])))
-
-        path = f'collections/{collection_id}/items'
-        return self._request(path=path, kwargs=kwargs)
+        if 'datetime_' in kwargs:
+            kwargs['datetime'] = kwargs['datetime_']
+
+        if 'cql' in kwargs:
+            LOGGER.debug('CQL query detected')
+            kwargs2 = deepcopy(kwargs)
+            cql = kwargs2.pop('cql')
+            path = f'collections/{collection_id}/items?{urlencode(kwargs2)}'
+            return self._request(method='POST', path=path, data=cql, kwargs=kwargs2)
+        else:
+            path = f'collections/{collection_id}/items'
+            return self._request(path=path, kwargs=kwargs)
 
     def collection_item(self, collection_id: str, identifier: str) -> dict:
         """
@@ -82,3 +93,57 @@ class Features(Collections):
 
         path = f'collections/{collection_id}/items/{identifier}'
         return self._request(path=path)
+
+    def collection_item_create(self, collection_id: str, data: str) -> bool:
+        """
+        implements POST /collections/{collectionId}/items
+
+        @type collection_id: string
+        @param collection_id: id of collection
+        @type data: string
+        @param data: raw representation of data
+
+        @returns: single feature result
+        """
+
+        path = f'collections/{collection_id}/items'
+        _ = self._request(method='POST', path=path, data=data)
+
+        return True
+
+    def collection_item_update(self, collection_id: str, identifier: str,
+                               data: str) -> bool:
+        """
+        implements PUT /collections/{collectionId}/items/{featureId}
+
+        @type collection_id: string
+        @param collection_id: id of collection
+        @type identifier: string
+        @param identifier: feature identifier
+        @type data: string
+        @param data: raw representation of data
+
+        @returns: ``bool`` of deletion result
+        """
+
+        path = f'collections/{collection_id}/items/{identifier}'
+        _ = self._request(method='PUT', path=path, data=data)
+
+        return True
+
+    def collection_item_delete(self, collection_id: str, identifier: str) -> bool:
+        """
+        implements DELETE /collections/{collectionId}/items/{featureId}
+
+        @type collection_id: string
+        @param collection_id: id of collection
+        @type identifier: string
+        @param identifier: feature identifier
+
+        @returns: ``bool`` of deletion result
+        """
+
+        path = f'collections/{collection_id}/items/{identifier}'
+        _ = self._request(method='DELETE', path=path)
+
+        return True


=====================================
owslib/ogcapi/maps.py
=====================================
@@ -0,0 +1,89 @@
+# =============================================================================
+# Copyright (c) 2022 Tom Kralidis
+#
+# Author: Tom Kralidis <tomkralidis at gmail.com>
+#
+# Contact email: tomkralidis at gmail.com
+# =============================================================================
+
+from io import BytesIO
+import logging
+from typing import BinaryIO
+
+from owslib.ogcapi import Collections
+from owslib.util import Authentication
+
+LOGGER = logging.getLogger(__name__)
+
+
+class Maps(Collections):
+    """Abstraction for OGC API - Maps"""
+
+    def __init__(self, url: str, json_: str = None, timeout: int = 30,
+                 headers: dict = None, auth: Authentication = None):
+        __doc__ = Collections.__doc__  # noqa
+        super().__init__(url, json_, timeout, headers, auth)
+
+    def maps(self) -> list:
+        """
+        implements /collections filtered on maps
+
+        @returns: `list` of filtered collections object
+        """
+
+        maps_ = []
+        collections_ = super().collections()
+
+        for c_ in collections_['collections']:
+            for l_ in c_['links']:
+                if 'map' in l_['rel']:
+                    maps_.append(c_['id'])
+                    break
+
+        return maps_
+
+    def map(self, collection_id: str, **kwargs: dict) -> BinaryIO:
+        """
+        implements /collection/{collectionId}/map
+
+        @type collection_id: string
+        @param collection_id: id of collection
+        @type bbox: list
+        @param bbox: list of minx,miny,maxx,maxy
+        @type bbox_crs: str
+        @param bbox_crs: CRS of output map
+        @type datetime_: string
+        @param datetime_: time extent or time instant
+        @type width: int
+        @param width: width of output map
+        @type height: int
+        @param height: height of output map
+        @type transparent: bool
+        @param transparent: whether to apply transparecy to
+                            output map (default=True)
+        @type style: str
+        @param style: style name
+
+        @returns: map image
+        """
+
+        kwargs_ = {}
+
+        if 'bbox' in kwargs:
+            kwargs_['bbox'] = ','.join(list(map(str, kwargs['bbox'])))
+        if 'bbox_crs' in kwargs:
+            kwargs_['bbox-crs'] = kwargs['bbox_crs']
+        if 'datetime_' in kwargs:
+            kwargs_['datetime'] = kwargs['datetime_']
+
+        kwargs_['width'] = kwargs.get('width', 800)
+        kwargs_['height'] = kwargs.get('height', 600)
+
+        kwargs_['transparent'] = str(kwargs.get('transparent', True)).lower()
+
+        if 'style' in kwargs:
+            path = f'collections/{collection_id}/styles/{kwargs["style"]}/map'
+        else:
+            path = f'collections/{collection_id}/map'
+
+        return BytesIO(self._request(path=path, as_dict=False, kwargs=kwargs_))


=====================================
owslib/ogcapi/processes.py
=====================================
@@ -22,15 +22,15 @@ class Processes(Collections):
         __doc__ = Collections.__doc__  # noqa
         super().__init__(url, json_, timeout, headers, auth)
 
-    def processes(self) -> dict:
+    def processes(self) -> list:
         """
         implements /processes
 
-        @returns: `dict` of available processes
+        @returns: `list` of available processes
         """
 
         path = 'processes'
-        return self._request(path)
+        return self._request(path=path)['processes']
 
     def process(self, process_id: str) -> dict:
         """
@@ -43,4 +43,4 @@ class Processes(Collections):
         """
 
         path = f'processes/{process_id}'
-        return self._request(path)
+        return self._request(path=path)


=====================================
owslib/ogcapi/records.py
=====================================
@@ -22,11 +22,11 @@ class Records(Features):
         __doc__ = Features.__doc__  # noqa
         super().__init__(url, json_, timeout, headers, auth)
 
-    def records(self) -> dict:
+    def records(self) -> list:
         """
         implements /collections filtered on records
 
-        @returns: `dict` of filtered collections object
+        @returns: `list` of filtered collections object
         """
 
         records_ = []


=====================================
owslib/util.py
=====================================
@@ -1,6 +1,6 @@
 # -*- coding: ISO-8859-15 -*-
 # =============================================================================
-# Copyright (c) 2008 Tom Kralidis
+# Copyright (c) 2022 Tom Kralidis
 #
 # Authors : Tom Kralidis <tomkralidis at gmail.com>
 #
@@ -444,7 +444,7 @@ def http_post(url=None, request=None, lang='en-US', timeout=10, username=None, p
         return requests.post(url, json=request, headers=headers_, **rkwargs)
 
 
-def http_get(*args, **kwargs):
+def http_prepare(*args, **kwargs):
     # Copy input kwargs so the dict can be modified
     rkwargs = copy.deepcopy(kwargs)
 
@@ -475,9 +475,33 @@ def http_get(*args, **kwargs):
         rkwargs.setdefault('auth', None)
     rkwargs.setdefault('cert', rkwargs.get('cert'))
     rkwargs.setdefault('verify', rkwargs.get('verify', True))
+
+    return rkwargs
+
+
+def http_get(*args, **kwargs):
+    rkwargs = http_prepare(*args, **kwargs)
     return requests.get(*args, **rkwargs)
 
 
+def http_put(*args, **kwargs):
+    rkwargs = http_prepare(*args, **kwargs)
+
+    if 'data' in kwargs:
+        if isinstance(kwargs['data'], dict):
+            rkwargs['json'] = kwargs['data']
+            rkwargs.pop('data')
+        else:
+            rkwargs['data'] = kwargs['data']
+
+    return requests.put(*args, **rkwargs)
+
+
+def http_delete(*args, **kwargs):
+    rkwargs = http_prepare(*args, **kwargs)
+    return requests.delete(*args, **rkwargs)
+
+
 def element_to_string(element, encoding=None, xml_declaration=False):
     """
     Returns a string from a XML object


=====================================
owslib/wmts.py
=====================================
@@ -362,12 +362,14 @@ class WebMapTileService(object):
             # choose random ResourceURL if more than one available
             resindex = randint(0, numres - 1)
             resurl = tileresourceurls[resindex]['template']
-            resurl = resurl.replace('{TileMatrixSet}', tilematrixset)
-            resurl = resurl.replace('{TileMatrix}', tilematrix)
-            resurl = resurl.replace('{TileRow}', str(row))
-            resurl = resurl.replace('{TileCol}', str(column))
-            resurl = resurl.replace('{Style}', style)
-            return resurl
+            return resurl.format(
+                TileMatrixSet=tilematrixset,
+                TileMatrix=tilematrix,
+                TileRow=str(row),
+                TileCol=str(column),
+                Style=style,
+                **kwargs
+            )
 
         return None
 


=====================================
tests/doctests/wfs_MapServerWFSFeature.txt → tests/_broken/doctests/wfs_MapServerWFSFeature.txt
=====================================


=====================================
tests/test_ogcapi_maps_cubewerx.py
=====================================
@@ -0,0 +1,32 @@
+from io import BytesIO
+
+from tests.utils import service_ok
+
+import pytest
+
+from owslib.ogcapi.maps import Maps
+
+SERVICE_URL = 'https://test.cubewerx.com/cubewerx/cubeserv/demo/ogcapi/EuroRegionalMap/'  # noqa
+
+
+ at pytest.mark.online
+ at pytest.mark.skipif(not service_ok(SERVICE_URL),
+                    reason='service is unreachable')
+def test_ogcapi_maps_pygeoapi():
+    w = Maps(SERVICE_URL)
+
+    assert w.url == SERVICE_URL
+    assert w.url_query_string is None
+
+    collections = w.collections()
+    assert len(collections) > 0
+
+    maps = w.maps()
+    assert len(maps) > 0
+
+    erm = w.collection('erm')
+    assert erm['id'] == 'erm'
+    assert erm['title'] == 'EuroRegionalMap'
+
+    data = w.map('erm', width=1200, height=800, transparent=False)
+    assert isinstance(data, BytesIO)


=====================================
tests/test_ogcapi_processes_pygeoapi.py
=====================================
@@ -29,7 +29,7 @@ def test_ogcapi_processes_pygeoapi():
     assert len(collections) > 0
 
     processes = p.processes()
-    assert len(processes) == 1
+    assert len(processes) == 3
 
     hello_world = p.process('hello-world')
     assert hello_world['id'] == 'hello-world'


=====================================
tests/test_ogcapi_records_pycsw.py
=====================================
@@ -23,7 +23,7 @@ def test_ogcapi_records_pycsw():
     assert paths['/collections/{collectionId}'] is not None
 
     conformance = w.conformance()
-    assert len(conformance['conformsTo']) == 12
+    assert len(conformance['conformsTo']) == 13
 
     collections = w.collections()
     assert len(collections) > 0


=====================================
tests/test_wfs_postrequest.py
=====================================
@@ -44,6 +44,16 @@ xml_filter_2_0_0 = """<?xml version="1.0" ?>
     </wfs:GetFeature>
     """
 
+raw_2_0_filter = """
+    <fes:Filter  xmlns:fes="http://www.opengis.net/fes/2.0"
+    xmlns:erl="http://xmlns.earthresourceml.org/earthresourceml-lite/1.0">
+          <fes:PropertyIsEqualTo>
+            <fes:ValueReference>reference</fes:ValueReference>
+            <fes:Literal>gold</fes:Literal>
+          </fes:PropertyIsEqualTo>
+    </fes:Filter>
+    """
+
 typename = "ns:typename"
 
 
@@ -272,3 +282,17 @@ class TestPostRequest_v_2_0_0():
         assert equal_elem is not None
         assert propertyname == "status"
         assert literal == "rejected"
+
+    def test_filter_root_query(self, requestv200):
+        """Same as test_filter_query, but the filter is the root element."""
+        requestv200.set_filter(raw_2_0_filter)
+
+        filter_elem = requestv200._query.find(util.nspath("Filter", FES_NAMESPACE))
+        equal_elem = filter_elem.find(util.nspath("PropertyIsEqualTo", FES_NAMESPACE))
+        propertyname = equal_elem.findtext(util.nspath("ValueReference", FES_NAMESPACE))
+        literal = equal_elem.findtext(util.nspath("Literal", FES_NAMESPACE))
+
+        assert filter_elem is not None
+        assert equal_elem is not None
+        assert propertyname == "reference"
+        assert literal == "gold"



View it on GitLab: https://salsa.debian.org/debian-gis-team/owslib/-/commit/adf95013ffbacd9406eb9b4bf6d7623f7a4f97c2

-- 
View it on GitLab: https://salsa.debian.org/debian-gis-team/owslib/-/commit/adf95013ffbacd9406eb9b4bf6d7623f7a4f97c2
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/20230220/6ee65e8b/attachment-0001.htm>


More information about the Pkg-grass-devel mailing list