From b9d4eb2412e1f26bdaaaf29cc01f17a62c57b66a Mon Sep 17 00:00:00 2001 From: Jakob Miksch Date: Sun, 31 May 2026 19:55:20 +0200 Subject: [PATCH 1/3] Handle WFS version mismatch --- owslib/feature/common.py | 3 +++ owslib/feature/wfs100.py | 7 +++++++ owslib/feature/wfs110.py | 7 +++++++ owslib/feature/wfs200.py | 9 ++++++++- tests/test_wfs_generic.py | 35 +++++++++++++++++++++++++++++++++++ 5 files changed, 60 insertions(+), 1 deletion(-) diff --git a/owslib/feature/common.py b/owslib/feature/common.py index a6fc4a9e..c873934c 100644 --- a/owslib/feature/common.py +++ b/owslib/feature/common.py @@ -3,6 +3,9 @@ from urllib.parse import urlencode, parse_qsl +class CapabilitiesError(Exception): + pass + class WFSCapabilitiesReader(object): """Read and parse capabilities document into a lxml.etree infoset diff --git a/owslib/feature/wfs100.py b/owslib/feature/wfs100.py index c148a1e7..b08c8c7d 100644 --- a/owslib/feature/wfs100.py +++ b/owslib/feature/wfs100.py @@ -28,6 +28,7 @@ from owslib.namespaces import Namespaces from owslib.feature.schema import get_schema from owslib.feature.common import ( + CapabilitiesError, WFSCapabilitiesReader, AbstractContentMetadata, ) @@ -112,6 +113,12 @@ def __init__( self._buildMetadata(parse_remote_metadata) def _buildMetadata(self, parse_remote_metadata=False): + # check if expected version matches actual version in XML + actual_wfs_version = self._capabilities.attrib.get("version") + if not actual_wfs_version.startswith(self.version): + error_message = f"WFS version mismatch. Actual: {actual_wfs_version}, Requested: {self.version}" + raise CapabilitiesError(error_message) + """set up capabilities metadata objects: """ self.updateSequence = self._capabilities.attrib.get("updateSequence") diff --git a/owslib/feature/wfs110.py b/owslib/feature/wfs110.py index 0a7c71d3..b19bc990 100644 --- a/owslib/feature/wfs110.py +++ b/owslib/feature/wfs110.py @@ -36,6 +36,7 @@ from owslib.feature.common import ( WFSCapabilitiesReader, AbstractContentMetadata, + CapabilitiesError, ) from owslib.namespaces import Namespaces from owslib.util import openURL @@ -98,6 +99,12 @@ def __init__( self._buildMetadata(parse_remote_metadata) def _buildMetadata(self, parse_remote_metadata=False): + # check if expected version matches actual version in XML + actual_wfs_version = self._capabilities.attrib.get("version") + if not actual_wfs_version.startswith(self.version): + error_message = f"WFS version mismatch. Actual: {actual_wfs_version}, Requested: {self.version}" + raise CapabilitiesError(error_message) + """set up capabilities metadata objects: """ self.updateSequence = self._capabilities.attrib.get("updateSequence") diff --git a/owslib/feature/wfs200.py b/owslib/feature/wfs200.py index b5ed7fda..79421f97 100644 --- a/owslib/feature/wfs200.py +++ b/owslib/feature/wfs200.py @@ -22,7 +22,8 @@ from owslib.feature import WebFeatureService_ from owslib.feature.common import ( WFSCapabilitiesReader, - AbstractContentMetadata + AbstractContentMetadata, + CapabilitiesError, ) from owslib.namespaces import Namespaces @@ -87,6 +88,12 @@ def __init__( self._buildMetadata(parse_remote_metadata) def _buildMetadata(self, parse_remote_metadata=False): + # check if expected version matches actual version in XML + actual_wfs_version = self._capabilities.attrib.get("version") + if not actual_wfs_version.startswith(self.version): + error_message = f"WFS version mismatch. Actual: {actual_wfs_version}, Requested: {self.version}" + raise CapabilitiesError(error_message) + """set up capabilities metadata objects: """ self.updateSequence = self._capabilities.attrib.get("updateSequence") diff --git a/tests/test_wfs_generic.py b/tests/test_wfs_generic.py index 1f2234fa..d2ccd193 100644 --- a/tests/test_wfs_generic.py +++ b/tests/test_wfs_generic.py @@ -1,3 +1,4 @@ +from owslib.feature.common import CapabilitiesError from owslib.wfs import WebFeatureService from owslib.util import ServiceException from owslib.fes import PropertyIsLike, etree @@ -57,6 +58,40 @@ def test_verbOptions_wfs_100(): verbOptions = [cm.verbOptions for cm in wfs.contents.values()] assert len(verbOptions[0]) == 2 +def create_mismatch_test_params(): + """Build combinations of mismatching WFS versions""" + params = [] + version_info = { + '1.0.0': "wfs_dov_getcapabilities_100_verbOptions.xml", + '1.1.0': "wfs_HSRS_GetCapabilities_1_1_0.xml", + '2.0.0': "wfs_CUZK_GetCapabilities_2_0_0.xml", + } + + for expected_version in version_info.keys(): + # get versions other than current one + other_versions = list(version_info.keys() - {expected_version}) + + for other_version in other_versions: + file_name = version_info[other_version] + + def make_param(version): + test_id = f"expects '{version}', but gets '{other_version}'" + return pytest.param(version, file_name, id=test_id) + + # create test case for both variants of the version number (e.g. '1.0' and '1.0.0') + version_variants = [expected_version[0:3], expected_version] + params.extend([make_param(variant) for variant in version_variants]) + + return params + + +@pytest.mark.parametrize("expected_version, xml_path_with_other_version", create_mismatch_test_params() ) +def test_raises_error_on_version_mismatch(expected_version, xml_path_with_other_version): + DUMMY_URL = 'http://gis.bnhelp.cz/ows/crwfs' + xml = open(resource_file(xml_path_with_other_version), "rb").read() + + with pytest.raises(CapabilitiesError): + WebFeatureService(DUMMY_URL, xml=xml, version=expected_version) @pytest.mark.online @pytest.mark.skipif(not service_ok(SERVICE_URL), From e5d6ba1137bd59cce0c59699f897575efda3cade Mon Sep 17 00:00:00 2001 From: Jakob Miksch Date: Sun, 31 May 2026 20:05:29 +0200 Subject: [PATCH 2/3] Lint --- owslib/feature/common.py | 1 + 1 file changed, 1 insertion(+) diff --git a/owslib/feature/common.py b/owslib/feature/common.py index c873934c..5c357b1e 100644 --- a/owslib/feature/common.py +++ b/owslib/feature/common.py @@ -3,6 +3,7 @@ from urllib.parse import urlencode, parse_qsl + class CapabilitiesError(Exception): pass From 3dbd4e24ee046b37b024a113b898d662c946a217 Mon Sep 17 00:00:00 2001 From: Jakob Miksch Date: Sun, 31 May 2026 22:43:40 +0200 Subject: [PATCH 3/3] Fix wrong input on test_get_schema --- tests/test_wfs_schema.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_wfs_schema.py b/tests/test_wfs_schema.py index 80a2e206..8848e82e 100644 --- a/tests/test_wfs_schema.py +++ b/tests/test_wfs_schema.py @@ -159,7 +159,7 @@ def test_get_schema_100(self, mp_wfs_100): assert list(wfs100.contents) == ['continents', 'cities'] - def test_get_schema(self, mp_wfs_100, mp_remote_describefeaturetype): + def test_get_schema(self, mp_wfs_110, mp_remote_describefeaturetype): """Test the get_schema method for a standard schema. Parameters