From 1ad71229fd1fbd0eabb3aa341aea26961acffb2b Mon Sep 17 00:00:00 2001 From: Piotr Sawicki Date: Mon, 14 Jul 2025 13:57:18 +0200 Subject: [PATCH 1/3] Add primitives for isinstance of built-in types --- mypyc/irbuild/specialize.py | 23 +++- mypyc/primitives/bytes_ops.py | 21 ++- mypyc/primitives/dict_ops.py | 9 ++ mypyc/primitives/float_ops.py | 9 ++ mypyc/primitives/int_ops.py | 9 ++ mypyc/primitives/misc_ops.py | 9 ++ mypyc/primitives/set_ops.py | 20 ++- mypyc/primitives/str_ops.py | 9 ++ mypyc/primitives/tuple_ops.py | 10 ++ mypyc/test-data/irbuild-basic.test | 37 ++---- mypyc/test-data/irbuild-i64.test | 28 ++-- mypyc/test-data/irbuild-isinstance.test | 162 ++++++++++++++++++------ mypyc/test-data/irbuild-optional.test | 30 ++--- mypyc/test-data/run-bools.test | 12 ++ mypyc/test-data/run-bytes.test | 49 +++++++ mypyc/test-data/run-dicts.test | 32 +++++ mypyc/test-data/run-floats.test | 19 +++ mypyc/test-data/run-integers.test | 21 +++ mypyc/test-data/run-sets.test | 52 ++++++++ mypyc/test-data/run-strings.test | 32 +++++ mypyc/test-data/run-tuples.test | 32 +++++ 21 files changed, 522 insertions(+), 103 deletions(-) diff --git a/mypyc/irbuild/specialize.py b/mypyc/irbuild/specialize.py index b490c2a52e57..3015640fb3fd 100644 --- a/mypyc/irbuild/specialize.py +++ b/mypyc/irbuild/specialize.py @@ -83,19 +83,26 @@ join_formatted_strings, tokenizer_format_call, ) +from mypyc.primitives.bytes_ops import isinstance_bytearray, isinstance_bytes from mypyc.primitives.dict_ops import ( dict_items_op, dict_keys_op, dict_setdefault_spec_init_op, dict_values_op, + isinstance_dict, ) +from mypyc.primitives.float_ops import isinstance_float +from mypyc.primitives.int_ops import isinstance_int 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 from mypyc.primitives.str_ops import ( + isinstance_str, str_encode_ascii_strict, str_encode_latin1_strict, str_encode_utf8_strict, ) -from mypyc.primitives.tuple_ops import new_tuple_set_item_op +from mypyc.primitives.tuple_ops import isinstance_tuple, new_tuple_set_item_op # Specializers are attempted before compiling the arguments to the # function. Specializers can return None to indicate that they failed @@ -546,7 +553,19 @@ def gen_inner_stmts() -> None: return retval -isinstance_primitives: Final = {"builtins.list": isinstance_list} +isinstance_primitives: Final = { + "builtins.bool": isinstance_bool, + "builtins.bytearray": isinstance_bytearray, + "builtins.bytes": isinstance_bytes, + "builtins.dict": isinstance_dict, + "builtins.float": isinstance_float, + "builtins.frozenset": isinstance_frozenset, + "builtins.int": isinstance_int, + "builtins.list": isinstance_list, + "builtins.set": isinstance_set, + "builtins.str": isinstance_str, + "builtins.tuple": isinstance_tuple, +} @specialize_function("builtins.isinstance") diff --git a/mypyc/primitives/bytes_ops.py b/mypyc/primitives/bytes_ops.py index 1afd196cff84..78aa67f3623e 100644 --- a/mypyc/primitives/bytes_ops.py +++ b/mypyc/primitives/bytes_ops.py @@ -2,9 +2,10 @@ from __future__ import annotations -from mypyc.ir.ops import ERR_MAGIC +from mypyc.ir.ops import ERR_MAGIC, ERR_NEVER from mypyc.ir.rtypes import ( RUnion, + bit_rprimitive, bytes_rprimitive, c_int_rprimitive, c_pyssize_t_rprimitive, @@ -35,6 +36,15 @@ error_kind=ERR_MAGIC, ) +# isinstance(obj, bytes) +isinstance_bytes = function_op( + name="builtins.isinstance", + arg_types=[object_rprimitive], + return_type=bit_rprimitive, + c_function_name="PyBytes_Check", + error_kind=ERR_NEVER, +) + # bytearray(obj) function_op( name="builtins.bytearray", @@ -44,6 +54,15 @@ error_kind=ERR_MAGIC, ) +# isinstance(obj, bytearray) +isinstance_bytearray = function_op( + name="builtins.isinstance", + arg_types=[object_rprimitive], + return_type=bit_rprimitive, + c_function_name="PyByteArray_Check", + error_kind=ERR_NEVER, +) + # bytes ==/!= (return -1/0/1) bytes_compare = custom_op( arg_types=[bytes_rprimitive, bytes_rprimitive], diff --git a/mypyc/primitives/dict_ops.py b/mypyc/primitives/dict_ops.py index 3f289c3c6f08..3c30a49c6b1e 100644 --- a/mypyc/primitives/dict_ops.py +++ b/mypyc/primitives/dict_ops.py @@ -71,6 +71,15 @@ error_kind=ERR_MAGIC, ) +# isinstance(obj, dict) +isinstance_dict = function_op( + name="builtins.isinstance", + arg_types=[object_rprimitive], + return_type=bit_rprimitive, + c_function_name="PyDict_Check", + error_kind=ERR_NEVER, +) + # dict[key] dict_get_item_op = method_op( name="__getitem__", diff --git a/mypyc/primitives/float_ops.py b/mypyc/primitives/float_ops.py index 14e8d4caf09c..dc4956b1437c 100644 --- a/mypyc/primitives/float_ops.py +++ b/mypyc/primitives/float_ops.py @@ -4,6 +4,7 @@ from mypyc.ir.ops import ERR_MAGIC, ERR_MAGIC_OVERLAPPING, ERR_NEVER from mypyc.ir.rtypes import ( + bit_rprimitive, bool_rprimitive, float_rprimitive, int_rprimitive, @@ -166,3 +167,11 @@ c_function_name="CPyFloat_IsNaN", error_kind=ERR_NEVER, ) + +isinstance_float = function_op( + name="builtins.isinstance", + arg_types=[object_rprimitive], + return_type=bit_rprimitive, + c_function_name="PyFloat_Check", + error_kind=ERR_NEVER, +) diff --git a/mypyc/primitives/int_ops.py b/mypyc/primitives/int_ops.py index 9b8b48da602d..75246a74bc59 100644 --- a/mypyc/primitives/int_ops.py +++ b/mypyc/primitives/int_ops.py @@ -296,3 +296,12 @@ def int_unary_op(name: str, c_function_name: str) -> PrimitiveDescription: c_function_name="CPyUInt8_Overflow", error_kind=ERR_ALWAYS, ) + +# isinstance(obj, int) +isinstance_int = function_op( + name="builtints.isinstance", + arg_types=[object_rprimitive], + return_type=bit_rprimitive, + c_function_name="PyLong_Check", + error_kind=ERR_NEVER, +) diff --git a/mypyc/primitives/misc_ops.py b/mypyc/primitives/misc_ops.py index 7494b46790ce..114a5f0a9823 100644 --- a/mypyc/primitives/misc_ops.py +++ b/mypyc/primitives/misc_ops.py @@ -191,6 +191,15 @@ truncated_type=bool_rprimitive, ) +# isinstance(obj, bool) +isinstance_bool = function_op( + name="builtins.isinstance", + arg_types=[object_rprimitive], + return_type=bit_rprimitive, + c_function_name="PyBool_Check", + error_kind=ERR_NEVER, +) + # slice(start, stop, step) new_slice_op = function_op( name="builtins.slice", diff --git a/mypyc/primitives/set_ops.py b/mypyc/primitives/set_ops.py index eb7c9b46609d..89c88d6a8ca7 100644 --- a/mypyc/primitives/set_ops.py +++ b/mypyc/primitives/set_ops.py @@ -2,7 +2,7 @@ from __future__ import annotations -from mypyc.ir.ops import ERR_FALSE, ERR_MAGIC +from mypyc.ir.ops import ERR_FALSE, ERR_MAGIC, ERR_NEVER from mypyc.ir.rtypes import ( bit_rprimitive, bool_rprimitive, @@ -64,6 +64,24 @@ error_kind=ERR_MAGIC, ) +# isinstance(obj, set) +isinstance_set = function_op( + name="builtins.isinstance", + arg_types=[object_rprimitive], + return_type=bit_rprimitive, + c_function_name="PySet_Check", + error_kind=ERR_NEVER, +) + +# isinstance(obj, frozenset) +isinstance_frozenset = function_op( + name="builtins.isinstance", + arg_types=[object_rprimitive], + return_type=bit_rprimitive, + c_function_name="PyFrozenSet_Check", + error_kind=ERR_NEVER, +) + # item in set set_in_op = binary_op( name="in", diff --git a/mypyc/primitives/str_ops.py b/mypyc/primitives/str_ops.py index 37dbdf21bb5d..305c830b9565 100644 --- a/mypyc/primitives/str_ops.py +++ b/mypyc/primitives/str_ops.py @@ -48,6 +48,15 @@ error_kind=ERR_MAGIC, ) +# isinstance(obj, str) +isinstance_str = function_op( + name="builtins.isinstance", + arg_types=[object_rprimitive], + return_type=bit_rprimitive, + c_function_name="PyUnicode_Check", + error_kind=ERR_NEVER, +) + # str1 + str2 binary_op( name="+", diff --git a/mypyc/primitives/tuple_ops.py b/mypyc/primitives/tuple_ops.py index e680b6943d84..3b25c350cf03 100644 --- a/mypyc/primitives/tuple_ops.py +++ b/mypyc/primitives/tuple_ops.py @@ -9,6 +9,7 @@ from mypyc.ir.ops import ERR_MAGIC, ERR_NEVER from mypyc.ir.rtypes import ( c_pyssize_t_rprimitive, + bit_rprimitive, int_rprimitive, list_rprimitive, object_rprimitive, @@ -83,6 +84,15 @@ error_kind=ERR_MAGIC, ) +# isinstance(obj, tuple) +isinstance_tuple = function_op( + name="builtins.isinstance", + arg_types=[object_rprimitive], + return_type=bit_rprimitive, + c_function_name="PyTuple_Check", + error_kind=ERR_NEVER, +) + # tuple + tuple binary_op( name="+", diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index ea1b3d06869a..4a7d315ec836 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -1581,24 +1581,18 @@ def main() -> None: [out] def foo(x): x :: union[int, str] - r0 :: object - r1 :: i32 - r2 :: bit - r3 :: bool - r4 :: __main__.B - r5 :: __main__.A + r0 :: bit + r1 :: __main__.B + r2 :: __main__.A L0: - r0 = load_address PyLong_Type - r1 = PyObject_IsInstance(x, r0) - r2 = r1 >= 0 :: signed - r3 = truncate r1: i32 to builtins.bool - if r3 goto L1 else goto L2 :: bool + r0 = PyLong_Check(x) + if r0 goto L1 else goto L2 :: bool L1: - r4 = B() - return r4 + r1 = B() + return r1 L2: - r5 = A() - return r5 + r2 = A() + return r2 def main(): r0 :: object r1 :: __main__.A @@ -3389,16 +3383,11 @@ def f(x: object) -> bool: return isinstance(x, bool) [out] def f(x): - x, r0 :: object - r1 :: i32 - r2 :: bit - r3 :: bool + x :: object + r0 :: bit L0: - r0 = load_address PyBool_Type - r1 = PyObject_IsInstance(x, r0) - r2 = r1 >= 0 :: signed - r3 = truncate r1: i32 to builtins.bool - return r3 + r0 = PyBool_Check(x) + return r0 [case testRangeObject] def range_object() -> None: diff --git a/mypyc/test-data/irbuild-i64.test b/mypyc/test-data/irbuild-i64.test index c59e306b09df..e55c3bfe2acc 100644 --- a/mypyc/test-data/irbuild-i64.test +++ b/mypyc/test-data/irbuild-i64.test @@ -2046,27 +2046,21 @@ L2: return r6 def narrow2(x): x :: union[__main__.C, i64] - r0 :: object - r1 :: i32 - r2 :: bit - r3 :: bool - r4 :: i64 - r5 :: __main__.C - r6 :: i64 + r0 :: bit + r1 :: i64 + r2 :: __main__.C + r3 :: i64 L0: - r0 = load_address PyLong_Type - r1 = PyObject_IsInstance(x, r0) - r2 = r1 >= 0 :: signed - r3 = truncate r1: i32 to builtins.bool - if r3 goto L1 else goto L2 :: bool + r0 = PyLong_Check(x) + if r0 goto L1 else goto L2 :: bool L1: - r4 = unbox(i64, x) - return r4 + r1 = unbox(i64, x) + return r1 L2: - r5 = borrow cast(__main__.C, x) - r6 = r5.a + r2 = borrow cast(__main__.C, x) + r3 = r2.a keep_alive x - return r6 + return r3 [case testI64ConvertBetweenTuples_64bit] from __future__ import annotations diff --git a/mypyc/test-data/irbuild-isinstance.test b/mypyc/test-data/irbuild-isinstance.test index 78da2e9c1e19..30adfe61e384 100644 --- a/mypyc/test-data/irbuild-isinstance.test +++ b/mypyc/test-data/irbuild-isinstance.test @@ -4,16 +4,11 @@ def is_int(value: object) -> bool: [out] def is_int(value): - value, r0 :: object - r1 :: i32 - r2 :: bit - r3 :: bool + value :: object + r0 :: bit L0: - r0 = load_address PyLong_Type - r1 = PyObject_IsInstance(value, r0) - r2 = r1 >= 0 :: signed - r3 = truncate r1: i32 to builtins.bool - return r3 + r0 = PyLong_Check(value) + return r0 [case testIsinstanceNotBool1] def is_not_bool(value: object) -> bool: @@ -21,17 +16,12 @@ def is_not_bool(value: object) -> bool: [out] def is_not_bool(value): - value, r0 :: object - r1 :: i32 - r2 :: bit - r3, r4 :: bool + value :: object + r0, r1 :: bit L0: - r0 = load_address PyBool_Type - r1 = PyObject_IsInstance(value, r0) - r2 = r1 >= 0 :: signed - r3 = truncate r1: i32 to builtins.bool - r4 = r3 ^ 1 - return r4 + r0 = PyBool_Check(value) + r1 = r0 ^ 1 + return r1 [case testIsinstanceIntAndNotBool] # This test is to ensure that 'value' doesn't get coerced to int when we are @@ -41,32 +31,22 @@ def is_not_bool_and_is_int(value: object) -> bool: [out] def is_not_bool_and_is_int(value): - value, r0 :: object - r1 :: i32 - r2 :: bit - r3, r4 :: bool - r5 :: object - r6 :: i32 - r7 :: bit - r8, r9 :: bool + value :: object + r0 :: bit + r1 :: bool + r2, r3 :: bit L0: - r0 = load_address PyLong_Type - r1 = PyObject_IsInstance(value, r0) - r2 = r1 >= 0 :: signed - r3 = truncate r1: i32 to builtins.bool - if r3 goto L2 else goto L1 :: bool + r0 = PyLong_Check(value) + if r0 goto L2 else goto L1 :: bool L1: - r4 = r3 + r1 = r0 goto L3 L2: - r5 = load_address PyBool_Type - r6 = PyObject_IsInstance(value, r5) - r7 = r6 >= 0 :: signed - r8 = truncate r6: i32 to builtins.bool - r9 = r8 ^ 1 - r4 = r9 + r2 = PyBool_Check(value) + r3 = r2 ^ 1 + r1 = r3 L3: - return r4 + return r1 [case testBorrowSpecialCaseWithIsinstance] class C: @@ -107,3 +87,105 @@ L1: keep_alive x L2: return 1 + +[case testBytes] +from typing import Any + +def is_bytes(x: Any) -> bool: + return isinstance(x, bytes) + +def is_bytearray(x: Any) -> bool: + return isinstance(x, bytearray) + +[out] +def is_bytes(x): + x :: object + r0 :: bit +L0: + r0 = PyBytes_Check(x) + return r0 +def is_bytearray(x): + x :: object + r0 :: bit +L0: + r0 = PyByteArray_Check(x) + return r0 + +[case testDict] +from typing import Any + +def is_dict(x: Any) -> bool: + return isinstance(x, dict) + +[out] +def is_dict(x): + x :: object + r0 :: bit +L0: + r0 = PyDict_Check(x) + return r0 + +[case testFloat] +from typing import Any + +def is_float(x: Any) -> bool: + return isinstance(x, float) + +[out] +def is_float(x): + x :: object + r0 :: bit +L0: + r0 = PyFloat_Check(x) + return r0 + +[case testSet] +from typing import Any + +def is_set(x: Any) -> bool: + return isinstance(x, set) + +def is_frozenset(x: Any) -> bool: + return isinstance(x, frozenset) + +[out] +def is_set(x): + x :: object + r0 :: bit +L0: + r0 = PySet_Check(x) + return r0 +def is_frozenset(x): + x :: object + r0 :: bit +L0: + r0 = PyFrozenSet_Check(x) + return r0 + +[case testStr] +from typing import Any + +def is_str(x: Any) -> bool: + return isinstance(x, str) + +[out] +def is_str(x): + x :: object + r0 :: bit +L0: + r0 = PyUnicode_Check(x) + return r0 + +[case testTuple] +from typing import Any + +def is_tuple(x: Any) -> bool: + return isinstance(x, tuple) + +[out] +def is_tuple(x): + x :: object + r0 :: bit +L0: + r0 = PyTuple_Check(x) + return r0 diff --git a/mypyc/test-data/irbuild-optional.test b/mypyc/test-data/irbuild-optional.test index 75c008586999..b81465d362ba 100644 --- a/mypyc/test-data/irbuild-optional.test +++ b/mypyc/test-data/irbuild-optional.test @@ -251,28 +251,22 @@ def f(x: Union[int, A]) -> int: [out] def f(x): x :: union[int, __main__.A] - r0 :: object - r1 :: i32 - r2 :: bit - r3 :: bool - r4, r5 :: int - r6 :: __main__.A - r7 :: int + r0 :: bit + r1, r2 :: int + r3 :: __main__.A + r4 :: int L0: - r0 = load_address PyLong_Type - r1 = PyObject_IsInstance(x, r0) - r2 = r1 >= 0 :: signed - r3 = truncate r1: i32 to builtins.bool - if r3 goto L1 else goto L2 :: bool + r0 = PyLong_Check(x) + if r0 goto L1 else goto L2 :: bool L1: - r4 = unbox(int, x) - r5 = CPyTagged_Add(r4, 2) - return r5 + r1 = unbox(int, x) + r2 = CPyTagged_Add(r1, 2) + return r2 L2: - r6 = borrow cast(__main__.A, x) - r7 = r6.a + r3 = borrow cast(__main__.A, x) + r4 = r3.a keep_alive x - return r7 + return r4 L3: unreachable diff --git a/mypyc/test-data/run-bools.test b/mypyc/test-data/run-bools.test index 3409665bfb37..fbaae28fc9b4 100644 --- a/mypyc/test-data/run-bools.test +++ b/mypyc/test-data/run-bools.test @@ -228,3 +228,15 @@ def test_mix() -> None: print((y or 0) and True) [out] 0 + +[case testIsInstance] +def test_built_in() -> None: + assert isinstance(True, bool) + assert isinstance(False, bool) + + assert not isinstance(set(), bool) + assert not isinstance((), bool) + assert not isinstance((True, False), bool) + assert not isinstance({False, True}, bool) + assert not isinstance(1, bool) + assert not isinstance('False', bool) diff --git a/mypyc/test-data/run-bytes.test b/mypyc/test-data/run-bytes.test index bee6b6fe9f76..d26d409d2e42 100644 --- a/mypyc/test-data/run-bytes.test +++ b/mypyc/test-data/run-bytes.test @@ -323,3 +323,52 @@ class A: def test_bytes_dunder() -> None: assert b'%b' % A() == b'aaa' assert b'%s' % A() == b'aaa' + +[case testIsInstance] +from copysubclass import subbytes, subbytearray +def test_bytes() -> None: + assert isinstance(b'', bytes) + assert isinstance(b'123', bytes) + assert isinstance(b'\xff', bytes) + assert isinstance(subbytes(), bytes) + assert isinstance(subbytes(b'123'), bytes) + assert isinstance(subbytes(b'\xff'), bytes) + + assert not isinstance(set(), bytes) + assert not isinstance((), bytes) + assert not isinstance((b'1',b'2',b'3'), bytes) + assert not isinstance({b'a',b'b'}, bytes) + assert not isinstance(1, bytes) + assert not isinstance('a', bytes) + +def test_user_defined_bytes() -> None: + from userdefinedbytes import bytes + + assert isinstance(bytes(), bytes) + assert not isinstance(b'\x7f', bytes) + +def test_bytearray() -> None: + assert isinstance(bytearray(), bytearray) + assert isinstance(bytearray(b'123'), bytearray) + assert isinstance(bytearray(b'\xff'), bytearray) + assert isinstance(subbytearray(), bytearray) + assert isinstance(subbytearray(bytearray(b'123')), bytearray) + assert isinstance(subbytearray(bytearray(b'\xff')), bytearray) + + assert not isinstance(set(), bytearray) + assert not isinstance((), bytearray) + assert not isinstance((bytearray(b'1'),bytearray(b'2'),bytearray(b'3')), bytearray) + assert not isinstance([bytearray(b'a'),bytearray(b'b')], bytearray) + assert not isinstance(1, bytearray) + assert not isinstance('a', bytearray) + +[file copysubclass.py] +class subbytes(bytes): + pass + +class subbytearray(bytearray): + pass + +[file userdefinedbytes.py] +class bytes: + pass diff --git a/mypyc/test-data/run-dicts.test b/mypyc/test-data/run-dicts.test index 2a3be188ad00..a41206daa07a 100644 --- a/mypyc/test-data/run-dicts.test +++ b/mypyc/test-data/run-dicts.test @@ -336,3 +336,35 @@ def test_dict_to_bool() -> None: for x in tmp_list: assert is_true(x) assert not is_false(x) + +[case testIsInstance] +from copysubclass import subc +def test_built_in() -> None: + assert isinstance({}, dict) + assert isinstance({'one': 1, 'two': 2}, dict) + assert isinstance({1: 1, 'two': 2}, dict) + assert isinstance(subc(), dict) + assert isinstance(subc({'a': 1, 'b': 2}), dict) + assert isinstance(subc({1: 'a', 2: 'b'}), dict) + + assert not isinstance(set(), dict) + assert not isinstance((), dict) + assert not isinstance((1,2,3), dict) + assert not isinstance({'a','b'}, dict) + assert not isinstance(1, dict) + assert not isinstance('a', dict) + +def test_user_defined() -> None: + from userdefineddict import dict + + assert isinstance(dict(), dict) + assert not isinstance({1: dict()}, dict) + +[file copysubclass.py] +from typing import Any +class subc(dict[Any, Any]): + pass + +[file userdefineddict.py] +class dict: + pass diff --git a/mypyc/test-data/run-floats.test b/mypyc/test-data/run-floats.test index 49620f6448c7..1199205b331e 100644 --- a/mypyc/test-data/run-floats.test +++ b/mypyc/test-data/run-floats.test @@ -512,3 +512,22 @@ def test_implement_trait_attribute() -> None: a.y = 8.0 assert a.x == 7 assert a.y == 8 + +[case testIsInstance] +from copysubclass import subc +from testutil import float_vals +def test_built_in() -> None: + for f in float_vals: + assert isinstance(f, float) + assert isinstance(subc(f), float) + + assert not isinstance(set(), float) + assert not isinstance((), float) + assert not isinstance((1.0, 2.0), float) + assert not isinstance({3.14}, float) + assert not isinstance(1, float) + assert not isinstance('4.2', float) + +[file copysubclass.py] +class subc(float): + pass diff --git a/mypyc/test-data/run-integers.test b/mypyc/test-data/run-integers.test index d575e141b567..6778f53492fb 100644 --- a/mypyc/test-data/run-integers.test +++ b/mypyc/test-data/run-integers.test @@ -538,3 +538,24 @@ def test_int_bool_min_max() -> None: assert min(u, z) == -10 assert max(u, y) == False assert max(u, z) == True + +[case testIsInstance] +from copysubclass import subc +def test_built_in() -> None: + assert isinstance(0, int) + assert isinstance(9223372036854775808, int) + assert isinstance(-9223372036854775808, int) + assert isinstance(subc(), int) + assert isinstance(subc(9223372036854775808), int) + assert isinstance(subc(-9223372036854775808), int) + + assert not isinstance(set(), int) + assert not isinstance((), int) + assert not isinstance((1,2,3), int) + assert not isinstance({1,2}, int) + assert not isinstance(1.0, int) + assert not isinstance('1', int) + +[file copysubclass.py] +class subc(int): + pass diff --git a/mypyc/test-data/run-sets.test b/mypyc/test-data/run-sets.test index 68edd1e6b77d..486dee6f07c3 100644 --- a/mypyc/test-data/run-sets.test +++ b/mypyc/test-data/run-sets.test @@ -265,3 +265,55 @@ def test_in_set() -> None: def test_for_set() -> None: assert not s ^ {None, False, 1, 2.0, "3", b"4", 5j, (6,), CONST}, s + +[case testIsInstance] +from copysubclass import subset, subfrozenset +def test_built_in_set() -> None: + assert isinstance(set(), set) + assert isinstance({'one', 'two'}, set) + assert isinstance({'a', 1}, set) + assert isinstance(subset(), set) + assert isinstance(subset({'one', 'two'}), set) + assert isinstance(subset({'a', 1}), set) + + assert not isinstance(frozenset(), set) + assert not isinstance({}, set) + assert not isinstance([], set) + assert not isinstance((1,2,3), set) + assert not isinstance({1:'a', 2:'b'}, set) + assert not isinstance(1, set) + assert not isinstance('a', set) + +def test_user_defined_set() -> None: + from userdefinedset import set + + assert isinstance(set(), set) + assert not isinstance({set()}, set) + +def test_built_in_frozenset() -> None: + assert isinstance(frozenset(), frozenset) + assert isinstance(frozenset({'one', 'two'}), frozenset) + assert isinstance(frozenset({'a', 1}), frozenset) + assert isinstance(subfrozenset(), frozenset) + assert isinstance(subfrozenset({'one', 'two'}), frozenset) + assert isinstance(subfrozenset({'a', 1}), frozenset) + + assert not isinstance(set(), frozenset) + assert not isinstance({}, frozenset) + assert not isinstance([], frozenset) + assert not isinstance((1,2,3), frozenset) + assert not isinstance({1:'a', 2:'b'}, frozenset) + assert not isinstance(1, frozenset) + assert not isinstance('a', frozenset) + +[file copysubclass.py] +from typing import Any +class subset(set[Any]): + pass + +class subfrozenset(frozenset[Any]): + pass + +[file userdefinedset.py] +class set: + pass diff --git a/mypyc/test-data/run-strings.test b/mypyc/test-data/run-strings.test index 6551d9c352df..bbaacdddfb6d 100644 --- a/mypyc/test-data/run-strings.test +++ b/mypyc/test-data/run-strings.test @@ -964,3 +964,35 @@ def test_count_multi_start_end_emoji() -> None: assert string.count("😴😴😴", 0, 12) == 1, string.count("😴😴😴", 0, 12) assert string.count("🚀🚀🚀", 0, 12) == 2, string.count("🚀🚀🚀", 0, 12) assert string.count("ñññ", 0, 12) == 1, string.count("ñññ", 0, 12) + +[case testIsInstance] +from copysubclass import subc +def test_built_in() -> None: + assert isinstance("", str) + assert isinstance("test", str) + assert isinstance("ñññ", str) + assert isinstance(subc(), str) + assert isinstance(subc("test"), str) + assert isinstance(subc("ñññ"), str) + + assert not isinstance(set(), str) + assert not isinstance((), str) + assert not isinstance(('a','b'), str) + assert not isinstance({'a','b'}, str) + assert not isinstance(1, str) + assert not isinstance(['a','b'], str) + +def test_user_defined() -> None: + from userdefinedstr import str + + assert isinstance(str(), str) + assert not isinstance("str", str) + +[file copysubclass.py] +from typing import Any +class subc(str): + pass + +[file userdefinedstr.py] +class str: + pass diff --git a/mypyc/test-data/run-tuples.test b/mypyc/test-data/run-tuples.test index fe9a8dff08c6..cbc0e20a7c36 100644 --- a/mypyc/test-data/run-tuples.test +++ b/mypyc/test-data/run-tuples.test @@ -294,3 +294,35 @@ def test_multiply() -> None: assert (1,) * 3 == res assert 3 * (1,) == res assert multiply((1,), 3) == res + +[case testIsInstance] +from copysubclass import subc +def test_built_in() -> None: + assert isinstance((), tuple) + assert isinstance((1, 2), tuple) + assert isinstance(('a', 'b', 'c'), tuple) + assert isinstance(subc(()), tuple) + assert isinstance(subc((1, 2)), tuple) + assert isinstance(subc(('a', 'b', 'c')), tuple) + + assert not isinstance(set(), tuple) + assert not isinstance({}, tuple) + assert not isinstance([1,2,3], tuple) + assert not isinstance({'a','b'}, tuple) + assert not isinstance(1, tuple) + assert not isinstance('a', tuple) + +def test_user_defined() -> None: + from userdefinedtuple import tuple + + assert isinstance(tuple(), tuple) + assert not isinstance((1, tuple()), tuple) + +[file copysubclass.py] +from typing import Any +class subc(tuple[Any]): + pass + +[file userdefinedtuple.py] +class tuple: + pass From 8aee818fe9382a27265d6cd12f4cda44ba87d068 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 14 Jul 2025 12:14:07 +0000 Subject: [PATCH 2/3] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypyc/primitives/tuple_ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypyc/primitives/tuple_ops.py b/mypyc/primitives/tuple_ops.py index 3b25c350cf03..b4919f4d6d70 100644 --- a/mypyc/primitives/tuple_ops.py +++ b/mypyc/primitives/tuple_ops.py @@ -8,8 +8,8 @@ from mypyc.ir.ops import ERR_MAGIC, ERR_NEVER from mypyc.ir.rtypes import ( - c_pyssize_t_rprimitive, bit_rprimitive, + c_pyssize_t_rprimitive, int_rprimitive, list_rprimitive, object_rprimitive, From 6a6f32cd72dfcf3fb56bad7dd003bb8c53866473 Mon Sep 17 00:00:00 2001 From: Piotr Sawicki Date: Mon, 14 Jul 2025 23:23:43 +0200 Subject: [PATCH 3/3] Address review comments --- mypyc/primitives/bytes_ops.py | 4 ++-- mypyc/primitives/dict_ops.py | 2 +- mypyc/primitives/float_ops.py | 1 + mypyc/primitives/int_ops.py | 2 +- mypyc/primitives/list_ops.py | 2 +- mypyc/primitives/set_ops.py | 4 ++-- mypyc/primitives/str_ops.py | 2 +- mypyc/primitives/tuple_ops.py | 2 +- mypyc/test-data/run-bools.test | 22 ++++++++++++++++++---- mypyc/test-data/run-bytes.test | 20 +++++++++++--------- mypyc/test-data/run-dicts.test | 4 ++-- mypyc/test-data/run-floats.test | 18 +++++++++++++++--- mypyc/test-data/run-integers.test | 23 ++++++++++++++++++----- mypyc/test-data/run-sets.test | 8 ++++---- mypyc/test-data/run-strings.test | 13 ++++++++----- mypyc/test-data/run-tuples.test | 4 ++-- 16 files changed, 88 insertions(+), 43 deletions(-) diff --git a/mypyc/primitives/bytes_ops.py b/mypyc/primitives/bytes_ops.py index 78aa67f3623e..c88e89d1a2ba 100644 --- a/mypyc/primitives/bytes_ops.py +++ b/mypyc/primitives/bytes_ops.py @@ -36,7 +36,7 @@ error_kind=ERR_MAGIC, ) -# isinstance(obj, bytes) +# translate isinstance(obj, bytes) isinstance_bytes = function_op( name="builtins.isinstance", arg_types=[object_rprimitive], @@ -54,7 +54,7 @@ error_kind=ERR_MAGIC, ) -# isinstance(obj, bytearray) +# translate isinstance(obj, bytearray) isinstance_bytearray = function_op( name="builtins.isinstance", arg_types=[object_rprimitive], diff --git a/mypyc/primitives/dict_ops.py b/mypyc/primitives/dict_ops.py index 3c30a49c6b1e..ac928bb0eb50 100644 --- a/mypyc/primitives/dict_ops.py +++ b/mypyc/primitives/dict_ops.py @@ -71,7 +71,7 @@ error_kind=ERR_MAGIC, ) -# isinstance(obj, dict) +# translate isinstance(obj, dict) isinstance_dict = function_op( name="builtins.isinstance", arg_types=[object_rprimitive], diff --git a/mypyc/primitives/float_ops.py b/mypyc/primitives/float_ops.py index dc4956b1437c..542192add542 100644 --- a/mypyc/primitives/float_ops.py +++ b/mypyc/primitives/float_ops.py @@ -168,6 +168,7 @@ error_kind=ERR_NEVER, ) +# translate isinstance(obj, float) isinstance_float = function_op( name="builtins.isinstance", arg_types=[object_rprimitive], diff --git a/mypyc/primitives/int_ops.py b/mypyc/primitives/int_ops.py index 75246a74bc59..d723c9b63a86 100644 --- a/mypyc/primitives/int_ops.py +++ b/mypyc/primitives/int_ops.py @@ -297,7 +297,7 @@ def int_unary_op(name: str, c_function_name: str) -> PrimitiveDescription: error_kind=ERR_ALWAYS, ) -# isinstance(obj, int) +# translate isinstance(obj, int) isinstance_int = function_op( name="builtints.isinstance", arg_types=[object_rprimitive], diff --git a/mypyc/primitives/list_ops.py b/mypyc/primitives/list_ops.py index 57cb541fdbb8..516d9e1a4e02 100644 --- a/mypyc/primitives/list_ops.py +++ b/mypyc/primitives/list_ops.py @@ -56,7 +56,7 @@ extra_int_constants=[(0, int_rprimitive)], ) -# isinstance(obj, list) +# translate isinstance(obj, list) isinstance_list = function_op( name="builtins.isinstance", arg_types=[object_rprimitive], diff --git a/mypyc/primitives/set_ops.py b/mypyc/primitives/set_ops.py index 89c88d6a8ca7..786de008746d 100644 --- a/mypyc/primitives/set_ops.py +++ b/mypyc/primitives/set_ops.py @@ -64,7 +64,7 @@ error_kind=ERR_MAGIC, ) -# isinstance(obj, set) +# translate isinstance(obj, set) isinstance_set = function_op( name="builtins.isinstance", arg_types=[object_rprimitive], @@ -73,7 +73,7 @@ error_kind=ERR_NEVER, ) -# isinstance(obj, frozenset) +# translate isinstance(obj, frozenset) isinstance_frozenset = function_op( name="builtins.isinstance", arg_types=[object_rprimitive], diff --git a/mypyc/primitives/str_ops.py b/mypyc/primitives/str_ops.py index 305c830b9565..31f361719765 100644 --- a/mypyc/primitives/str_ops.py +++ b/mypyc/primitives/str_ops.py @@ -48,7 +48,7 @@ error_kind=ERR_MAGIC, ) -# isinstance(obj, str) +# translate isinstance(obj, str) isinstance_str = function_op( name="builtins.isinstance", arg_types=[object_rprimitive], diff --git a/mypyc/primitives/tuple_ops.py b/mypyc/primitives/tuple_ops.py index b4919f4d6d70..d95161acf853 100644 --- a/mypyc/primitives/tuple_ops.py +++ b/mypyc/primitives/tuple_ops.py @@ -84,7 +84,7 @@ error_kind=ERR_MAGIC, ) -# isinstance(obj, tuple) +# translate isinstance(obj, tuple) isinstance_tuple = function_op( name="builtins.isinstance", arg_types=[object_rprimitive], diff --git a/mypyc/test-data/run-bools.test b/mypyc/test-data/run-bools.test index fbaae28fc9b4..b34fedebaa9f 100644 --- a/mypyc/test-data/run-bools.test +++ b/mypyc/test-data/run-bools.test @@ -230,13 +230,27 @@ def test_mix() -> None: 0 [case testIsInstance] +from typing import Any def test_built_in() -> None: - assert isinstance(True, bool) - assert isinstance(False, bool) + true: Any = True + false: Any = False + assert isinstance(true, bool) + assert isinstance(false, bool) assert not isinstance(set(), bool) assert not isinstance((), bool) assert not isinstance((True, False), bool) assert not isinstance({False, True}, bool) - assert not isinstance(1, bool) - assert not isinstance('False', bool) + assert not isinstance(int() + 1, bool) + assert not isinstance(str() + 'False', bool) + +def test_user_defined() -> None: + from userdefinedbool import bool + + b: Any = True + assert isinstance(bool(), bool) + assert not isinstance(b, bool) + +[file userdefinedbool.py] +class bool: + pass diff --git a/mypyc/test-data/run-bytes.test b/mypyc/test-data/run-bytes.test index d26d409d2e42..5a285320c849 100644 --- a/mypyc/test-data/run-bytes.test +++ b/mypyc/test-data/run-bytes.test @@ -326,20 +326,22 @@ def test_bytes_dunder() -> None: [case testIsInstance] from copysubclass import subbytes, subbytearray +from typing import Any def test_bytes() -> None: - assert isinstance(b'', bytes) - assert isinstance(b'123', bytes) - assert isinstance(b'\xff', bytes) + b: Any = b'' + assert isinstance(b, bytes) + assert isinstance(b + b'123', bytes) + assert isinstance(b + b'\xff', bytes) assert isinstance(subbytes(), bytes) - assert isinstance(subbytes(b'123'), bytes) - assert isinstance(subbytes(b'\xff'), bytes) + assert isinstance(subbytes(b + b'123'), bytes) + assert isinstance(subbytes(b + b'\xff'), bytes) assert not isinstance(set(), bytes) assert not isinstance((), bytes) assert not isinstance((b'1',b'2',b'3'), bytes) assert not isinstance({b'a',b'b'}, bytes) - assert not isinstance(1, bytes) - assert not isinstance('a', bytes) + assert not isinstance(int() + 1, bytes) + assert not isinstance(str() + 'a', bytes) def test_user_defined_bytes() -> None: from userdefinedbytes import bytes @@ -359,8 +361,8 @@ def test_bytearray() -> None: assert not isinstance((), bytearray) assert not isinstance((bytearray(b'1'),bytearray(b'2'),bytearray(b'3')), bytearray) assert not isinstance([bytearray(b'a'),bytearray(b'b')], bytearray) - assert not isinstance(1, bytearray) - assert not isinstance('a', bytearray) + assert not isinstance(int() + 1, bytearray) + assert not isinstance(str() + 'a', bytearray) [file copysubclass.py] class subbytes(bytes): diff --git a/mypyc/test-data/run-dicts.test b/mypyc/test-data/run-dicts.test index a41206daa07a..2b75b32c906e 100644 --- a/mypyc/test-data/run-dicts.test +++ b/mypyc/test-data/run-dicts.test @@ -351,8 +351,8 @@ def test_built_in() -> None: assert not isinstance((), dict) assert not isinstance((1,2,3), dict) assert not isinstance({'a','b'}, dict) - assert not isinstance(1, dict) - assert not isinstance('a', dict) + assert not isinstance(int() + 1, dict) + assert not isinstance(str() + 'a', dict) def test_user_defined() -> None: from userdefineddict import dict diff --git a/mypyc/test-data/run-floats.test b/mypyc/test-data/run-floats.test index 1199205b331e..424d52cdb0d5 100644 --- a/mypyc/test-data/run-floats.test +++ b/mypyc/test-data/run-floats.test @@ -516,18 +516,30 @@ def test_implement_trait_attribute() -> None: [case testIsInstance] from copysubclass import subc from testutil import float_vals +from typing import Any def test_built_in() -> None: for f in float_vals: - assert isinstance(f, float) + assert isinstance(float(0) + f, float) assert isinstance(subc(f), float) assert not isinstance(set(), float) assert not isinstance((), float) assert not isinstance((1.0, 2.0), float) assert not isinstance({3.14}, float) - assert not isinstance(1, float) - assert not isinstance('4.2', float) + assert not isinstance(int() + 1, float) + assert not isinstance(str() + '4.2', float) + +def test_user_defined() -> None: + from userdefinedfloat import float + + f: Any = 3.14 + assert isinstance(float(), float) + assert not isinstance(f, float) [file copysubclass.py] class subc(float): pass + +[file userdefinedfloat.py] +class float: + pass diff --git a/mypyc/test-data/run-integers.test b/mypyc/test-data/run-integers.test index 6778f53492fb..1163c9d942f7 100644 --- a/mypyc/test-data/run-integers.test +++ b/mypyc/test-data/run-integers.test @@ -541,10 +541,12 @@ def test_int_bool_min_max() -> None: [case testIsInstance] from copysubclass import subc +from typing import Any def test_built_in() -> None: - assert isinstance(0, int) - assert isinstance(9223372036854775808, int) - assert isinstance(-9223372036854775808, int) + i: Any = 0 + assert isinstance(i + 0, int) + assert isinstance(i + 9223372036854775808, int) + assert isinstance(i + -9223372036854775808, int) assert isinstance(subc(), int) assert isinstance(subc(9223372036854775808), int) assert isinstance(subc(-9223372036854775808), int) @@ -553,9 +555,20 @@ def test_built_in() -> None: assert not isinstance((), int) assert not isinstance((1,2,3), int) assert not isinstance({1,2}, int) - assert not isinstance(1.0, int) - assert not isinstance('1', int) + assert not isinstance(float(0) + 1.0, int) + assert not isinstance(str() + '1', int) + +def test_user_defined() -> None: + from userdefinedint import int + + i: Any = 42 + assert isinstance(int(), int) + assert not isinstance(i, int) [file copysubclass.py] class subc(int): pass + +[file userdefinedint.py] +class int: + pass diff --git a/mypyc/test-data/run-sets.test b/mypyc/test-data/run-sets.test index 486dee6f07c3..2668d63bcdac 100644 --- a/mypyc/test-data/run-sets.test +++ b/mypyc/test-data/run-sets.test @@ -281,8 +281,8 @@ def test_built_in_set() -> None: assert not isinstance([], set) assert not isinstance((1,2,3), set) assert not isinstance({1:'a', 2:'b'}, set) - assert not isinstance(1, set) - assert not isinstance('a', set) + assert not isinstance(int() + 1, set) + assert not isinstance(str() + 'a', set) def test_user_defined_set() -> None: from userdefinedset import set @@ -303,8 +303,8 @@ def test_built_in_frozenset() -> None: assert not isinstance([], frozenset) assert not isinstance((1,2,3), frozenset) assert not isinstance({1:'a', 2:'b'}, frozenset) - assert not isinstance(1, frozenset) - assert not isinstance('a', frozenset) + assert not isinstance(int() + 1, frozenset) + assert not isinstance(str() + 'a', frozenset) [file copysubclass.py] from typing import Any diff --git a/mypyc/test-data/run-strings.test b/mypyc/test-data/run-strings.test index bbaacdddfb6d..8a914c08bfb2 100644 --- a/mypyc/test-data/run-strings.test +++ b/mypyc/test-data/run-strings.test @@ -967,10 +967,12 @@ def test_count_multi_start_end_emoji() -> None: [case testIsInstance] from copysubclass import subc +from typing import Any def test_built_in() -> None: - assert isinstance("", str) - assert isinstance("test", str) - assert isinstance("ñññ", str) + s: Any = str() + assert isinstance(s, str) + assert isinstance(s + "test", str) + assert isinstance(s + "ñññ", str) assert isinstance(subc(), str) assert isinstance(subc("test"), str) assert isinstance(subc("ñññ"), str) @@ -979,14 +981,15 @@ def test_built_in() -> None: assert not isinstance((), str) assert not isinstance(('a','b'), str) assert not isinstance({'a','b'}, str) - assert not isinstance(1, str) + assert not isinstance(int() + 1, str) assert not isinstance(['a','b'], str) def test_user_defined() -> None: from userdefinedstr import str + s: Any = "str" assert isinstance(str(), str) - assert not isinstance("str", str) + assert not isinstance(s, str) [file copysubclass.py] from typing import Any diff --git a/mypyc/test-data/run-tuples.test b/mypyc/test-data/run-tuples.test index cbc0e20a7c36..ea0a1cb8d852 100644 --- a/mypyc/test-data/run-tuples.test +++ b/mypyc/test-data/run-tuples.test @@ -309,8 +309,8 @@ def test_built_in() -> None: assert not isinstance({}, tuple) assert not isinstance([1,2,3], tuple) assert not isinstance({'a','b'}, tuple) - assert not isinstance(1, tuple) - assert not isinstance('a', tuple) + assert not isinstance(int() + 1, tuple) + assert not isinstance(str() + 'a', tuple) def test_user_defined() -> None: from userdefinedtuple import tuple pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy