Move capsule code into C++

This commit is contained in:
Siu Kwan Lam 2013-12-20 18:17:08 -06:00
commit 078f1fe5c1
3 changed files with 708 additions and 169 deletions

View file

@ -48,7 +48,6 @@ def run(verbosity=1):
testresult = runner.run(suite)
print(hp.heap())
if testresult:
# Run isolated tests
print("run isolated tests".center(80, '-'))
@ -60,7 +59,7 @@ def run(verbosity=1):
p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
for line in p.stdout:
print(line, end='')
print(line.decode('utf8'), end='')
p.wait()
if p.returncode:
raise Exception("%s returned: %d" % p.returncode)

View file

@ -1,55 +1,95 @@
#include <Python.h>
#include <structmember.h>
#include <python3adapt.h>
#include <capsulethunk.h>
#include <llvm_binding/capsule_context.h>
#include <llvm_binding/auto_pyobject.h>
#include <string>
#include <sstream>
static PyObject* TheAPIModule = NULL;
static PyObject* TheCapsuleModule = NULL;
static PyObject* TheCapsuleClass = NULL;
static PyObject* TheWrapperClass = NULL;
static PyObject* TheCache = NULL;
static PyObject* TheAddrDtorDict = NULL;
static PyObject* TheClassesDict = NULL;
static PyObject* TheAddrRefCt = NULL;
static PyObject* ConstantOne = NULL;
static PyObject* TheDowncastModule = NULL;
static
PyObject* getName(PyObject* self, PyObject* args) {
PyObject* obj;
if (!PyArg_ParseTuple(args, "O", &obj)){
return NULL;
}
const char* name = PyCapsule_GetName(obj);
if (!name) return NULL;
return PyString_FromString(name);
PyObject* GetAPIModule(){
if (NULL == TheAPIModule)
TheAPIModule = PyImport_ImportModule("llvmpy._api");
return TheAPIModule;
}
static
PyObject* getPointer(PyObject* self, PyObject* args) {
PyObject* obj;
if (!PyArg_ParseTuple(args, "O", &obj)){
return NULL;
}
void* pointer = PyCapsule_GetPointer(obj, PyCapsule_GetName(obj));
if (!pointer) return NULL;
return PyLong_FromVoidPtr(pointer);
PyObject* GetCapsuleModule(){
if (NULL == TheCapsuleModule)
TheCapsuleModule = PyImport_ImportModule("llvmpy.capsule");
return TheCapsuleModule;
}
static
PyObject* check(PyObject* self, PyObject* args) {
PyObject* obj;
if (!PyArg_ParseTuple(args, "O", &obj)){
return NULL;
}
if (PyCapsule_CheckExact(obj)) {
Py_RETURN_TRUE;
} else {
Py_RETURN_FALSE;
}
PyObject* GetCapsuleClass() {
if (NULL == TheCapsuleClass)
TheCapsuleClass = PyObject_GetAttrString(GetCapsuleModule(),
"Capsule");
return TheCapsuleClass;
}
// ------------------
// PyCapsule Context
// ------------------
static
PyObject* GetWrapperClass() {
if (NULL == TheWrapperClass)
TheWrapperClass = PyObject_GetAttrString(GetCapsuleModule(),
"Wrapper");
return TheWrapperClass;
}
static
CapsuleContext* getContext(PyObject* self, PyObject* args) {
PyObject* obj;
if (!PyArg_ParseTuple(args, "O", &obj)) {
return NULL;
}
PyObject* GetCache() {
if (NULL == TheCache)
TheCache = PyObject_GetAttrString(GetCapsuleModule(), "_cache");
return TheCache;
}
static
PyObject* GetAddrDtorDict() {
if (NULL == TheAddrDtorDict)
TheAddrDtorDict = PyObject_GetAttrString(GetCapsuleModule(),
"_addr2dtor");
return TheAddrDtorDict;
}
static
PyObject* GetClassesDict() {
if (NULL == TheClassesDict)
TheClassesDict = PyObject_GetAttrString(GetCapsuleModule(),
"_pyclasses");
return TheClassesDict;
}
static
PyObject* GetAddrRefCt() {
if (NULL == TheAddrRefCt)
TheAddrRefCt = PyObject_GetAttrString(GetCapsuleModule(),
"_addr2refct");
return TheAddrRefCt;
}
static
PyObject* GetDowncastModule() {
if (NULL == TheDowncastModule)
TheDowncastModule = PyObject_GetAttrString(GetAPIModule(),
"downcast");
return TheDowncastModule;
}
static
CapsuleContext* GetContext(PyObject *obj) {
void* context = PyCapsule_GetContext(obj);
if (!context) {
PyErr_SetString(PyExc_TypeError, "PyCapsule has no context.");
@ -59,9 +99,8 @@ CapsuleContext* getContext(PyObject* self, PyObject* args) {
}
static
PyObject* getClassName(PyObject* self, PyObject* args) {
CapsuleContext* context = getContext(self, args);
//Assert(context->_magic == 0xdead);
PyObject* GetClassName(PyObject* obj) {
CapsuleContext* context = GetContext(obj);
if (!context) {
return NULL;
} else {
@ -69,6 +108,474 @@ PyObject* getClassName(PyObject* self, PyObject* args) {
}
}
static
PyObject* getClassName(PyObject* self, PyObject* args) {
PyObject* obj;
if (!PyArg_ParseTuple(args, "O", &obj)) {
return NULL;
}
return GetClassName(obj);
}
static
PyObject* GetName(PyObject* obj) {
const char* name = PyCapsule_GetName(obj);
if (!name) return NULL;
return PyString_FromString(name);
}
static
PyObject* getName(PyObject* self, PyObject* args) {
PyObject* obj;
if (!PyArg_ParseTuple(args, "O", &obj)){
return NULL;
}
return GetName(obj);
}
static
PyObject* GetPointer(PyObject* obj){
void *pointer = PyCapsule_GetPointer(obj, PyCapsule_GetName(obj));
if (!pointer) return NULL;
return PyLong_FromVoidPtr(pointer);
}
static
PyObject* getPointer(PyObject* self, PyObject* args) {
PyObject* obj;
if (!PyArg_ParseTuple(args, "O", &obj)){
return NULL;
}
return GetPointer(obj);
}
static
bool Check(PyObject* obj){
return PyCapsule_CheckExact(obj);
}
static
PyObject* check(PyObject* self, PyObject* args) {
PyObject* obj;
if (!PyArg_ParseTuple(args, "O", &obj)){
return NULL;
}
if (Check(obj)) {
Py_RETURN_TRUE;
} else {
Py_RETURN_FALSE;
}
}
static PyObject* Unwrap(PyObject* obj) {
if (PyObject_IsInstance(obj, GetWrapperClass())) {
return PyObject_GetAttrString(obj, "_ptr");
} else {
Py_INCREF(obj);
return obj;
}
}
/*
Unwrap a Wrapper instance into the underlying PyCapsule.
If `obj` is not a Wrapper instance, returns `obj`.
*/
static PyObject* unwrap(PyObject* self, PyObject* args) {
PyObject *obj;
if (!PyArg_ParseTuple(args, "O", &obj)) {
return NULL;
}
return Unwrap(obj);
}
static bool HasOwnership(PyObject* obj) {
PyObject* addr = GetPointer(obj);
PyObject* name = GetName(obj);
auto_pyobject nameaddr = PyTuple_Pack(2, name, addr);
PyObject* dtor = PyDict_GetItem(GetAddrDtorDict(), *nameaddr);
if (!dtor || dtor == Py_None) {
return false;
} else {
return true;
}
}
static PyObject* has_ownership(PyObject* self, PyObject* args) {
PyObject *obj;
if (!PyArg_ParseTuple(args, "O", &obj)) {
return NULL;
}
if (HasOwnership(obj)) {
Py_RETURN_TRUE;
} else {
Py_RETURN_FALSE;
}
}
static
void NormalizeString(std::ostream &os, const char* str) {
for(; *str; ++str){
if (*str == ':') {
os << '_';
if(*(str + 1) == ':') { ++str; }
} else {
os << *str;
}
}
}
static
PyObject* WrapCore(PyObject *oldCap, bool owned) {
auto_pyobject cap = PyObject_CallFunctionObjArgs(GetCapsuleClass(), oldCap,
NULL);
auto_pyobject cls = PyObject_CallMethod(*cap, "get_class", "");
auto_pyobject addr = GetPointer(oldCap);
auto_pyobject name = GetName(oldCap);
// look up cached object
auto_pyobject cache_cls = PyObject_GetItem(GetCache(), *cls);
Assert(*cache_cls);
int addr_in_cache = PyMapping_HasKey(*cache_cls, *addr);
PyObject* obj = NULL;
if (addr_in_cache) {
obj = PyObject_GetItem(*cache_cls, *addr);
} else {
if (!owned) {
auto_pyobject hasDtor = PyObject_CallMethod(*cls, "_has_dtor", "");
if (PyObject_IsTrue(*hasDtor)) {
auto_pyobject key = PyTuple_Pack(2, *name, *addr);
auto_pyobject val = PyObject_GetAttrString(*cls, "_delete_");
int ok = PyDict_SetItem(GetAddrDtorDict(), *key, *val);
Assert(ok != -1);
}
}
obj = PyObject_CallMethod(*cap, "instantiate", "");
int ok = PyObject_SetItem(*cache_cls, *addr, obj);
Assert(ok != -1);
}
Assert(obj);
return obj;
}
static
PyObject* Wrap(PyObject* cap, bool owned){
if (!Check(cap)) {
if (PyList_Check(cap)) {
const int N = PyList_Size(cap);
PyObject* result = PyList_New(N);
for (int i = 0; i < N; ++i){
PyObject* item = PyList_GetItem(cap, i);
if (!item)
return NULL;
PyObject* out = Wrap(item, false);
if (!out) return NULL;
if (-1 == PyList_SetItem(result, i, out))
return NULL;
}
return result;
} else {
Py_INCREF(cap);
return cap;
}
}
return WrapCore(cap, owned);
}
static
PyObject* wrap(PyObject* self, PyObject* args) {
PyObject* obj;
PyObject* owned = NULL;
if (!PyArg_ParseTuple(args, "O|O", &obj, &owned)) {
return NULL;
}
bool ownedFlag = false;
if (owned) {
ownedFlag = PyObject_IsTrue(owned);
}
return Wrap(obj, ownedFlag);
}
static
PyObject* downcast(PyObject* self, PyObject* args) {
PyObject *obj, *cls;
if (!PyArg_ParseTuple(args, "OO", &obj, &cls)) {
return NULL;
}
auto_pyobject objType = PyObject_Type(obj);
if (*objType == cls) {
Py_INCREF(obj);
return obj;
}
PyObject* apiModule = GetAPIModule();
auto_pyobject fromTy = PyObject_GetAttrString(obj, "_llvm_type_");
auto_pyobject toTy = PyObject_GetAttrString(cls, "_llvm_type_");
std::ostringstream oss;
auto_pyobject fromTyStr = PyObject_Str(*fromTy);
auto_pyobject toTyStr = PyObject_Str(*toTy);
const char * fromCS = PyString_AsString(*fromTyStr);
const char * toCS = PyString_AsString(*toTyStr);
oss << "downcast_";
NormalizeString(oss, fromCS);
oss << "_to_";
NormalizeString(oss, toCS);
std::string fname = oss.str();
auto_pyobject caster = PyObject_GetAttrString(GetDowncastModule(),
fname.c_str());
if (!caster) {
std::ostringstream oss;
oss << "Downcast from " << fromCS << " to " << toCS;
std::string errmsg = oss.str();
PyErr_SetString(PyExc_TypeError, errmsg.c_str());
return NULL;
}
auto_pyobject oldObj = Unwrap(obj);
auto_pyobject newObj = PyObject_CallFunctionObjArgs(*caster, *oldObj,
NULL);
bool used_to_own = HasOwnership(*oldObj);
PyObject *result = Wrap(*newObj, !used_to_own);
int status = PyObject_Not(result);
switch(status) {
case 0:
return result;
case 1:
default:
PyErr_SetString(PyExc_ValueError, "Downcast failed");
Py_XDECREF(result);
return NULL;
}
}
///////////////////////////////////////////////////////////////////////////////
struct CapsuleObject {
PyObject_HEAD;
PyObject *capsule;
};
static
void Capsule_dealloc(CapsuleObject* self) {
Py_XDECREF(self->capsule);
Py_TYPE(self)->tp_free((PyObject*)self);
}
static
int Capsule_init(CapsuleObject *self, PyObject *args, PyObject *kwds)
{
PyObject *cap;
if (!PyArg_ParseTuple(args, "O", &cap)) {
return -1;
}
if (!Check(cap)) {
PyErr_SetString(PyExc_TypeError, "Expected PyCapsule object");
return -1;
}
Py_INCREF(cap);
self->capsule = cap;
PyObject* addr2refct = GetAddrRefCt();
auto_pyobject ptr = GetPointer(self->capsule);
auto_pyobject refct = PyObject_GetItem(addr2refct, *ptr);
auto_pyobject inc = PyNumber_InPlaceAdd(*refct, ConstantOne);
return PyObject_SetItem(addr2refct, *ptr, *inc);
}
static
PyObject* Capsule_getclassname(CapsuleObject* self, void *closure) {
return GetClassName(self->capsule);
}
static
PyObject* Capsule_getname(CapsuleObject* self, void *closure) {
return GetName(self->capsule);
}
static
PyObject* Capsule_getpointer(CapsuleObject* self, void *closure) {
return GetPointer(self->capsule);
}
static
PyObject* Capsule_GetClass(CapsuleObject *self){
PyObject *pycls = GetClassesDict();
auto_pyobject key = GetClassName(self->capsule);
return PyDict_GetItem(pycls, *key); // borrowed reference
}
static
PyObject* Capsule_get_class(CapsuleObject* self, PyObject* args) {
PyObject* out = Capsule_GetClass(self);
Py_XINCREF(out);
return out;
}
static
PyObject* Capsule_instantiate(CapsuleObject* self, PyObject* args) {
return PyObject_CallFunctionObjArgs(Capsule_GetClass(self), self, NULL);
}
/// Rotate Right
static unsigned long RotR(unsigned long hash, int offset){
if (offset == 0) return hash;
unsigned long out = hash << (sizeof(hash) * 8 - offset);
out |= hash >> offset;
return out;
}
/*
This is called everytime an object is returned from LLVM.
It derserves to be optimized to reduce unnecessary Python object allocation.
The following implements a simple hash function that uses XOR and
right-rotation.
*/
static
long Capsule_hash(CapsuleObject *self) {
const char* name = PyCapsule_GetName(self->capsule);
void *pointer = PyCapsule_GetPointer(self->capsule, name);
unsigned long hash = 0xabcd1234 ^ (unsigned long)pointer;
// The first loop accounts for the different LLVM class name and the
// length of the name.
for(const char* p = name; *p != '\0'; ++p) {
hash ^= *p;
hash = RotR(hash, 11);
}
// The second loop accounts for the pointer identity.
for(int i = 0; i <sizeof(pointer); ++i) {
hash ^= ((unsigned char*)&pointer)[i];
hash = RotR(hash, 11);
}
return hash;
}
static
bool Capsule_eq(PyObject *self, PyObject *other) {
if (PyObject_Type(self) == PyObject_Type(other)) {
CapsuleObject *a = (CapsuleObject*)self;
CapsuleObject *b = (CapsuleObject*)other;
void* pa = PyCapsule_GetPointer(a->capsule,
PyCapsule_GetName(a->capsule));
void* pb = PyCapsule_GetPointer(b->capsule,
PyCapsule_GetName(b->capsule));
return pa == pb;
}
return false;
}
static
PyObject* Capsule_richcmp(PyObject *a, PyObject *b, int op) {
bool ret = Capsule_eq(a, b);
switch(op) {
case Py_EQ: break;
case Py_NE: ret = !ret; break;
default:
return Py_NotImplemented;
}
if (ret) {
Py_RETURN_TRUE;
} else {
Py_RETURN_FALSE;
}
}
static PyMemberDef Capsule_members[] = {
{"capsule", T_OBJECT_EX, offsetof(CapsuleObject, capsule), READONLY, "capsule"},
{ NULL },
};
static PyMethodDef Capsule_methods[] = {
{ "get_class", (PyCFunction)Capsule_get_class, METH_NOARGS,
"Get Capsule class"},
{ "instantiate", (PyCFunction)Capsule_instantiate, METH_NOARGS,
"create a new instance"},
{ NULL },
};
static PyGetSetDef Capsule_getseters[] = {
{"classname", (getter)Capsule_getclassname, NULL, "class name", NULL},
{"name", (getter)Capsule_getname, NULL, "name", NULL},
{"pointer", (getter)Capsule_getpointer, NULL, "pointer", NULL},
{ NULL },
};
static PyTypeObject CapsuleType = {
PyObject_HEAD_INIT(NULL)
#if (PY_MAJOR_VERSION < 3)
0, /*ob_size*/
#endif
"_capsule.Capsule", /*tp_name*/
sizeof(CapsuleObject), /*tp_basicsize*/
0, /*tp_itemsize*/
(destructor)Capsule_dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
(hashfunc)Capsule_hash, /*tp_hash */
0, /*tp_call*/
0, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT, /*tp_flags*/
"Capsule object", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
(richcmpfunc)Capsule_richcmp, /*tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
Capsule_methods, /* tp_methods */
Capsule_members, /* tp_members */
Capsule_getseters, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)Capsule_init, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
};
///////////////////////////////////////////////////////////////////////////////
static PyMethodDef core_methods[] = {
#define declmethod(func) { #func , ( PyCFunction )func , METH_VARARGS , NULL }
@ -76,6 +583,10 @@ static PyMethodDef core_methods[] = {
declmethod(getPointer),
declmethod(check),
declmethod(getClassName),
declmethod(unwrap),
declmethod(wrap),
declmethod(has_ownership),
declmethod(downcast),
{ NULL },
#undef declmethod
};
@ -106,10 +617,21 @@ extern "C" {
#else
PyObject *module = Py_InitModule("_capsule", core_methods);
#endif
if (module == NULL)
if (module == NULL){
INITERROR;
}
if (module) {
CapsuleType.tp_new = PyType_GenericNew;
if (PyType_Ready(&CapsuleType) < 0) {
INITERROR;
}
Py_INCREF(&CapsuleType);
PyModule_AddObject(module, "Capsule", (PyObject*)(&CapsuleType));
ConstantOne = PyInt_FromLong(1);
}
#if PY_MAJOR_VERSION >= 3
return module;
#endif
}

View file

@ -1,19 +1,27 @@
from weakref import WeakKeyDictionary, WeakValueDictionary, ref
from weakref import WeakValueDictionary
from collections import defaultdict
import logging
from llvmpy._capsule import (unwrap, has_ownership, downcast, wrap,
getClassName, getName, getPointer, Capsule)
logger = logging.getLogger(__name__)
NO_DEBUG = False
def silent_logger():
'''
Silent logger for unless we have a error message.
'''
global NO_DEBUG
logger.setLevel(logging.ERROR)
NO_DEBUG = True
# comment out the line below to re-enable logging at DEBUG level.
silent_logger()
def set_debug(enabled):
'''
Side-effect: configure logger with it is not configured.
@ -28,80 +36,63 @@ def set_debug(enabled):
else:
logger.setLevel(logging.WARNING)
def _capsule_weakref_dtor(item):
addr = item.pointer
name = item.name
_addr2refct[addr] -= 1
refct = _addr2refct[addr]
assert refct >= 0, "RefCt drop below 0"
if refct == 0:
dtor = _addr2dtor.pop((name, addr), None)
if dtor is not None:
if not NO_DEBUG:
# Some globals in logger could be removed by python GC
# at interpreter teardown.
# That can cause exception raised and ignored message.
logger.debug('Destroy %s %s', name, hex(addr))
dtor(item.capsule)
class Capsule(object):
"Wraps PyCapsule so that we can build weakref of it."
__slots__ = 'pointer', 'capsule', 'name', '__weakref__'
#class Capsule(object):
# "Wraps PyCapsule so that we can build weakref of it."
# from ._capsule import check, getClassName, getName, getPointer
# __slots__ = "capsule"
#
# def __init__(self, capsule):
# assert Capsule.valid(capsule)
# self.capsule = capsule
#
# #weak = WeakRef(self, _capsule_weakref_dtor)
# #weak.pointer = self.pointer
# #weak.capsule = capsule
# #weak.name = self.name
# #_capsule2weak[self] = weak
# _addr2refct[self.pointer] += 1
#
# @property
# def classname(self):
# return self.getClassName(self.capsule)
#
# @property
# def name(self):
# return self.getName(self.capsule)
#
# @property
# def pointer(self):
# return self.getPointer(self.capsule)
#
# @staticmethod
# def valid(capsule):
# return Capsule.check(capsule)
#
# def get_class(self):
# return _pyclasses[self.classname]
#
# def instantiate(self):
# cls = self.get_class()
# return cls(self)
#
# def __eq__(self, other):
# if isinstance(other, Capsule) and self.pointer == other.pointer:
# assert self.name == other.name
# return True
# else:
# return False
#
# def __hash__(self):
# return hash((self.pointer, self.name))
#
# def __ne__(self, other):
# return not (self == other)
from ._capsule import check, getClassName, getName, getPointer
def __init__(self, capsule):
assert Capsule.valid(capsule)
self.capsule = capsule
weak = WeakRef(self, _capsule_weakref_dtor)
weak.pointer = self.pointer
weak.capsule = capsule
weak.name = self.name
_capsule2weak[self] = weak
_addr2refct[self.pointer] += 1
@property
def classname(self):
return self.getClassName(self.capsule)
@property
def name(self):
return self.getName(self.capsule)
@property
def pointer(self):
return self.getPointer(self.capsule)
@staticmethod
def valid(capsule):
return Capsule.check(capsule)
def get_class(self):
return _pyclasses[self.classname]
def instantiate(self):
cls = self.get_class()
return cls(self)
def __eq__(self, other):
if isinstance(other, Capsule) and self.pointer == other.pointer:
assert self.name == other.name
return True
else:
return False
def __hash__(self):
return hash((self.pointer, self.name))
def __ne__(self, other):
return not (self == other)
class WeakRef(ref):
__slots__ = 'pointer', 'name', 'capsule'
_addr2refct = defaultdict(lambda: 0)
_capsule2weak = WeakKeyDictionary()
#_capsule2weak = WeakKeyDictionary()
_addr2dtor = {}
_pyclasses = {}
@ -109,15 +100,16 @@ _pyclasses = {}
# NOTE: The same 'addr' may appear in multiple class bins.
_cache = defaultdict(WeakValueDictionary)
def release_ownership(old):
logger.debug('Release %s', old)
addr = Capsule.getPointer(old)
name = Capsule.getName(old)
addr = getPointer(old)
name = getName(old)
if _addr2dtor.get((name, addr)) is None:
clsname = Capsule.getClassName(old)
clsname = getClassName(old)
if not _pyclasses[clsname]._has_dtor():
return
# Guard duplicated release
# Guard duplicated release
raise Exception("Already released")
_addr2dtor[(name, addr)] = None
@ -130,58 +122,83 @@ def obtain_ownership(cap):
assert _addr2dtor[(name, addr)] is None
_addr2dtor[(name, addr)] = cls._delete_
def has_ownership(cap):
addr = Capsule.getPointer(cap)
name = Capsule.getName(cap)
return _addr2dtor.get((name, addr)) is not None
def wrap(cap, owned=False):
'''Wrap a PyCapsule with the corresponding Wrapper class.
If `cap` is not a PyCapsule, returns `cap`
'''
if not Capsule.valid(cap):
if isinstance(cap, list):
return list(map(wrap, cap))
return cap # bypass if cap is not a PyCapsule and not a list
#def has_ownership(cap):
# addr = Capsule.getPointer(cap)
# name = Capsule.getName(cap)
# return _addr2dtor.get((name, addr)) is not None
cap = Capsule(cap)
cls = cap.get_class()
addr = cap.pointer
name = cap.name
# lookup cached object
if cls in _cache and addr in _cache[cls]:
obj = _cache[cls][addr]
else:
if not owned and cls._has_dtor():
_addr2dtor[(name, addr)] = cls._delete_
obj = cap.instantiate()
_cache[cls][addr] = obj # cache it
return obj
def unwrap(obj):
#def wrap(cap, owned=False):
# '''Wrap a PyCapsule with the corresponding Wrapper class.
# If `cap` is not a PyCapsule, returns `cap`
# '''
# if not Capsule.valid(cap):
# if isinstance(cap, list):
# return list(map(wrap, cap))
# return cap # bypass if cap is not a PyCapsule and not a list
#
# cap = Capsule(cap)
# cls = cap.get_class()
# addr = cap.pointer
# name = cap.name
# # lookup cached object
# if cls in _cache and addr in _cache[cls]:
# obj = _cache[cls][addr]
# else:
# if not owned and cls._has_dtor():
# _addr2dtor[(name, addr)] = cls._delete_
# obj = cap.instantiate()
# _cache[cls][addr] = obj # cache it
# return obj
#def unwrap(obj):
'''Unwrap a Wrapper instance into the underlying PyCapsule.
If `obj` is not a Wrapper instance, returns `obj`.
'''
if isinstance(obj, Wrapper):
return obj._ptr
else:
return obj
# if isinstance(obj, Wrapper):
# return obj._ptr
# else:
# return obj
def register_class(clsname):
def _wrapped(cls):
_pyclasses[clsname] = cls
return cls
return _wrapped
class Wrapper(object):
__slots__ = '__capsule'
def __init__(self, capsule):
self.__capsule = capsule
def __del__(self):
if _addr2refct is None:
# System is tearing down
# No need to free anything
return
item = self.__capsule
addr = item.pointer
name = item.name
_addr2refct[addr] -= 1
refct = _addr2refct[addr]
assert refct >= 0, "RefCt drop below 0"
if refct == 0:
dtor = _addr2dtor.pop((name, addr), None)
if dtor is not None:
if not NO_DEBUG:
# Some globals in logger could be removed by python GC
# at interpreter teardown.
# That can cause exception raised and ignored message.
logger.debug('Destroy %s %s', name, hex(addr))
dtor(item.capsule)
@property
def _capsule(self):
return self.__capsule
@ -198,7 +215,7 @@ class Wrapper(object):
return self._capsule == other._capsule
def __ne__(self, other):
return not(self == other)
return not (self == other)
def _downcast(self, newcls):
return downcast(self, newcls)
@ -207,24 +224,25 @@ class Wrapper(object):
def _has_dtor(cls):
return hasattr(cls, '_delete_')
def downcast(obj, cls):
from . import _api
if type(obj) is cls:
return obj
fromty = obj._llvm_type_
toty = cls._llvm_type_
logger.debug("Downcast %s to %s" , fromty, toty)
fname = 'downcast_%s_to_%s' % (fromty, toty)
fname = fname.replace('::', '_')
if not hasattr(_api.downcast, fname):
fmt = "Downcast from %s to %s is not supported"
raise TypeError(fmt % (fromty, toty))
caster = getattr(_api.downcast, fname)
old = unwrap(obj)
new = caster(old)
used_to_own = has_ownership(old)
res = wrap(new, owned=not used_to_own)
if not res:
raise ValueError("Downcast failed")
return res
#def downcast(obj, cls):
# from . import _api
#
# if type(obj) is cls:
# return obj
# fromty = obj._llvm_type_
# toty = cls._llvm_type_
# logger.debug("Downcast %s to %s", fromty, toty)
# fname = 'downcast_%s_to_%s' % (fromty, toty)
# fname = fname.replace('::', '_')
# if not hasattr(_api.downcast, fname):
# fmt = "Downcast from %s to %s is not supported"
# raise TypeError(fmt % (fromty, toty))
# caster = getattr(_api.downcast, fname)
# old = unwrap(obj)
# new = caster(old)
# used_to_own = has_ownership(old)
# res = wrap(new, owned=not used_to_own)
# if not res:
# raise ValueError("Downcast failed")
# return res