Skip to content

Commit 382d8a9

Browse files
committed
Merge branch 'int_to_from_bytes' into v0.12.x
2 parents 7c0a98f + aea1b7f commit 382d8a9

File tree

3 files changed

+330
-2
lines changed

3 files changed

+330
-2
lines changed

docs/whatsnew.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ What's new in version 0.12.5
1313
(issue #81). It still converts ``obj.next()`` method calls to
1414
``next(obj)`` correctly.
1515
- Add :ref:`compatible-idioms` from Ed Schofield's PyConAU 2014 talk.
16+
- Add ``int.to_bytes()`` and ``int.from_bytes()`` (issue #85)
1617
- Add ``future.utils.raise_from`` as an equivalent to Py3's ``raise ... from ...`` syntax (issue #86).
1718

1819

future/tests/test_int.py

Lines changed: 246 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@
66
print_function, unicode_literals)
77
from future.builtins import *
88
from future.tests.base import unittest, expectedFailurePY2
9-
from future.utils import PY26
9+
from future.utils import PY26, PY2, raise_from
1010

1111
import sys
1212
import random
13+
import array
1314

1415
try:
1516
import numpy as np
@@ -778,5 +779,249 @@ def test_unary_operators(self):
778779
self.assertEqual(-b, 3)
779780
self.assertTrue(isinstance(-a, int))
780781

782+
def test_to_bytes(self):
783+
def check(tests, byteorder, signed=False):
784+
for test, expected in tests.items():
785+
try:
786+
self.assertEqual(
787+
int(test).to_bytes(len(expected), byteorder, signed=signed),
788+
expected)
789+
except Exception as err:
790+
raise_from(AssertionError(
791+
"failed to convert {0} with byteorder={1} and signed={2}"
792+
.format(test, byteorder, signed)), err)
793+
794+
# Convert integers to signed big-endian byte arrays.
795+
tests1 = {
796+
0: bytes(b'\x00'),
797+
1: bytes(b'\x01'),
798+
-1: bytes(b'\xff'),
799+
-127: bytes(b'\x81'),
800+
-128: bytes(b'\x80'),
801+
-129: bytes(b'\xff\x7f'),
802+
127: bytes(b'\x7f'),
803+
129: bytes(b'\x00\x81'),
804+
-255: bytes(b'\xff\x01'),
805+
-256: bytes(b'\xff\x00'),
806+
255: bytes(b'\x00\xff'),
807+
256: bytes(b'\x01\x00'),
808+
32767: bytes(b'\x7f\xff'),
809+
-32768: bytes(b'\xff\x80\x00'),
810+
65535: bytes(b'\x00\xff\xff'),
811+
-65536: bytes(b'\xff\x00\x00'),
812+
-8388608: bytes(b'\x80\x00\x00')
813+
}
814+
# check(tests1, 'big', signed=True)
815+
816+
# Convert integers to signed little-endian byte arrays.
817+
tests2 = {
818+
0: bytes(b'\x00'),
819+
1: bytes(b'\x01'),
820+
-1: bytes(b'\xff'),
821+
-127: bytes(b'\x81'),
822+
-128: bytes(b'\x80'),
823+
-129: bytes(b'\x7f\xff'),
824+
127: bytes(b'\x7f'),
825+
129: bytes(b'\x81\x00'),
826+
-255: bytes(b'\x01\xff'),
827+
-256: bytes(b'\x00\xff'),
828+
255: bytes(b'\xff\x00'),
829+
256: bytes(b'\x00\x01'),
830+
32767: bytes(b'\xff\x7f'),
831+
-32768: bytes(b'\x00\x80'),
832+
65535: bytes(b'\xff\xff\x00'),
833+
-65536: bytes(b'\x00\x00\xff'),
834+
-8388608: bytes(b'\x00\x00\x80')
835+
}
836+
# check(tests2, 'little', signed=True)
837+
838+
# Convert integers to unsigned big-endian byte arrays.
839+
tests3 = {
840+
0: bytes(b'\x00'),
841+
1: bytes(b'\x01'),
842+
127: bytes(b'\x7f'),
843+
128: bytes(b'\x80'),
844+
255: bytes(b'\xff'),
845+
256: bytes(b'\x01\x00'),
846+
32767: bytes(b'\x7f\xff'),
847+
32768: bytes(b'\x80\x00'),
848+
65535: bytes(b'\xff\xff'),
849+
65536: bytes(b'\x01\x00\x00')
850+
}
851+
check(tests3, 'big', signed=False)
852+
853+
# Convert integers to unsigned little-endian byte arrays.
854+
tests4 = {
855+
0: bytes(b'\x00'),
856+
1: bytes(b'\x01'),
857+
127: bytes(b'\x7f'),
858+
128: bytes(b'\x80'),
859+
255: bytes(b'\xff'),
860+
256: bytes(b'\x00\x01'),
861+
32767: bytes(b'\xff\x7f'),
862+
32768: bytes(b'\x00\x80'),
863+
65535: bytes(b'\xff\xff'),
864+
65536: bytes(b'\x00\x00\x01')
865+
}
866+
check(tests4, 'little', signed=False)
867+
868+
self.assertRaises(OverflowError, int(256).to_bytes, 1, 'big', signed=False)
869+
# self.assertRaises(OverflowError, int(256).to_bytes, 1, 'big', signed=True)
870+
self.assertRaises(OverflowError, int(256).to_bytes, 1, 'little', signed=False)
871+
# self.assertRaises(OverflowError, int(256).to_bytes, 1, 'little', signed=True)
872+
self.assertRaises(OverflowError, int(-1).to_bytes, 2, 'big', signed=False),
873+
self.assertRaises(OverflowError, int(-1).to_bytes, 2, 'little', signed=False)
874+
self.assertEqual(int(0).to_bytes(0, 'big'), b'')
875+
self.assertEqual(int(1).to_bytes(5, 'big'), b'\x00\x00\x00\x00\x01')
876+
self.assertEqual(int(0).to_bytes(5, 'big'), b'\x00\x00\x00\x00\x00')
877+
# self.assertEqual(int(-1).to_bytes(5, 'big', signed=True),
878+
# bytes(b'\xff\xff\xff\xff\xff'))
879+
self.assertRaises(OverflowError, int(1).to_bytes, 0, 'big')
880+
881+
def test_from_bytes(self):
882+
def check(tests, byteorder, signed=False):
883+
for test, expected in tests.items():
884+
try:
885+
self.assertEqual(
886+
int.from_bytes(test, byteorder, signed=signed),
887+
int(expected))
888+
except Exception as err:
889+
raise_from(AssertionError(
890+
"failed to convert {0} with byteorder={1!r} and signed={2}"
891+
.format(test, byteorder, signed)), err)
892+
893+
# Convert signed big-endian byte arrays to integers.
894+
tests1 = {
895+
bytes(b''): 0,
896+
bytes(b'\x00'): 0,
897+
bytes(b'\x00\x00'): 0,
898+
bytes(b'\x01'): 1,
899+
bytes(b'\x00\x01'): 1,
900+
bytes(b'\xff'): -1,
901+
bytes(b'\xff\xff'): -1,
902+
bytes(b'\x81'): -127,
903+
bytes(b'\x80'): -128,
904+
bytes(b'\xff\x7f'): -129,
905+
bytes(b'\x7f'): 127,
906+
bytes(b'\x00\x81'): 129,
907+
bytes(b'\xff\x01'): -255,
908+
bytes(b'\xff\x00'): -256,
909+
bytes(b'\x00\xff'): 255,
910+
bytes(b'\x01\x00'): 256,
911+
bytes(b'\x7f\xff'): 32767,
912+
bytes(b'\x80\x00'): -32768,
913+
bytes(b'\x00\xff\xff'): 65535,
914+
bytes(b'\xff\x00\x00'): -65536,
915+
bytes(b'\x80\x00\x00'): -8388608
916+
}
917+
# check(tests1, 'big', signed=True)
918+
919+
# Convert signed little-endian byte arrays to integers.
920+
tests2 = {
921+
bytes(b''): 0,
922+
bytes(b'\x00'): 0,
923+
bytes(b'\x00\x00'): 0,
924+
bytes(b'\x01'): 1,
925+
bytes(b'\x00\x01'): 256,
926+
bytes(b'\xff'): -1,
927+
bytes(b'\xff\xff'): -1,
928+
bytes(b'\x81'): -127,
929+
bytes(b'\x80'): -128,
930+
bytes(b'\x7f\xff'): -129,
931+
bytes(b'\x7f'): 127,
932+
bytes(b'\x81\x00'): 129,
933+
bytes(b'\x01\xff'): -255,
934+
bytes(b'\x00\xff'): -256,
935+
bytes(b'\xff\x00'): 255,
936+
bytes(b'\x00\x01'): 256,
937+
bytes(b'\xff\x7f'): 32767,
938+
bytes(b'\x00\x80'): -32768,
939+
bytes(b'\xff\xff\x00'): 65535,
940+
bytes(b'\x00\x00\xff'): -65536,
941+
bytes(b'\x00\x00\x80'): -8388608
942+
}
943+
# check(tests2, 'little', signed=True)
944+
945+
# Convert unsigned big-endian byte arrays to integers.
946+
tests3 = {
947+
bytes(b''): 0,
948+
bytes(b'\x00'): 0,
949+
bytes(b'\x01'): 1,
950+
bytes(b'\x7f'): 127,
951+
bytes(b'\x80'): 128,
952+
bytes(b'\xff'): 255,
953+
bytes(b'\x01\x00'): 256,
954+
bytes(b'\x7f\xff'): 32767,
955+
bytes(b'\x80\x00'): 32768,
956+
bytes(b'\xff\xff'): 65535,
957+
bytes(b'\x01\x00\x00'): 65536,
958+
}
959+
check(tests3, 'big', signed=False)
960+
961+
# Convert integers to unsigned little-endian byte arrays.
962+
tests4 = {
963+
bytes(b''): 0,
964+
bytes(b'\x00'): 0,
965+
bytes(b'\x01'): 1,
966+
bytes(b'\x7f'): 127,
967+
bytes(b'\x80'): 128,
968+
bytes(b'\xff'): 255,
969+
bytes(b'\x00\x01'): 256,
970+
bytes(b'\xff\x7f'): 32767,
971+
bytes(b'\x00\x80'): 32768,
972+
bytes(b'\xff\xff'): 65535,
973+
bytes(b'\x00\x00\x01'): 65536,
974+
}
975+
check(tests4, 'little', signed=False)
976+
977+
class myint(int):
978+
pass
979+
980+
if PY2:
981+
import __builtin__
982+
oldbytes = __builtin__.bytes
983+
types = (bytes, oldbytes)
984+
else:
985+
types = (bytes,)
986+
for mytype in types:
987+
self.assertIs(type(myint.from_bytes(mytype(b'\x00'), 'big')), myint)
988+
self.assertEqual(myint.from_bytes(mytype(b'\x01'), 'big'), 1)
989+
self.assertIs(
990+
type(myint.from_bytes(mytype(b'\x00'), 'big', signed=False)), myint)
991+
self.assertEqual(myint.from_bytes(mytype(b'\x01'), 'big', signed=False), 1)
992+
self.assertIs(type(myint.from_bytes(mytype(b'\x00'), 'little')), myint)
993+
self.assertEqual(myint.from_bytes(mytype(b'\x01'), 'little'), 1)
994+
self.assertIs(type(myint.from_bytes(
995+
mytype(b'\x00'), 'little', signed=False)), myint)
996+
self.assertEqual(myint.from_bytes(mytype(b'\x01'), 'little', signed=False), 1)
997+
# self.assertEqual(
998+
# int.from_bytes([255, 0, 0], 'big', signed=True), -65536)
999+
# self.assertEqual(
1000+
# int.from_bytes((255, 0, 0), 'big', signed=True), -65536)
1001+
# self.assertEqual(int.from_bytes(
1002+
# bytearray(mytype(b'\xff\x00\x00')), 'big', signed=True), -65536)
1003+
# self.assertEqual(int.from_bytes(
1004+
# bytearray(mytype(b'\xff\x00\x00')), 'big', signed=True), -65536)
1005+
# self.assertEqual(int.from_bytes(
1006+
# array.array('B', mytype(b'\xff\x00\x00')), 'big', signed=True), -65536)
1007+
# self.assertEqual(int.from_bytes(
1008+
# memoryview(mytype(b'\xff\x00\x00')), 'big', signed=True), -65536)
1009+
1010+
self.assertRaises(TypeError, int.from_bytes, u"", 'big')
1011+
self.assertRaises(TypeError, int.from_bytes, u"\x00", 'big')
1012+
self.assertRaises(TypeError, myint.from_bytes, u"", 'big')
1013+
self.assertRaises(TypeError, myint.from_bytes, u"\x00", 'big')
1014+
1015+
types = (int, lambda x: x) if PY2 else (lambda x: x,)
1016+
for mytype in types:
1017+
self.assertRaises(ValueError, int.from_bytes, [mytype(256)], 'big')
1018+
self.assertRaises(ValueError, int.from_bytes, [mytype(0)], 'big\x00')
1019+
self.assertRaises(ValueError, int.from_bytes, [mytype(0)], 'little\x00')
1020+
self.assertRaises(TypeError, int.from_bytes, mytype(0), 'big')
1021+
# self.assertRaises(TypeError, int.from_bytes, mytype(0), 'big', True)
1022+
self.assertRaises(TypeError, myint.from_bytes, mytype(0), 'big')
1023+
# self.assertRaises(TypeError, int.from_bytes, mytype(0), 'big', True)
1024+
1025+
7811026
if __name__ == "__main__":
7821027
unittest.main()

future/types/newint.py

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@
77
"""
88
from __future__ import division
99

10+
import struct
11+
import collections
12+
1013
from future.types.newbytes import newbytes
11-
from future.utils import PY3, isint, istext, isbytes, with_metaclass
14+
from future.utils import PY3, isint, istext, isbytes, with_metaclass, native
1215

1316

1417
if PY3:
@@ -274,5 +277,84 @@ def __bool__(self):
274277
def __native__(self):
275278
return long(self)
276279

280+
def to_bytes(self, length, byteorder='big', signed=False):
281+
"""
282+
Return an array of bytes representing an integer.
283+
284+
The integer is represented using length bytes. An OverflowError is
285+
raised if the integer is not representable with the given number of
286+
bytes.
287+
288+
The byteorder argument determines the byte order used to represent the
289+
integer. If byteorder is 'big', the most significant byte is at the
290+
beginning of the byte array. If byteorder is 'little', the most
291+
significant byte is at the end of the byte array. To request the native
292+
byte order of the host system, use `sys.byteorder' as the byte order value.
293+
294+
The signed keyword-only argument determines whether two's complement is
295+
used to represent the integer. If signed is False and a negative integer
296+
is given, an OverflowError is raised.
297+
"""
298+
if signed:
299+
raise NotImplementedError("Not yet implemented. Please contribute a patch at http://python-future.org")
300+
else:
301+
if self < 0:
302+
raise OverflowError("can't convert negative int to unsigned")
303+
num = self
304+
if byteorder not in ('little', 'big'):
305+
raise ValueError("byteorder must be either 'little' or 'big'")
306+
if length < 0:
307+
raise ValueError("length argument must be non-negative")
308+
if length == 0 and num == 0:
309+
return newbytes()
310+
h = b'%x' % num
311+
s = newbytes((b'0'*(len(h) % 2) + h).zfill(length*2).decode('hex'))
312+
if len(s) > length:
313+
raise OverflowError("int too big to convert")
314+
return s if byteorder == 'big' else s[::-1]
315+
316+
@classmethod
317+
def from_bytes(cls, mybytes, byteorder='big', signed=False):
318+
"""
319+
Return the integer represented by the given array of bytes.
320+
321+
The mybytes argument must either support the buffer protocol or be an
322+
iterable object producing bytes. Bytes and bytearray are examples of
323+
built-in objects that support the buffer protocol.
324+
325+
The byteorder argument determines the byte order used to represent the
326+
integer. If byteorder is 'big', the most significant byte is at the
327+
beginning of the byte array. If byteorder is 'little', the most
328+
significant byte is at the end of the byte array. To request the native
329+
byte order of the host system, use `sys.byteorder' as the byte order value.
330+
331+
The signed keyword-only argument indicates whether two's complement is
332+
used to represent the integer.
333+
"""
334+
if signed:
335+
raise NotImplementedError("Not yet implemented. Please contribute a patch at http://python-future.org")
336+
if byteorder not in ('little', 'big'):
337+
raise ValueError("byteorder must be either 'little' or 'big'")
338+
if isinstance(mybytes, unicode):
339+
raise TypeError("cannot convert unicode objects to bytes")
340+
# mybytes can also be passed as a sequence of integers on Py3.
341+
# Test for this:
342+
elif isinstance(mybytes, collections.Iterable):
343+
mybytes = newbytes(mybytes)
344+
b = mybytes if byteorder == 'big' else mybytes[::-1]
345+
if len(b) == 0:
346+
b = b'\x00'
347+
# The encode() method has been disabled by newbytes, but Py2's
348+
# str has it:
349+
num = int(native(b).encode('hex'), 16)
350+
return cls(num)
351+
352+
353+
# def _twos_comp(val, bits):
354+
# """compute the 2's compliment of int value val"""
355+
# if( (val&(1<<(bits-1))) != 0 ):
356+
# val = val - (1<<bits)
357+
# return val
358+
277359

278360
__all__ = ['newint']

0 commit comments

Comments
 (0)