Skip to content

Commit 38f0c54

Browse files
authored
Make int.from_bytes recognize __bytes__ in all cases (#970)
1 parent a79f22f commit 38f0c54

File tree

4 files changed

+16
-17
lines changed

4 files changed

+16
-17
lines changed

Src/IronPython/Runtime/Bytes.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,8 @@ internal static Bytes FromObject(CodeContext context, object? o) {
165165
return (Bytes)o;
166166
} else if (TryInvokeBytesOperator(context, o, out Bytes? res)) {
167167
return res;
168+
} else if (o is IBufferProtocol bp) {
169+
return new Bytes(bp);
168170
} else if (o is string || o is ExtensibleString) {
169171
throw PythonOps.TypeError("cannot convert unicode object to bytes");
170172
} else {

Src/IronPython/Runtime/Operations/IntOps.cs

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -528,30 +528,20 @@ public static Bytes to_bytes(Int32 value, int length, string byteorder, bool sig
528528
return Bytes.Make(res.ToArray());
529529
}
530530

531-
public static BigInteger from_bytes([NotNull, BytesLike]IList<byte> bytes, string byteorder, bool signed=false) {
531+
public static BigInteger from_bytes(CodeContext context, object bytes, [NotNull] string byteorder, bool signed = false) {
532532
// TODO: signed should be a keyword only argument
533533
// TODO: return int when possible?
534+
// TODO: if called on a subclass of Extensible<BigInteger>, return that subclass
534535

535536
bool isLittle = byteorder == "little";
536537
if (!isLittle && byteorder != "big") throw PythonOps.ValueError("byteorder must be either 'little' or 'big'");
537538

538-
return FromBytes(bytes, isLittle, signed);
539-
}
540-
541-
public static BigInteger from_bytes(CodeContext context, object bytes, string byteorder, bool signed = false) {
542-
// TODO: signed should be a keyword only argument
543-
// TODO: return int when possible?
544-
545-
bool isLittle = byteorder == "little";
546-
if (!isLittle && byteorder != "big") throw PythonOps.ValueError("byteorder must be either 'little' or 'big'");
547-
548-
return FromBytes(Bytes.FromObject(context, bytes), isLittle, signed);
549-
}
550-
551-
private static BigInteger FromBytes(IList<byte> bytes, bool isLittle, bool signed) {
552-
if (!bytes.Any()) return 0;
539+
byte[] bytesArr = Bytes.FromObject(context, bytes).UnsafeByteArray;
540+
if (bytesArr.Length == 0) return 0;
553541

554-
byte[] bytesArr = bytes as byte[] ?? ((bytes is Bytes) ? ((Bytes)bytes).UnsafeByteArray : bytes.ToArray());
542+
#if NETCOREAPP
543+
return new BigInteger(bytesArr.AsSpan(), isUnsigned: !signed, isBigEndian: !isLittle);
544+
#else
555545

556546
if (isLittle) {
557547
bool msbSet = (bytesArr[bytesArr.Length - 1] & 0x80) == 0x80;
@@ -562,6 +552,7 @@ private static BigInteger FromBytes(IList<byte> bytes, bool isLittle, bool signe
562552
if (!msbSet) return new BigInteger(bytesArr.Reverse());
563553
return new BigInteger(bytesArr.Reverse().Concat(Enumerable.Repeat<byte>(signed ? (byte)0xff : (byte)0, 1)).ToArray());
564554
}
555+
#endif
565556
}
566557

567558
public static int __round__(int self) {

Tests/test_bytes.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ def __bytes__(self):
213213
self.assertEqual(bytes(BytesBytesSubclass(b"JUST BYTES")), b"BYTES FROM BYTES")
214214
self.assertIs(type(bytes(BytesBytesSubclass(b"JUST BYTES"))), BytesBytesSubclass)
215215
self.assertEqual(int.from_bytes(bytes(BytesBytesSubclass(b"JUST BYTES")), 'big'), int.from_bytes(b"BYTES FROM BYTES", 'big'))
216+
self.assertEqual(int.from_bytes(BytesBytesSubclass(b"JUST BYTES"), 'big'), int.from_bytes(b"BYTES FROM BYTES", 'big'))
216217

217218
class ListSubclass(bytes):
218219
def __bytes__(self):

Tests/test_memoryview.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,11 @@ def test_unicode_array(self):
247247
with self.assertRaisesRegex(TypeError, "^memoryview: cannot cast between two non-byte formats$"):
248248
mv.cast('H')
249249

250+
def test_conv(self):
251+
self.assertEqual(int.from_bytes(memoryview(b'abcd'), 'big'), 0x61626364)
252+
self.assertEqual(int.from_bytes(memoryview(b'abcd')[::2], 'big'), 0x6163)
253+
self.assertEqual(int.from_bytes(memoryview(b'abcd')[::-2], 'big'), 0x6462)
254+
250255
class CastTests(unittest.TestCase):
251256
def test_get_int_alignment(self):
252257
a = array.array('b', range(8))

0 commit comments

Comments
 (0)