Bug report #8258

QgsVectorDataProvider.uniqueValues returns not None but QPyNullVariant

Added by Minoru Akagi over 10 years ago. Updated over 10 years ago.

Status:Closed
Priority:Normal
Assignee:-
Category:Python plugins
Affected QGIS version:master Regression?:No
Operating System: Easy fix?:No
Pull Request or Patch supplied:No Resolution:fixed
Crashes QGIS or corrupts data:No Copied to github as #:17062

Description

Open polygons.shp (testdata on #8219). The second field of the attribute table is a string type and includes NULL values. Then, in the python console:

>>> layer = iface.activeLayer()
>>> provider = layer.dataProvider()
>>> provider.uniqueValues(1)
[<PyQt4.QtCore.QPyNullVariant object at 0x10CABFB8>, u'pentagon', u'quadrangle', u'triangle']

>>> f = QgsFeature()
>>> provider.getFeatures( QgsFeatureRequest().setFilterFid( 0 ) ).nextFeature( f )
True
>>> f.attributes()
[u'f', None, 10]

>>> provider.uniqueValues(1)[0] == None
False

QGIS version:
1.9.0-Master Master, 630427f

History

#1 Updated by Matthias Kuhn over 10 years ago

Will now be:

>>> layer = iface.activeLayer()
>>> provider = layer.dataProvider()
>>> provider.uniqueValues(1)
[NULL, u'pentagon', u'quadrangle', u'triangle']

>>> f = provider.getFeatures( QgsFeatureRequest().setFilterFid( 0 ) ).next()
True
>>> f.attributes()
[u'f', NULL, 10]

>>> provider.uniqueValues(1)[0] == None
False
>>> provider.uniqueValues(1)[0] is None
False
>>> provider.uniqueValues(1)[0].isNull()
True
>>> if not provider.uniqueValues(1)[0]:
>>>     'it is NOT'
'it is NOT'

Now everything returning a NULL attribute returns a QPyNullVariant (by design) but will be represented as "NULL" on the console

#2 Updated by Minoru Akagi over 10 years ago

The evaluation of (None == None) is True. But the evaluation of the following conditional expression is False.

>>> provider.uniqueValues(1)[0] == f.attributes()[1]
False

It makes sense to me that (QPyNullVariant == QPyNullVariant) is evaluated as True.

#3 Updated by Nathan Woodrow over 10 years ago

You would think so but it's not the case.

We should be able to fix that by overloading the eq method. Hold tight.

#4 Updated by Nathan Woodrow over 10 years ago

We can just do this:


from types import MethodType
from PyQt4.QtCore import QPyNullVariant
def __nonzero__(self):
    return False

def __eq__(self, other):
    return True

QPyNullVariant.__nonzero__ = MethodType(__nonzero__, None, QPyNullVariant)
QPyNullVariant.__eq__ = MethodType(__eq__, None, QPyNullVariant)

which will work like so:


>>> n = QPyNullVariant('QString')
>>> n2 = QPyNullVariant('QString')
>>> n == n2
True

Just be aware this doesn't compare the internal type of QPyNullVariant so maybe we should just compare that rather then returning True. In practice should this work?


>>> n = QPyNullVariant('QString')
>>> n2 = QPyNullVariant('int')
>>> n == n2
True

or do we not really care in this case? Because we are just trying to do: NULL == NULL

#5 Updated by Nathan Woodrow over 10 years ago

  • Status changed from Open to Closed
  • Resolution set to fixed

This is as fixed as we are going to be able to do.


>>> n = QPyNullVariant('QString')
>>> n2 = QPyNullVariant('QString')
>>> n == n2
True
>>> n = QPyNullVariant('QString')
>>> n2 = QPyNullVariant('int')
>>> n == n2
True
>>> n = QPyNullVariant('QString')
>>> n == None
True
>>> n = QPyNullVariant('QString')
>>> n is None
False
>>> n = 100
>>> n2 = QPyNullVariant('int')
>>> n == n2
False

There is also a new shortcut NULL type in qgis.core which is a QPyNullVariant. So you can just do:


>>> from qgis.core import NULL
>>> provider.uniqueValues(1)[0] == NULL
True

#6 Updated by Minoru Akagi over 10 years ago

  • Status changed from Closed to Reopened

Great works! Thanks, Matthias and Nathan!!

Now the evaluation of the following conditional expression is True.

>>>  provider.uniqueValues(1)[0] == f.attributes()[1]
True

However, the following is also True!

>>>  provider.uniqueValues(1)[0] != f.attributes()[1]
True

Probably, we should override ne method too as below:

    def __ne__(self, other):
        return not self.__eq__(other)

    QPyNullVariant.__ne__ = MethodType(__ne__, None, QPyNullVariant)

#7 Updated by Nathan Woodrow over 10 years ago

Good catch!

Will fix that now.

#8 Updated by Minoru Akagi over 10 years ago

  • % Done changed from 0 to 100
  • Status changed from Reopened to Closed

Also available in: Atom PDF