Skip to content

Commit 9875446

Browse files
committed
Add PyTypePtr as a subclass of PyPtr
1 parent d3b88b4 commit 9875446

File tree

2 files changed

+117
-8
lines changed

2 files changed

+117
-8
lines changed

ext/pycall/pyptr/pyptr.c

Lines changed: 113 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
static VALUE mPyCall;
1212
static VALUE cPyPtr;
13+
static VALUE cPyTypePtr;
1314

1415
static PyObject *Py_None = NULL;
1516
static PyTypeObject *PyType_Type = NULL;
@@ -233,7 +234,7 @@ pycall_pyptr_new(PyObject *pyobj)
233234
}
234235

235236
static VALUE
236-
pycall_pyptr_s_new(VALUE klass, VALUE val)
237+
pycall_pyptr_initialize(VALUE pyptr, VALUE val)
237238
{
238239
VALUE addr;
239240
PyObject *pyobj;
@@ -244,7 +245,8 @@ pycall_pyptr_s_new(VALUE klass, VALUE val)
244245
}
245246

246247
pyobj = (PyObject *)NUM2PTR(addr);
247-
return pycall_pyptr_new_with_klass(klass, pyobj);
248+
DATA_PTR(pyptr) = pyobj;
249+
return pyptr;
248250
}
249251

250252
static VALUE
@@ -301,10 +303,28 @@ pycall_pyptr_inspect(VALUE obj)
301303
return str;
302304
}
303305

306+
static VALUE
307+
class_or_module_required(VALUE klass)
308+
{
309+
if (SPECIAL_CONST_P(klass)) goto not_class;
310+
switch (BUILTIN_TYPE(klass)) {
311+
case T_MODULE:
312+
case T_CLASS:
313+
case T_ICLASS:
314+
break;
315+
316+
default:
317+
not_class:
318+
rb_raise(rb_eTypeError, "class or module required");
319+
}
320+
return klass;
321+
}
322+
304323
static VALUE
305324
pycall_pyptr_is_kind_of(VALUE obj, VALUE klass)
306325
{
307326
PyObject* pyobj = get_pyobj_ptr(obj);
327+
VALUE res;
308328

309329
if (is_pycall_pyptr(klass)) {
310330
PyObject* pyobj_klass = get_pyobj_ptr(klass);
@@ -314,14 +334,94 @@ pycall_pyptr_is_kind_of(VALUE obj, VALUE klass)
314334
}
315335
}
316336

317-
return rb_call_super(1, &klass);
337+
klass = class_or_module_required(klass);
338+
res = rb_class_inherited_p(CLASS_OF(obj), klass);
339+
return NIL_P(res) ? Qfalse : res;
340+
}
341+
342+
343+
static const rb_data_type_t pycall_pytypeptr_data_type = {
344+
"PyCall::PyTypePtr",
345+
{ 0, pycall_pyptr_free, pycall_pyptr_memsize, },
346+
#ifdef RUBY_TYPED_FREE_IMMEDIATELY
347+
&pycall_pyptr_data_type, 0, RUBY_TYPED_FREE_IMMEDIATELY
348+
#endif
349+
};
350+
351+
static inline int
352+
is_pycall_pytypeptr(VALUE obj)
353+
{
354+
return rb_typeddata_is_kind_of(obj, &pycall_pytypeptr_data_type);
355+
}
356+
357+
static inline PyTypeObject*
358+
get_pytype_ptr(VALUE obj)
359+
{
360+
PyTypeObject *pytype;
361+
TypedData_Get_Struct(obj, PyTypeObject, &pycall_pytypeptr_data_type, pytype);
362+
return pytype;
363+
}
364+
365+
static inline PyTypeObject*
366+
try_get_pytype_ptr(VALUE obj)
367+
{
368+
if (is_pycall_pytypeptr(obj)) return NULL;
369+
return (PyTypeObject*)DATA_PTR(obj);
370+
}
371+
372+
PyTypeObject*
373+
pycall_pytypeptr_get_pytype_ptr(VALUE obj)
374+
{
375+
return try_get_pytype_ptr(obj);
376+
}
377+
378+
static VALUE
379+
pycall_pytypeptr_allocate(VALUE klass)
380+
{
381+
return TypedData_Wrap_Struct(klass, &pycall_pytypeptr_data_type, NULL);
382+
}
383+
384+
static VALUE
385+
pycall_pytypeptr_get_ob_size(VALUE obj)
386+
{
387+
PyTypeObject* pytype = get_pytype_ptr(obj);
388+
if (pytype)
389+
return SSIZET2NUM(pytype->ob_base.ob_size);
390+
return Qnil;
391+
}
392+
393+
static VALUE
394+
pycall_pytypeptr_get_tp_name(VALUE obj)
395+
{
396+
PyTypeObject* pytype = get_pytype_ptr(obj);
397+
if (pytype)
398+
return rb_str_new2(pytype->tp_name);
399+
return Qnil;
400+
}
401+
402+
static VALUE
403+
pycall_pytypeptr_get_tp_basicsize(VALUE obj)
404+
{
405+
PyTypeObject* pytype = get_pytype_ptr(obj);
406+
if (pytype)
407+
return SSIZET2NUM(pytype->tp_basicsize);
408+
return Qnil;
409+
}
410+
411+
static VALUE
412+
pycall_pytypeptr_get_tp_flags(VALUE obj)
413+
{
414+
PyTypeObject* pytype = get_pytype_ptr(obj);
415+
if (pytype)
416+
return ULONG2NUM(pytype->tp_flags);
417+
return Qnil;
318418
}
319419

320420
void
321421
Init_pyptr(void)
322422
{
323423
mPyCall = rb_define_module("PyCall");
324-
cPyPtr = rb_define_class_under(mPyCall, "PyPtr", rb_cData);
424+
cPyPtr = rb_define_class_under(mPyCall, "PyPtr", rb_cBasicObject);
325425

326426
rb_define_singleton_method(cPyPtr, "__initialize__", pycall_pyptr_s_initialize, 1);
327427
rb_define_singleton_method(cPyPtr, "__initialized__", pycall_pyptr_s_get_initialized, 0);
@@ -330,7 +430,8 @@ Init_pyptr(void)
330430
rb_define_singleton_method(cPyPtr, "decref", pycall_pyptr_s_decref, 1);
331431
rb_define_singleton_method(cPyPtr, "sizeof", pycall_pyptr_s_sizeof, 1);
332432

333-
rb_define_singleton_method(cPyPtr, "new", pycall_pyptr_s_new, 1);
433+
rb_define_alloc_func(cPyPtr, pycall_pyptr_allocate);
434+
rb_define_method(cPyPtr, "initialize", pycall_pyptr_initialize, 1);
334435
rb_define_method(cPyPtr, "null?", pycall_pyptr_is_null, 0);
335436
rb_define_method(cPyPtr, "none?", pycall_pyptr_is_none, 0);
336437
rb_define_method(cPyPtr, "__address__", pycall_pyptr_get_ptr_address, 0);
@@ -344,4 +445,11 @@ Init_pyptr(void)
344445
VALUE pyptr_null = pycall_pyptr_new(NULL);
345446
rb_define_const(cPyPtr, "NULL", pyptr_null);
346447
}
448+
449+
cPyTypePtr = rb_define_class_under(mPyCall, "PyTypePtr", cPyPtr);
450+
rb_define_alloc_func(cPyTypePtr, pycall_pytypeptr_allocate);
451+
rb_define_method(cPyTypePtr, "__ob_size__", pycall_pytypeptr_get_ob_size, 0);
452+
rb_define_method(cPyTypePtr, "__tp_name__", pycall_pytypeptr_get_tp_name, 0);
453+
rb_define_method(cPyTypePtr, "__tp_basicsize__", pycall_pytypeptr_get_tp_basicsize, 0);
454+
rb_define_method(cPyTypePtr, "__tp_flags__", pycall_pytypeptr_get_tp_flags, 0);
347455
}

spec/pycall/pyptr_spec.rb

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,15 @@ module PyCall
5353
describe '#kind_of?' do
5454
it 'works normally for Ruby class objects' do
5555
expect(PyPtr::None.kind_of?(PyPtr)).to eq(true)
56-
expect(PyPtr::None.kind_of?(Object)).to eq(true)
56+
expect(PyPtr::None.kind_of?(BasicObject)).to eq(true)
57+
expect(PyPtr::None.kind_of?(Object)).to eq(false)
5758
expect(PyPtr::None.kind_of?(Array)).to eq(false)
5859
expect { PyPtr::None.kind_of?(Object.new) }.to raise_error(TypeError)
5960
end
6061

6162
it 'works for Python type objects' do
62-
pytype_type = PyPtr.incref(PyPtr.new(LibPython.PyType_Type.to_ptr.address))
63-
pylong_type = PyPtr.incref(PyPtr.new(LibPython.PyLong_Type.to_ptr.address))
63+
pytype_type = PyPtr.incref(PyTypePtr.new(LibPython.PyType_Type.to_ptr.address))
64+
pylong_type = PyPtr.incref(PyTypePtr.new(LibPython.PyLong_Type.to_ptr.address))
6465

6566
expect(pylong_type.kind_of?(pytype_type)).to eq(true)
6667
expect(pylong_type.kind_of?(pylong_type)).to eq(false)

0 commit comments

Comments
 (0)