-
-
Notifications
You must be signed in to change notification settings - Fork 30.9k
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
Allow struct.pack to handle objects with an __index__ method. #52547
Comments
In Python 2.7, struct.pack with an integer format can handle non-integers that provide an __int__ method (although this *does* raise a DeprecationWarning). Python 2.7a4+ (trunk:79659:79661, Apr 3 2010, 11:28:19)
[GCC 4.2.1 (Apple Inc. build 5646) (dot 1)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from struct import pack
[35194 refs]
>>> pack('L', 3.1415)
'\x03\x00\x00\x00\x00\x00\x00\x00'
[35210 refs] This behaviour isn't particularly desirable for floats or Decimal instances, but it's useful for integer-like objects. In Python 3.x, there's no provision for handling integer-like objects than aren't actually integers. I propose that in 3.x, struct.pack should try to convert any non-integer to an integer by using its __index__ method, before packing. |
Here's a patch for trunk. It combines the docs and tests from Meador Inge's patch in bpo-1530559 with a C-level change to get_pylong in Modules/struct.c. |
Adding Meador Inge to nosy. |
That patch was a bit hasty in many respects; here's a better one. For 2.7, the scheme is as follows: when packing a non-integer with an integer format: (1) First __index__ is tried |
Committed this patch to trunk in r79674. Will forward port to py3k. |
I may be missing something subtle, but how can 'PyNumber_Index(v) != NULL' *and* '!PyInt_Check(v) && !PyLong_Check(v)' both be satisfied in the 'get_pylong' mods? It seems to me that 'PyNumber_Index' only returns non-NULL when the object being returned is an 'int' or 'long'. Attached a patch with the extra check removed and a few more test cases. |
Probably both those conditions can't be satisfied; I'm wasn't sure what happened if something's __index__ method returned something other than an int or long. But now I bother to look at the source (in Objects/abstract.c) I see that there *is* already an explicit check for the result of nb_index being int or long (with TypeError being raised if the result isn't one of those). Mea culpa. I'll remove those lines (though I may leave an assert, just to be on the safe side). The 2.x behaviour isn't ideal: I'd prefer to just stop if the __index__ method is present and raises TypeError, rather than going on to check __int__ in that case. But that presents problems with old-style classes, where PyIndex_Check is true even when no __index__ method is explicitly defined. Thanks for the extra tests! |
Committed (with some tabs in test_struct.py changed to spaces) to trunk in r79745. |
Merged to py3k in r79746. Meador, does this all look okay, now? |
Looks good to me. |
Thanks. |
Why isn't this implemented to work with __int__ as well? |
Because (arguably) we don't want to be able to pack non-integral floats (or Decimal instances, or ...) using integer formats: >>> import struct
[56090 refs]
>>> struct.pack('L', 2.3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
struct.error: required argument is not an integer
[56125 refs] |
Thanks Mark for clearing that up. I found this link to be useful in explaining the purpose of __index__: http://docs.python.org/release/2.5.1/whatsnew/pep-357.html I think the choice of allowing only __index__ was the right choice. |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: