Skip to content

Commit

Permalink
Fix Python2.7 logging, close issue gfrd#83 and gfrd#84
Browse files Browse the repository at this point in the history
  • Loading branch information
Seynen committed Oct 21, 2014
1 parent 2afbe8e commit d0e8ab5
Show file tree
Hide file tree
Showing 3 changed files with 245 additions and 8 deletions.
11 changes: 11 additions & 0 deletions acinclude.m4
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,14 @@ AC_DEFUN([ECELL_CHECK_NUMPY_ARRAY_DESCR], [
])
CPPFLAGS="$ac_save_CPPFLAGS"
])

AC_DEFUN([ECELL_CHECK_LOGGING_MODULE], [
AC_MSG_CHECKING([if classes in the Python's logging module is old-fashioned])
AC_REQUIRE([AM_PATH_PYTHON])
if "$PYTHON" -c "import sys, logging; sys.exit(type(logging.Handler) == type)"; then
AC_MSG_RESULT(yes)
AC_DEFINE([HAVE_OLD_FASHIONED_LOGGER_CLASSES], [1], [Defined to 1 if Python's logging module is old-fashioned])
else
AC_MSG_RESULT(no)
fi
])
241 changes: 233 additions & 8 deletions binding/PythonAppender.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,11 @@ class PythonAppender: public LogAppender
boost::python::object makeRecord_;
};

// XXX: logging.Handler is a old-style class... *sigh*
class CppLoggerHandler
{
public:
// XXX: logging.Handler is a old-style class... *sigh*
#ifdef HAVE_OLD_FASHIONED_LOGGER_CLASSES
static PyObject* __class_init__(const char* name, PyObject* mod)
{
using namespace boost::python;
Expand Down Expand Up @@ -120,11 +121,6 @@ class CppLoggerHandler
return klass.get();
}

static void __dealloc__(void* ptr)
{
delete reinterpret_cast<CppLoggerHandler*>(ptr);
}

static CppLoggerHandler* get_self(PyObject* _self)
{
boost::python::handle<> ptr(
Expand Down Expand Up @@ -200,6 +196,154 @@ class CppLoggerHandler
return NULL;
}

static PyObject* __init__(PyObject* _self, PyObject* logger)
try
{
BOOST_ASSERT(PyInstance_Check(_self));

CppLoggerHandler* self(new CppLoggerHandler(
*boost::python::extract<Logger*>(logger)()));

boost::python::handle<> ptr(
boost::python::allow_null(
PyCObject_FromVoidPtr(self, &__dealloc__)));
if (!ptr)
{
delete self;
return NULL;
}

if (PyObject_SetAttrString(_self, "__ptr__", ptr.get()))
return NULL;

boost::python::handle<> bases(
boost::python::borrowed(
reinterpret_cast<PyInstanceObject*>(_self)->in_class->cl_bases));

if (!PyTuple_Check(bases.get()))
return NULL;

boost::python::handle<> super_arg(PyTuple_Pack(1, _self));

for (std::size_t i(0), l(PyTuple_GET_SIZE(bases.get())); i < l; ++i)
{
boost::python::handle<> base(
boost::python::borrowed(PyTuple_GET_ITEM(bases.get(), i)));
boost::python::handle<> super_init(
boost::python::borrowed(
PyObject_GetAttrString(base.get(), "__init__")));
if (PyErr_Occurred())
{
PyErr_Clear();
continue;
}

if (!boost::python::handle<>(
boost::python::allow_null(
PyObject_Call(super_init.get(), super_arg.get(), NULL))))
{
return NULL;
}
}

return boost::python::incref(Py_None);
}
catch (boost::python::error_already_set const&)
{
return NULL;
}
#else
void* operator new(size_t)
{
PyObject* retval = PyObject_New(PyObject, &__class__);
return retval;
}

void operator delete(void* ptr)
{
reinterpret_cast<PyObject*>(ptr)->ob_type->tp_free(reinterpret_cast<PyObject*>(ptr));
}

static PyObject* __class_init__(const char* name, PyObject* mod)
{
using namespace boost::python;

import_logging_module();
__name__ = static_cast<std::string>(
extract<std::string>(object(borrowed(mod)).attr("__name__")))
+ "." + name;
__class__.tp_name = const_cast<char*>(__name__.c_str());
__class__.tp_base = reinterpret_cast<PyTypeObject*>(getattr(logging_module, "Handler").ptr());
if (PyType_Ready(&__class__) < 0)
{
return 0;
}
return reinterpret_cast<PyObject*>(&__class__);
}

static CppLoggerHandler* get_self(PyObject* _self)
{
return reinterpret_cast<CppLoggerHandler*>(_self);
}

static PyObject* __new__(PyTypeObject* klass, PyObject* arg, PyObject* kwarg)
try
{
if (PyTuple_Size(arg) != 1)
{
PyErr_SetString(PyExc_TypeError, "the number of arguments must be 1");
return NULL;
}

CppLoggerHandler* retval(
new CppLoggerHandler(
*boost::python::extract<Logger*>(
PyTuple_GetItem(arg, 0))()));

if (PyErr_Occurred())
{
boost::python::decref(reinterpret_cast<PyObject*>(retval));
return NULL;
}
return reinterpret_cast<PyObject*>(retval);
}
catch (boost::python::error_already_set const&)
{
return NULL;
}

static int __init__(PyObject* self, PyObject* arg, PyObject* kwarg)
{
using namespace boost::python;
return handle<>(allow_null(
PyObject_Call(
PyObject_GetAttrString(
reinterpret_cast<PyObject*>(Py_TYPE(self)->tp_base),
const_cast<char*>("__init__")),
boost::python::handle<>(PyTuple_Pack(1, self)).get(),
NULL))) ? 0: 1;
}

static int __traverse__(PyObject *self, visitproc visit, void *arg)
{
Py_VISIT(reinterpret_cast<CppLoggerHandler*>(self)->__dict__.get());
return 0;
}

#endif
~CppLoggerHandler()
{
if (__weakreflist__)
{
PyObject_ClearWeakRefs(reinterpret_cast<PyObject*>(this));
}
}

static void __dealloc__(void* ptr)
{
delete reinterpret_cast<CppLoggerHandler*>(ptr);
}

static PyObject* createLock(PyObject* _self)
{
CppLoggerHandler* const self(get_self(_self));
Expand Down Expand Up @@ -308,7 +452,25 @@ class CppLoggerHandler
return boost::python::reference_existing_object::apply<Logger*>::type()(self->impl_);
}

CppLoggerHandler(Logger& impl): impl_(impl) {}

#ifndef HAVE_OLD_FASHIONED_LOGGER_CLASSES
static PyObject* get___dict__(PyObject *_self, void* context)
{
CppLoggerHandler* const self(get_self(_self));
if (!self)
return NULL;
return boost::python::incref(self->__dict__.get());
}
#endif

CppLoggerHandler(Logger& impl)
:
#ifndef HAVE_OLD_FASHIONED_LOGGER_CLASSES
__weakreflist__(0),
__dict__(PyDict_New()),
#endif
impl_(impl)
{}

protected:
static enum Logger::level translate_level(PyObject* _level)
Expand All @@ -330,16 +492,28 @@ class CppLoggerHandler


protected:
#ifdef HAVE_OLD_FASHIONED_LOGGER_CLASSES
static boost::python::handle<> __class__;
#else
PyObject_VAR_HEAD
static PyTypeObject __class__;
static std::string __name__;
PyObject *__weakreflist__;
boost::python::handle<> __dict__;
#endif
static PyMethodDef __methods__[];
static PyGetSetDef __getsets__[];
Logger& impl_;
};

boost::python::handle<> CppLoggerHandler::__class__;
#ifndef HAVE_OLD_FASHIONED_LOGGER_CLASSES
std::string CppLoggerHandler::__name__;
#endif

PyMethodDef CppLoggerHandler::__methods__[] = {
#ifdef HAVE_OLD_FASHIONED_LOGGER_CLASSES
{ "__init__", (PyCFunction)CppLoggerHandler::__init__, METH_O, "" },
#endif
{ "createLock", (PyCFunction)CppLoggerHandler::createLock, METH_NOARGS, "" },
{ "acquire", (PyCFunction)CppLoggerHandler::acquire, METH_NOARGS, "" },
{ "release", (PyCFunction)CppLoggerHandler::release, METH_NOARGS, "" },
Expand All @@ -353,10 +527,61 @@ PyMethodDef CppLoggerHandler::__methods__[] = {
};

PyGetSetDef CppLoggerHandler::__getsets__[] = {
#ifndef HAVE_OLD_FASHIONED_LOGGER_CLASSES
{ const_cast<char*>("__dict__"), (getter)&CppLoggerHandler::get___dict__, NULL, NULL },
#endif
{ const_cast<char*>("logger"), (getter)&CppLoggerHandler::get_logger, NULL, NULL },
{ NULL, NULL }
};

#ifdef HAVE_OLD_FASHIONED_LOGGER_CLASSES
boost::python::handle<> CppLoggerHandler::__class__;
#else
PyTypeObject CppLoggerHandler::__class__ = {
PyObject_HEAD_INIT(&PyType_Type)
0, /* ob_size */
0, /* tp_name */
sizeof(CppLoggerHandler), /* tp_basicsize */
0, /* tp_itemsize */
/* methods */
(destructor)&CppLoggerHandler::__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 */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_HAVE_CLASS | Py_TPFLAGS_HAVE_WEAKREFS | Py_TPFLAGS_BASETYPE, /* tp_flags */
0, /* tp_doc */
CppLoggerHandler::__traverse__, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
offsetof(CppLoggerHandler, __weakreflist__), /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
CppLoggerHandler::__methods__, /* tp_methods */
0, /* tp_members */
CppLoggerHandler::__getsets__, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
offsetof(CppLoggerHandler, __dict__), /* tp_dictoffset */
CppLoggerHandler::__init__, /* tp_init */
0, /* tp_alloc */
CppLoggerHandler::__new__, /*tp_new */
0 /* tp_free */
};
#endif

boost::python::object
register_logger_handler_class(char const* name)
{
Expand Down
1 change: 1 addition & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ dnl
dnl
ECELL_CHECK_NUMPY
ECELL_CHECK_NUMPY_ARRAY_DESCR
ECELL_CHECK_LOGGING_MODULE
dnl
AM_CHECK_PYTHON_HEADERS()
ac_save_CPPFLAGS="$CPPFLAGS"
Expand Down

0 comments on commit d0e8ab5

Please sign in to comment.