Skip to content
Merged
Changes from 1 commit
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
eb4a407
bpo-34788 Add tests for ipv6 scope_id support
May 31, 2019
8a5c94f
bpo-34788 Add support for scoped IPv6 addresses
May 31, 2019
a841e9a
bpo-34788 Add documentation for scope id support
May 31, 2019
9a3f7ec
bpo-34788 Refactor address split and validation
May 31, 2019
a17538a
bpo-34788 Convert scope_id to property
Jun 3, 2019
6da269e
bpo-34788 Update documentation
Jun 3, 2019
35750de
bpo-34788 Remove redundant check
Jun 3, 2019
681cfa5
bpo-34788 Fix newline
Jun 3, 2019
fb99b6b
bpo-34788 Documentation fix
Jun 6, 2019
d8bb674
📜🤖 Added by blurb_it.
blurb-it[bot] Jul 17, 2019
ccd83ed
bpo-34788 Update NEWS
opavlyuk Jul 17, 2019
dc540e9
bpo-34788 Fix NEWS filename
opavlyuk Jul 17, 2019
c03d4a5
bpo-34788 Add sample IPv6 to ignore list
Jul 17, 2019
c12e533
bpo-34788 Add sample IPv6 to ignore list
Jul 17, 2019
033f031
bpo-34788 Update Misc/NEWS.d
opavlyuk Jul 18, 2019
27c5cf4
bpo-34788 Refactor condition
opavlyuk Jul 18, 2019
3b495c0
bpo-34788 Fix excessive property call
opavlyuk Jul 18, 2019
1de1ae6
Merge branch 'master' into fix-issue-34788
opavlyuk Aug 9, 2019
b0a4fe9
bpo-34788 Mention RFC 4007 in the docstring
opavlyuk Aug 12, 2019
ac19fad
bpo-34788 Remove unnecessary comma
opavlyuk Aug 12, 2019
25d3ac7
bpo-34788 Add scope_id check
opavlyuk Aug 12, 2019
66db707
bpo-34788 Add tests for invalid scope id
opavlyuk Aug 12, 2019
064e019
bpo-34788 Improve scoped IPv6 samples
Aug 14, 2019
ef91d9c
bpo-34788 Remove trailing whitespaces
Aug 14, 2019
d0eb91b
bpo-34788 Improve scope_id documentation
Aug 14, 2019
0735d3f
bpo-34788 Fix susp-ignored
Aug 14, 2019
fb47e34
bpo-34788 Improve IPv6Address documentation
Aug 14, 2019
b8dffe5
bpo-34788 Move self._scope_id initialization under if statements
Sep 16, 2019
fc2425f
bpo-34788 Remove newline
Sep 16, 2019
f9d38c0
bpo-34788 Improve documentation
Sep 16, 2019
d8972af
bpo-34788 Consider scope id while comparing IPv6 addresses
Nov 26, 2019
0b37c30
bpo-34788 Update susp-ignored.csv
Nov 26, 2019
41ce543
bpo-34788 Update documentation
Nov 26, 2019
7f3fae8
bpo-34788 Update susp-ignored
Nov 26, 2019
6c5a845
bpo-34788 Update susp-ignored
Nov 26, 2019
e569cd5
bpo-34788 Update susp-ignored
Nov 26, 2019
3982d78
bpo-34788 Update susp-ignored
Nov 26, 2019
6b5198f
bpo-34788 Update susp-ignored
Nov 26, 2019
97a7707
bpo-34788 Update tests according to new comparison policy
Nov 27, 2019
6339732
bpo-34788 Update documentation
Nov 27, 2019
eff04f6
bpo-34788 Add 3.9 whatsnew record
Nov 27, 2019
06e66e9
Merge branch 'master' into fix-issue-34788
opavlyuk Nov 28, 2019
c25a327
bpo-34788 Improve __hash__()
Nov 28, 2019
84f4b3d
Merge branch 'fix-issue-34788' of github.com:opavlyuk/cpython into fi…
Nov 28, 2019
c6695bf
bpo-34788 Update whatsnew
Nov 28, 2019
d30ea41
Update Doc/whatsnew/3.9.rst
opavlyuk Nov 28, 2019
e971130
Update Doc/whatsnew/3.9.rst
opavlyuk Nov 28, 2019
22c310e
Update Doc/whatsnew/3.9.rst
opavlyuk Nov 29, 2019
5882d06
bpo-34788 Split conditional statement
opavlyuk Nov 29, 2019
2c6fd54
bpo-34788 Fix eq
Nov 29, 2019
0ec1572
bpo-34788 Unify docstring styles
Nov 29, 2019
5530a79
bpo-34788 Remove unnecessary arg
Nov 29, 2019
2f7de77
bpo-34788 Improve comparison
Dec 2, 2019
ef42a2d
bpo-34788 Fix typo
Dec 2, 2019
641bc6b
bpo-34788 Unify scope id naming
Dec 3, 2019
34728d2
bpo-34788 Unify naming
Dec 3, 2019
af90771
Update Doc/whatsnew/3.9.rst
opavlyuk Dec 11, 2019
e041c2e
Merge branch 'master' into fix-issue-34788
opavlyuk Jan 14, 2020
0e250bf
Merge remote-tracking branch 'origin/master' into fix-issue-34788
Feb 14, 2020
2c4d3d5
Cleanup Whatsnew
opavlyuk Feb 26, 2020
555f59f
Fix typo
opavlyuk Feb 26, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
bpo-34788 Fix eq
  • Loading branch information
opavliuk
opavliuk committed Nov 29, 2019
commit 2c6fd5451a1b838530f3a2aea54a31f08f2c3c9c
5 changes: 2 additions & 3 deletions Lib/ipaddress.py
Original file line number Diff line number Diff line change
Expand Up @@ -1842,7 +1842,7 @@ def _split_scope_id(ip_str):

See RFC 4007 for details.

Arg:
Args:
ip_str: A string, the IPv6 address.

Returns:
Expand Down Expand Up @@ -1918,12 +1918,11 @@ def __hash__(self):
return hash((self._ip, self._scope_id))

def __eq__(self, other):
address_equal = _BaseAddress.__eq__(self, other)
address_equal = super().__eq__(self, other)
if address_equal is NotImplemented:
return NotImplemented
if not address_equal:
return False
return address_equal
return self._scope_id == other._scope_id

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

What do we want to do in the scenario that the other object doesn't have a _scope_id attribute? An AttributeError would not be particularly clear from a user perspective when comparing the equality of two different IPv6 address objects.

Returning False seems to make the most sense to me, but only in the scenario that self actually has a _scope_id that isn't None (since we already established that the addresses are equal at this point).

Suggested change
return self._scope_id == other._scope_id
try:
return self._scope_id == other._scope_id
except AttributeError:
if self._scope_id is None:
return True
else:
return False

@opavlyuk opavlyuk Nov 29, 2019 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

As I see it, IPv6Address object shall always have a _scope_id attribute. In case it is created from IPv6 address without scope id, the attribute would be set to None.
Case with absent _scope_id attribute could appear only if someone removes it explicitly.
May be, it would be ok to use public property scope_id in __eq__ method, and add try block to it?

    @property
    def scope_id(self):
        """Identifier of a particular zone of the address's scope.

        See RFC 4007 for details.

        Returns:
            A string identifying the zone of the address if specified, else None.

        """
        return getattr(self, '_scope_id', None)    

    def __eq__(self, other):
        address_equal = super().__eq__(other)
        if address_equal is NotImplemented:
            return NotImplemented
        if not address_equal:
            return False
        return self.scope_id == other.scope_id

@aeros aeros Nov 29, 2019 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@opavlyuk

As I see it, IPv6Address object shall always have a _scope_id attribute.
Case with absent _scope_id attribute could appear only if someone removes it explicitly.

If the object was instantiated from the version of the IPv6Address class in this PR (where _scope_id was just added), then yes. But if it's an object instantiated from an older version of the class or some other custom implementation, then _scope_id might not be present.

IMO, it seems worthwhile to provide some basic amount of compatibility with objects that don't have a _scope_id attribute, considering it is just being added now.

    @property
    def scope_id(self):
        return getattr(self, '_scope_id', None)    

    def __eq__(self, other):
        address_equal = super().__eq__(other)
        if address_equal is NotImplemented:
            return NotImplemented
        if not address_equal:
            return False
        return self.scope_id == other.scope_id

The above would only cover the situations in which the self object doesn't have a _scope_id, no? My concern is with the other object, as self is much more likely to have a _scope_id attribute in the first place. The above situation I was referring to was when other is an object that wasn't instantiated from this version of IPv6Address or is using some other implementation.

Also, in this case, I find the EAFP (try-except) approach in my above suggestion to be more explicit than LBYL (if hasattr() or getattr() with default). Mostly because the situation in which the other object does not have a _scope_id is an exception and not just a general conditional to check for.

A try-except is generally better performance wise (when in the vast majority of cases, the exception does not occur) in comparison to using getattr() with a default every single time the property is accessed or if hasattr() every time __eq__() is used.

What do you think @asvetlov?

@opavlyuk opavlyuk Nov 29, 2019 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@aeros , now I understood, and it makes sense.
At the same time, following seems more clean for me:

    def __eq__(self, other):
        address_equal = super().__eq__(other)
        if address_equal is NotImplemented:
            return NotImplemented
        if not address_equal:
            return False
        return self.scope_id == getattr(other, _'scope_id', None)

So, we consider, if other object has no _scope_id attribute, then it is the same as it is None.
Having measured the execution time on Python 3.7.4 on my GNU\Linux machine, I saw that option with getattr seems to be slightly faster in case when attribute exists and 5x faster in case with non-existent attribute.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I like the last version

@aeros aeros Dec 1, 2019 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I have no objections to the last version either, self.scope_id == getattr(other, _'scope_id', None) should work fine. My main issue with the first one was using return getattr(self, '_scope_id', None) as the getter for _scope_id, but that's not an issue in the most recent version.

Edit: I still think the EAFP approach might be a bit more idiomatic in this case. However, if it's a significant performance difference, getattr with a default should be okay.

@aeros aeros Dec 1, 2019 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm curious as to how this would perform in comparison to the above two, I might test it out later when I get the chance:

try:
    other_scope_id = other._scope_id
except AttributeError:
    other_scope_id = None
return self._scope_id == other_scope_id

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@aeros I've pushed my version, but lets make the final decision after your benchmark.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@opavlyuk

I've pushed my version, but lets make the final decision after your benchmark.

Sounds good to me. I should have time to evaluate it later, I've been a bit occupied recently with studying for finals and working on other projects this last week.

If it gets to the point where everything else is finished and this is the last remaining step, feel free to move ahead with the PR. At the end of the day, I think either version would be acceptable.


@property
Expand Down