From 2058f817849309e3d3e4e46085f97d0f86d0e2c9 Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Sat, 16 Aug 2025 15:48:25 +0000 Subject: [PATCH 01/44] [mypyc] feat: new primitive for `int.to_bytes` --- mypyc/lib-rt/int_ops.c | 51 +++++++++++++++++++++++++++++++ mypyc/primitives/int_ops.py | 21 ++++++++++++- mypyc/test-data/irbuild-int.test | 11 +++++++ mypyc/test-data/run-integers.test | 9 ++++++ 4 files changed, 91 insertions(+), 1 deletion(-) diff --git a/mypyc/lib-rt/int_ops.c b/mypyc/lib-rt/int_ops.c index e2c302eea576..1df8d3ffef16 100644 --- a/mypyc/lib-rt/int_ops.c +++ b/mypyc/lib-rt/int_ops.c @@ -581,3 +581,54 @@ double CPyTagged_TrueDivide(CPyTagged x, CPyTagged y) { } return 1.0; } + +// int.to_bytes(length, byteorder, signed=False) +PyObject *CPyInt_ToBytes(CPyTagged self, Py_ssize_t length, PyObject *byteorder, int signed_flag) { + PyObject *pyint = CPyTagged_StealAsObject(self); + if (!PyLong_Check(pyint)) { + Py_DECREF(pyint); + PyErr_SetString(PyExc_TypeError, "self must be int"); + return NULL; + } + if (!PyUnicode_Check(byteorder)) { + Py_DECREF(pyint); + PyErr_SetString(PyExc_TypeError, "byteorder must be str"); + return NULL; + } + const char *order = PyUnicode_AsUTF8(byteorder); + if (!order) { + Py_DECREF(pyint); + return NULL; + } + PyObject *result = PyLong_ToBytes(pyint, length, order, signed_flag); + Py_DECREF(pyint); + return result; +} + +// Helper for PyLong_ToBytes (Python 3.2+) +PyObject *PyLong_ToBytes(PyObject *v, Py_ssize_t length, const char *byteorder, int signed_flag) { + // This is a wrapper for PyLong_AsByteArray and PyBytes_FromStringAndSize + unsigned char *bytes = (unsigned char *)PyMem_Malloc(length); + if (!bytes) { + PyErr_NoMemory(); + return NULL; + } + int little_endian = 0; + if (strcmp(byteorder, "little") == 0) { + little_endian = 1; + } else if (strcmp(byteorder, "big") == 0) { + little_endian = 0; + } else { + PyMem_Free(bytes); + PyErr_SetString(PyExc_ValueError, "byteorder must be either 'little' or 'big'"); + return NULL; + } + int res = PyLong_AsByteArray((PyLongObject *)v, bytes, length, little_endian, signed_flag); + if (res < 0) { + PyMem_Free(bytes); + return NULL; + } + PyObject *result = PyBytes_FromStringAndSize((const char *)bytes, length); + PyMem_Free(bytes); + return result; +} diff --git a/mypyc/primitives/int_ops.py b/mypyc/primitives/int_ops.py index d723c9b63a86..0ba1c595a215 100644 --- a/mypyc/primitives/int_ops.py +++ b/mypyc/primitives/int_ops.py @@ -21,6 +21,7 @@ RType, bit_rprimitive, bool_rprimitive, + bytes_rprimitive, c_pyssize_t_rprimitive, float_rprimitive, int16_rprimitive, @@ -31,7 +32,7 @@ str_rprimitive, void_rtype, ) -from mypyc.primitives.registry import binary_op, custom_op, function_op, load_address_op, unary_op +from mypyc.primitives.registry import binary_op, custom_op, function_op, load_address_op, method_op, unary_op # Constructors for builtins.int and native int types have the same behavior. In # interpreted mode, native int types are just aliases to 'int'. @@ -305,3 +306,21 @@ def int_unary_op(name: str, c_function_name: str) -> PrimitiveDescription: c_function_name="PyLong_Check", error_kind=ERR_NEVER, ) + +# int.to_bytes(length, byteorder, *, signed=False) +method_op( + name="to_bytes", + arg_types=[int_rprimitive, int_rprimitive, str_rprimitive, bool_rprimitive], + return_type=bytes_rprimitive, + c_function_name="CPyInt_ToBytes", + error_kind=ERR_MAGIC, +) + +# int.bit_length() +method_op( + name="bit_length", + arg_types=[int_rprimitive], + return_type=int_rprimitive, + c_function_name="CPyInt_BitLength", + error_kind=ERR_MAGIC, +) diff --git a/mypyc/test-data/irbuild-int.test b/mypyc/test-data/irbuild-int.test index bdf9127b722a..bad8826fee1b 100644 --- a/mypyc/test-data/irbuild-int.test +++ b/mypyc/test-data/irbuild-int.test @@ -210,3 +210,14 @@ L0: r0 = CPyTagged_Invert(n) x = r0 return x + +[case testIntToBytes] +def f(x: int) -> bytes: + return x.to_bytes(2, "big") +[out] +def f(x): + x :: int + r0 :: bytes +L0: + r0 = int_to_bytes x, 2, 'big', 0 + return r0 diff --git a/mypyc/test-data/run-integers.test b/mypyc/test-data/run-integers.test index 1163c9d942f7..55289bde3835 100644 --- a/mypyc/test-data/run-integers.test +++ b/mypyc/test-data/run-integers.test @@ -572,3 +572,12 @@ class subc(int): [file userdefinedint.py] class int: pass + +[case testIntToBytes] +def to_bytes(n: int, length: int, byteorder: str, signed: bool = False) -> bytes: + return n.to_bytes(length, byteorder, signed=signed) +def test_to_bytes() -> None: + assert to_bytes(255, 2, "big") == b'\x00\xff' + assert to_bytes(255, 2, "little") == b'\xff\x00' + assert to_bytes(-1, 2, "big", True) == b'\xff\xff' + assert to_bytes(0, 1, "big") == b'\x00' From f62bfd2efe6c777868dcd83aa70e0501ce4ce9ac Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 16 Aug 2025 15:53:54 +0000 Subject: [PATCH 02/44] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypyc/primitives/int_ops.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/mypyc/primitives/int_ops.py b/mypyc/primitives/int_ops.py index 0ba1c595a215..6294a13aa080 100644 --- a/mypyc/primitives/int_ops.py +++ b/mypyc/primitives/int_ops.py @@ -32,7 +32,14 @@ str_rprimitive, void_rtype, ) -from mypyc.primitives.registry import binary_op, custom_op, function_op, load_address_op, method_op, unary_op +from mypyc.primitives.registry import ( + binary_op, + custom_op, + function_op, + load_address_op, + method_op, + unary_op, +) # Constructors for builtins.int and native int types have the same behavior. In # interpreted mode, native int types are just aliases to 'int'. From 813c5104958e6cb00a98b947893b08043f605fe2 Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Sat, 16 Aug 2025 16:04:05 +0000 Subject: [PATCH 03/44] add headers --- mypyc/lib-rt/CPy.h | 1 + mypyc/lib-rt/int_ops.c | 2 +- mypyc/primitives/int_ops.py | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/mypyc/lib-rt/CPy.h b/mypyc/lib-rt/CPy.h index 1881aa97f308..cbe1c49a4627 100644 --- a/mypyc/lib-rt/CPy.h +++ b/mypyc/lib-rt/CPy.h @@ -139,6 +139,7 @@ CPyTagged CPyTagged_Remainder_(CPyTagged left, CPyTagged right); CPyTagged CPyTagged_BitwiseLongOp_(CPyTagged a, CPyTagged b, char op); CPyTagged CPyTagged_Rshift_(CPyTagged left, CPyTagged right); CPyTagged CPyTagged_Lshift_(CPyTagged left, CPyTagged right); +PyObject *CPyTagged_ToBytes(CPyTagged self, Py_ssize_t length, PyObject *byteorder, int signed_flag) PyObject *CPyTagged_Str(CPyTagged n); CPyTagged CPyTagged_FromFloat(double f); diff --git a/mypyc/lib-rt/int_ops.c b/mypyc/lib-rt/int_ops.c index 1df8d3ffef16..91cb7a709406 100644 --- a/mypyc/lib-rt/int_ops.c +++ b/mypyc/lib-rt/int_ops.c @@ -583,7 +583,7 @@ double CPyTagged_TrueDivide(CPyTagged x, CPyTagged y) { } // int.to_bytes(length, byteorder, signed=False) -PyObject *CPyInt_ToBytes(CPyTagged self, Py_ssize_t length, PyObject *byteorder, int signed_flag) { +PyObject *CPyTagged_ToBytes(CPyTagged self, Py_ssize_t length, PyObject *byteorder, int signed_flag) { PyObject *pyint = CPyTagged_StealAsObject(self); if (!PyLong_Check(pyint)) { Py_DECREF(pyint); diff --git a/mypyc/primitives/int_ops.py b/mypyc/primitives/int_ops.py index 0ba1c595a215..9c817412af24 100644 --- a/mypyc/primitives/int_ops.py +++ b/mypyc/primitives/int_ops.py @@ -312,7 +312,7 @@ def int_unary_op(name: str, c_function_name: str) -> PrimitiveDescription: name="to_bytes", arg_types=[int_rprimitive, int_rprimitive, str_rprimitive, bool_rprimitive], return_type=bytes_rprimitive, - c_function_name="CPyInt_ToBytes", + c_function_name="CPyTagged_ToBytes", error_kind=ERR_MAGIC, ) From cb93329d9fead84aa64fea49c3782a7ecee9a0c7 Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Sat, 16 Aug 2025 21:40:38 +0000 Subject: [PATCH 04/44] cover all arg combos --- mypyc/primitives/int_ops.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/mypyc/primitives/int_ops.py b/mypyc/primitives/int_ops.py index 87e2c1e9eb61..52a7076984e1 100644 --- a/mypyc/primitives/int_ops.py +++ b/mypyc/primitives/int_ops.py @@ -314,20 +314,21 @@ def int_unary_op(name: str, c_function_name: str) -> PrimitiveDescription: error_kind=ERR_NEVER, ) -# int.to_bytes(length, byteorder, *, signed=False) +# int.to_bytes(length, byteorder) method_op( name="to_bytes", - arg_types=[int_rprimitive, int_rprimitive, str_rprimitive, bool_rprimitive], + arg_types=[int_rprimitive, int_rprimitive, str_rprimitive], + extra_int_constants=[(0, bool_rprimitive)], return_type=bytes_rprimitive, c_function_name="CPyTagged_ToBytes", error_kind=ERR_MAGIC, ) -# int.bit_length() +# int.to_bytes(length, byteorder, signed) method_op( - name="bit_length", - arg_types=[int_rprimitive], - return_type=int_rprimitive, - c_function_name="CPyInt_BitLength", + name="to_bytes", + arg_types=[int_rprimitive, int_rprimitive, str_rprimitive, bool_rprimitive], + return_type=bytes_rprimitive, + c_function_name="CPyTagged_ToBytes", error_kind=ERR_MAGIC, ) From 6ff1c9b78a1b68f4ef3c41a078a581443858f21b Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Sat, 16 Aug 2025 21:44:40 +0000 Subject: [PATCH 05/44] CPyLong_ToBytes header --- mypyc/lib-rt/CPy.h | 1 + mypyc/lib-rt/int_ops.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/mypyc/lib-rt/CPy.h b/mypyc/lib-rt/CPy.h index cbe1c49a4627..426905acd34b 100644 --- a/mypyc/lib-rt/CPy.h +++ b/mypyc/lib-rt/CPy.h @@ -140,6 +140,7 @@ CPyTagged CPyTagged_BitwiseLongOp_(CPyTagged a, CPyTagged b, char op); CPyTagged CPyTagged_Rshift_(CPyTagged left, CPyTagged right); CPyTagged CPyTagged_Lshift_(CPyTagged left, CPyTagged right); PyObject *CPyTagged_ToBytes(CPyTagged self, Py_ssize_t length, PyObject *byteorder, int signed_flag) +PyObject *CPyLong_ToBytes(PyObject *v, Py_ssize_t length, const char *byteorder, int signed_flag) PyObject *CPyTagged_Str(CPyTagged n); CPyTagged CPyTagged_FromFloat(double f); diff --git a/mypyc/lib-rt/int_ops.c b/mypyc/lib-rt/int_ops.c index 91cb7a709406..a492b0a345c7 100644 --- a/mypyc/lib-rt/int_ops.c +++ b/mypyc/lib-rt/int_ops.c @@ -606,7 +606,7 @@ PyObject *CPyTagged_ToBytes(CPyTagged self, Py_ssize_t length, PyObject *byteord } // Helper for PyLong_ToBytes (Python 3.2+) -PyObject *PyLong_ToBytes(PyObject *v, Py_ssize_t length, const char *byteorder, int signed_flag) { +PyObject *CPyLong_ToBytes(PyObject *v, Py_ssize_t length, const char *byteorder, int signed_flag) { // This is a wrapper for PyLong_AsByteArray and PyBytes_FromStringAndSize unsigned char *bytes = (unsigned char *)PyMem_Malloc(length); if (!bytes) { From 166b6f47f7f4a681549132de12b1d49f02e93a7d Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Sat, 16 Aug 2025 21:46:00 +0000 Subject: [PATCH 06/44] ; --- mypyc/lib-rt/CPy.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mypyc/lib-rt/CPy.h b/mypyc/lib-rt/CPy.h index 426905acd34b..8494a69bf931 100644 --- a/mypyc/lib-rt/CPy.h +++ b/mypyc/lib-rt/CPy.h @@ -139,8 +139,8 @@ CPyTagged CPyTagged_Remainder_(CPyTagged left, CPyTagged right); CPyTagged CPyTagged_BitwiseLongOp_(CPyTagged a, CPyTagged b, char op); CPyTagged CPyTagged_Rshift_(CPyTagged left, CPyTagged right); CPyTagged CPyTagged_Lshift_(CPyTagged left, CPyTagged right); -PyObject *CPyTagged_ToBytes(CPyTagged self, Py_ssize_t length, PyObject *byteorder, int signed_flag) -PyObject *CPyLong_ToBytes(PyObject *v, Py_ssize_t length, const char *byteorder, int signed_flag) +PyObject *CPyTagged_ToBytes(CPyTagged self, Py_ssize_t length, PyObject *byteorder, int signed_flag); +PyObject *CPyLong_ToBytes(PyObject *v, Py_ssize_t length, const char *byteorder, int signed_flag); PyObject *CPyTagged_Str(CPyTagged n); CPyTagged CPyTagged_FromFloat(double f); From be2d2deb88da8b279c1fae222834661663f6fe8c Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Sat, 16 Aug 2025 21:58:58 +0000 Subject: [PATCH 07/44] fix name --- mypyc/lib-rt/int_ops.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mypyc/lib-rt/int_ops.c b/mypyc/lib-rt/int_ops.c index a492b0a345c7..48d79de605e6 100644 --- a/mypyc/lib-rt/int_ops.c +++ b/mypyc/lib-rt/int_ops.c @@ -600,12 +600,12 @@ PyObject *CPyTagged_ToBytes(CPyTagged self, Py_ssize_t length, PyObject *byteord Py_DECREF(pyint); return NULL; } - PyObject *result = PyLong_ToBytes(pyint, length, order, signed_flag); + PyObject *result = CPyLong_ToBytes(pyint, length, order, signed_flag); Py_DECREF(pyint); return result; } -// Helper for PyLong_ToBytes (Python 3.2+) +// Helper for CPyLong_ToBytes (Python 3.2+) PyObject *CPyLong_ToBytes(PyObject *v, Py_ssize_t length, const char *byteorder, int signed_flag) { // This is a wrapper for PyLong_AsByteArray and PyBytes_FromStringAndSize unsigned char *bytes = (unsigned char *)PyMem_Malloc(length); From 9ff3a7c7c5833c24c73cd7a3383815e5abe780f4 Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Sat, 16 Aug 2025 22:00:50 +0000 Subject: [PATCH 08/44] fix _PyLong_AsByteArray compile err --- mypyc/lib-rt/int_ops.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/mypyc/lib-rt/int_ops.c b/mypyc/lib-rt/int_ops.c index 48d79de605e6..a189ba7456fe 100644 --- a/mypyc/lib-rt/int_ops.c +++ b/mypyc/lib-rt/int_ops.c @@ -605,6 +605,12 @@ PyObject *CPyTagged_ToBytes(CPyTagged self, Py_ssize_t length, PyObject *byteord return result; } +#if PY_VERSION_HEX >= 0x030A0000 + #define CPy_PyLong_AsByteArray PyLong_AsByteArray +#else + #define CPy_PyLong_AsByteArray _PyLong_AsByteArray +#endif + // Helper for CPyLong_ToBytes (Python 3.2+) PyObject *CPyLong_ToBytes(PyObject *v, Py_ssize_t length, const char *byteorder, int signed_flag) { // This is a wrapper for PyLong_AsByteArray and PyBytes_FromStringAndSize @@ -623,7 +629,7 @@ PyObject *CPyLong_ToBytes(PyObject *v, Py_ssize_t length, const char *byteorder, PyErr_SetString(PyExc_ValueError, "byteorder must be either 'little' or 'big'"); return NULL; } - int res = PyLong_AsByteArray((PyLongObject *)v, bytes, length, little_endian, signed_flag); + int res = CPy_PyLong_AsByteArray((PyLongObject *)v, bytes, length, little_endian, signed_flag); if (res < 0) { PyMem_Free(bytes); return NULL; From 83996192ca9c4f1a90ee307301885d2ed00a1ade Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Sat, 16 Aug 2025 23:54:57 -0400 Subject: [PATCH 09/44] Update ir.py --- mypyc/test-data/fixtures/ir.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mypyc/test-data/fixtures/ir.py b/mypyc/test-data/fixtures/ir.py index 661ae50fd5f3..fd640080dcb4 100644 --- a/mypyc/test-data/fixtures/ir.py +++ b/mypyc/test-data/fixtures/ir.py @@ -4,7 +4,7 @@ import _typeshed from typing import ( TypeVar, Generic, List, Iterator, Iterable, Dict, Optional, Tuple, Any, Set, - overload, Mapping, Union, Callable, Sequence, FrozenSet, Protocol + overload, Mapping, Union, Callable, Sequence, FrozenSet, Protocol, Literal, ) _T = TypeVar('_T') @@ -123,6 +123,7 @@ def removeprefix(self, prefix: str, /) -> str: ... def removesuffix(self, suffix: str, /) -> str: ... def islower(self) -> bool: ... def count(self, substr: str, start: Optional[int] = None, end: Optional[int] = None) -> int: pass + def to_bytes(self, length: int, order: Literal["small", "big"], signed: bool = False) -> bytes: pass class float: def __init__(self, x: object) -> None: pass From db7b483e3cf4f12ec8f5500a02b22ccff63fd28a Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Sun, 17 Aug 2025 04:00:09 +0000 Subject: [PATCH 10/44] define header --- mypyc/lib-rt/int_ops.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/mypyc/lib-rt/int_ops.c b/mypyc/lib-rt/int_ops.c index a189ba7456fe..213a0bc290a4 100644 --- a/mypyc/lib-rt/int_ops.c +++ b/mypyc/lib-rt/int_ops.c @@ -606,6 +606,16 @@ PyObject *CPyTagged_ToBytes(CPyTagged self, Py_ssize_t length, PyObject *byteord } #if PY_VERSION_HEX >= 0x030A0000 +// If not declared, declare it ourselves +#ifndef PyLong_AsByteArray +PyAPI_FUNC(int) PyLong_AsByteArray( + PyLongObject* v, + unsigned char* bytes, + size_t n, + int little_endian, + int is_signed +); +#endif #define CPy_PyLong_AsByteArray PyLong_AsByteArray #else #define CPy_PyLong_AsByteArray _PyLong_AsByteArray From a0c147bb968b40121ff2cef6c3c31af2e5bf2ccc Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Sun, 17 Aug 2025 04:16:07 +0000 Subject: [PATCH 11/44] fix ir --- mypyc/test-data/fixtures/ir.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypyc/test-data/fixtures/ir.py b/mypyc/test-data/fixtures/ir.py index fd640080dcb4..d3d63c056f81 100644 --- a/mypyc/test-data/fixtures/ir.py +++ b/mypyc/test-data/fixtures/ir.py @@ -81,6 +81,7 @@ def __lt__(self, n: int) -> bool: pass def __gt__(self, n: int) -> bool: pass def __le__(self, n: int) -> bool: pass def __ge__(self, n: int) -> bool: pass + def to_bytes(self, length: int, order: Literal["small", "big"], signed: bool = False) -> bytes: pass class str: @overload @@ -123,7 +124,6 @@ def removeprefix(self, prefix: str, /) -> str: ... def removesuffix(self, suffix: str, /) -> str: ... def islower(self) -> bool: ... def count(self, substr: str, start: Optional[int] = None, end: Optional[int] = None) -> int: pass - def to_bytes(self, length: int, order: Literal["small", "big"], signed: bool = False) -> bytes: pass class float: def __init__(self, x: object) -> None: pass From 5bab2656e0ff00e624bc3be9463ffdfeb3d3f93b Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Sun, 17 Aug 2025 00:39:30 -0400 Subject: [PATCH 12/44] Update ir.py --- mypyc/test-data/fixtures/ir.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mypyc/test-data/fixtures/ir.py b/mypyc/test-data/fixtures/ir.py index d3d63c056f81..cba8a4a0aa42 100644 --- a/mypyc/test-data/fixtures/ir.py +++ b/mypyc/test-data/fixtures/ir.py @@ -4,7 +4,7 @@ import _typeshed from typing import ( TypeVar, Generic, List, Iterator, Iterable, Dict, Optional, Tuple, Any, Set, - overload, Mapping, Union, Callable, Sequence, FrozenSet, Protocol, Literal, + overload, Mapping, Union, Callable, Sequence, FrozenSet, Protocol, ) _T = TypeVar('_T') @@ -81,7 +81,7 @@ def __lt__(self, n: int) -> bool: pass def __gt__(self, n: int) -> bool: pass def __le__(self, n: int) -> bool: pass def __ge__(self, n: int) -> bool: pass - def to_bytes(self, length: int, order: Literal["small", "big"], signed: bool = False) -> bytes: pass + def to_bytes(self, length: int, order: str, signed: bool = False) -> bytes: pass class str: @overload From b346bbfee205286c1a9cb8177e2e10f7406cccf2 Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Sun, 17 Aug 2025 06:16:03 +0000 Subject: [PATCH 13/44] fix ir --- mypyc/test-data/irbuild-int.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypyc/test-data/irbuild-int.test b/mypyc/test-data/irbuild-int.test index bad8826fee1b..28e4e293e464 100644 --- a/mypyc/test-data/irbuild-int.test +++ b/mypyc/test-data/irbuild-int.test @@ -219,5 +219,5 @@ def f(x): x :: int r0 :: bytes L0: - r0 = int_to_bytes x, 2, 'big', 0 + r0 = CPyTagged_ToBytes(x, 4, 'big', 0) return r0 From 85652a2479c31d9ac44302433f9f71b58080854b Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Sun, 17 Aug 2025 06:17:00 +0000 Subject: [PATCH 14/44] fix ir --- mypyc/test-data/irbuild-int.test | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mypyc/test-data/irbuild-int.test b/mypyc/test-data/irbuild-int.test index 28e4e293e464..93ab9b9b7e58 100644 --- a/mypyc/test-data/irbuild-int.test +++ b/mypyc/test-data/irbuild-int.test @@ -217,7 +217,9 @@ def f(x: int) -> bytes: [out] def f(x): x :: int - r0 :: bytes + r0 :: str + r1 :: bytes L0: - r0 = CPyTagged_ToBytes(x, 4, 'big', 0) + r0 = 'big' + r1 = CPyTagged_ToBytes(x, 4, r1, 0) return r0 From 8e9716529b34fbe8b3fc41b91fccd3c66e9eab99 Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Sun, 17 Aug 2025 06:20:20 +0000 Subject: [PATCH 15/44] add ir test --- mypyc/test-data/irbuild-int.test | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/mypyc/test-data/irbuild-int.test b/mypyc/test-data/irbuild-int.test index 93ab9b9b7e58..6fce8979492a 100644 --- a/mypyc/test-data/irbuild-int.test +++ b/mypyc/test-data/irbuild-int.test @@ -214,6 +214,8 @@ L0: [case testIntToBytes] def f(x: int) -> bytes: return x.to_bytes(2, "big") +def g(x: int) -> bytes: + return x.to_bytes(4, "little", True) [out] def f(x): x :: int @@ -221,5 +223,13 @@ def f(x): r1 :: bytes L0: r0 = 'big' - r1 = CPyTagged_ToBytes(x, 4, r1, 0) - return r0 + r1 = CPyTagged_ToBytes(x, 4, r0, 0) + return r1 +def g(x): + x :: int + r0 :: str + r1 :: bytes +L0: + r0 = 'little' + r1 = CPyTagged_ToBytes(x, 8, r0, 1) + return r1 From 3660a015818440b079752aab2578eefb3907fc93 Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Sun, 17 Aug 2025 06:21:58 +0000 Subject: [PATCH 16/44] fix py 3.10 --- mypyc/lib-rt/int_ops.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypyc/lib-rt/int_ops.c b/mypyc/lib-rt/int_ops.c index 213a0bc290a4..06ec9c5f2d43 100644 --- a/mypyc/lib-rt/int_ops.c +++ b/mypyc/lib-rt/int_ops.c @@ -605,7 +605,7 @@ PyObject *CPyTagged_ToBytes(CPyTagged self, Py_ssize_t length, PyObject *byteord return result; } -#if PY_VERSION_HEX >= 0x030A0000 +#if PY_VERSION_HEX >= 0x030C0000 // If not declared, declare it ourselves #ifndef PyLong_AsByteArray PyAPI_FUNC(int) PyLong_AsByteArray( From 6a8a83cacf9799aef791e4e731d851ebe63b57b1 Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Sun, 17 Aug 2025 06:25:00 +0000 Subject: [PATCH 17/44] use _PyLong_AsByteArray on all pythons --- mypyc/lib-rt/int_ops.c | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/mypyc/lib-rt/int_ops.c b/mypyc/lib-rt/int_ops.c index 06ec9c5f2d43..b27416732d49 100644 --- a/mypyc/lib-rt/int_ops.c +++ b/mypyc/lib-rt/int_ops.c @@ -605,21 +605,6 @@ PyObject *CPyTagged_ToBytes(CPyTagged self, Py_ssize_t length, PyObject *byteord return result; } -#if PY_VERSION_HEX >= 0x030C0000 -// If not declared, declare it ourselves -#ifndef PyLong_AsByteArray -PyAPI_FUNC(int) PyLong_AsByteArray( - PyLongObject* v, - unsigned char* bytes, - size_t n, - int little_endian, - int is_signed -); -#endif - #define CPy_PyLong_AsByteArray PyLong_AsByteArray -#else - #define CPy_PyLong_AsByteArray _PyLong_AsByteArray -#endif // Helper for CPyLong_ToBytes (Python 3.2+) PyObject *CPyLong_ToBytes(PyObject *v, Py_ssize_t length, const char *byteorder, int signed_flag) { @@ -639,7 +624,7 @@ PyObject *CPyLong_ToBytes(PyObject *v, Py_ssize_t length, const char *byteorder, PyErr_SetString(PyExc_ValueError, "byteorder must be either 'little' or 'big'"); return NULL; } - int res = CPy_PyLong_AsByteArray((PyLongObject *)v, bytes, length, little_endian, signed_flag); + int res = _PyLong_AsByteArray((PyLongObject *)v, bytes, length, little_endian, signed_flag); if (res < 0) { PyMem_Free(bytes); return NULL; From ec95008be3b02c3d42be6d60f6e818f7fbce99ef Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Sun, 17 Aug 2025 06:37:00 +0000 Subject: [PATCH 18/44] fix: py3.13 and 3.14 --- mypyc/lib-rt/int_ops.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mypyc/lib-rt/int_ops.c b/mypyc/lib-rt/int_ops.c index b27416732d49..62f870a1024e 100644 --- a/mypyc/lib-rt/int_ops.c +++ b/mypyc/lib-rt/int_ops.c @@ -624,7 +624,11 @@ PyObject *CPyLong_ToBytes(PyObject *v, Py_ssize_t length, const char *byteorder, PyErr_SetString(PyExc_ValueError, "byteorder must be either 'little' or 'big'"); return NULL; } +#if PY_VERSION_HEX >= 0x030D0000 // 3.13.0 + int res = _PyLong_AsByteArray((PyLongObject *)v, bytes, length, little_endian, signed_flag, 1); +#else int res = _PyLong_AsByteArray((PyLongObject *)v, bytes, length, little_endian, signed_flag); +#endif if (res < 0) { PyMem_Free(bytes); return NULL; From 95fa8b077bbd223027bb7c3ea7ab874b40001239 Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Sun, 17 Aug 2025 06:41:13 +0000 Subject: [PATCH 19/44] optimize if check --- mypyc/lib-rt/int_ops.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mypyc/lib-rt/int_ops.c b/mypyc/lib-rt/int_ops.c index 62f870a1024e..f6f670750117 100644 --- a/mypyc/lib-rt/int_ops.c +++ b/mypyc/lib-rt/int_ops.c @@ -614,11 +614,11 @@ PyObject *CPyLong_ToBytes(PyObject *v, Py_ssize_t length, const char *byteorder, PyErr_NoMemory(); return NULL; } - int little_endian = 0; - if (strcmp(byteorder, "little") == 0) { - little_endian = 1; - } else if (strcmp(byteorder, "big") == 0) { + int little_endian; + if (strcmp(byteorder, "big") == 0) { little_endian = 0; + } else if (strcmp(byteorder, "little") == 0) { + little_endian = 1; } else { PyMem_Free(bytes); PyErr_SetString(PyExc_ValueError, "byteorder must be either 'little' or 'big'"); From 326484bc3edc06b034b41a33474d1f0a15f78853 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Thu, 21 Aug 2025 09:46:33 -0400 Subject: [PATCH 20/44] Update CPy.h --- mypyc/lib-rt/CPy.h | 1 - 1 file changed, 1 deletion(-) diff --git a/mypyc/lib-rt/CPy.h b/mypyc/lib-rt/CPy.h index 4968b01f30d6..879cf128ba9f 100644 --- a/mypyc/lib-rt/CPy.h +++ b/mypyc/lib-rt/CPy.h @@ -140,7 +140,6 @@ CPyTagged CPyTagged_BitwiseLongOp_(CPyTagged a, CPyTagged b, char op); CPyTagged CPyTagged_Rshift_(CPyTagged left, CPyTagged right); CPyTagged CPyTagged_Lshift_(CPyTagged left, CPyTagged right); PyObject *CPyTagged_ToBytes(CPyTagged self, Py_ssize_t length, PyObject *byteorder, int signed_flag); -PyObject *CPyLong_ToBytes(PyObject *v, Py_ssize_t length, const char *byteorder, int signed_flag); PyObject *CPyTagged_Str(CPyTagged n); CPyTagged CPyTagged_FromFloat(double f); From 245a122eb5d17557e5a5b4f96e29c99940120e38 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Thu, 21 Aug 2025 09:47:56 -0400 Subject: [PATCH 21/44] Update int_ops.c --- mypyc/lib-rt/int_ops.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mypyc/lib-rt/int_ops.c b/mypyc/lib-rt/int_ops.c index f6f670750117..12a294d0d4f8 100644 --- a/mypyc/lib-rt/int_ops.c +++ b/mypyc/lib-rt/int_ops.c @@ -606,8 +606,7 @@ PyObject *CPyTagged_ToBytes(CPyTagged self, Py_ssize_t length, PyObject *byteord } -// Helper for CPyLong_ToBytes (Python 3.2+) -PyObject *CPyLong_ToBytes(PyObject *v, Py_ssize_t length, const char *byteorder, int signed_flag) { +static PyObject *CPyLong_ToBytes(PyObject *v, Py_ssize_t length, const char *byteorder, int signed_flag) { // This is a wrapper for PyLong_AsByteArray and PyBytes_FromStringAndSize unsigned char *bytes = (unsigned char *)PyMem_Malloc(length); if (!bytes) { From d2994e1d2c9f0c00b64627571b70434289d00180 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Thu, 21 Aug 2025 13:43:38 -0400 Subject: [PATCH 22/44] Update int_ops.c --- mypyc/lib-rt/int_ops.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/mypyc/lib-rt/int_ops.c b/mypyc/lib-rt/int_ops.c index 12a294d0d4f8..7bc23087064c 100644 --- a/mypyc/lib-rt/int_ops.c +++ b/mypyc/lib-rt/int_ops.c @@ -584,12 +584,7 @@ double CPyTagged_TrueDivide(CPyTagged x, CPyTagged y) { // int.to_bytes(length, byteorder, signed=False) PyObject *CPyTagged_ToBytes(CPyTagged self, Py_ssize_t length, PyObject *byteorder, int signed_flag) { - PyObject *pyint = CPyTagged_StealAsObject(self); - if (!PyLong_Check(pyint)) { - Py_DECREF(pyint); - PyErr_SetString(PyExc_TypeError, "self must be int"); - return NULL; - } + PyObject *pyint = CPyTagged_AsObject(self); if (!PyUnicode_Check(byteorder)) { Py_DECREF(pyint); PyErr_SetString(PyExc_TypeError, "byteorder must be str"); From 0715ca2e4c8ced7a6f148c277b24552fcb613dad Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Mon, 25 Aug 2025 13:45:24 +0000 Subject: [PATCH 23/44] test long --- mypyc/test-data/run-integers.test | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mypyc/test-data/run-integers.test b/mypyc/test-data/run-integers.test index 55289bde3835..1b1cf6430983 100644 --- a/mypyc/test-data/run-integers.test +++ b/mypyc/test-data/run-integers.test @@ -581,3 +581,5 @@ def test_to_bytes() -> None: assert to_bytes(255, 2, "little") == b'\xff\x00' assert to_bytes(-1, 2, "big", True) == b'\xff\xff' assert to_bytes(0, 1, "big") == b'\x00' + # test with a value that does not fit in 64 bits + assert to_bytes(10**30, 16, "big") == b'\x00\x00\x00\x0c\x9f,\x9c\xd0Ft\xed\xea@\x00\x00\x00' From 7cce1e73d20bef33c53ece4bc7a825bc46769608 Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Mon, 25 Aug 2025 13:50:06 +0000 Subject: [PATCH 24/44] add overflow error tests --- mypyc/test-data/run-integers.test | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/mypyc/test-data/run-integers.test b/mypyc/test-data/run-integers.test index 1b1cf6430983..cd4ed37cd0aa 100644 --- a/mypyc/test-data/run-integers.test +++ b/mypyc/test-data/run-integers.test @@ -574,6 +574,7 @@ class int: pass [case testIntToBytes] +from testutil import assertRaises def to_bytes(n: int, length: int, byteorder: str, signed: bool = False) -> bytes: return n.to_bytes(length, byteorder, signed=signed) def test_to_bytes() -> None: @@ -583,3 +584,12 @@ def test_to_bytes() -> None: assert to_bytes(0, 1, "big") == b'\x00' # test with a value that does not fit in 64 bits assert to_bytes(10**30, 16, "big") == b'\x00\x00\x00\x0c\x9f,\x9c\xd0Ft\xed\xea@\x00\x00\x00' + # unsigned, too large for 1 byte + with assertRaises(OverflowError): + to_bytes(256, 1, "big") + # signed, too small for 1 byte + with assertRaises(OverflowError): + to_bytes(-129, 1, "big", True) + # signed, too large for 1 byte + with assertRaises(OverflowError): + to_bytes(128, 1, "big", True) From 0d7cb57365595cef3bc2c94730a1f3b3166d1eb4 Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Mon, 8 Sep 2025 17:47:46 +0000 Subject: [PATCH 25/44] fix: move helper func up in C file --- mypyc/lib-rt/int_ops.c | 52 +++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 28 deletions(-) diff --git a/mypyc/lib-rt/int_ops.c b/mypyc/lib-rt/int_ops.c index 7bc23087064c..e30de88a26a5 100644 --- a/mypyc/lib-rt/int_ops.c +++ b/mypyc/lib-rt/int_ops.c @@ -582,52 +582,48 @@ double CPyTagged_TrueDivide(CPyTagged x, CPyTagged y) { return 1.0; } -// int.to_bytes(length, byteorder, signed=False) -PyObject *CPyTagged_ToBytes(CPyTagged self, Py_ssize_t length, PyObject *byteorder, int signed_flag) { - PyObject *pyint = CPyTagged_AsObject(self); - if (!PyUnicode_Check(byteorder)) { - Py_DECREF(pyint); - PyErr_SetString(PyExc_TypeError, "byteorder must be str"); - return NULL; - } - const char *order = PyUnicode_AsUTF8(byteorder); - if (!order) { - Py_DECREF(pyint); - return NULL; - } - PyObject *result = CPyLong_ToBytes(pyint, length, order, signed_flag); - Py_DECREF(pyint); - return result; -} - - static PyObject *CPyLong_ToBytes(PyObject *v, Py_ssize_t length, const char *byteorder, int signed_flag) { // This is a wrapper for PyLong_AsByteArray and PyBytes_FromStringAndSize - unsigned char *bytes = (unsigned char *)PyMem_Malloc(length); - if (!bytes) { - PyErr_NoMemory(); - return NULL; - } int little_endian; if (strcmp(byteorder, "big") == 0) { little_endian = 0; } else if (strcmp(byteorder, "little") == 0) { little_endian = 1; } else { - PyMem_Free(bytes); PyErr_SetString(PyExc_ValueError, "byteorder must be either 'little' or 'big'"); return NULL; } + PyObject *result = PyBytes_FromStringAndSize(NULL, length); + if (!result) { + return NULL; + } + unsigned char *bytes = (unsigned char *)PyBytes_AS_STRING(result); #if PY_VERSION_HEX >= 0x030D0000 // 3.13.0 int res = _PyLong_AsByteArray((PyLongObject *)v, bytes, length, little_endian, signed_flag, 1); #else int res = _PyLong_AsByteArray((PyLongObject *)v, bytes, length, little_endian, signed_flag); #endif if (res < 0) { - PyMem_Free(bytes); + Py_DECREF(result); + return NULL; + } + return result; +} + +// int.to_bytes(length, byteorder, signed=False) +PyObject *CPyTagged_ToBytes(CPyTagged self, Py_ssize_t length, PyObject *byteorder, int signed_flag) { + PyObject *pyint = CPyTagged_AsObject(self); + if (!PyUnicode_Check(byteorder)) { + Py_DECREF(pyint); + PyErr_SetString(PyExc_TypeError, "byteorder must be str"); + return NULL; + } + const char *order = PyUnicode_AsUTF8(byteorder); + if (!order) { + Py_DECREF(pyint); return NULL; } - PyObject *result = PyBytes_FromStringAndSize((const char *)bytes, length); - PyMem_Free(bytes); + PyObject *result = CPyLong_ToBytes(pyint, length, order, signed_flag); + Py_DECREF(pyint); return result; } From db09bc0c1ac9af983751e3a335d1d57aeb338a08 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Tue, 9 Sep 2025 12:51:55 -0400 Subject: [PATCH 26/44] Update ir.py --- mypyc/test-data/fixtures/ir.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypyc/test-data/fixtures/ir.py b/mypyc/test-data/fixtures/ir.py index 35b32f9af69f..c931f012cefe 100644 --- a/mypyc/test-data/fixtures/ir.py +++ b/mypyc/test-data/fixtures/ir.py @@ -84,7 +84,7 @@ def __lt__(self, n: int) -> bool: pass def __gt__(self, n: int) -> bool: pass def __le__(self, n: int) -> bool: pass def __ge__(self, n: int) -> bool: pass - def to_bytes(self, length: int, order: str, signed: bool = False) -> bytes: pass + def to_bytes(self, length: int, order: str, *, signed: bool = False) -> bytes: pass class str: @overload From a7151dd1fdbe3338e3070d8bd8668ac67ffa095e Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Tue, 9 Sep 2025 13:03:21 -0400 Subject: [PATCH 27/44] Update irbuild-int.test --- mypyc/test-data/irbuild-int.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypyc/test-data/irbuild-int.test b/mypyc/test-data/irbuild-int.test index 6fce8979492a..f830392377a5 100644 --- a/mypyc/test-data/irbuild-int.test +++ b/mypyc/test-data/irbuild-int.test @@ -215,7 +215,7 @@ L0: def f(x: int) -> bytes: return x.to_bytes(2, "big") def g(x: int) -> bytes: - return x.to_bytes(4, "little", True) + return x.to_bytes(4, "little", signed=True) [out] def f(x): x :: int From bdd320ec005aa163b48bc9bc83d497598f90bfc2 Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Wed, 10 Sep 2025 07:27:39 +0000 Subject: [PATCH 28/44] feat: specialize --- mypyc/irbuild/specialize.py | 28 +++++++++++++++++++++++++++- mypyc/lib-rt/CPy.h | 2 ++ mypyc/lib-rt/int_ops.c | 37 ++++++++++++++++++++++++++----------- mypyc/primitives/int_ops.py | 24 ++++++++++++++++++++++-- 4 files changed, 77 insertions(+), 14 deletions(-) diff --git a/mypyc/irbuild/specialize.py b/mypyc/irbuild/specialize.py index 0880c62bc7a5..50e31db4a708 100644 --- a/mypyc/irbuild/specialize.py +++ b/mypyc/irbuild/specialize.py @@ -94,7 +94,12 @@ isinstance_dict, ) from mypyc.primitives.float_ops import isinstance_float -from mypyc.primitives.int_ops import isinstance_int +from mypyc.primitives.int_ops import ( + isinstance_int, + int_to_big_endian_op, + int_to_bytes_op, + int_to_little_endian_op, +) from mypyc.primitives.list_ops import isinstance_list, new_list_set_item_op from mypyc.primitives.misc_ops import isinstance_bool from mypyc.primitives.set_ops import isinstance_frozenset, isinstance_set @@ -1000,3 +1005,24 @@ def translate_ord(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value if isinstance(arg, (StrExpr, BytesExpr)) and len(arg.value) == 1: return Integer(ord(arg.value)) return None + +@specialize_function("to_bytes", int_rprimitive) +def specialize_int_to_bytes(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value | None: + # int.to_bytes(length, byteorder, signed=False) + # args: [self, length, byteorder, (optional) signed] + if len(expr.args) < 3 or len(expr.args) > 4: + return None + self_arg = builder.accept(expr.args[0]) + length_arg = builder.accept(expr.args[1]) + byteorder_expr = expr.args[2] + signed_arg = builder.accept(expr.args[3]) if len(expr.args) == 4 else builder.false() + if isinstance(byteorder_expr, StrExpr): + if byteorder_expr.value == "little": + return builder.call_c(int_to_little_endian_op, [self_arg, length_arg, signed_arg], expr.line) + elif byteorder_expr.value == "big": + return builder.call_c(int_to_big_endian_op, [self_arg, length_arg, signed_arg], expr.line) + # Fallback to generic primitive op + byteorder_arg = builder.accept(byteorder_expr) + return builder.primitive_op( + int_to_bytes_op, [self_arg, length_arg, byteorder_arg, signed_arg], expr.line + ) diff --git a/mypyc/lib-rt/CPy.h b/mypyc/lib-rt/CPy.h index 06e453407870..f1f3def3e874 100644 --- a/mypyc/lib-rt/CPy.h +++ b/mypyc/lib-rt/CPy.h @@ -149,6 +149,8 @@ CPyTagged CPyTagged_BitwiseLongOp_(CPyTagged a, CPyTagged b, char op); CPyTagged CPyTagged_Rshift_(CPyTagged left, CPyTagged right); CPyTagged CPyTagged_Lshift_(CPyTagged left, CPyTagged right); PyObject *CPyTagged_ToBytes(CPyTagged self, Py_ssize_t length, PyObject *byteorder, int signed_flag); +PyObject *CPyTagged_ToBigEndianBytes(CPyTagged self, Py_ssize_t length, int signed_flag); +PyObject *CPyTagged_ToLittleEndianBytes(CPyTagged self, Py_ssize_t length, int signed_flag); PyObject *CPyTagged_Str(CPyTagged n); CPyTagged CPyTagged_FromFloat(double f); diff --git a/mypyc/lib-rt/int_ops.c b/mypyc/lib-rt/int_ops.c index e30de88a26a5..5ce8f34b24ac 100644 --- a/mypyc/lib-rt/int_ops.c +++ b/mypyc/lib-rt/int_ops.c @@ -582,17 +582,8 @@ double CPyTagged_TrueDivide(CPyTagged x, CPyTagged y) { return 1.0; } -static PyObject *CPyLong_ToBytes(PyObject *v, Py_ssize_t length, const char *byteorder, int signed_flag) { +static PyObject *CPyLong_ToBytes(PyObject *v, Py_ssize_t length, int little_endian, int signed_flag) { // This is a wrapper for PyLong_AsByteArray and PyBytes_FromStringAndSize - int little_endian; - if (strcmp(byteorder, "big") == 0) { - little_endian = 0; - } else if (strcmp(byteorder, "little") == 0) { - little_endian = 1; - } else { - PyErr_SetString(PyExc_ValueError, "byteorder must be either 'little' or 'big'"); - return NULL; - } PyObject *result = PyBytes_FromStringAndSize(NULL, length); if (!result) { return NULL; @@ -623,7 +614,31 @@ PyObject *CPyTagged_ToBytes(CPyTagged self, Py_ssize_t length, PyObject *byteord Py_DECREF(pyint); return NULL; } - PyObject *result = CPyLong_ToBytes(pyint, length, order, signed_flag); + int little_endian; + if (strcmp(order, "big") == 0) { + little_endian = 0; + } else if (strcmp(order, "little") == 0) { + little_endian = 1; + } else { + PyErr_SetString(PyExc_ValueError, "byteorder must be either 'little' or 'big'"); + return NULL; + } + PyObject *result = CPyLong_ToBytes(pyint, length, little_endian, signed_flag); + Py_DECREF(pyint); + return result; + +// int.to_bytes(length, byteorder="little", signed=False) +PyObject *CPyTagged_ToLittleEndianBytes(CPyTagged self, Py_ssize_t length, int signed_flag) { + PyObject *pyint = CPyTagged_AsObject(self); + PyObject *result = CPyLong_ToBytes(pyint, length, 1, signed_flag); + Py_DECREF(pyint); + return result; +} + +// int.to_bytes(length, "big", signed=False) +PyObject *CPyTagged_ToBigEndianBytes(CPyTagged self, Py_ssize_t length, int signed_flag) { + PyObject *pyint = CPyTagged_AsObject(self); + PyObject *result = CPyLong_ToBytes(pyint, length, 0, signed_flag); Py_DECREF(pyint); return result; } diff --git a/mypyc/primitives/int_ops.py b/mypyc/primitives/int_ops.py index 52a7076984e1..5d7dd8ecc602 100644 --- a/mypyc/primitives/int_ops.py +++ b/mypyc/primitives/int_ops.py @@ -324,11 +324,31 @@ def int_unary_op(name: str, c_function_name: str) -> PrimitiveDescription: error_kind=ERR_MAGIC, ) -# int.to_bytes(length, byteorder, signed) -method_op( +# int.to_bytes(length, byteorder, signed=...) +int_to_bytes_op = method_op( name="to_bytes", arg_types=[int_rprimitive, int_rprimitive, str_rprimitive, bool_rprimitive], return_type=bytes_rprimitive, c_function_name="CPyTagged_ToBytes", error_kind=ERR_MAGIC, ) + +# specialized custom_op cases for int.to_bytes: + +# int.to_bytes(length, "big") +# int.to_bytes(length, "big", signed=...) +int_to_big_endian_op = custom_op( + arg_types=[int_rprimitive, int_rprimitive, bool_rprimitive], + return_type=bytes_rprimitive, + c_function_name="CPyTagged_ToBigEndianBytes", + error_kind=ERR_MAGIC, +) + +# int.to_bytes(length, "little") +# int.to_bytes(length, "little", signed=...) +int_to_little_endian_op = custom_op( + arg_types=[int_rprimitive, int_rprimitive, bool_rprimitive], + return_type=bytes_rprimitive, + c_function_name="CPyTagged_ToLittleEndianBytes", + error_kind=ERR_MAGIC, +) From 4518aeaf02fc11f901bb27bd7afabc0d1b51d986 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 10 Sep 2025 07:29:09 +0000 Subject: [PATCH 29/44] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypyc/irbuild/specialize.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/mypyc/irbuild/specialize.py b/mypyc/irbuild/specialize.py index 50e31db4a708..704ac9b35616 100644 --- a/mypyc/irbuild/specialize.py +++ b/mypyc/irbuild/specialize.py @@ -95,10 +95,10 @@ ) from mypyc.primitives.float_ops import isinstance_float from mypyc.primitives.int_ops import ( - isinstance_int, int_to_big_endian_op, int_to_bytes_op, int_to_little_endian_op, + isinstance_int, ) from mypyc.primitives.list_ops import isinstance_list, new_list_set_item_op from mypyc.primitives.misc_ops import isinstance_bool @@ -1006,6 +1006,7 @@ def translate_ord(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value return Integer(ord(arg.value)) return None + @specialize_function("to_bytes", int_rprimitive) def specialize_int_to_bytes(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value | None: # int.to_bytes(length, byteorder, signed=False) @@ -1018,9 +1019,13 @@ def specialize_int_to_bytes(builder: IRBuilder, expr: CallExpr, callee: RefExpr) signed_arg = builder.accept(expr.args[3]) if len(expr.args) == 4 else builder.false() if isinstance(byteorder_expr, StrExpr): if byteorder_expr.value == "little": - return builder.call_c(int_to_little_endian_op, [self_arg, length_arg, signed_arg], expr.line) + return builder.call_c( + int_to_little_endian_op, [self_arg, length_arg, signed_arg], expr.line + ) elif byteorder_expr.value == "big": - return builder.call_c(int_to_big_endian_op, [self_arg, length_arg, signed_arg], expr.line) + return builder.call_c( + int_to_big_endian_op, [self_arg, length_arg, signed_arg], expr.line + ) # Fallback to generic primitive op byteorder_arg = builder.accept(byteorder_expr) return builder.primitive_op( From 97873da3fa1466f5efd719fc410ef69c6ef90a8b Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Wed, 10 Sep 2025 07:34:45 +0000 Subject: [PATCH 30/44] missing } --- mypyc/lib-rt/int_ops.c | 1 + 1 file changed, 1 insertion(+) diff --git a/mypyc/lib-rt/int_ops.c b/mypyc/lib-rt/int_ops.c index 5ce8f34b24ac..5abebb9d1048 100644 --- a/mypyc/lib-rt/int_ops.c +++ b/mypyc/lib-rt/int_ops.c @@ -626,6 +626,7 @@ PyObject *CPyTagged_ToBytes(CPyTagged self, Py_ssize_t length, PyObject *byteord PyObject *result = CPyLong_ToBytes(pyint, length, little_endian, signed_flag); Py_DECREF(pyint); return result; +} // int.to_bytes(length, byteorder="little", signed=False) PyObject *CPyTagged_ToLittleEndianBytes(CPyTagged self, Py_ssize_t length, int signed_flag) { From 26cd5c617996d5fdaa5ac2b287d39203bd578a56 Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Wed, 10 Sep 2025 07:48:10 +0000 Subject: [PATCH 31/44] fix: specialize --- mypyc/irbuild/specialize.py | 2 +- mypyc/primitives/int_ops.py | 12 +----------- mypyc/test-data/irbuild-int.test | 26 ++++++++++++++++---------- 3 files changed, 18 insertions(+), 22 deletions(-) diff --git a/mypyc/irbuild/specialize.py b/mypyc/irbuild/specialize.py index 704ac9b35616..b7f0b7124de2 100644 --- a/mypyc/irbuild/specialize.py +++ b/mypyc/irbuild/specialize.py @@ -1028,6 +1028,6 @@ def specialize_int_to_bytes(builder: IRBuilder, expr: CallExpr, callee: RefExpr) ) # Fallback to generic primitive op byteorder_arg = builder.accept(byteorder_expr) - return builder.primitive_op( + return builder.call_c( int_to_bytes_op, [self_arg, length_arg, byteorder_arg, signed_arg], expr.line ) diff --git a/mypyc/primitives/int_ops.py b/mypyc/primitives/int_ops.py index 5d7dd8ecc602..9edadf8335ca 100644 --- a/mypyc/primitives/int_ops.py +++ b/mypyc/primitives/int_ops.py @@ -314,19 +314,9 @@ def int_unary_op(name: str, c_function_name: str) -> PrimitiveDescription: error_kind=ERR_NEVER, ) -# int.to_bytes(length, byteorder) -method_op( - name="to_bytes", - arg_types=[int_rprimitive, int_rprimitive, str_rprimitive], - extra_int_constants=[(0, bool_rprimitive)], - return_type=bytes_rprimitive, - c_function_name="CPyTagged_ToBytes", - error_kind=ERR_MAGIC, -) # int.to_bytes(length, byteorder, signed=...) -int_to_bytes_op = method_op( - name="to_bytes", +int_to_bytes_op = custom_op( arg_types=[int_rprimitive, int_rprimitive, str_rprimitive, bool_rprimitive], return_type=bytes_rprimitive, c_function_name="CPyTagged_ToBytes", diff --git a/mypyc/test-data/irbuild-int.test b/mypyc/test-data/irbuild-int.test index f830392377a5..f04f9a9368d9 100644 --- a/mypyc/test-data/irbuild-int.test +++ b/mypyc/test-data/irbuild-int.test @@ -216,20 +216,26 @@ def f(x: int) -> bytes: return x.to_bytes(2, "big") def g(x: int) -> bytes: return x.to_bytes(4, "little", signed=True) +def h(x: int, byteorder: str) -> bytes: + return x.to_bytes(8, byteorder) + [out] def f(x): x :: int - r0 :: str - r1 :: bytes + r0 :: bytes L0: - r0 = 'big' - r1 = CPyTagged_ToBytes(x, 4, r0, 0) - return r1 + r0 = CPyTagged_ToBigEndianBytes(x, 4, 0) + return r0 def g(x): x :: int - r0 :: str - r1 :: bytes + r0 :: bytes L0: - r0 = 'little' - r1 = CPyTagged_ToBytes(x, 8, r0, 1) - return r1 + r0 = CPyTagged_ToLittleEndianBytes(x, 8, 1) + return r0 +def h(x): + x :: int + byteorder :: str + r0 :: bytes +L0: + r0 = CPyTagged_ToBytes(x, 8, byteorder, 0) + return r0 From 30c4a309f511d96c4984f0e1fa5b542ed0f6b4a6 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 10 Sep 2025 07:49:37 +0000 Subject: [PATCH 32/44] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypyc/primitives/int_ops.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mypyc/primitives/int_ops.py b/mypyc/primitives/int_ops.py index 9edadf8335ca..a809c029a3b5 100644 --- a/mypyc/primitives/int_ops.py +++ b/mypyc/primitives/int_ops.py @@ -37,7 +37,6 @@ custom_op, function_op, load_address_op, - method_op, unary_op, ) From 84acab31f86b35a83d20212207cf14151b86f04e Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Wed, 10 Sep 2025 08:07:40 +0000 Subject: [PATCH 33/44] fix --- mypyc/irbuild/specialize.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/mypyc/irbuild/specialize.py b/mypyc/irbuild/specialize.py index b7f0b7124de2..3a3dc9e260eb 100644 --- a/mypyc/irbuild/specialize.py +++ b/mypyc/irbuild/specialize.py @@ -66,6 +66,7 @@ is_int64_rprimitive, is_int_rprimitive, is_list_rprimitive, + is_str_rprimitive, is_uint8_rprimitive, list_rprimitive, set_rprimitive, @@ -1011,12 +1012,18 @@ def translate_ord(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value def specialize_int_to_bytes(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value | None: # int.to_bytes(length, byteorder, signed=False) # args: [self, length, byteorder, (optional) signed] - if len(expr.args) < 3 or len(expr.args) > 4: + if len(expr.args) == 2: + signed_arg = builder.false() + elif len(expr.args) == 3: + signed_arg = builder.accept(expr.args[2]) + else: + return None + if not isinstance(callee, MemberExpr) or not is_str_rprimitive(builder.node_type(byteorder_expr)): return None - self_arg = builder.accept(expr.args[0]) - length_arg = builder.accept(expr.args[1]) - byteorder_expr = expr.args[2] - signed_arg = builder.accept(expr.args[3]) if len(expr.args) == 4 else builder.false() + + self_arg = builder.accept(callee.expr) + length_arg = builder.accept(expr.args[0]) + byteorder_expr = expr.args[1] if isinstance(byteorder_expr, StrExpr): if byteorder_expr.value == "little": return builder.call_c( From b5a6c061b65121e0d7bf0166a382cffda8502625 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 10 Sep 2025 08:09:20 +0000 Subject: [PATCH 34/44] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypyc/irbuild/specialize.py | 6 ++++-- mypyc/primitives/int_ops.py | 8 +------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/mypyc/irbuild/specialize.py b/mypyc/irbuild/specialize.py index 3a3dc9e260eb..a307dcacd977 100644 --- a/mypyc/irbuild/specialize.py +++ b/mypyc/irbuild/specialize.py @@ -1018,9 +1018,11 @@ def specialize_int_to_bytes(builder: IRBuilder, expr: CallExpr, callee: RefExpr) signed_arg = builder.accept(expr.args[2]) else: return None - if not isinstance(callee, MemberExpr) or not is_str_rprimitive(builder.node_type(byteorder_expr)): + if not isinstance(callee, MemberExpr) or not is_str_rprimitive( + builder.node_type(byteorder_expr) + ): return None - + self_arg = builder.accept(callee.expr) length_arg = builder.accept(expr.args[0]) byteorder_expr = expr.args[1] diff --git a/mypyc/primitives/int_ops.py b/mypyc/primitives/int_ops.py index a809c029a3b5..cba204b3520c 100644 --- a/mypyc/primitives/int_ops.py +++ b/mypyc/primitives/int_ops.py @@ -32,13 +32,7 @@ str_rprimitive, void_rtype, ) -from mypyc.primitives.registry import ( - binary_op, - custom_op, - function_op, - load_address_op, - unary_op, -) +from mypyc.primitives.registry import binary_op, custom_op, function_op, load_address_op, unary_op # Constructors for builtins.int and native int types have the same behavior. In # interpreted mode, native int types are just aliases to 'int'. From ef5b1d785bc55de7217dfe39bab3961a5b8ae405 Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Wed, 10 Sep 2025 08:15:55 +0000 Subject: [PATCH 35/44] fix --- mypyc/irbuild/specialize.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mypyc/irbuild/specialize.py b/mypyc/irbuild/specialize.py index 3a3dc9e260eb..5c99c10d4f9b 100644 --- a/mypyc/irbuild/specialize.py +++ b/mypyc/irbuild/specialize.py @@ -1018,12 +1018,11 @@ def specialize_int_to_bytes(builder: IRBuilder, expr: CallExpr, callee: RefExpr) signed_arg = builder.accept(expr.args[2]) else: return None - if not isinstance(callee, MemberExpr) or not is_str_rprimitive(builder.node_type(byteorder_expr)): - return None - self_arg = builder.accept(callee.expr) length_arg = builder.accept(expr.args[0]) byteorder_expr = expr.args[1] + if not isinstance(callee, MemberExpr) or not is_str_rprimitive(builder.node_type(byteorder_expr)): + return None if isinstance(byteorder_expr, StrExpr): if byteorder_expr.value == "little": return builder.call_c( From 9b69d58c571c20e019021eef3b9042a18f3178bd Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 10 Sep 2025 08:18:29 +0000 Subject: [PATCH 36/44] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypyc/irbuild/specialize.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mypyc/irbuild/specialize.py b/mypyc/irbuild/specialize.py index 5c99c10d4f9b..abcc0cff9008 100644 --- a/mypyc/irbuild/specialize.py +++ b/mypyc/irbuild/specialize.py @@ -1021,7 +1021,9 @@ def specialize_int_to_bytes(builder: IRBuilder, expr: CallExpr, callee: RefExpr) self_arg = builder.accept(callee.expr) length_arg = builder.accept(expr.args[0]) byteorder_expr = expr.args[1] - if not isinstance(callee, MemberExpr) or not is_str_rprimitive(builder.node_type(byteorder_expr)): + if not isinstance(callee, MemberExpr) or not is_str_rprimitive( + builder.node_type(byteorder_expr) + ): return None if isinstance(byteorder_expr, StrExpr): if byteorder_expr.value == "little": From 6a5de072011cea3579a2cb079e3c25dee4419434 Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Wed, 10 Sep 2025 08:19:49 +0000 Subject: [PATCH 37/44] fix --- mypyc/irbuild/specialize.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mypyc/irbuild/specialize.py b/mypyc/irbuild/specialize.py index 5c99c10d4f9b..5a781ae35f80 100644 --- a/mypyc/irbuild/specialize.py +++ b/mypyc/irbuild/specialize.py @@ -1018,10 +1018,12 @@ def specialize_int_to_bytes(builder: IRBuilder, expr: CallExpr, callee: RefExpr) signed_arg = builder.accept(expr.args[2]) else: return None + if not isinstance(callee, MemberExpr): + return None self_arg = builder.accept(callee.expr) length_arg = builder.accept(expr.args[0]) byteorder_expr = expr.args[1] - if not isinstance(callee, MemberExpr) or not is_str_rprimitive(builder.node_type(byteorder_expr)): + if not is_str_rprimitive(builder.node_type(byteorder_expr)): return None if isinstance(byteorder_expr, StrExpr): if byteorder_expr.value == "little": From f5f9d308f7520eb27a6b26ffeed4dfef6df0a8a0 Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Wed, 10 Sep 2025 08:21:30 +0000 Subject: [PATCH 38/44] fix --- mypyc/irbuild/specialize.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mypyc/irbuild/specialize.py b/mypyc/irbuild/specialize.py index 5a781ae35f80..8ffda86d1b30 100644 --- a/mypyc/irbuild/specialize.py +++ b/mypyc/irbuild/specialize.py @@ -1023,7 +1023,11 @@ def specialize_int_to_bytes(builder: IRBuilder, expr: CallExpr, callee: RefExpr) self_arg = builder.accept(callee.expr) length_arg = builder.accept(expr.args[0]) byteorder_expr = expr.args[1] - if not is_str_rprimitive(builder.node_type(byteorder_expr)): + if not ( + is_int_rprimitive(builder.node_type(length_arg)) + and is_str_rprimitive(builder.node_type(byteorder_expr)) + and is_bool_rprimitive(builder.node_type(signed_arg)) + ): return None if isinstance(byteorder_expr, StrExpr): if byteorder_expr.value == "little": From 9a27e5dd1dad48a0702b1a0bb1badd32368b650e Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Wed, 10 Sep 2025 05:11:46 -0400 Subject: [PATCH 39/44] Update irbuild-int.test --- mypyc/test-data/irbuild-int.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypyc/test-data/irbuild-int.test b/mypyc/test-data/irbuild-int.test index f04f9a9368d9..9555588abf27 100644 --- a/mypyc/test-data/irbuild-int.test +++ b/mypyc/test-data/irbuild-int.test @@ -237,5 +237,5 @@ def h(x): byteorder :: str r0 :: bytes L0: - r0 = CPyTagged_ToBytes(x, 8, byteorder, 0) + r0 = CPyTagged_ToBytes(x, 16, byteorder, 0) return r0 From 6c82357dcc33e617d86317ba8829fdf3f15b0cbd Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Wed, 10 Sep 2025 05:13:35 -0400 Subject: [PATCH 40/44] Update run-integers.test --- mypyc/test-data/run-integers.test | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/mypyc/test-data/run-integers.test b/mypyc/test-data/run-integers.test index cd4ed37cd0aa..576ae38d54eb 100644 --- a/mypyc/test-data/run-integers.test +++ b/mypyc/test-data/run-integers.test @@ -578,12 +578,12 @@ from testutil import assertRaises def to_bytes(n: int, length: int, byteorder: str, signed: bool = False) -> bytes: return n.to_bytes(length, byteorder, signed=signed) def test_to_bytes() -> None: - assert to_bytes(255, 2, "big") == b'\x00\xff' - assert to_bytes(255, 2, "little") == b'\xff\x00' - assert to_bytes(-1, 2, "big", True) == b'\xff\xff' - assert to_bytes(0, 1, "big") == b'\x00' + assert to_bytes(255, 2, "big") == b'\x00\xff', to_bytes(255, 2, "big") + assert to_bytes(255, 2, "little") == b'\xff\x00', to_bytes(255, 2, "little") + assert to_bytes(-1, 2, "big", True) == b'\xff\xff', to_bytes(-1, 2, "big", True) + assert to_bytes(0, 1, "big") == b'\x00', to_bytes(0, 1, "big") # test with a value that does not fit in 64 bits - assert to_bytes(10**30, 16, "big") == b'\x00\x00\x00\x0c\x9f,\x9c\xd0Ft\xed\xea@\x00\x00\x00' + assert to_bytes(10**30, 16, "big") == b'\x00\x00\x00\x0c\x9f,\x9c\xd0Ft\xed\xea@\x00\x00\x00', to_bytes(10**30, 16, "big") # unsigned, too large for 1 byte with assertRaises(OverflowError): to_bytes(256, 1, "big") From c4d967385b77490add7aeafd0b635a1103732869 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Wed, 10 Sep 2025 05:14:02 -0400 Subject: [PATCH 41/44] Update irbuild-int.test --- mypyc/test-data/irbuild-int.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypyc/test-data/irbuild-int.test b/mypyc/test-data/irbuild-int.test index 9555588abf27..72d0d2a591aa 100644 --- a/mypyc/test-data/irbuild-int.test +++ b/mypyc/test-data/irbuild-int.test @@ -232,7 +232,7 @@ def g(x): L0: r0 = CPyTagged_ToLittleEndianBytes(x, 8, 1) return r0 -def h(x): +def h(x, byteorder): x :: int byteorder :: str r0 :: bytes From 3ad1e504ae7bc07bbdff6f7144dd6218a8728ffa Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Wed, 10 Sep 2025 05:24:40 -0400 Subject: [PATCH 42/44] maybe fix --- mypyc/primitives/int_ops.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mypyc/primitives/int_ops.py b/mypyc/primitives/int_ops.py index cba204b3520c..d296ff2b2595 100644 --- a/mypyc/primitives/int_ops.py +++ b/mypyc/primitives/int_ops.py @@ -310,7 +310,7 @@ def int_unary_op(name: str, c_function_name: str) -> PrimitiveDescription: # int.to_bytes(length, byteorder, signed=...) int_to_bytes_op = custom_op( - arg_types=[int_rprimitive, int_rprimitive, str_rprimitive, bool_rprimitive], + arg_types=[int_rprimitive, c_pyssize_t_rprimitive, str_rprimitive, bool_rprimitive], return_type=bytes_rprimitive, c_function_name="CPyTagged_ToBytes", error_kind=ERR_MAGIC, @@ -321,7 +321,7 @@ def int_unary_op(name: str, c_function_name: str) -> PrimitiveDescription: # int.to_bytes(length, "big") # int.to_bytes(length, "big", signed=...) int_to_big_endian_op = custom_op( - arg_types=[int_rprimitive, int_rprimitive, bool_rprimitive], + arg_types=[int_rprimitive, c_pyssize_t_rprimitive, bool_rprimitive], return_type=bytes_rprimitive, c_function_name="CPyTagged_ToBigEndianBytes", error_kind=ERR_MAGIC, @@ -330,7 +330,7 @@ def int_unary_op(name: str, c_function_name: str) -> PrimitiveDescription: # int.to_bytes(length, "little") # int.to_bytes(length, "little", signed=...) int_to_little_endian_op = custom_op( - arg_types=[int_rprimitive, int_rprimitive, bool_rprimitive], + arg_types=[int_rprimitive, c_pyssize_t_rprimitive, bool_rprimitive], return_type=bytes_rprimitive, c_function_name="CPyTagged_ToLittleEndianBytes", error_kind=ERR_MAGIC, From 77535d34cff4b906789f9d6c6e2d12fc2a53f196 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Wed, 10 Sep 2025 05:25:40 -0400 Subject: [PATCH 43/44] Update int_ops.py --- mypyc/primitives/int_ops.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/mypyc/primitives/int_ops.py b/mypyc/primitives/int_ops.py index d296ff2b2595..2af53075d3a6 100644 --- a/mypyc/primitives/int_ops.py +++ b/mypyc/primitives/int_ops.py @@ -307,16 +307,7 @@ def int_unary_op(name: str, c_function_name: str) -> PrimitiveDescription: error_kind=ERR_NEVER, ) - -# int.to_bytes(length, byteorder, signed=...) -int_to_bytes_op = custom_op( - arg_types=[int_rprimitive, c_pyssize_t_rprimitive, str_rprimitive, bool_rprimitive], - return_type=bytes_rprimitive, - c_function_name="CPyTagged_ToBytes", - error_kind=ERR_MAGIC, -) - -# specialized custom_op cases for int.to_bytes: +# specialized custom_op cases for int.to_bytes # int.to_bytes(length, "big") # int.to_bytes(length, "big", signed=...) @@ -335,3 +326,11 @@ def int_unary_op(name: str, c_function_name: str) -> PrimitiveDescription: c_function_name="CPyTagged_ToLittleEndianBytes", error_kind=ERR_MAGIC, ) + +# int.to_bytes(length, byteorder, signed=...) +int_to_bytes_op = custom_op( + arg_types=[int_rprimitive, c_pyssize_t_rprimitive, str_rprimitive, bool_rprimitive], + return_type=bytes_rprimitive, + c_function_name="CPyTagged_ToBytes", + error_kind=ERR_MAGIC, +) From d291fb1f450ddf9a0218a42a54efe42f0ed85bba Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Wed, 10 Sep 2025 05:36:29 -0400 Subject: [PATCH 44/44] Update irbuild-int.test --- mypyc/test-data/irbuild-int.test | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mypyc/test-data/irbuild-int.test b/mypyc/test-data/irbuild-int.test index 72d0d2a591aa..82d6d85907c6 100644 --- a/mypyc/test-data/irbuild-int.test +++ b/mypyc/test-data/irbuild-int.test @@ -224,18 +224,18 @@ def f(x): x :: int r0 :: bytes L0: - r0 = CPyTagged_ToBigEndianBytes(x, 4, 0) + r0 = CPyTagged_ToBigEndianBytes(x, 2, 0) return r0 def g(x): x :: int r0 :: bytes L0: - r0 = CPyTagged_ToLittleEndianBytes(x, 8, 1) + r0 = CPyTagged_ToLittleEndianBytes(x, 4, 1) return r0 def h(x, byteorder): x :: int byteorder :: str r0 :: bytes L0: - r0 = CPyTagged_ToBytes(x, 16, byteorder, 0) + r0 = CPyTagged_ToBytes(x, 8, byteorder, 0) return r0