Skip to content

Commit 09283a7

Browse files
authored
[mypyc] Support isinstance with librt.vecs.vec objects (python#20699)
Use a common base class 'vec' for isinstance support. The base class is currently only used for isinstance, but we could add some other common functionality there in the future. I used some coding agent assist.
1 parent 195ab98 commit 09283a7

File tree

9 files changed

+44
-6
lines changed

9 files changed

+44
-6
lines changed

mypyc/lib-rt/vecs/librt_vecs.c

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -241,10 +241,8 @@ PyTypeObject VecGenericAliasType = {
241241
// The 'vec' type
242242
//
243243
// This cannot be instantiated, and it's only used for isinstance and indexing: vec[T].
244-
245-
typedef struct {
246-
PyObject_HEAD
247-
} Vec;
244+
// All specialized vec types (VecI64Type, VecI32Type, etc.) inherit from this base type,
245+
// so isinstance(v, vec) returns True for any specialized vec instance.
248246

249247
static PyObject *extract_optional_item(PyObject *item) {
250248
PyObject *args = PyObject_GetAttrString(item, "__args__");
@@ -374,9 +372,9 @@ static PyMethodDef vec_methods[] = {
374372
PyTypeObject VecType = {
375373
PyVarObject_HEAD_INIT(NULL, 0)
376374
.tp_name = "vec",
377-
.tp_basicsize = sizeof(Vec),
375+
.tp_basicsize = sizeof(VecBaseObject),
378376
.tp_itemsize = 0,
379-
.tp_flags = Py_TPFLAGS_DEFAULT,
377+
.tp_flags = Py_TPFLAGS_DEFAULT, // No Py_TPFLAGS_BASETYPE - prevent Python subclassing
380378
.tp_methods = vec_methods,
381379
};
382380

mypyc/lib-rt/vecs/librt_vecs.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,13 @@ typedef struct _VecObject {
157157
Py_ssize_t len;
158158
} VecObject;
159159

160+
// Base vec type object (for isinstance checks)
161+
// This is an abstract base type that all specialized vec types inherit from.
162+
// It cannot be instantiated directly - only used for isinstance(x, vec).
163+
typedef struct _VecBaseObject {
164+
PyObject_HEAD
165+
} VecBaseObject;
166+
160167
// Boxed vec[i64]
161168
typedef struct _VecI64Object {
162169
PyObject_HEAD

mypyc/lib-rt/vecs/vec_nested.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#define PY_SSIZE_T_CLEAN
1010
#include <Python.h>
1111
#include "librt_vecs.h"
12+
#include "vecs_internal.h"
1213

1314
static inline VecNested vec_error() {
1415
VecNested v = { .len = -1 };
@@ -439,6 +440,7 @@ PyTypeObject VecNestedType = {
439440
.tp_doc = "Mutable sequence-like container optimized for compilation with mypyc",
440441
.tp_basicsize = sizeof(VecNestedObject),
441442
.tp_itemsize = 0,
443+
.tp_base = &VecType, // Inherit from base vec type for isinstance() support
442444
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
443445
.tp_traverse = (traverseproc)VecNested_traverse,
444446
.tp_clear = (inquiry)VecNested_clear,

mypyc/lib-rt/vecs/vec_t.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#define PY_SSIZE_T_CLEAN
1212
#include <Python.h>
1313
#include "librt_vecs.h"
14+
#include "vecs_internal.h"
1415

1516
static inline VecT vec_error() {
1617
VecT v = { .len = -1 };
@@ -433,6 +434,7 @@ PyTypeObject VecTType = {
433434
.tp_doc = "Mutable sequence-like container optimized for compilation with mypyc",
434435
.tp_basicsize = sizeof(VecTObject),
435436
.tp_itemsize = 0,
437+
.tp_base = &VecType, // Inherit from base vec type for isinstance() support
436438
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
437439
.tp_traverse = (traverseproc)VecT_traverse,
438440
.tp_clear = (inquiry)VecT_clear,

mypyc/lib-rt/vecs/vec_template.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#define PY_SSIZE_T_CLEAN
2929
#include <Python.h>
3030
#include "librt_vecs.h"
31+
#include "vecs_internal.h"
3132

3233
inline static VEC vec_error() {
3334
VEC v = { .len = -1 };
@@ -370,6 +371,7 @@ PyTypeObject VEC_TYPE = {
370371
.tp_doc = "Mutable sequence-like container optimized for compilation with mypyc",
371372
.tp_basicsize = sizeof(VEC_OBJECT),
372373
.tp_itemsize = 0,
374+
.tp_base = &VecType, // Inherit from base vec type for isinstance() support
373375
.tp_flags = Py_TPFLAGS_DEFAULT,
374376
.tp_new = vec_new,
375377
//.tp_free = PyObject_Del,

mypyc/lib-rt/vecs/vecs_internal.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#ifndef VECS_INTERNAL_H_INCL
2+
#define VECS_INTERNAL_H_INCL
3+
4+
// Internal header for mypyc/lib-rt/vecs implementation
5+
6+
#include <Python.h>
7+
8+
// The base vec type
9+
extern PyTypeObject VecType;
10+
11+
#endif // VECS_INTERNAL_H_INCL

mypyc/test-data/run-vecs-misc-interp.test

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,14 @@ def test_construct_empty() -> None:
6969
assert type(v) is vec[t]
7070
assert len(v) == 0
7171

72+
def test_isinstance() -> None:
73+
for t in ITEM_TYPES:
74+
v = vec[t]()
75+
assert isinstance(v, vec)
76+
assert not isinstance(vec[t], vec)
77+
assert not isinstance("x", vec)
78+
assert not isinstance(vec, vec)
79+
7280
def test_basic_int_operations() -> None:
7381
# All of the non-bool item types support int values 0 to 255
7482
for t in ITEM_TYPES:

mypyc/test-data/run-vecs-nested-interp.test

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ def test_construct_from_initializer_nested() -> None:
6262
with assertRaises(TypeError):
6363
vec[str]([vec[bytes]()])
6464

65+
def test_isinstance() -> None:
66+
assert isinstance(vec[vec[str]](), vec)
67+
assert isinstance(vec[vec[Optional[str]]](), vec)
68+
6569
def test_repr() -> None:
6670
assert str(vec[vec[str]]()) == "vec[vec[str]]([])"
6771
assert str(vec[vec[Optional[str]]]()) == "vec[vec[str | None]]([])"

mypyc/test-data/run-vecs-t-interp.test

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,10 @@ def test_construct_from_initializer_optional() -> None:
100100
with assertRaises(TypeError):
101101
vec[Optional[str]]([1])
102102

103+
def test_isinstance() -> None:
104+
assert isinstance(vec[str](), vec)
105+
assert isinstance(vec[Optional[str]](), vec)
106+
103107
def test_append() -> None:
104108
v = vec[str]()
105109
v = append(v, '1')

0 commit comments

Comments
 (0)