Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Keys remember whether to use deprecated syntax #6

Merged
merged 13 commits into from
Jun 17, 2014

Conversation

sarina
Copy link
Contributor

@sarina sarina commented May 30, 2014

and the deprecated key handler registers itself with the base key type
LMS-2752

@sarina
Copy link
Contributor Author

sarina commented May 30, 2014

Heads up Power Team LMS @cpennington @flowerhack @dianakhuang -- this is the start to https://edx-wiki.atlassian.net/browse/LMS-2752

@@ -172,7 +175,7 @@ def replace(self, **kwargs):
return type(self)(**existing_values)

def __setattr__(self, name, value):
if getattr(self, '_initialized', False):
if name != 'deprecated' and getattr(self, '_initialized', False):
Copy link
Contributor

Choose a reason for hiding this comment

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

Why are we allowing deprecated to flip after initialization?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@cpennington
Copy link
Contributor

Globally, deprecated should be passed in the constructor, so that keys continue to act like immutable values, and self.deprecated should be used preferentially to getattr and setattr.

@sarina
Copy link
Contributor Author

sarina commented Jun 2, 2014

Pulling in @dmitchell since these changes were originally authored by him. Don, can you look over this and help me respond to comments made?

@sarina
Copy link
Contributor Author

sarina commented Jun 2, 2014

wrt CourseKey.deprecated_fallback = SlashSeparatedCourseKey in opaque_keys/edx/locations.py

I worry a little about this silently breaking depending import order (if some other class has already set CourseKey.deprecated_fallback, this line will overwrite it). Perhaps a class method is in order on the base class to handle that?

@cpennington I'm not sure I'm following what you intend here - what do you want to handle, and how?

@sarina
Copy link
Contributor Author

sarina commented Jun 2, 2014

Heads up that I force-pushed the branch.

These changes are now done:

  • Globally, deprecated should be passed in the constructor, so that keys continue to act like immutable values.
  • Base class enforces that only one deprecated_fallback can be set.

def test_hash_equality(self):
self.assertEquals(hash(DummyKey.from_string('hex:0x10')), hash(DummyKey.from_string('hex:0x10')))
# TODO: These two hash to the same value - is this a feature or a bug?
#self.assertNotEquals(hash(DummyKey.from_string('hex:0x10')), hash(DummyKey.from_string('base10:16')))
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Was trying to increase test coverage and found that hashing two different keys w/ the same values leads to the same hash. Wanted to know if we consider this a feature (in which case will change this to an assertEquals) or a bug (in which case I'll change how __hash__ is implemented).

Copy link
Contributor

Choose a reason for hiding this comment

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

Two key objects of the same type with the same values should hash to the same value, so that they can be used in dictionaries as keys in a useful way. Things that compare equal should also hash equal.

Two keys with different implementation types shouldn't hash to the same value, though.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@cpennington thanks, that's what I thought. Right now,

hash(DummyKey.from_string('hex:0x10')) == hash(DummyKey.from_string('base10:16'))

while

DummyKey.from_string('hex:0x10') != DummyKey.from_string('base10:16')

I think the best way to fix the hash method may be to incorporate the CANONICAL_NAMESPACE into the __hash__ method, as opposed to just using hash(self._key). Thoughts?

Copy link
Contributor

Choose a reason for hiding this comment

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

Another option would be to include type(self) in self._key, and remove the check from __eq__.

@sarina
Copy link
Contributor Author

sarina commented Jun 3, 2014

@cpennington @dmitchell @flowerhack @dianakhuang I think this is ready for review

@@ -184,6 +189,9 @@ def __unicode__(self):
"""
Serialize this :class:`OpaqueKey`, in the form ``<CANONICAL_NAMESPACE>:<value of _to_string>``.
"""
if self.deprecated:
# no namespace on deprecated
return self._to_string()
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm surprised we're using _to_string rather than to_deprecated_string. By using _to_string we're requiring the non-deprecated syntax to == the deprecated syntax sans the namespace giving us no flexibility to change serialization syntax. (or does _to_string also look for the deprecated flag?)

Copy link
Contributor

Choose a reason for hiding this comment

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

Ok, I see _to_string() does consult the deprecated flag; so, this is probably ok, but it's not quite parallel to from_string().

@dmitchell
Copy link
Contributor

Who's removing locators.py, locations.py, & opaque_keys from edx-platform and moving test-locators, test_location to this module?

@cpennington
Copy link
Contributor

and the deprecated key handler registers itself with the base key type
LMS-2752
@@ -130,7 +164,7 @@ def _separate_namespace(cls, serialized):
MissingNamespace: Raised when no namespace can be
extracted from `serialized`.
"""
namespace, _, rest = serialized.partition(cls.NAMESPACE_SEPARATOR)
namespace, __, rest = serialized.partition(cls.NAMESPACE_SEPARATOR)

# If ':' isn't found in the string, then the source string
# is returned as the first result (i.e. namespace)
Copy link
Contributor

Choose a reason for hiding this comment

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

Could we add a note here that we'd expect this to happen either in the case of (1) a totally borked string, or (2) a deprecated string? Otherwise it's a little confusing, reading through, that deprecated strings would be considered invalid

@dianakhuang
Copy link
Contributor

I think that's my only other comment. Everything else is 👍

@coveralls
Copy link

Coverage Status

Coverage increased (+8.17%) when pulling e028b5e on sarina/handle-old-new-keys-transparently into cf29dfe on master.

@flowerhack
Copy link
Contributor

It wasn't introduced in this PR, but does anyone know the deal with the is_fully_specified function in locator.py? It's a fine function to have, but we don't seem to set a useful value for is_fully_specified anywhere, thus rendering it sort of pointless

@flowerhack
Copy link
Contributor

besides that, ⛵

@coveralls
Copy link

Coverage Status

Coverage increased (+8.17%) when pulling baf0eb5 on sarina/handle-old-new-keys-transparently into cf29dfe on master.

@@ -232,7 +285,7 @@ def __lt__(self, other):
return self._key < other._key # pylint: disable=protected-access

def __hash__(self):
return hash(self._key)
return hash(self._key) + hash(type(self))
Copy link
Contributor

Choose a reason for hiding this comment

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

I think that rather than using + to add the hashes here, we should include type(self) in _key.

Copy link
Contributor

Choose a reason for hiding this comment

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

Probably at the end of the tuple, so that it's just a tiebreaker for comparisons.

@cpennington
Copy link
Contributor

👍 after the hash fix.

@coveralls
Copy link

Coverage Status

Coverage increased (+8.36%) when pulling 7800777 on sarina/handle-old-new-keys-transparently into cf29dfe on master.

@coveralls
Copy link

Coverage Status

Coverage increased (+8.36%) when pulling 69b154c on sarina/handle-old-new-keys-transparently into cf29dfe on master.

sarina added a commit that referenced this pull request Jun 17, 2014
Keys remember whether to use deprecated syntax
@sarina sarina merged commit fc67e5e into master Jun 17, 2014
@sarina sarina deleted the sarina/handle-old-new-keys-transparently branch June 17, 2014 16:09
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.

6 participants