diff --git a/python/core/auto_generated/providers/qgsabstractdatabaseproviderconnection.sip.in b/python/core/auto_generated/providers/qgsabstractdatabaseproviderconnection.sip.in index 8fab90a658b..0cb23c509bb 100644 --- a/python/core/auto_generated/providers/qgsabstractdatabaseproviderconnection.sip.in +++ b/python/core/auto_generated/providers/qgsabstractdatabaseproviderconnection.sip.in @@ -806,7 +806,7 @@ The caller takes ownership of the returned object. .. versionadded:: 3.28 %End - virtual QList searchLayerMetadata( const QgsMetadataSearchContext &searchContext, const QString &searchString = QString(), const QgsRectangle &geographicExtent = QgsRectangle(), QgsFeedback *feedback = 0 ) const throw( QgsProviderConnectionException ); + virtual QList searchLayerMetadata( const QgsMetadataSearchContext &searchContext, const QString &searchString = QString(), const QgsRectangle &geographicExtent = QgsRectangle(), QgsFeedback *feedback = 0 ) const throw( QgsProviderConnectionException, QgsNotSupportedException ); %Docstring Search the stored layer metadata in the connection, optionally limiting the search to the metadata identifier, title, @@ -815,14 +815,19 @@ abstract, keywords and categories. ``searchString`` limit the search to metadata having an extent intersecting ``geographicExtent``, an optional ``feedback`` can be used to monitor and control the search process. -The default implementation raises a :py:class:`QgsProviderConnectionException`, data providers may implement +The default implementation raises a :py:class:`QgsNotSupportedException`, data providers may implement the search functionality. +A :py:class:`QgsProviderConnectionException` is raised in case of errors happening during the search for +providers that implement the search functionality. + :return: a (possibly empty) list of :py:class:`QgsLayerMetadataProviderResult`, throws a :py:class:`QgsProviderConnectionException` if any error occurred during the search. :raises QgsProviderConnectionException: +:raises QgsNotSupportedException: + .. versionadded:: 3.28 %End diff --git a/scripts/sipify.pl b/scripts/sipify.pl index cfda42a39d0..18e9f79265e 100755 --- a/scripts/sipify.pl +++ b/scripts/sipify.pl @@ -513,7 +513,7 @@ sub fix_annotations { $line =~ s/SIP_PYNAME\(\s*(\w+)\s*\)/\/PyName=$1\//; $line =~ s/SIP_TYPEHINT\(\s*([\w\.\s,\[\]]+?)\s*\)/\/TypeHint="$1"\//g; $line =~ s/SIP_VIRTUALERRORHANDLER\(\s*(\w+)\s*\)/\/VirtualErrorHandler=$1\//; - $line =~ s/SIP_THROW\(\s*(\w+)\s*\)/throw\( $1 \)/; + $line =~ s/SIP_THROW\(\s*([\w\s,]+?)\s*\)/throw\( $1 \)/; # combine multiple annotations # https://regex101.com/r/uvCt4M/5 diff --git a/src/core/providers/ogr/qgsgeopackageproviderconnection.cpp b/src/core/providers/ogr/qgsgeopackageproviderconnection.cpp index 87c0466d2e0..bc0f20b4f06 100644 --- a/src/core/providers/ogr/qgsgeopackageproviderconnection.cpp +++ b/src/core/providers/ogr/qgsgeopackageproviderconnection.cpp @@ -516,13 +516,13 @@ QList QgsGeoPackageProviderConnection::searchLay } else { - QgsDebugMsg( QStringLiteral( "Error reading XML metdadata from connection %1" ).arg( uri() ) ); + throw QgsProviderConnectionException( QStringLiteral( "Error reading XML metdadata from connection %1" ).arg( uri() ) ); } } } catch ( const QgsProviderConnectionException &ex ) { - QgsDebugMsg( QStringLiteral( "Error fetching metdadata from connection %1: %2" ).arg( uri(), ex.what() ) ); + throw QgsProviderConnectionException( QStringLiteral( "Error fetching metdadata from connection %1: %2" ).arg( uri(), ex.what() ) ); } } return results; diff --git a/src/core/providers/qgsabstractdatabaseproviderconnection.cpp b/src/core/providers/qgsabstractdatabaseproviderconnection.cpp index 29c57de50cb..341f5969288 100644 --- a/src/core/providers/qgsabstractdatabaseproviderconnection.cpp +++ b/src/core/providers/qgsabstractdatabaseproviderconnection.cpp @@ -1087,7 +1087,7 @@ QList QgsAbstractDatabaseProviderConnection::sea Q_UNUSED( searchContext ); Q_UNUSED( searchString ); Q_UNUSED( geographicExtent ); - throw QgsProviderConnectionException( QObject::tr( "Provider %1 has no %2 method" ).arg( providerKey(), QStringLiteral( "searchLayerMetadata" ) ) ); + throw QgsNotSupportedException( QObject::tr( "Provider %1 has no %2 method" ).arg( providerKey(), QStringLiteral( "searchLayerMetadata" ) ) ); } void QgsAbstractDatabaseProviderConnection::dropRasterTable( const QString &, const QString & ) const diff --git a/src/core/providers/qgsabstractdatabaseproviderconnection.h b/src/core/providers/qgsabstractdatabaseproviderconnection.h index d76525447e3..cb3945a3bd0 100644 --- a/src/core/providers/qgsabstractdatabaseproviderconnection.h +++ b/src/core/providers/qgsabstractdatabaseproviderconnection.h @@ -923,15 +923,19 @@ class CORE_EXPORT QgsAbstractDatabaseProviderConnection : public QgsAbstractProv * \a searchString limit the search to metadata having an extent intersecting \a geographicExtent, * an optional \a feedback can be used to monitor and control the search process. * - * The default implementation raises a QgsProviderConnectionException, data providers may implement + * The default implementation raises a QgsNotSupportedException, data providers may implement * the search functionality. * + * A QgsProviderConnectionException is raised in case of errors happening during the search for + * providers that implement the search functionality. + * * \returns a (possibly empty) list of QgsLayerMetadataProviderResult, throws a QgsProviderConnectionException * if any error occurred during the search. * \throws QgsProviderConnectionException + * \throws QgsNotSupportedException * \since QGIS 3.28 */ - virtual QList searchLayerMetadata( const QgsMetadataSearchContext &searchContext, const QString &searchString = QString(), const QgsRectangle &geographicExtent = QgsRectangle(), QgsFeedback *feedback = nullptr ) const SIP_THROW( QgsProviderConnectionException ); + virtual QList searchLayerMetadata( const QgsMetadataSearchContext &searchContext, const QString &searchString = QString(), const QgsRectangle &geographicExtent = QgsRectangle(), QgsFeedback *feedback = nullptr ) const SIP_THROW( QgsProviderConnectionException, QgsNotSupportedException ); protected: diff --git a/src/core/qgis_sip.h b/src/core/qgis_sip.h index a2b6b9155c5..e4d4e40bd98 100644 --- a/src/core/qgis_sip.h +++ b/src/core/qgis_sip.h @@ -195,7 +195,7 @@ * try/catch blocks around call and catch the correct exception, otherwise only * unknown generic exceptions are available for Python code. */ -#define SIP_THROW(name) +#define SIP_THROW(name, ...) /* * Will insert a `%End` directive in sip files diff --git a/tests/src/python/test_qgslayermetadataprovider_python.py b/tests/src/python/test_qgslayermetadataprovider_python.py index 7f791ad10e3..2ba4d38ccdd 100644 --- a/tests/src/python/test_qgslayermetadataprovider_python.py +++ b/tests/src/python/test_qgslayermetadataprovider_python.py @@ -13,6 +13,8 @@ __copyright__ = 'Copyright 2022, ItOpen' import os import shutil +from functools import partial +from stat import S_IREAD, S_IRGRP, S_IROTH, S_IWUSR from qgis.core import ( QgsPolygon, @@ -21,10 +23,12 @@ from qgis.core import ( QgsMapLayerType, QgsProviderRegistry, QgsAbstractLayerMetadataProvider, - QgsLayerMetadataSearchResult, + QgsLayerMetadataSearchResults, QgsLayerMetadataProviderResult, QgsMetadataSearchContext, QgsLayerMetadata, + QgsNotSupportedException, + QgsProviderConnectionException, ) from qgis.PyQt.QtCore import QTemporaryDir @@ -102,7 +106,7 @@ class PythonLayerMetadataProvider(QgsAbstractLayerMetadataProvider): assert result.identifier() == 'MD012345' - results = QgsLayerMetadataSearchResult() + results = QgsLayerMetadataSearchResults() results.addMetadata(result) results.addError('Bad news from PythonLayerMetadataProvider :(') @@ -114,6 +118,16 @@ QGIS_APP = start_app() class TestPythonLayerMetadataProvider(unittest.TestCase): + def setUp(self): + + super().setUp() + srcpath = os.path.join(TEST_DATA_DIR, 'provider') + shutil.copy(os.path.join(srcpath, 'geopackage.gpkg'), temp_path) + self.conn = os.path.join(temp_path, 'geopackage.gpkg') + + shutil.copy(os.path.join(srcpath, 'spatialite.db'), temp_path) + self.conn_sl = os.path.join(temp_path, 'spatialite.db') + def test_metadataRegistryApi(self): reg = QGIS_APP.layerMetadataProviderRegistry() @@ -141,13 +155,26 @@ class TestPythonLayerMetadataProvider(unittest.TestCase): reg.unregisterLayerMetadataProvider(md_provider) self.assertIsNone(reg.layerMetadataProviderFromId('python')) - def setUp(self): + def testExceptions(self): - super().setUp() - srcpath = os.path.join(TEST_DATA_DIR, 'provider') - shutil.copy(os.path.join(srcpath, 'geopackage.gpkg'), temp_path) - self.conn = os.path.join(temp_path, 'geopackage.gpkg') - md = QgsProviderRegistry.instance().providerMetadata('ogr') + def _spatialite(path): + + md = QgsProviderRegistry.instance().providerMetadata('spatialite') + conn = md.createConnection(path, {}) + conn.searchLayerMetadata(QgsMetadataSearchContext()) + + def _ogr(path): + + md = QgsProviderRegistry.instance().providerMetadata('ogr') + conn = md.createConnection(path, {}) + os.chmod(path, S_IREAD | S_IRGRP | S_IROTH) + conn.searchLayerMetadata(QgsMetadataSearchContext()) + + self.assertRaises(QgsNotSupportedException, partial(_spatialite, self.conn_sl)) + self.assertRaises(QgsProviderConnectionException, partial(_ogr, self.conn)) + self.assertRaises(QgsNotSupportedException, partial(_ogr, self.conn_sl)) + os.chmod(self.conn, S_IWUSR | S_IREAD) + os.chmod(self.conn_sl, S_IWUSR | S_IREAD) if __name__ == '__main__':