wx.Colour - not hashable in Phoenix/Py3

I am working on making ObjectListView comptable with Py3 and Phoenix and get an error in the tests.

case.py (c:\Python34\Lib\unittest): 57
             yield
case.py (c:\Python34\Lib\unittest): 574
                     testMethod()
test_ObjectListView.py (d:\devTools\ObjectListView\test): 320
             self.objectListView.oddRowsBackColor in set(bkgdColours))
testNoAlternateColours
Exception: TypeError: unhashable type: 'Colour'

This test passes with Py2.7 and Phoenix, so it looks like a Py3 issue. Anyone has a type on how to fix this?

Werner

Hi,

I am working on making ObjectListView comptable with Py3 and Phoenix and get an error in the tests.

case.py (c:\Python34\Lib\unittest): 57
            yield
case.py (c:\Python34\Lib\unittest): 574
                    testMethod()
test_ObjectListView.py (d:\devTools\ObjectListView\test): 320
            self.objectListView.oddRowsBackColor in set(bkgdColours))
testNoAlternateColours
Exception: TypeError: unhashable type: 'Colour'

This test passes with Py2.7 and Phoenix, so it looks like a Py3 issue. Anyone has a type on how to fix this?

It looks like the '__hash__' method is not defined in Phoenix under Py3.4.
c = wx.Colour()
c.__hash__ < returns None

In Py2.7 I get:
c = wx.Colour()
c.__hash__
<method-wrapper '__hash__' of Colour object at 0x0222AD68>

Werner

···

On 11/18/2014 15:11, Werner wrote:

Hi,

I am working on making ObjectListView comptable with Py3 and Phoenix and
get an error in the tests.

case.py (c:\Python34\Lib\unittest): 57
            yield
case.py (c:\Python34\Lib\unittest): 574
                    testMethod()
test_ObjectListView.py (d:\devTools\ObjectListView\test): 320
            self.objectListView.oddRowsBackColor in set(bkgdColours))
testNoAlternateColours
Exception: TypeError: unhashable type: 'Colour'

This test passes with Py2.7 and Phoenix, so it looks like a Py3 issue.
Anyone has a type on how to fix this?

It looks like the '__hash__' method is not defined in Phoenix under Py3.4.
c = wx.Colour()
c.__hash__ < returns None

In Py2.7 I get:
c = wx.Colour()
c.__hash__
<method-wrapper '__hash__' of Colour object at 0x0222AD68>

But is it correct?
For example, can you check whether hash(wx.Colour(1,2,3)) ==
hash(wx.Colour(1,2,3)) ?

In Python3, when a class defines __eq__ it must also define __hash__,
otherwise it is considered unhashable.

In Python2, this check does not exist, and can lead to surprising results;
for example, dictionary lookups will silently fail...

···

2014-11-27 14:38 GMT+01:00 Werner <wernerfbd@gmx.ch>:

On 11/18/2014 15:11, Werner wrote:

Werner

--
You received this message because you are subscribed to the Google Groups
"wxPython-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an
email to wxPython-dev+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

--
Amaury Forgeot d'Arc

In py2.7:
hash(wx.Colour(1,2,3)) == hash(wx.Colour(1,2,3))
True
But in py3.4:
c = wx.Colour()
c.hash
hash(wx.Colour(1,2,3)) == hash(wx.Colour(1,2,3))
Traceback (most recent call last):
File “”, line 1, in
builtins.TypeError: unhashable type: ‘Colour’
I guess something with the sip magic is not working correctly in
Py3.x.
Hope above will help Robin to pin point it when he gets to this.
Werner

···

Hi,

  On 11/27/2014 15:26, Amaury Forgeot d'Arc wrote:

2014-11-27 14:38 GMT+01:00 Werner wernerfbd@gmx.ch:

              I

am working on making ObjectListView comptable with Py3
and Phoenix and get an error in the tests.

              case.py (c:\Python34\Lib\unittest): 57

                          yield

              case.py (c:\Python34\Lib\unittest): 574

                                  testMethod()

              test_ObjectListView.py (d:\devTools\ObjectListView\test):

320

                          self.objectListView.oddRowsBackColor in

set(bkgdColours))

              testNoAlternateColours

              Exception: TypeError: unhashable type: 'Colour'





              This test passes with Py2.7 and Phoenix, so it looks

like a Py3 issue. Anyone has a type on how to fix
this?
Hi,

            On 11/18/2014 15:11, Werner wrote:

          It looks like the '__hash__' method is not defined in

Phoenix under Py3.4.

          c = wx.Colour()

          c.__hash__  < returns None



          In Py2.7 I get:

          c = wx.Colour()

          c.__hash__

          <method-wrapper '__hash__' of Colour object at

0x0222AD68>

But is it correct?

          For example, can you check whether

hash(wx.Colour(1,2,3)) == hash(wx.Colour(1,2,3)) ?

Werner wrote:

Hi,

2014-11-27 14:38 GMT+01:00 Werner <wernerfbd@gmx.ch
<mailto:wernerfbd@gmx.ch>>:

    Hi,

        I am working on making ObjectListView comptable with Py3 and
        Phoenix and get an error in the tests.

        case.py (c:\Python34\Lib\unittest): 57
                    yield
        case.py (c:\Python34\Lib\unittest): 574
                            testMethod()
        test_ObjectListView.py (d:\devTools\ObjectListView\test): 320
                    self.objectListView.oddRowsBackColor in
        set(bkgdColours))
        testNoAlternateColours
        Exception: TypeError: unhashable type: 'Colour'

        This test passes with Py2.7 and Phoenix, so it looks like a
        Py3 issue. Anyone has a type on how to fix this?

    It looks like the '__hash__' method is not defined in Phoenix
    under Py3.4.
    c = wx.Colour()
    c.__hash__ < returns None

    In Py2.7 I get:
    c = wx.Colour()
    c.__hash__
    <method-wrapper '__hash__' of Colour object at 0x0222AD68>

But is it correct?
For example, can you check whether hash(wx.Colour(1,2,3)) ==
hash(wx.Colour(1,2,3)) ?

In py2.7:
hash(wx.Colour(1,2,3)) == hash(wx.Colour(1,2,3))
True

But in py3.4:
c = wx.Colour()
c.__hash__
hash(wx.Colour(1,2,3)) == hash(wx.Colour(1,2,3))
Traceback (most recent call last):
   File "<string>", line 1, in <fragment>
builtins.TypeError: unhashable type: 'Colour'

I guess something with the sip magic is not working correctly in Py3.x.

Hope above will help Robin to pin point it when he gets to this.

From the docs for both Python2 and Python3:
"""
An object is hashable if it has a hash value which never changes during its lifetime (it needs a __hash__() method), and can be compared to other objects (it needs an __eq__() method). Hashable objects which compare equal must have the same hash value.

Hashability makes an object usable as a dictionary key and a set member, because these data structures use the hash value internally.

All of Python’s immutable built-in objects are hashable, while non mutable containers (such as lists or dictionaries) are not. Objects which are instances of user-defined classes are hashable by default; they all compare unequal (except with themselves), and their hash value is derived from their id().
"""

SIP doesn't add a default __hash__ in either build, it just relies on the default Python behavior. In Python2 that seems to be a hash value derived from the object's ID. In other words, you could think of it like this:

   class HashableColour(wx.Colour):
       def __hash__(self):
           return do_something(id(self))

Based on the above text I would have expected the same from Python3, but perhaps the definition of "user-defined classes" was changed to not include extension types. Or maybe that doc needs updating.

Either way, I prefer the Python3 way because basing a default hash value on the id() means that you must use the same instance of the key to fetch the value again, you can't simply use a new key with the same value. For example, in Python2:

  >>> c1 = wx.Colour(1,2,3)
  >>> c2 = wx.Colour(1,2,3)
  >>>
  >>> d = {c1: 'one', c2: 'two'}
  >>> d[c1]
  'one'
  >>> d[c2]
  'two'
  >>> d[wx.Colour(1,2,3)]
  Traceback (most recent call last):
    File "<input>", line 1, in <module>
  KeyError: wx.Colour(1, 2, 3, 255)
  >>>

In Python3 Python makes you actually think about what makes sense for your class's hash values, it doesn't just give you some mindless substandard thing for free. For wx.Colour I might choose something like:

   class HashableColour(wx.Colour):
       def __hash__(self):
           return self.GetRGBA()

or maybe even:

           return hash(self.Get()) # the hash of the tuple of color components

But I suppose the real question is whether we should be adding our own __hash__ methods to at least some of the wrapped wxPython classes. I'll give that some thought.

···

On 11/27/2014 15:26, Amaury Forgeot d'Arc wrote:

    On 11/18/2014 15:11, Werner wrote:

--
Robin Dunn
Software Craftsman

Until either SIP adds hash or another solution is found, here’s a workaround:

In …\site-packages\wx\core.py, add the following code around line 557, where the rest of the Colour class is defined:

def Colour___hash_(self):

return hash(tuple(self.Get()))

Colour.hash = Colour___hash_

del Colour___hash_

I know that the top of core.py says to not edit the file, but I couldn’t help myself :-D.

I’ve done a tiny bit of testing, only verifying that

  1. hash(wx.Colour(1,1,1,255)) does not raise a TypeError
  2. hash(wx.Colour(1,1,1,255)) == hash((1,1,1,255))
  3. My project actually runs.

Robin, making this change won’t cause the world to end, will it? I know that it will get overwritten the next time SIP generates core.py, but that I can live with.

Thanks,

···

On Wednesday, March 11, 2015 at 6:30:11 PM UTC-7, Robin Dunn wrote:

From the docs for both Python2 and Python3:

“”"

An object is hashable if it has a hash value which never changes during
its lifetime (it needs a hash() method), and can be compared to
other objects (it needs an eq() method). Hashable objects which
compare equal must have the same hash value.

Hashability makes an object usable as a dictionary key and a set member,
because these data structures use the hash value internally.

All of Python’s immutable built-in objects are hashable, while non
mutable containers (such as lists or dictionaries) are not. Objects
which are instances of user-defined classes are hashable by default;
they all compare unequal (except with themselves), and their hash value
is derived from their id().

“”"

SIP doesn’t add a default hash in either build, it just relies on
the default Python behavior. In Python2 that seems to be a hash value
derived from the object’s ID. In other words, you could think of it
like this:

class HashableColour(wx.Colour):

   def __hash__(self):

       return do_something(id(self))

Based on the above text I would have expected the same from Python3, but
perhaps the definition of “user-defined classes” was changed to not
include extension types. Or maybe that doc needs updating.

Either way, I prefer the Python3 way because basing a default hash value
on the id() means that you must use the same instance of the key to
fetch the value again, you can’t simply use a new key with the same
value. For example, in Python2:

c1 = wx.Colour(1,2,3)

c2 = wx.Colour(1,2,3)

d = {c1: ‘one’, c2: ‘two’}

d[c1]

‘one’

d[c2]

‘two’

d[wx.Colour(1,2,3)]

Traceback (most recent call last):

File "<input>", line 1, in <module>

KeyError: wx.Colour(1, 2, 3, 255)

In Python3 Python makes you actually think about what makes sense for
your class’s hash values, it doesn’t just give you some mindless
substandard thing for free. For wx.Colour I might choose something like:

class HashableColour(wx.Colour):

   def __hash__(self):

       return self.GetRGBA()

or maybe even:

       return hash(self.Get()) # the hash of the tuple of color

components

But I suppose the real question is whether we should be adding our own
hash methods to at least some of the wrapped wxPython classes. I’ll
give that some thought.


Robin Dunn

Software Craftsman

http://wxPython.org

Has there been any more investigation into this? Should I add it as an issue to the wxWidgets/Phoenix github repo?

Thanks,

···

On Friday, May 8, 2015 at 5:29:23 PM UTC-7, Douglas Thor wrote:

Until either SIP adds hash or another solution is found, here’s a workaround:

In …\site-packages\wx\core.py, add the following code around line 557, where the rest of the Colour class is defined:

def Colour___hash_(self):

return hash(tuple(self.Get()))

Colour.hash = Colour___hash_

del Colour___hash_

I know that the top of core.py says to not edit the file, but I couldn’t help myself :-D.

I’ve done a tiny bit of testing, only verifying that

  1. hash(wx.Colour(1,1,1,255)) does not raise a TypeError
  2. hash(wx.Colour(1,1,1,255)) == hash((1,1,1,255))
  3. My project actually runs.

Robin, making this change won’t cause the world to end, will it? I know that it will get overwritten the next time SIP generates core.py, but that I can live with.

Thanks,

On Wednesday, March 11, 2015 at 6:30:11 PM UTC-7, Robin Dunn wrote:

From the docs for both Python2 and Python3:

“”"

An object is hashable if it has a hash value which never changes during
its lifetime (it needs a hash() method), and can be compared to
other objects (it needs an eq() method). Hashable objects which
compare equal must have the same hash value.

Hashability makes an object usable as a dictionary key and a set member,
because these data structures use the hash value internally.

All of Python’s immutable built-in objects are hashable, while non
mutable containers (such as lists or dictionaries) are not. Objects
which are instances of user-defined classes are hashable by default;
they all compare unequal (except with themselves), and their hash value
is derived from their id().

“”"

SIP doesn’t add a default hash in either build, it just relies on
the default Python behavior. In Python2 that seems to be a hash value
derived from the object’s ID. In other words, you could think of it
like this:

class HashableColour(wx.Colour):

   def __hash__(self):

       return do_something(id(self))

Based on the above text I would have expected the same from Python3, but
perhaps the definition of “user-defined classes” was changed to not
include extension types. Or maybe that doc needs updating.

Either way, I prefer the Python3 way because basing a default hash value
on the id() means that you must use the same instance of the key to
fetch the value again, you can’t simply use a new key with the same
value. For example, in Python2:

c1 = wx.Colour(1,2,3)

c2 = wx.Colour(1,2,3)

d = {c1: ‘one’, c2: ‘two’}

d[c1]

‘one’

d[c2]

‘two’

d[wx.Colour(1,2,3)]

Traceback (most recent call last):

File "<input>", line 1, in <module>

KeyError: wx.Colour(1, 2, 3, 255)

In Python3 Python makes you actually think about what makes sense for
your class’s hash values, it doesn’t just give you some mindless
substandard thing for free. For wx.Colour I might choose something like:

class HashableColour(wx.Colour):

   def __hash__(self):

       return self.GetRGBA()

or maybe even:

       return hash(self.Get()) # the hash of the tuple of color

components

But I suppose the real question is whether we should be adding our own
hash methods to at least some of the wrapped wxPython classes. I’ll
give that some thought.


Robin Dunn

Software Craftsman

http://wxPython.org

Just in case anyone finds this in the future:
This has been fixed in PR #277 by the GetIM() (GetImmutable) method.

···

On Tuesday, March 22, 2016 at 10:04:27 AM UTC-7, Douglas Thor wrote:

Has there been any more investigation into this? Should I add it as an issue to the wxWidgets/Phoenix github repo?

Thanks,

On Friday, May 8, 2015 at 5:29:23 PM UTC-7, Douglas Thor wrote:

Until either SIP adds hash or another solution is found, here’s a workaround:

In …\site-packages\wx\core.py, add the following code around line 557, where the rest of the Colour class is defined:

def Colour___hash_(self):

return hash(tuple(self.Get()))

Colour.hash = Colour___hash_

del Colour___hash_

I know that the top of core.py says to not edit the file, but I couldn’t help myself :-D.

I’ve done a tiny bit of testing, only verifying that

  1. hash(wx.Colour(1,1,1,255)) does not raise a TypeError
  2. hash(wx.Colour(1,1,1,255)) == hash((1,1,1,255))
  3. My project actually runs.

Robin, making this change won’t cause the world to end, will it? I know that it will get overwritten the next time SIP generates core.py, but that I can live with.

Thanks,

On Wednesday, March 11, 2015 at 6:30:11 PM UTC-7, Robin Dunn wrote:

From the docs for both Python2 and Python3:

“”"

An object is hashable if it has a hash value which never changes during
its lifetime (it needs a hash() method), and can be compared to
other objects (it needs an eq() method). Hashable objects which
compare equal must have the same hash value.

Hashability makes an object usable as a dictionary key and a set member,
because these data structures use the hash value internally.

All of Python’s immutable built-in objects are hashable, while non
mutable containers (such as lists or dictionaries) are not. Objects
which are instances of user-defined classes are hashable by default;
they all compare unequal (except with themselves), and their hash value
is derived from their id().

“”"

SIP doesn’t add a default hash in either build, it just relies on
the default Python behavior. In Python2 that seems to be a hash value
derived from the object’s ID. In other words, you could think of it
like this:

class HashableColour(wx.Colour):

   def __hash__(self):

       return do_something(id(self))

Based on the above text I would have expected the same from Python3, but
perhaps the definition of “user-defined classes” was changed to not
include extension types. Or maybe that doc needs updating.

Either way, I prefer the Python3 way because basing a default hash value
on the id() means that you must use the same instance of the key to
fetch the value again, you can’t simply use a new key with the same
value. For example, in Python2:

c1 = wx.Colour(1,2,3)

c2 = wx.Colour(1,2,3)

d = {c1: ‘one’, c2: ‘two’}

d[c1]

‘one’

d[c2]

‘two’

d[wx.Colour(1,2,3)]

Traceback (most recent call last):

File "<input>", line 1, in <module>

KeyError: wx.Colour(1, 2, 3, 255)

In Python3 Python makes you actually think about what makes sense for
your class’s hash values, it doesn’t just give you some mindless
substandard thing for free. For wx.Colour I might choose something like:

class HashableColour(wx.Colour):

   def __hash__(self):

       return self.GetRGBA()

or maybe even:

       return hash(self.Get()) # the hash of the tuple of color

components

But I suppose the real question is whether we should be adding our own
hash methods to at least some of the wrapped wxPython classes. I’ll
give that some thought.


Robin Dunn

Software Craftsman

http://wxPython.org