Maya 2022 PyMel MeshVertex not hashable

Switching to Maya 2022 and Python 3.7 has been mostly smooth sailing at our company so far. We have however found one issue that we would like some more insight in to, so I’m reaching out here!

It seems that PyMel MeshVertex objects (and edges, faces and other Components) are no longer hashable and can’t be used as dictionary keys.

This works perfectly in Maya 2020:

import pymel.core as pm
cube = pm.polyCube()[0]
print(cube.vtx[2].__hash__())

In Maya 2022 it gives an error because

cube.vtx[2].__hash__ == None

Is this something that should be expected from Python 3.7? Is it a bug in PyMel? Do anyone know?

Cheers!

Could be a bug but Py3 changed hashing to make sure it was only on immutable objects.
Py2 erroneously allowed some mutable objects to be hashed ( QTreeWidgetItems for example ), which was problematic, so this might be due to it being cleaned up.
Normally you can subclass and add your own hash or monkey patch in a hash method onto the class but I doubt it will be possible in this instance.

I’d open an issue on their github, if its on purpose they’ll let you know. If its not on purpose, a fix can probably be worked out pretty easily.

@Munkybutt Ah, thanks for the info. We’ll just rewrite our code to use something else as keys in this case. It would be good to know if this is an intentional change or not though, so I opened an issue at GitHub like @bob.w suggested.

Thank you for your answers!

1 Like

All that said, I’m kinda curious why you’re hashing floating point values?
Floats are notoriously fuzzy, so unless you’re always using the exact same object as the key every time (at which point you should have the vertex number and should be using that instead), you’ll probably run into reliability problems at some point 'cause of this crap: assert 0.1 + 0.2 == 0.3
Or at least that’s what happened to me.

Hashing a float is fine, they are immutable and as such a stable hash can be computed, otherwise we couldn’t use them in sets or dictionaries.

Float comparisons are a very different beast, and yes something like math.is_close (py 3.5+) should be used to compare floats.

1 Like

And that’s why I asked why they’re hashing floats. When I see someone hashing floats, I get worried that they’re using it in place of an equality comparison when they should be using is_close because that’s a mistake I made.

2 Likes

All that said, I’m kinda curious why you’re hashing floating point values?
Floats are notoriously fuzzy, so unless you’re always using the exact same object as the key every time (at which point you should have the vertex number and should be using that instead), you’ll probably run into reliability problems at some point 'cause of this crap: assert 0.1 + 0.2 == 0.3
Or at least that’s what happened to me.

I’m afraid I don’t entirely understand how that is related to our issue?
We’re hashing MeshVertex objects, not floats?

Oh snap. I made the assumption that .vtx[] in pymel was getting vertices (ie: tuple of 3 floats), not some equivalent of api iterators. Yeah, those should definitely be hashable. My bad.

1 Like

No worries! Thank you for wanting to help preventing us from making float comparison mistakes :slight_smile:

Ok. To make up for my confusion, I took a deeper look at this.

In Maya2022 Py2 it still works, and I determined the .__hash__ method comes from
<class 'pymel.util.utilitytypes.ProxyUnicode'> inherited class.
So I’m hypothesizing that the object acts kind of like a unicode string. This led me to test if hash(vtx) == hash(str(vtx)), and that’s exactly the case. So hypothesis confirmed.

In Py3, the MeshVertex class eventually inherits from
<class 'pymel.util.utilitytypes.proxyClass.<locals>.Proxy'> instead of the ProxyUnicode

Looking at the implementation of pymel.util.utilitytypes.ProxyUnicode, there’s this comment:

# Note - for backwards compatibility reasons, PyNodes still inherit from
# ProxyUnicode, even though we are now discouraging their use 'like strings',
# and ProxyUnicode itself has now had so many methods removed from it that
# it's no longer really a good proxy for unicode.

So they’re discouraging that usage anyway. Unfortunately, I can’t find where it really diverges between Py2 and Py3. The code is essentially the same for both versions, so I would chalk it up to the difference between the two.

If worse comes to worse, you could probably monkey-patch some classes so that the .__hash__ method returns the hash of the string of the object, and that would give you equivalent behavior… but that’s one of those “last resort” kind of things that I wouldn’t actually recommend.