Skip to content

Commit

Permalink
Added QgsDataProvider::ProviderOptions to the bindings
Browse files Browse the repository at this point in the history
  • Loading branch information
elpaso committed Jun 1, 2018
1 parent 0fa88e6 commit 3aa630c
Show file tree
Hide file tree
Showing 5 changed files with 30 additions and 88 deletions.
4 changes: 2 additions & 2 deletions python/core/auto_generated/qgsprovidermetadata.sip.in
Expand Up @@ -50,14 +50,14 @@ no library is involved.

Py_BEGIN_ALLOW_THREADS

sipCpp = new QgsProviderMetadata( *a0, *a1, [a2]( const QString &dataSource ) -> QgsDataProvider*
sipCpp = new QgsProviderMetadata( *a0, *a1, [a2]( const QString &dataSource, const QgsDataProvider::ProviderOptions &providerOptions ) -> QgsDataProvider*
{
QgsDataProvider *provider;
provider = nullptr;
PyObject *sipResObj;
SIP_BLOCK_THREADS

sipResObj = sipCallMethod( NULL, a2, "D", new QString( dataSource ), sipType_QString, NULL );
sipResObj = sipCallMethod( NULL, a2, "DD", new QString( dataSource ), sipType_QString, NULL, new QgsDataProvider::ProviderOptions( providerOptions ), sipType_QgsDataProvider_ProviderOptions, NULL );

if ( sipResObj )
{
Expand Down
2 changes: 1 addition & 1 deletion python/core/auto_generated/qgsproviderregistry.sip.in
Expand Up @@ -182,7 +182,7 @@ Returns a string containing the available protocol drivers

void registerProvider( QgsProviderMetadata *providerMetadata /Transfer/ );
%Docstring
register a new vector data provider from its ``providerMetadata``
register a new vector data provider from its ``providerMetadata``

.. note::

Expand Down
4 changes: 2 additions & 2 deletions src/core/qgsprovidermetadata.h
Expand Up @@ -71,14 +71,14 @@ class CORE_EXPORT QgsProviderMetadata

Py_BEGIN_ALLOW_THREADS

sipCpp = new QgsProviderMetadata( *a0, *a1, [a2]( const QString &dataSource ) -> QgsDataProvider*
sipCpp = new QgsProviderMetadata( *a0, *a1, [a2]( const QString &dataSource, const QgsDataProvider::ProviderOptions &providerOptions ) -> QgsDataProvider*
{
QgsDataProvider *provider;
provider = nullptr;
PyObject *sipResObj;
SIP_BLOCK_THREADS

sipResObj = sipCallMethod( NULL, a2, "D", new QString( dataSource ), sipType_QString, NULL );
sipResObj = sipCallMethod( NULL, a2, "DD", new QString( dataSource ), sipType_QString, NULL, new QgsDataProvider::ProviderOptions( providerOptions ), sipType_QgsDataProvider_ProviderOptions, NULL );

if ( sipResObj )
{
Expand Down
25 changes: 16 additions & 9 deletions tests/src/python/provider_python.py
Expand Up @@ -46,6 +46,7 @@
QgsProviderMetadata,
QgsGeometryEngine,
QgsSpatialIndex,
QgsDataProvider,
)

from qgis.PyQt.QtCore import QVariant
Expand All @@ -54,7 +55,7 @@
class PyFeatureIterator(QgsAbstractFeatureIterator):

def __init__(self, source, request):
super(PyFeatureIterator, self).__init__(request)
super().__init__(request)
self._request = request if request is not None else QgsFeatureRequest()
self._source = source
self._index = 0
Expand Down Expand Up @@ -187,13 +188,13 @@ def description(cls):
return 'Python Test Provider'

@classmethod
def createProvider(cls, uri):
return PyProvider(uri)
def createProvider(cls, uri, providerOptions):
return PyProvider(uri, providerOptions)

# Implementation of functions from QgsVectorDataProvider

def __init__(self, uri=''):
super(PyProvider, self).__init__(uri)
def __init__(self, uri='', providerOptions=QgsDataProvider.ProviderOptions()):
super().__init__(uri)
# Use the memory layer to parse the uri
mlayer = QgsVectorLayer(uri, 'ml', 'memory')
self.setNativeTypes(mlayer.dataProvider().nativeTypes())
Expand All @@ -206,6 +207,7 @@ def __init__(self, uri=''):
self._subset_string = ''
self._crs = mlayer.crs()
self._spatialindex = None
self._provider_options = providerOptions
if 'index=yes'in self._uri:
self.createSpatialIndex()

Expand Down Expand Up @@ -307,16 +309,21 @@ def addAttributes(self, attrs):

def renameAttributes(self, renamedAttributes):
result = True
for key, new_name in renamedAttributes:
fieldIndex = key
# We need to replace all fields because python bindings return a copy from [] and at()
new_fields = [self._fields.at(i) for i in range(self._fields.count())]
for fieldIndex, new_name in renamedAttributes.items():
if fieldIndex < 0 or fieldIndex >= self._fields.count():
result = False
continue
if new_name in self._fields.indexFromName(new_name) >= 0:
if self._fields.indexFromName(new_name) >= 0:
#field name already in use
result = False
continue
self._fields[fieldIndex].setName(new_name)
new_fields[fieldIndex].setName(new_name)
if result:
self._fields = QgsFields()
for i in range(len(new_fields)):
self._fields.append(new_fields[i])
return result

def deleteAttributes(self, attributes):
Expand Down
83 changes: 9 additions & 74 deletions tests/src/python/test_provider_python.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
"""QGIS Unit tests for the python layer provider.
"""QGIS Unit tests for the python dataprovider.
.. note:: This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -144,13 +144,13 @@ def getEditableLayer(self):
return self.createLayer()

def testGetFeaturesSubsetAttributes2(self):
""" Override and skip this test for memory provider, as it's actually more efficient for the memory provider to return
""" Override and skip this test for pythonprovider provider, as it's actually more efficient for the pythonprovider provider to return
its features as direct copies (due to implicit sharing of QgsFeature)
"""
pass

def testGetFeaturesNoGeometry(self):
""" Override and skip this test for memory provider, as it's actually more efficient for the memory provider to return
""" Override and skip this test for pythonprovider provider, as it's actually more efficient for the pythonprovider provider to return
its features as direct copies (due to implicit sharing of QgsFeature)
"""
pass
Expand All @@ -163,8 +163,8 @@ def testGetFeaturesDestinationCrs(self):
def testCtors(self):
testVectors = ["Point", "LineString", "Polygon", "MultiPoint", "MultiLineString", "MultiPolygon", "None"]
for v in testVectors:
layer = QgsVectorLayer(v, "test", "memory")
assert layer.isValid(), "Failed to create valid %s memory layer" % (v)
layer = QgsVectorLayer(v, "test", "pythonprovider")
assert layer.isValid(), "Failed to create valid %s pythonprovider layer" % (v)

def testLayerGeometry(self):
testVectors = [("Point", QgsWkbTypes.PointGeometry, QgsWkbTypes.Point),
Expand Down Expand Up @@ -199,7 +199,7 @@ def testLayerGeometry(self):
("MultiPolygon25D", QgsWkbTypes.PolygonGeometry, QgsWkbTypes.MultiPolygon25D),
("None", QgsWkbTypes.NullGeometry, QgsWkbTypes.NoGeometry)]
for v in testVectors:
layer = QgsVectorLayer(v[0], "test", "memory")
layer = QgsVectorLayer(v[0], "test", "pythonprovider")

myMessage = ('Expected: %s\nGot: %s\n' %
(v[1], layer.geometryType()))
Expand All @@ -210,7 +210,7 @@ def testLayerGeometry(self):
assert layer.wkbType() == v[2], myMessage

def testAddFeatures(self):
layer = QgsVectorLayer("Point", "test", "memory")
layer = QgsVectorLayer("Point", "test", "pythonprovider")
provider = layer.dataProvider()

res = provider.addAttributes([QgsField("name", QVariant.String),
Expand Down Expand Up @@ -260,7 +260,7 @@ def testAddFeatures(self):
assert compareWkt(str(geom.asWkt()), "Point (10 10)"), myMessage

def testGetFields(self):
layer = QgsVectorLayer("Point", "test", "memory")
layer = QgsVectorLayer("Point", "test", "pythonprovider")
provider = layer.dataProvider()

provider.addAttributes([QgsField("name", QVariant.String),
Expand Down Expand Up @@ -345,7 +345,7 @@ def testSaveFields(self):
assert f == importedFields.field(f.name())

def testRenameAttributes(self):
layer = QgsVectorLayer("Point", "test", "memory")
layer = QgsVectorLayer("Point", "test", "pythonprovider")
provider = layer.dataProvider()

res = provider.addAttributes([QgsField("name", QVariant.String),
Expand Down Expand Up @@ -382,71 +382,6 @@ def testRenameAttributes(self):
self.assertEqual(fet.fields()[1].name(), 'mapinfo_is_the_stone_age')
self.assertEqual(fet.fields()[2].name(), 'super_size')

def testUniqueSource(self):
"""
Similar memory layers should have unique source - some code checks layer source to identify
matching layers
"""
layer = QgsVectorLayer("Point", "test", "memory")
layer2 = QgsVectorLayer("Point", "test2", "memory")
self.assertNotEqual(layer.source(), layer2.source())

def testCreateMemoryLayer(self):
"""
Test QgsMemoryProviderUtils.createMemoryLayer()
"""

# no fields
layer = QgsMemoryProviderUtils.createMemoryLayer('my name', QgsFields())
self.assertTrue(layer.isValid())
self.assertEqual(layer.name(), 'my name')
self.assertTrue(layer.fields().isEmpty())

# similar layers should have unique sources
layer2 = QgsMemoryProviderUtils.createMemoryLayer('my name', QgsFields())
self.assertNotEqual(layer.source(), layer2.source())

# geometry type
layer = QgsMemoryProviderUtils.createMemoryLayer('my name', QgsFields(), QgsWkbTypes.Point)
self.assertTrue(layer.isValid())
self.assertEqual(layer.wkbType(), QgsWkbTypes.Point)
layer = QgsMemoryProviderUtils.createMemoryLayer('my name', QgsFields(), QgsWkbTypes.PolygonZM)
self.assertTrue(layer.isValid())
self.assertEqual(layer.wkbType(), QgsWkbTypes.PolygonZM)

# crs
layer = QgsMemoryProviderUtils.createMemoryLayer('my name', QgsFields(), QgsWkbTypes.PolygonZM, QgsCoordinateReferenceSystem.fromEpsgId(3111))
self.assertTrue(layer.isValid())
self.assertEqual(layer.wkbType(), QgsWkbTypes.PolygonZM)
self.assertTrue(layer.crs().isValid())
self.assertEqual(layer.crs().authid(), 'EPSG:3111')

# fields
fields = QgsFields()
fields.append(QgsField("string", QVariant.String))
fields.append(QgsField("long", QVariant.LongLong))
fields.append(QgsField("double", QVariant.Double))
fields.append(QgsField("integer", QVariant.Int))
fields.append(QgsField("date", QVariant.Date))
fields.append(QgsField("datetime", QVariant.DateTime))
fields.append(QgsField("time", QVariant.Time))
layer = QgsMemoryProviderUtils.createMemoryLayer('my name', fields)
self.assertTrue(layer.isValid())
self.assertFalse(layer.fields().isEmpty())
self.assertEqual(len(layer.fields()), len(fields))
for i in range(len(fields)):
self.assertEqual(layer.fields()[i].name(), fields[i].name())
self.assertEqual(layer.fields()[i].type(), fields[i].type())

# unsupported field type
fields = QgsFields()
fields.append(QgsField("rect", QVariant.RectF))
layer = QgsMemoryProviderUtils.createMemoryLayer('my name', fields)
self.assertTrue(layer.isValid())
self.assertFalse(layer.fields().isEmpty())
self.assertEqual(layer.fields()[0].name(), 'rect')
self.assertEqual(layer.fields()[0].type(), QVariant.String) # should be mapped to string

def testThreadSafetyWithIndex(self):
layer = QgsVectorLayer('Point?crs=epsg:4326&index=yes&field=pk:integer&field=cnt:int8&field=name:string(0)&field=name2:string(0)&field=num_char:string&key=pk',
'test', 'pythonprovider')
Expand Down

0 comments on commit 3aa630c

Please sign in to comment.