-
Notifications
You must be signed in to change notification settings - Fork 41
Support ipaddress objects in "get" method #48
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
Conversation
|
For this to be merged, the C extension would have to be updated to support it and there would need to be tests. |
|
I'm fine with updating the C extension and tests if the feature is welcomed. I'll follow-up once I've done that. |
|
As long as the feature works on all supported Python versions with both the C extension and pure Python implementation and doesn't affect performance, I'd be happy to include it. |
The "test_too_many_get_args" methods was not actually testing what the author intended because unittest.TestCase.assertRaises method accepts multiple function arguments as positional parameters, not tuples as used in the code, so the assertRaises invocation has been updated to correct this mistake.
1 similar comment
|
The code is ready to be reviewed. This is my first time working on Python C extension code, and I may have not handled the reference counting correctly. I originally had more Py_DECREF calls than I currently do. I initially thought they were correct, but removing them resolved some segfaults I observed running the test suite. |
This reverts commit b190d97 because it breaks continuous integration.
The changes the PY_MAJOR_VERSION checks to match the expression used by the maintainers.
|
@oschwald Any idea when someone will be able to review this PR? |
|
@ericpruitt, sorry for the delayed response. I am hoping to get to this soon. The C code will need some closer review. I'll add a couple of comments now however based on a cursory review. |
oschwald
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some initial comments.
| return NULL; | ||
| } | ||
|
|
||
| PyObject* ipaddress_IPv4Address = PyDict_GetItemString( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't we be using PyObject_IsInstance or something here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm calling PyObject_IsInstance below. PyObject_IsInstance requires a PyObject as its second argument, so I don't understand how I'm supposed to use PyObject_IsInstance without getting the IPv4Address class first.
| if (!is_ipaddress_object) { | ||
| PyErr_SetString(PyExc_TypeError, "IP address must be a string," | ||
| " ipaddress.IPv4Address or ipaddress.IPv6Address object"); | ||
| } else if ((str = PyObject_Str(object)) != NULL) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is a little bit weird that we are doing all of these gymnastics and creating these heavy Python string and byte objects. I wonder if we could just get the packed address. Ideally, we wouldn't even bother converting that back to a *char, but it probably doesn't hurt too much to do that conversion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only simpler method I could think of would be casting the argument to a str, but then you lose out on type safety.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we could just get the packed address
That is exposed:
~$ python
Python 3.5.3 (default, Sep 27 2018, 17:25:39)
[GCC 6.3.0 20170516] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import ipaddress
>>> i = ipaddress.ip_address("127.0.0.1")
>>> i.packed
b'\x7f\x00\x00\x01'
| } else if ((str = PyObject_Str(object)) != NULL) { | ||
| #if PY_MAJOR_VERSION >= 3 | ||
| bytes = PyUnicode_AsEncodedString(str, "UTF-8", "strict"); | ||
| ip_address = PyBytes_AS_STRING(bytes); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this code is kept, I think you can just decrement the reference count on bytes here and get rid of the ones below. There are also leaks from early returns as it is done now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does Python guarantee that garbage collection will not immediately take place when the reference counter drops to 0? The reason I'm i'm decrementing the reference count in more than one place is because the ip_address pointer points into bytes, and if decrementing the reference counter frees that memory, it introduces a use-after-free bug.
| char *ip_address = NULL; | ||
| PyObject *object = NULL; | ||
| #if PY_MAJOR_VERSION >= 3 | ||
| PyObject *bytes = NULL; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to declare the variables at the top of the function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where do you propose I declare it? I don't think I can declare it inside the "if" condition since that will cause "bytes" to be scoped to that block which will mean it can't be used in the code further down.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also can't declare object inside the "if" block because I need to use object in the condition expression.
|
I had some more time to think about this, and I think it would be cleaner using Based on my testing, this is also much faster, 30% or so. I assume a lot of that is due to just allowing any object with a |
|
Alright, I'll close out this pull request. You probably want to at least pull in the unit test change I made to fix the arguments for assertRaises. EDIT: I overlooked that this was branched from my pull request, so that change is already included. |
|
#50 adds support for ipaddress objects and will be in the next release. |
Thanks! |
This allows the user to provide an IP address in the form of an ipaddress.IPv4Address or ipaddress.IPv6Address as an argument for the "get" method.