Skip to content

Conversation

Juko
Copy link

@Juko Juko commented Oct 30, 2019

Actually not as crazy as expected. I'm still going to be pushing new tests to this branch so that we can confirm things a bit better...

Much easier to look at commit by commit. Click the commits tab, then the first commit, then use n to go to the next commit automagically

Copy link

@boronia boronia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved with comments 😁

Copy link

@hedgepigdaniel hedgepigdaniel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Much easier to look at commit by commit. Click the commits tab, then the first commit, then use n to go to the next commit automagically

👍

looks good

setup.py Outdated
long_description='Mongorm',
platforms=['any'],
install_requires=['pymongo >= 2.4.2', 'pysignals'],
install_requires=['pymongo >= 2.4.2', 'pysignals', 'iso8601'],

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if actually pymongo 2.9+ is required (since we updated to support mongo 3.0)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a few comments about using pymongo 3.0+ specific things already it seems (as in breaking changes that mean a version < 3.0 would break). I just think everyones been blissfully unaware because this setup.py pretty much just installs the latest version of pymongo (3.9.0 at the current time). So yeah, I think I'll update this to be a hard >= 3.0.0 requirement


# For python 2/3 compatibility
def __bool__( self ):
return True

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

objects are not truthy by default?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

Neither in python 2 or 3 are empty objects considered truthy. But because the file overwrites __getattr__ (so that it returns None instead of an AttributeError), the way the future object class checks truthiness makes it error because it believes the attribute (the __bool__ function) exists on itself, but when it calls it, it dies. But the old behaviour of the library is that an empty Document is truthy, so this just keeps that compatibility

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

damn

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

Neither in python 2 or 3 are empty objects considered truthy. But because the file overwrites __getattr__ (so that it returns None instead of an AttributeError), the way the future object class checks truthiness makes it error because it believes the attribute (the __bool__ function) exists on itself, but when it calls it, it dies. But the old behaviour of the library is that an empty Document is truthy, so this just keeps that compatibility

That sounds like a bug; __getattr__ should raise an AttributeError for things that aren't fields. Actually all objects in Python are considered True unless they specify a __bool__ method which returns False or __len__ method which returns 0. Also if you decide to go ahead with this workaround you should probably also specify a __nonzero__ method for Python 2.x (__bool__ was added in Python 3.x)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I don't like that __getattr__ doesn't raise an AttributeError either ahaha. But it's been that way in the library since it's inception it seems, so I don't want to break functionality as core as that in OpenLearning which expects it. After a better inspection across the whole OpenLearning codebase to check it's uses, I'd be more comfortable to recommend that we make that change!

Actually all objects in Python are considered True

Yeap, whoops. Working on JS my brain confused object and dict. Yeap, it's not that object is falsy, it's that the future version of object that we're importing (aka from builtins import object) first checks if the __bool__ method is available and uses that to determine it's truthiness (because it defines the __nonzero__ method) that python 2 uses. But because of the above AttributeError not being raised, it tries to call that method, and dies because it doesn't actually exist to be called. So this was just there to short circuit it into an acceptable response that we want!

except pymongo.errors.OperationFailure as err:
message = 'Could not save document (%s)'
if u'duplicate key' in unicode(err):
if u'duplicate key' in str(err):
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Of all the things Python 2.x. did horribly, strings are definitely at the top of the list! :P I would highly suggest including the six library and using its six.text_type here to save yourself further headaches.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ended up using future over six for this migration (just because it seems to better transform the code into something that's py3 compatible, and doesn't require you to do almost two rewrites (one that's six compatible, and one that's solely py3 compatible in the future)). And I used the future library with the from builtins import str at the top :D

def toMongo( self, document, forUpdate=False, modifier=None ):
newSearch = {}
for (name, value) in self.query.iteritems( ):
for (name, value) in self.query.items( ):
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will incur a performance hit on Python 2.x. You might want to consider using six.iteritems instead if you want to avoid it.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the performance impact will be small - most of the larger arrays that were in our mongo documents have been factored out into their own collections (for reasons of mongo/memcached performance).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I'm surprised it makes that one of the default changes as part of the auto upgrade tool actually... I would have expected it to have gone with option 3 from their site: https://python-future.org/compatible_idioms.html#iterating-through-dict-keys-values-items

# Python 2 and 3: option 3
from future.utils import iteritems
# or
from six import iteritems

for (key, value) in iteritems(heights):
    ...

I guess they try default into making it as python3 compatible as possible from the get go, to hint that you should really be moving to py3 sooner rather than later :P

There's a few places that have this change (aka, the Convert iterX methods to use py3 syntax commit). Do you think it's worth updating them to use that import?

@hedgepigdaniel
Copy link

Also hi @johnnyg, nice to hear from you! Hope life is good.

@Juko
Copy link
Author

Juko commented Nov 19, 2019

And thanks for the review @johnnyg 😄 I appreciate you taking the time out to do it!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants