Implementation of equality for Settings class#464
Implementation of equality for Settings class#464bhavishyagopesh wants to merge 66 commits intopython-hyper:masterfrom
Conversation
Lukasa
left a comment
There was a problem hiding this comment.
Ok, good start! There are some stylistic problems here, but we're well on our way. The biggest problems are going to be on the testing front.
I've left more detailed notes below.
h2/settings.py
Outdated
|
|
||
|
|
||
| def __eq__(self,other): | ||
| return self._settings==other._settings |
There was a problem hiding this comment.
Style nit: we need a space either side of the operator.
I should also note that this does not behave correctly when compared with objects that are not Settings objects: specifically, it'll probably throw AttributeErrors.
To deal with this, you need to confirm that the other object is an instance of Settings before doing the comparison. If it's not, you want to indicate that we don't know how to compare them by returning NotImplemented.
h2/settings.py
Outdated
| return len(self._settings) | ||
|
|
||
|
|
||
| def __eq__(self,other): |
h2/settings.py
Outdated
| def __eq__(self,other): | ||
| return self._settings==other._settings | ||
|
|
||
| def __ne__(self,other): |
h2/settings.py
Outdated
| return self._settings==other._settings | ||
|
|
||
| def __ne__(self,other): | ||
| return not self._settings==other._settings |
There was a problem hiding this comment.
So it's usually best to implement this in terms of equality unless there's a good reason not to, so I'd write this as return not self == other.
There was a problem hiding this comment.
Maybe even skip the middle-man and call return not self.__eq__(other) :)
There was a problem hiding this comment.
Actually, you don't want to do that. ;)
The reason is that the rich equality tools in Python have some complex logic, and we don't want to privilege our own execution order. It's better to let Python work it out then to say "you must look at this specific method first".
h2/settings.py
Outdated
|
|
||
|
|
||
|
|
||
|
|
There was a problem hiding this comment.
We don't want to add all this whitespace.
test/test_h2_upgrade.py
Outdated
| assert ( | ||
| client.local_settings._settings == server.remote_settings._settings | ||
| ) | ||
| ) No newline at end of file |
There was a problem hiding this comment.
You've made some changes in here that are only whitespace: please remove them.
test/test_settings.py
Outdated
| assert s.max_header_list_size is None | ||
|
|
||
| with pytest.raises(h2.exceptions.InvalidSettingsValueError) as e: | ||
| with pytest.raises(h2.exceptions.InvalidSettingsValueError) as e: |
There was a problem hiding this comment.
Please remove the trailing whitespace from here.
test/test_settings.py
Outdated
| def notequal_objects_are_notequal(s1,s2): | ||
| s1.acknowledge() | ||
| s2.acknowledge() | ||
| assert s1._settings!=s2._settings No newline at end of file |
There was a problem hiding this comment.
Ok, so these tests cannot possibly work right now for a number of reasons. Let's discuss why.
Firstly, and most importantly, where are s1 and s2 coming from? Nowhere else in the code knows how to provide them. If you look at tests higher up the file you'll see they create their own Settings objects: your tests need to do that as well.
Secondly, you need to think a bit harder about what these tests are actually testing. What you want to do is to confirm that the code you just wrote actually works. That means you want to test that two Settings entities that are the same are equal, and two that are different are not equal.
As an example, the simplest test that meaningfully tests something looks like this:
def test_newly_created_settings_are_equal(self):
s1 = h2.settings.Settings()
s2 = h2.settings.Settings()
assert s1 == s2Note as well some basic rules of Python code: specifically, note that these test methods are methods, and so must take a self parameter, and note as well that to have them count as a test they need to have names that begin withtest_.
There was a problem hiding this comment.
I have done most of the changes as you said but , tox is showing AssertionError with
"assert not s1 == s2"
or
"assert s1 != s2"
tox shows this error
assert <h2.settings.Settings object at 0x10a749410> != <h2.settings.Settings object at 0x10a749490>
| if isinstance(other, Settings): | ||
| return self._settings == other._settings | ||
| else: | ||
| return NotImplemented |
There was a problem hiding this comment.
If other isn't a Settings object we can pretty easily say we're not equal. Should return False in this case.
There was a problem hiding this comment.
Sorry @SethMichaelLarson, but again I disagree. =) Per the documentation, NotImplemented should be returned when we have no method of determining equality with respect to the other type. This is to allow other rich comparison methods to potentially be invoked that may know how to do an equality comparison.
The correct thing to do here is to return NotImplemented when we don't have matching types.
There was a problem hiding this comment.
@Lukasa Huh! TIL that NotImplemented exists. I never knew of this construct. I will keep this in mind! Thanks! :)
| if isinstance(other, Settings): | ||
| return self._settings != other._settings | ||
| else: | ||
| return NotImplemented |
h2/settings.py
Outdated
| @@ -254,12 +254,18 @@ def __len__(self): | |||
| return len(self._settings) | |||
|
|
|||
|
|
|||
There was a problem hiding this comment.
This whitespace should probably get removed. Only supposed to be 1 line between class methods.
|
@bhavishyagopesh Okay, my suggestions are fulfilled, now go through @Lukasa's change requests. |
alexwlchan
left a comment
There was a problem hiding this comment.
Two comments, and I’ll provide feedback on the tests once they’re a bit more fleshed out.
FWIW, this EqualityTestsMixin class from the pyOpenSSL tests has pretty good coverage of the sort of testing I’d expect to see of __eq__ and __ne__ methods: https://github.com/pyca/pyopenssl/blob/b748099426db3963c7b06d2e0aec29e12e016c07/tests/util.py#L35
| return False | ||
|
|
||
| def __ne__(self, other): | ||
| if isinstance(other, Settings): |
There was a problem hiding this comment.
I’d push for
return not self == otherunless there’s a good reason not to, as otherwise we’re duplicating logic from the __eq__ method.
There was a problem hiding this comment.
I’d go one step further and drop the isinstance() call – we’re still duplicating that. Simply:
def __ne__(self, other):
return not self == other
test/test_h2_upgrade.py
Outdated
| assert ( | ||
| client.local_settings._settings == server.remote_settings._settings | ||
| ) | ||
|
No newline at end of file |
There was a problem hiding this comment.
Please revert these unrelated whitespace changes.
test/test_h2_upgrade.py
Outdated
| assert ( | ||
| client.local_settings._settings == server.remote_settings._settings | ||
| ) | ||
| ) |
There was a problem hiding this comment.
We still have whitespace changes creeping into this file.
There was a problem hiding this comment.
There are still whitespace changes here that need removing.
h2/settings.py
Outdated
|
|
||
| def __eq__(self, other): | ||
| if isinstance(other, Settings): | ||
| return self == other |
There was a problem hiding this comment.
So, this doesn't work because it leads to a recursive definition. Specifically, it says self is equal to other if self equals other. That's tautologically accurate, but doesn't help Python to actually work out what equality looks like.
This should be changed to specifically compare the _settings properties of the two objects.
| return False | ||
|
|
||
| def __ne__(self, other): | ||
| if isinstance(other, Settings): |
|
@SethMichaelLarson I was asking for values of |
|
@bhavishyagopesh Look around the other tests within |
|
@SethMichaelLarson I gave some values to |
|
@Lukasa I guess, I have corrected linting errors, now what changes(additional tests to be added...) need to be made to cover line 257-260 and 263 in |
Lukasa
left a comment
There was a problem hiding this comment.
The reason you are missing coverage is because these tests aren't actually running, as the class name doesn't have the right form. Making that change should get your coverage up.
test/test_settings.py
Outdated
| s[h2.settings.MAX_HEADER_LIST_SIZE] | ||
|
|
||
|
|
||
| class test_settings_equality(object): |
There was a problem hiding this comment.
This class name should be TestSettingsEquality.
|
@Lukasa Comparing two lists return |
|
@SethMichaelLarson Comparing two lists return assertionerror with ne but not with eq ? |
|
@Lukasa If you could cite some resource for what's really happening?Why it isn't passing? |
|
Is anyone there who would be kind enough to tell the error. Please...... |
Lukasa
left a comment
There was a problem hiding this comment.
@bhavishyagopesh Please be patient. You aren't being ignored: the developers who can review your work are all busy people who have jobs and other parts of their lives they need to attend to, and cannot necessarily respond quickly to you. Remain patient, and trust that you aren't being ignored.
I've left explanatory feedback in the diff.
h2/settings.py
Outdated
| return NotImplemented | ||
|
|
||
| def __ne__(self, other): | ||
| return not self == other |
There was a problem hiding this comment.
Ok, this needs to be expanded a little bit. In particular, the if isinstance check needs to be repeated here so that we return NotImplemented if other is not a Settings object.
|
@Lukasa Sorry for being impatient(but you are certainly AWESOME =).) So what needs to be done next... |
|
Ok, so I'd call this "good enough" to get going with. I think I'd also like us to add some more complex testing based on Hypothesis, which is really perfect for testing this kind of thing. Would you like to tackle that too, or would you like a break? |
|
I think, I need to learn more about this project and python in general, so ,can this be merged first and I would love to continue doing whatever you suggest next but with a little more understanding of the project. |
|
Ok cool, so we just need to wait until @alexwlchan is happy. =) @alexwlchan whenever you're happy, go ahead and merge. |
|
Personally, I’d prefer to see the commits squashed/rebased before merging. 66 is a few too many for my tastes. (I’m happy to do the rebase and/or be overruled on this one.) |
|
@alexwlchan Can you do a "rebase and merge" when you go ahead? We don't need the 66 commits. |
|
Superseded by #465. |
|
Thanks so much @bhavishyagopesh! Per our one of the team policy, you'll find a GitHub invitation to the Hyper organisation in your inbox. |
I have defined
__eq__and__ne__for Settings class, and wrote tests for the same.