[Git][debian-gis-team/owslib][upstream] New upstream version 0.35.0
Bas Couwenberg (@sebastic)
gitlab at salsa.debian.org
Wed Oct 29 04:30:35 GMT 2025
Bas Couwenberg pushed to branch upstream at Debian GIS Project / owslib
Commits:
293d8778 by Bas Couwenberg at 2025-10-29T05:24:18+01:00
New upstream version 0.35.0
- - - - -
23 changed files:
- .github/workflows/main.yml
- .github/workflows/windows.yml
- README.md
- binder/postBuild
- docs/source/development.rst
- docs/source/installation.rst
- owslib/__init__.py
- owslib/coverage/wcs100.py
- owslib/coverage/wcs110.py
- owslib/coverage/wcs200.py
- owslib/coverage/wcs201.py
- owslib/iso.py
- owslib/map/wms130.py
- owslib/ogcapi/__init__.py
- owslib/util.py
- + pyproject.toml
- setup.py
- tests/test_ogcapi_connectedsystems_osh.py
- tests/test_ogcapi_edr_pygeoapi.py
- tests/test_ogcapi_records_pycsw.py
- tests/test_ogcapi_records_pygeoapi.py
- tests/test_util.py
- tests/test_wcs_200.py
Changes:
=====================================
.github/workflows/main.yml
=====================================
@@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- python-version: ["3.10", "3.11"]
+ python-version: ["3.12"]
steps:
- uses: actions/checkout at master
- uses: actions/setup-python at v5
@@ -25,7 +25,7 @@ jobs:
- name: run tests ⚙️
run: python3 -m pytest
- name: run tests in offline mode
- if: matrix.python-version == '3.10'
+ if: matrix.python-version == '3.12'
run: |
python3 -m pytest \
-m "not online" \
=====================================
.github/workflows/windows.yml
=====================================
@@ -7,7 +7,7 @@ jobs:
runs-on: windows-latest
strategy:
matrix:
- python-version: ["3.11"]
+ python-version: ["3.12"]
steps:
- uses: actions/checkout at master
- uses: actions/setup-python at v5
=====================================
README.md
=====================================
@@ -45,7 +45,7 @@ source bin/activate
# clone codebase and install
git clone https://github.com/geopython/OWSLib.git
cd OWSLib
-python3 setup.py install
+pip3 install .
```
## Running
=====================================
binder/postBuild
=====================================
@@ -1 +1 @@
-python3 setup.py install
+pip3 install .
=====================================
docs/source/development.rst
=====================================
@@ -10,12 +10,6 @@ You can find out about software metrics at the OWSLib OpenHub page at https://ww
Testing
-------
-.. code-block:: bash
-
- python3 setup.py test
-
-Or ...
-
.. code-block:: bash
# install requirements
=====================================
docs/source/installation.rst
=====================================
@@ -37,7 +37,7 @@ Anaconda:
.. code-block:: bash
- git conda install -c conda-forge owslib
+ conda install -c conda-forge owslib
openSUSE:
=====================================
owslib/__init__.py
=====================================
@@ -1 +1 @@
-__version__ = '0.34.1'
+__version__ = '0.35.0'
=====================================
owslib/coverage/wcs100.py
=====================================
@@ -102,7 +102,7 @@ class WebCoverageService_1_0_0(WCSBase):
note: additional **kwargs helps with multi-version implementation
core keyword arguments should be supported cross version
example:
- cvg=wcs.getCoverage(identifier=['TuMYrRQ4'], timeSequence=['2792-06-01T00:00:00.0'], bbox=(-112,36,-106,41),
+ cvg=wcs.getCoverage(identifier='TuMYrRQ4', timeSequence=['2792-06-01T00:00:00.0'], bbox=(-112,36,-106,41),
format='cf-netcdf')
is equivalent to:
@@ -123,7 +123,6 @@ class WebCoverageService_1_0_0(WCSBase):
# process kwargs
request = {'version': self.version, 'request': 'GetCoverage', 'service': 'WCS'}
- assert len(identifier) > 0
request['Coverage'] = identifier
# request['identifier'] = ','.join(identifier)
if bbox:
=====================================
owslib/coverage/wcs110.py
=====================================
@@ -148,7 +148,7 @@ class WebCoverageService_1_1_0(WCSBase):
note: additional **kwargs helps with multi-version implementation
core keyword arguments should be supported cross version
example:
- cvg=wcs.getCoverageRequest(identifier=['TuMYrRQ4'], time=['2792-06-01T00:00:00.0'], bbox=(-112,36,-106,41),
+ cvg=wcs.getCoverageRequest(identifier='TuMYrRQ4', time=['2792-06-01T00:00:00.0'], bbox=(-112,36,-106,41),
format='application/netcdf', store='true')
is equivalent to:
@@ -172,7 +172,6 @@ class WebCoverageService_1_1_0(WCSBase):
# process kwargs
request = {'version': self.version, 'request': 'GetCoverage', 'service': 'WCS'}
- assert len(identifier) > 0
request['identifier'] = identifier
# request['identifier'] = ','.join(identifier)
if bbox:
=====================================
owslib/coverage/wcs200.py
=====================================
@@ -139,7 +139,7 @@ class WebCoverageService_2_0_0(WCSBase):
note: additional **kwargs helps with multi-version implementation
core keyword arguments should be supported cross version
example:
- cvg=wcs.getCoverage(identifier=['TuMYrRQ4'], timeSequence=['2792-06-01T00:00:00.0'], bbox=(-112,36,-106,41),
+ cvg=wcs.getCoverage(identifier='TuMYrRQ4', timeSequence=['2792-06-01T00:00:00.0'], bbox=(-112,36,-106,41),
format='cf-netcdf')
is equivalent to:
@@ -149,7 +149,7 @@ class WebCoverageService_2_0_0(WCSBase):
http://earthserver.pml.ac.uk/rasdaman/ows?&SERVICE=WCS&VERSION=2.0.1&REQUEST=GetCoverage
&COVERAGEID=V2_monthly_CCI_chlor_a_insitu_test&SUBSET=Lat(40,50)&SUBSET=Long(-10,0)&SUBSET=ansi(144883,145000)&FORMAT=application/netcdf
- cvg=wcs.getCoverage(identifier=['myID'], format='application/netcdf', subsets=[('axisName',min,max),
+ cvg=wcs.getCoverage(identifier='myID', format='application/netcdf', subsets=[('axisName',min,max),
('axisName', min, max),('axisName',min,max)])
@@ -187,8 +187,7 @@ class WebCoverageService_2_0_0(WCSBase):
LOGGER.debug("WCS 2.0.0 DEBUG: base url of server: %s" % base_url)
request = {"version": self.version, "request": "GetCoverage", "service": "WCS"}
- assert len(identifier) > 0
- request["CoverageID"] = identifier[0]
+ request["CoverageID"] = identifier
if crs:
request["crs"] = crs
=====================================
owslib/coverage/wcs201.py
=====================================
@@ -139,7 +139,7 @@ class WebCoverageService_2_0_1(WCSBase):
note: additional **kwargs helps with multi-version implementation
core keyword arguments should be supported cross version
example:
- cvg=wcs.getCoverage(identifier=['TuMYrRQ4'], timeSequence=['2792-06-01T00:00:00.0'], bbox=(-112,36,-106,41),
+ cvg=wcs.getCoverage(identifier='TuMYrRQ4', timeSequence=['2792-06-01T00:00:00.0'], bbox=(-112,36,-106,41),
format='cf-netcdf')
is equivalent to:
@@ -149,7 +149,7 @@ class WebCoverageService_2_0_1(WCSBase):
http://earthserver.pml.ac.uk/rasdaman/ows?&SERVICE=WCS&VERSION=2.0.1&REQUEST=GetCoverage
&COVERAGEID=V2_monthly_CCI_chlor_a_insitu_test&SUBSET=Lat(40,50)&SUBSET=Long(-10,0)&SUBSET=ansi(144883,145000)&FORMAT=application/netcdf
- cvg=wcs.getCoverage(identifier=['myID'], format='application/netcdf', subsets=[('axisName',min,max),
+ cvg=wcs.getCoverage(identifier='myID', format='application/netcdf', subsets=[('axisName',min,max),
('axisName',min,max),('axisName',min,max)])
@@ -187,8 +187,7 @@ class WebCoverageService_2_0_1(WCSBase):
LOGGER.debug("WCS 2.0.1 DEBUG: base url of server: %s" % base_url)
request = {"version": self.version, "request": "GetCoverage", "service": "WCS"}
- assert len(identifier) > 0
- request["CoverageID"] = identifier[0]
+ request["CoverageID"] = identifier
if crs:
request["crs"] = crs
=====================================
owslib/iso.py
=====================================
@@ -201,7 +201,8 @@ class MD_Metadata(object):
for ct in ['creator', 'publisher', 'contributor']:
iict = getattr(ii, ct)
if iict:
- contacts.append(iict)
+ for iict2 in iict:
+ contacts.append(iict2)
return list(filter(None, contacts))
=====================================
owslib/map/wms130.py
=====================================
@@ -2,9 +2,11 @@
# =============================================================================
# Copyright (c) 2004, 2006 Sean C. Gillies
# Copyright (c) 2005 Nuxeo SARL <http://nuxeo.com>
+# Copyright (c) 2025 Tom Kralidis
#
# Authors : Sean Gillies <sgillies at frii.com>
# Julien Anguenot <ja at nuxeo.com>
+# Tom Kralidis <tomkralidis at gmail.com>
#
# Contact email: sgillies at frii.com
# =============================================================================
@@ -20,7 +22,7 @@ from math import sqrt
from owslib.etree import etree
from owslib.util import (openURL, ServiceException, testXMLValue,
extract_xml_list, xmltag_split, OrderedDict, nspath,
- nspath_eval, bind_url, Authentication)
+ nspath_eval, bind_url, Authentication, str2bool)
from owslib.fgdc import Metadata
from owslib.iso import MD_Metadata
from owslib.iso3 import MD_Metadata as MD_Metadata3 # ISO 19115 Part 3 XML
@@ -446,7 +448,7 @@ class ContentMetadata(AbstractContentMetadata):
self.id = self.name = testXMLValue(elem.find(nspath('Name', WMS_NAMESPACE)))
# layer attributes
- self.queryable = int(elem.attrib.get('queryable', 0))
+ self.queryable = int(str2bool(elem.attrib.get('queryable', '0')))
self.cascaded = int(elem.attrib.get('cascaded', 0))
self.opaque = int(elem.attrib.get('opaque', 0))
self.noSubsets = int(elem.attrib.get('noSubsets', 0))
=====================================
owslib/ogcapi/__init__.py
=====================================
@@ -187,7 +187,7 @@ class API:
elif method == 'PUT':
response = http_put(url, headers=self.headers, data=data, auth=self.auth)
elif method == 'DELETE':
- response = http_delete(url, auth=self.auth)
+ response = http_delete(url, headers=self.headers, auth=self.auth)
LOGGER.debug(f'URL: {response.url}')
LOGGER.debug(f'Response status code: {response.status_code}')
=====================================
owslib/util.py
=====================================
@@ -1,30 +1,31 @@
# -*- coding: ISO-8859-15 -*-
# =============================================================================
-# Copyright (c) 2024 Tom Kralidis
+# Copyright (c) 2025 Tom Kralidis
#
# Authors : Tom Kralidis <tomkralidis at gmail.com>
#
# Contact email: tomkralidis at gmail.com
# =============================================================================
-import os
-import sys
+import codecs
from collections import OrderedDict
-from dateutil import parser
-from datetime import datetime, timedelta, timezone
-from owslib.etree import etree, ParseError
-from owslib.namespaces import Namespaces
-from urllib.parse import urlsplit, urlencode, urlparse, parse_qs, urlunparse, parse_qsl
import copy
-
+from copy import deepcopy
+from datetime import datetime, timedelta, timezone
from io import StringIO, BytesIO
-
+import os
import re
-from copy import deepcopy
+import sys
+from typing import Union
+from urllib.parse import urlsplit, urlencode, urlparse, parse_qs, urlunparse, parse_qsl
import warnings
+
+from dateutil import parser
import requests
from requests.auth import AuthBase
-import codecs
+
+from owslib.etree import etree, ParseError
+from owslib.namespaces import Namespaces
"""
Utility functions and classes
@@ -1047,3 +1048,23 @@ class Authentication(object):
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)
+
+
+def str2bool(value: Union[bool, str]) -> bool:
+ """
+ helper function to return Python boolean
+ type (source: https://stackoverflow.com/a/715468)
+
+ :param value: value to be evaluated
+
+ :returns: `bool` of whether the value is boolean-ish
+ """
+
+ value2 = False
+
+ if isinstance(value, bool):
+ value2 = value
+ else:
+ value2 = value.lower() in ('yes', 'true', 't', '1', 'on')
+
+ return value2
=====================================
pyproject.toml
=====================================
@@ -0,0 +1,3 @@
+[build-system]
+requires = ["setuptools>=46.4", "wheel"]
+build-backend = "setuptools.build_meta"
=====================================
setup.py
=====================================
@@ -1,5 +1,5 @@
# =============================================================================
-# Copyright (c) 2024 Tom Kralidis
+# Copyright (c) 2025 Tom Kralidis
#
# Author: Tom Kralidis <tomkralidis at gmail.com>
#
@@ -10,19 +10,6 @@ from pathlib import Path
import re
from setuptools import setup, find_packages
from setuptools.command.test import test as TestCommand
-import sys
-
-
-class PyTest(TestCommand):
- def finalize_options(self):
- TestCommand.finalize_options(self)
- self.test_args = []
- self.test_suite = True
-
- def run_tests(self):
- import pytest
- errno = pytest.main(self.test_args)
- sys.exit(errno)
def read(filename, encoding='utf-8'):
@@ -53,7 +40,6 @@ MANIFEST = Path('MANIFEST')
if MANIFEST.exists():
MANIFEST.unlink()
-
setup(
name='OWSLib',
version=get_package_version(),
@@ -90,13 +76,11 @@ setup(
url='https://owslib.readthedocs.io',
install_requires=reqs,
python_requires='>=3.10',
- cmdclass={'test': PyTest},
packages=find_packages(exclude=["docs", "etc", "examples", "tests"]),
classifiers=[
'Development Status :: 4 - Beta',
'Intended Audience :: Developers',
'Intended Audience :: Science/Research',
- 'License :: OSI Approved :: BSD License',
'Natural Language :: English',
'Operating System :: OS Independent',
'Programming Language :: Python',
=====================================
tests/test_ogcapi_connectedsystems_osh.py
=====================================
@@ -9,19 +9,20 @@
from datetime import datetime
import json
+from tests.utils import service_ok
+
import pytest
from owslib.ogcapi.connectedsystems import Commands, ControlChannels, Datastreams, Deployments, Observations, \
Properties, SamplingFeatures, SystemEvents, SystemHistory, Systems
from owslib.util import Authentication
+# Directs to OSH (Open Sensor Hub) hosted test server
+TEST_URL = 'http://34.67.197.57:8585/sensorhub/api/'
@pytest.fixture(scope="session")
def fixtures():
class OSHFixtures:
- NODE_TEST_OK_URL = 'http://34.67.197.57:8585/sensorhub/test'
- # Directs to OSH hosted test server
- TEST_URL = 'http://34.67.197.57:8585/sensorhub/api/'
auth = Authentication('auto_test', 'automated_tester24')
sml_headers = {'Content-Type': 'application/sml+json'}
json_headers = {'Content-Type': 'application/json'}
@@ -214,6 +215,7 @@ def fixtures():
yield OSHFixtures()
+ at pytest.mark.skipif(not service_ok(TEST_URL), reason="OSH server is unreachable")
class TestSystems:
@pytest.mark.online
def test_system_readonly(self, fixtures):
@@ -255,6 +257,7 @@ class TestSystems:
assert res == {}
+ at pytest.mark.skipif(not service_ok(TEST_URL), reason="OSH server is unreachable")
class TestDeployments:
@pytest.mark.skip(reason="Skip transactional test")
def test_deployment_create(self, fixtures):
@@ -279,6 +282,7 @@ class TestDeployments:
assert res is not None
+ at pytest.mark.skipif(not service_ok(TEST_URL), reason="OSH server is unreachable")
class TestSamplingFeatures:
@pytest.mark.online
def test_sampling_features_readonly(self, fixtures):
@@ -324,6 +328,7 @@ class TestSamplingFeatures:
assert res == {'items': []}
+ at pytest.mark.skipif(not service_ok(TEST_URL), reason="OSH server is unreachable")
class TestDatastreams:
@pytest.mark.online
def test_datastreams_readonly(self, fixtures):
@@ -369,6 +374,7 @@ class TestDatastreams:
assert ds_delete == {}
+ at pytest.mark.skipif(not service_ok(TEST_URL), reason="OSH server is unreachable")
class TestObservations:
@pytest.mark.online
def test_observations_readonly(self, fixtures):
@@ -409,6 +415,7 @@ class TestObservations:
fixtures.delete_all_systems()
+ at pytest.mark.skipif(not service_ok(TEST_URL), reason="OSH server is unreachable")
class TestSystemHistory:
@pytest.mark.online
def test_system_history(self, fixtures):
=====================================
tests/test_ogcapi_edr_pygeoapi.py
=====================================
@@ -10,7 +10,7 @@ SERVICE_URL = 'https://demo.pygeoapi.io/master/'
@pytest.mark.online
@pytest.mark.skipif(not service_ok(SERVICE_URL),
reason='service is unreachable')
-def test_ogcapi_coverages_pygeoapi():
+def test_ogcapi_edr_pygeoapi():
w = EnvironmentalDataRetrieval(SERVICE_URL)
assert w.url == SERVICE_URL
@@ -39,5 +39,5 @@ def test_ogcapi_coverages_pygeoapi():
parameter_names = icoads['parameter_names'].keys()
assert sorted(parameter_names) == ['AIRT', 'SST', 'UWND', 'VWND']
- response = w.query_data('icoads-sst', 'position', coords='POINT(-75 45)')
+ response = w.query_data('icoads-sst', 'position', coords='POINT(0 0)')
assert isinstance(response, dict)
=====================================
tests/test_ogcapi_records_pycsw.py
=====================================
@@ -11,7 +11,7 @@ SERVICE_URL = 'https://demo.pycsw.org/cite'
@pytest.mark.skipif(not service_ok(SERVICE_URL),
reason='service is unreachable')
def test_ogcapi_records_pycsw():
- w = Records(SERVICE_URL)
+ w = Records(SERVICE_URL, timeout=60)
assert w.url == 'https://demo.pycsw.org/cite/'
assert w.url_query_string is None
@@ -77,5 +77,5 @@ def test_ogcapi_records_pycsw():
('collections/foo/https://example.org/11', 'https://demo.pycsw.org/cite/collections/foo/https://example.org/11') # noqa
])
def test_ogcapi_build_url(path, expected):
- w = Records(SERVICE_URL)
+ w = Records(SERVICE_URL, timeout=60)
assert w._build_url(path) == expected
=====================================
tests/test_ogcapi_records_pygeoapi.py
=====================================
@@ -40,14 +40,14 @@ def test_ogcapi_records_pygeoapi():
assert isinstance(w.response, dict)
dutch_metacat_queryables = w.collection_queryables('dutch-metadata')
- assert len(dutch_metacat_queryables['properties']) == 11
+ assert len(dutch_metacat_queryables['properties']) == 10
# Minimum of limit param is 1
with pytest.raises(RuntimeError):
dutch_metacat_query = w.collection_items('dutch-metadata', limit=0)
dutch_metacat_query = w.collection_items('dutch-metadata', limit=1)
- assert dutch_metacat_query['numberMatched'] == 308
+ assert dutch_metacat_query['numberMatched'] == 309
assert dutch_metacat_query['numberReturned'] == 1
assert len(dutch_metacat_query['features']) == 1
=====================================
tests/test_util.py
=====================================
@@ -1,10 +1,20 @@
# -*- coding: UTF-8 -*-
+# =============================================================================
+# Copyright (c) 2025 Tom Kralidis
+#
+# Authors : Tom Kralidis <tomkralidis at gmail.com>
+#
+# Contact email: tomkralidis at gmail.com
+# =============================================================================
+
+
import codecs
+from datetime import datetime, timezone
from unittest import mock
+
import pytest
-from owslib.util import clean_ows_url, build_get_url, strip_bom, extract_time, ResponseWrapper, getXMLTree
from owslib.etree import etree
-from datetime import datetime, timezone
+from owslib.util import clean_ows_url, build_get_url, strip_bom, extract_time, ResponseWrapper, getXMLTree, str2bool
def test_strip_bom():
@@ -127,3 +137,10 @@ def test_extract_time():
assert start.isoformat()[-6:] == "+00:00"
stop = extract_time(etree.fromstring(indefinite_sample))
assert stop.isoformat()[-6:] == "+00:00"
+
+
+def test_str2bool():
+ assert str2bool('1')
+ assert not str2bool('0')
+ assert int(str2bool('true')) == 1
+ assert int(str2bool('false')) == 0
=====================================
tests/test_wcs_200.py
=====================================
@@ -57,7 +57,7 @@ def test_wcs_200():
lat_subset = ('Lat', 40, 50)
long_subset = ('Long', -10, 0)
formatType = 'application/netcdf'
- output = wcs.getCoverage(identifier=[covID], format=formatType, subsets=[long_subset, lat_subset, time_subset])
+ output = wcs.getCoverage(identifier=covID, format=formatType, subsets=[long_subset, lat_subset, time_subset])
f = open(scratch_file('test_wcs_200.nc'), 'wb')
bytes_written = f.write(output.read())
f.close()
View it on GitLab: https://salsa.debian.org/debian-gis-team/owslib/-/commit/293d87786f4da56721d6ce843d01db314b95f170
--
View it on GitLab: https://salsa.debian.org/debian-gis-team/owslib/-/commit/293d87786f4da56721d6ce843d01db314b95f170
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/20251029/9861db55/attachment-0001.htm>
More information about the Pkg-grass-devel
mailing list