/*- * Public Domain 2008-2014 WiredTiger, Inc. * * This is free and unencumbered software released into the public domain. * * Anyone is free to copy, modify, publish, use, compile, sell, or * distribute this software, either in source code form or as a compiled * binary, for any purpose, commercial or non-commercial, and by any * means. * * In jurisdictions that recognize copyright laws, the author or authors * of this software dedicate any and all copyright interest in the * software to the public domain. We make this dedication for the benefit * of the public at large and to the detriment of our heirs and * successors. We intend this dedication to be an overt act of * relinquishment in perpetuity of all present and future rights to this * software under copyright law. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ /* * wiredtiger.i * The SWIG interface file defining the wiredtiger python API. */ %define DOCSTRING "@defgroup wt_python WiredTiger Python API Python wrappers aroung the WiredTiger C API. @{ @cond IGNORE" %enddef %module(docstring=DOCSTRING) wiredtiger %feature("autodoc", "0"); %pythoncode %{ from packing import pack, unpack ## @endcond %} /* Set the input argument to point to a temporary variable */ %typemap(in, numinputs=0) WT_CONNECTION ** (WT_CONNECTION *temp = NULL) { $1 = &temp; } %typemap(in, numinputs=0) WT_SESSION ** (WT_SESSION *temp = NULL) { $1 = &temp; } %typemap(in, numinputs=0) WT_ASYNC_OP ** (WT_ASYNC_OP *temp = NULL) { $1 = &temp; } %typemap(in, numinputs=0) WT_CURSOR ** (WT_CURSOR *temp = NULL) { $1 = &temp; } %typemap(in) WT_ASYNC_CALLBACK * (PyObject *callback_obj = NULL) %{ callback_obj = $input; $1 = &pyApiAsyncCallback; %} %typemap(in, numinputs=0) WT_EVENT_HANDLER * %{ $1 = &pyApiEventHandler; %} /* Set the return value to the returned connection, session, or cursor */ %typemap(argout) WT_CONNECTION ** { $result = SWIG_NewPointerObj(SWIG_as_voidptr(*$1), SWIGTYPE_p___wt_connection, 0); } %typemap(argout) WT_SESSION ** { $result = SWIG_NewPointerObj(SWIG_as_voidptr(*$1), SWIGTYPE_p___wt_session, 0); if (*$1 != NULL) { PY_CALLBACK *pcb; if (__wt_calloc_def((WT_SESSION_IMPL *)(*$1), 1, &pcb) != 0) SWIG_exception_fail(SWIG_MemoryError, "WT calloc failed"); else { Py_XINCREF($result); pcb->pyobj = $result; ((WT_SESSION_IMPL *)(*$1))->lang_private = pcb; } } } %typemap(argout) WT_ASYNC_OP ** { $result = SWIG_NewPointerObj(SWIG_as_voidptr(*$1), SWIGTYPE_p___wt_async_op, 0); if (*$1 != NULL) { PY_CALLBACK *pcb; (*$1)->c.flags |= WT_CURSTD_RAW; PyObject_SetAttrString($result, "is_column", PyBool_FromLong(strcmp((*$1)->key_format, "r") == 0)); PyObject_SetAttrString($result, "key_format", PyString_InternFromString((*$1)->key_format)); PyObject_SetAttrString($result, "value_format", PyString_InternFromString((*$1)->value_format)); if (__wt_calloc_def((WT_ASYNC_OP_IMPL *)(*$1), 1, &pcb) != 0) SWIG_exception_fail(SWIG_MemoryError, "WT calloc failed"); else { pcb->pyobj = $result; Py_XINCREF(pcb->pyobj); /* XXX Is there a way to avoid SWIG's numbering? */ pcb->pyasynccb = callback_obj5; Py_XINCREF(pcb->pyasynccb); (*$1)->c.lang_private = pcb; } } } %typemap(argout) WT_CURSOR ** { $result = SWIG_NewPointerObj(SWIG_as_voidptr(*$1), SWIGTYPE_p___wt_cursor, 0); if (*$1 != NULL) { PY_CALLBACK *pcb; uint32_t json; json = (*$1)->flags & WT_CURSTD_DUMP_JSON; if (!json) (*$1)->flags |= WT_CURSTD_RAW; PyObject_SetAttrString($result, "is_json", PyBool_FromLong(json != 0)); PyObject_SetAttrString($result, "is_column", PyBool_FromLong(strcmp((*$1)->key_format, "r") == 0)); PyObject_SetAttrString($result, "key_format", PyString_InternFromString((*$1)->key_format)); PyObject_SetAttrString($result, "value_format", PyString_InternFromString((*$1)->value_format)); if (__wt_calloc_def((WT_SESSION_IMPL *)(*$1)->session, 1, &pcb) != 0) SWIG_exception_fail(SWIG_MemoryError, "WT calloc failed"); else { Py_XINCREF($result); pcb->pyobj = $result; (*$1)->lang_private = pcb; } } } /* 64 bit typemaps. */ %typemap(in) uint64_t { $1 = PyLong_AsUnsignedLongLong($input); } %typemap(out) uint64_t { $result = PyLong_FromUnsignedLongLong($1); } /* Throw away references after close. */ %define DESTRUCTOR(class, method) %feature("shadow") class::method %{ def method(self, *args): '''close(self, config) -> int @copydoc class::method''' try: self._freecb() return $action(self, *args) finally: self.this = None %} %enddef DESTRUCTOR(__wt_connection, close) DESTRUCTOR(__wt_cursor, close) DESTRUCTOR(__wt_session, close) /* Don't require empty config strings. */ %typemap(default) const char *config { $1 = NULL; } %typemap(default) WT_CURSOR *to_dup { $1 = NULL; } /* * Error returns other than WT_NOTFOUND generate an exception. * Use our own exception type, in future tailored to the kind * of error. */ %header %{ #include "src/include/wt_internal.h" /* * Closed handle checking: * * The typedef WT_CURSOR_NULLABLE used in wiredtiger.h is only made * visible to the SWIG parser and is used to identify arguments of * Cursor type that are permitted to be null. Likewise, typedefs * WT_{CURSOR,SESSION,CONNECTION}_CLOSED identify 'close' calls that * need explicit nulling of the swigCPtr. We do not match the *_CLOSED * typedefs in Python SWIG, as we already have special cased 'close' methods. * * We want SWIG to see these 'fake' typenames, but not the compiler. */ #define WT_CURSOR_NULLABLE WT_CURSOR #define WT_CURSOR_CLOSED WT_CURSOR #define WT_SESSION_CLOSED WT_SESSION #define WT_CONNECTION_CLOSED WT_CONNECTION /* * For Connections, Sessions and Cursors created in Python, each of * WT_CONNECTION_IMPL, WT_SESSION_IMPL and WT_CURSOR have a * lang_private field that store a pointer to a PY_CALLBACK, alloced * during the various open calls. {conn,session,cursor}CloseHandler() * functions reach into the associated Python object, set the 'this' * asttribute to None, and free the PY_CALLBACK. */ typedef struct { PyObject *pyobj; /* the python Session/Cursor/AsyncOp object */ PyObject *pyasynccb; /* the callback to use for AsyncOp */ } PY_CALLBACK; static PyObject *wtError; static int sessionFreeHandler(WT_SESSION *session_arg); static int cursorFreeHandler(WT_CURSOR *cursor_arg); %} %init %{ /* * Create an exception type and put it into the _wiredtiger module. * First increment the reference count because PyModule_AddObject * decrements it. Then note that "m" is the local variable for the * module in the SWIG generated code. If there is a SWIG variable for * this, I haven't found it. */ wtError = PyErr_NewException("_wiredtiger.WiredTigerError", NULL, NULL); Py_INCREF(wtError); PyModule_AddObject(m, "WiredTigerError", wtError); %} %pythoncode %{ WiredTigerError = _wiredtiger.WiredTigerError ## @cond DISABLE # Implements the iterable contract class IterableCursor: def __init__(self, cursor): self.cursor = cursor def __iter__(self): return self def next(self): if self.cursor.next() == WT_NOTFOUND: raise StopIteration return self.cursor.get_keys() + self.cursor.get_values() ## @endcond # An abstract class, which must be subclassed with notify() overridden. class AsyncCallback: def __init__(self): raise NotImplementedError def notify(self, op, op_ret, flags): raise NotImplementedError %} /* Bail out if arg or arg.this is None, else set res to the C pointer. */ %define CONVERT_WITH_NULLCHECK(argp, res) if ($input == Py_None) { SWIG_exception_fail(SWIG_NullReferenceError, "in method '$symname', " "argument $argnum of type '$type' is None"); } else { res = SWIG_ConvertPtr($input, &argp, $descriptor, $disown | 0); if (!SWIG_IsOK(res)) { if (SWIG_Python_GetSwigThis($input) == 0) { SWIG_exception_fail(SWIG_NullReferenceError, "in method '$symname', " "argument $argnum of type '$type' is None"); } else { SWIG_exception_fail(SWIG_ArgError(res), "in method '$symname', " "argument $argnum of type '$type'"); } } } %enddef /* * Extra 'self' elimination. * The methods we're wrapping look like this: * struct __wt_xxx { * int method(WT_XXX *, ...otherargs...); * }; * To SWIG, that is equivalent to: * int method(struct __wt_xxx *self, WT_XXX *, ...otherargs...); * and we use consecutive argument matching of typemaps to convert two args to * one. */ %define SELFHELPER(type, name) %typemap(in) (type *self, type *name) (void *argp = 0, int res = 0) %{ CONVERT_WITH_NULLCHECK(argp, res) $2 = $1 = ($ltype)(argp); %} %typemap(in) type ## _NULLABLE * { $1 = *(type **)&$input; } %enddef SELFHELPER(struct __wt_connection, connection) SELFHELPER(struct __wt_async_op, op) SELFHELPER(struct __wt_session, session) SELFHELPER(struct __wt_cursor, cursor) /* * Create an error exception if it has not already * been done. */ %define SWIG_ERROR_IF_NOT_SET(result) do { if (PyErr_Occurred() == NULL) { /* We could use PyErr_SetObject for more complex reporting. */ SWIG_SetErrorMsg(wtError, wiredtiger_strerror(result)); } SWIG_fail; } while(0) %enddef /* Error handling. Default case: a non-zero return is an error. */ %exception { $action if (result != 0) SWIG_ERROR_IF_NOT_SET(result); } /* Async operations can return ENOMEM when no ops are available. */ %define ENOMEM_OK(m) %exception m { retry: $action if (result != 0 && result != ENOMEM) SWIG_ERROR_IF_NOT_SET(result); else if (result == ENOMEM) { __wt_sleep(0, 10000); goto retry; } } %enddef /* Any API that returns an enum type uses this. */ %define ENUM_OK(m) %exception m { $action } %enddef /* Cursor positioning methods can also return WT_NOTFOUND. */ %define NOTFOUND_OK(m) %exception m { $action if (result != 0 && result != WT_NOTFOUND) SWIG_ERROR_IF_NOT_SET(result); } %enddef /* Cursor compare can return any of -1, 0, 1 or WT_NOTFOUND. */ %define COMPARE_OK(m) %exception m { $action if ((result < -1 || result > 1) && result != WT_NOTFOUND) SWIG_ERROR_IF_NOT_SET(result); } %enddef ENOMEM_OK(__wt_connection::async_new_op) ENUM_OK(__wt_async_op::get_type) NOTFOUND_OK(__wt_cursor::next) NOTFOUND_OK(__wt_cursor::prev) NOTFOUND_OK(__wt_cursor::remove) NOTFOUND_OK(__wt_cursor::search) NOTFOUND_OK(__wt_cursor::update) COMPARE_OK(__wt_cursor::compare) COMPARE_OK(__wt_cursor::search_near) /* Lastly, some methods need no (additional) error checking. */ %exception __wt_connection::search_near; %exception __wt_connection::get_home; %exception __wt_connection::is_new; %exception __wt_async_op::_set_key; %exception __wt_async_op::_set_value; %exception __wt_cursor::_set_key; %exception __wt_cursor::_set_value; %exception wiredtiger_strerror; %exception wiredtiger_version; /* WT_ASYNC_OP customization. */ /* First, replace the varargs get / set methods with Python equivalents. */ %ignore __wt_async_op::get_key; %ignore __wt_async_op::get_value; %ignore __wt_async_op::set_key; %ignore __wt_async_op::set_value; %immutable __wt_async_op::connection; /* WT_CURSOR customization. */ /* First, replace the varargs get / set methods with Python equivalents. */ %ignore __wt_cursor::get_key; %ignore __wt_cursor::get_value; %ignore __wt_cursor::set_key; %ignore __wt_cursor::set_value; /* Next, override methods that return integers via arguments. */ %ignore __wt_cursor::compare(WT_CURSOR *, WT_CURSOR *, int *); %ignore __wt_cursor::search_near(WT_CURSOR *, int *); /* SWIG magic to turn Python byte strings into data / size. */ %apply (char *STRING, int LENGTH) { (char *data, int size) }; /* Handle binary data returns from get_key/value -- avoid cstring.i: it creates a list of returns. */ %typemap(in,numinputs=0) (char **datap, int *sizep) (char *data, int size) { $1 = &data; $2 = &size; } %typemap(frearg) (char **datap, int *sizep) ""; %typemap(argout) (char **datap, int *sizep) { if (*$1) $result = SWIG_FromCharPtrAndSize(*$1, *$2); } /* Handle record number returns from get_recno */ %typemap(in,numinputs=0) (uint64_t *recnop) (uint64_t recno) { $1 = &recno; } %typemap(frearg) (uint64_t *recnop) ""; %typemap(argout) (uint64_t *recnop) { $result = PyLong_FromUnsignedLongLong(*$1); } %{ typedef int int_void; %} typedef int int_void; %typemap(out) int_void { $result = VOID_Object; } %extend __wt_async_op { /* Get / set keys and values */ void _set_key(char *data, int size) { WT_ITEM k; k.data = data; k.size = (uint32_t)size; $self->set_key($self, &k); } int_void _set_recno(uint64_t recno) { WT_ITEM k; uint8_t recno_buf[20]; size_t size; int ret; if ((ret = wiredtiger_struct_size(NULL, &size, "r", recno)) != 0 || (ret = wiredtiger_struct_pack(NULL, recno_buf, sizeof (recno_buf), "r", recno)) != 0) return (ret); k.data = recno_buf; k.size = (uint32_t)size; $self->set_key($self, &k); return (ret); } void _set_value(char *data, int size) { WT_ITEM v; v.data = data; v.size = (uint32_t)size; $self->set_value($self, &v); } /* Don't return values, just throw exceptions on failure. */ int_void _get_key(char **datap, int *sizep) { WT_ITEM k; int ret = $self->get_key($self, &k); if (ret == 0) { *datap = (char *)k.data; *sizep = (int)k.size; } return (ret); } int_void _get_recno(uint64_t *recnop) { WT_ITEM k; int ret = $self->get_key($self, &k); if (ret == 0) ret = wiredtiger_struct_unpack(NULL, k.data, k.size, "q", recnop); return (ret); } int_void _get_value(char **datap, int *sizep) { WT_ITEM v; int ret = $self->get_value($self, &v); if (ret == 0) { *datap = (char *)v.data; *sizep = (int)v.size; } return (ret); } int _freecb() { return (cursorFreeHandler($self)); } %pythoncode %{ def get_key(self): '''get_key(self) -> object @copydoc WT_ASYNC_OP::get_key Returns only the first column.''' k = self.get_keys() if len(k) == 1: return k[0] return k def get_keys(self): '''get_keys(self) -> (object, ...) @copydoc WT_ASYNC_OP::get_key''' if self.is_column: return [self._get_recno(),] else: return unpack(self.key_format, self._get_key()) def get_value(self): '''get_value(self) -> object @copydoc WT_ASYNC_OP::get_value Returns only the first column.''' v = self.get_values() if len(v) == 1: return v[0] return v def get_values(self): '''get_values(self) -> (object, ...) @copydoc WT_ASYNC_OP::get_value''' return unpack(self.value_format, self._get_value()) def set_key(self, *args): '''set_key(self) -> None @copydoc WT_ASYNC_OP::set_key''' if len(args) == 1 and type(args[0]) == tuple: args = args[0] if self.is_column: self._set_recno(long(args[0])) else: # Keep the Python string pinned self._key = pack(self.key_format, *args) self._set_key(self._key) def set_value(self, *args): '''set_value(self) -> None @copydoc WT_ASYNC_OP::set_value''' if len(args) == 1 and type(args[0]) == tuple: args = args[0] # Keep the Python string pinned self._value = pack(self.value_format, *args) self._set_value(self._value) def __getitem__(self, key): '''Python convenience for searching''' self.set_key(key) if self.search() != 0: raise KeyError return self.get_value() %} }; %extend __wt_cursor { /* Get / set keys and values */ void _set_key(char *data, int size) { WT_ITEM k; k.data = data; k.size = (uint32_t)size; $self->set_key($self, &k); } int_void _set_recno(uint64_t recno) { WT_ITEM k; uint8_t recno_buf[20]; size_t size; int ret; if ((ret = wiredtiger_struct_size($self->session, &size, "r", recno)) != 0 || (ret = wiredtiger_struct_pack($self->session, recno_buf, sizeof (recno_buf), "r", recno)) != 0) return (ret); k.data = recno_buf; k.size = (uint32_t)size; $self->set_key($self, &k); return (ret); } void _set_value(char *data, int size) { WT_ITEM v; v.data = data; v.size = (uint32_t)size; $self->set_value($self, &v); } /* Don't return values, just throw exceptions on failure. */ int_void _get_key(char **datap, int *sizep) { WT_ITEM k; int ret = $self->get_key($self, &k); if (ret == 0) { *datap = (char *)k.data; *sizep = (int)k.size; } return (ret); } int_void _get_json_key(char **datap, int *sizep) { const char *k; int ret = $self->get_key($self, &k); if (ret == 0) { *datap = (char *)k; *sizep = strlen(k); } return (ret); } int_void _get_recno(uint64_t *recnop) { WT_ITEM k; int ret = $self->get_key($self, &k); if (ret == 0) ret = wiredtiger_struct_unpack($self->session, k.data, k.size, "q", recnop); return (ret); } int_void _get_value(char **datap, int *sizep) { WT_ITEM v; int ret = $self->get_value($self, &v); if (ret == 0) { *datap = (char *)v.data; *sizep = (int)v.size; } return (ret); } int_void _get_json_value(char **datap, int *sizep) { const char *k; int ret = $self->get_value($self, &k); if (ret == 0) { *datap = (char *)k; *sizep = strlen(k); } return (ret); } /* compare and search_near need special handling. */ int compare(WT_CURSOR *other) { int cmp = 0; int ret = 0; if (other == NULL) { SWIG_Error(SWIG_NullReferenceError, "in method 'Cursor_compare', " "argument 1 of type 'struct __wt_cursor *' " "is None"); ret = EINVAL; /* any non-zero value will do. */ } else { ret = $self->compare($self, other, &cmp); /* * Map less-than-zero to -1 and greater-than-zero to 1 * to avoid colliding with other errors. */ ret = ((ret != 0) ? ret : (cmp < 0) ? -1 : (cmp == 0) ? 0 : 1); } return (ret); } int search_near() { int cmp = 0; int ret = $self->search_near($self, &cmp); /* * Map less-than-zero to -1 and greater-than-zero to 1 to avoid * colliding with WT_NOTFOUND. */ return ((ret != 0) ? ret : (cmp < 0) ? -1 : (cmp == 0) ? 0 : 1); } int _freecb() { return (cursorFreeHandler($self)); } %pythoncode %{ def get_key(self): '''get_key(self) -> object @copydoc WT_CURSOR::get_key Returns only the first column.''' k = self.get_keys() if len(k) == 1: return k[0] return k def get_keys(self): '''get_keys(self) -> (object, ...) @copydoc WT_CURSOR::get_key''' if self.is_json: return [self._get_json_key()] elif self.is_column: return [self._get_recno(),] else: return unpack(self.key_format, self._get_key()) def get_value(self): '''get_value(self) -> object @copydoc WT_CURSOR::get_value Returns only the first column.''' v = self.get_values() if len(v) == 1: return v[0] return v def get_values(self): '''get_values(self) -> (object, ...) @copydoc WT_CURSOR::get_value''' if self.is_json: return [self._get_json_value()] else: return unpack(self.value_format, self._get_value()) def set_key(self, *args): '''set_key(self) -> None @copydoc WT_CURSOR::set_key''' if len(args) == 1 and type(args[0]) == tuple: args = args[0] if self.is_column: self._set_recno(long(args[0])) else: # Keep the Python string pinned self._key = pack(self.key_format, *args) self._set_key(self._key) def set_value(self, *args): '''set_value(self) -> None @copydoc WT_CURSOR::set_value''' if len(args) == 1 and type(args[0]) == tuple: args = args[0] # Keep the Python string pinned self._value = pack(self.value_format, *args) self._set_value(self._value) def __iter__(self): '''Cursor objects support iteration, equivalent to calling WT_CURSOR::next until it returns ::WT_NOTFOUND.''' if not hasattr(self, '_iterable'): self._iterable = IterableCursor(self) return self._iterable def __getitem__(self, key): '''Python convenience for searching''' self.set_key(key) if self.search() != 0: raise KeyError return self.get_value() %} }; %extend __wt_session { int log_printf(const char *msg) { return self->log_printf(self, "%s", msg); } int _freecb() { return (sessionFreeHandler(self)); } }; %extend __wt_connection { int _freecb() { return (0); } }; /* Remove / rename parts of the C API that we don't want in Python. */ %immutable __wt_cursor::session; %immutable __wt_cursor::uri; %ignore __wt_cursor::key_format; %ignore __wt_cursor::value_format; %immutable __wt_session::connection; %immutable __wt_async_op::connection; %immutable __wt_async_op::uri; %immutable __wt_async_op::config; %ignore __wt_async_op::key_format; %ignore __wt_async_op::value_format; %ignore __wt_async_callback; %ignore __wt_collator; %ignore __wt_compressor; %ignore __wt_config_item; %ignore __wt_data_source; %ignore __wt_event_handler; %ignore __wt_extractor; %ignore __wt_item; %ignore __wt_lsn; %ignore __wt_connection::add_collator; %ignore __wt_connection::add_compressor; %ignore __wt_connection::add_data_source; %ignore __wt_connection::add_extractor; %ignore __wt_connection::get_extension_api; %ignore __wt_session::log_printf; %ignore wiredtiger_struct_pack; %ignore wiredtiger_struct_size; %ignore wiredtiger_struct_unpack; %ignore wiredtiger_extension_init; %ignore wiredtiger_extension_terminate; /* Convert 'int *' to output args for wiredtiger_version */ %apply int *OUTPUT { int * }; %rename(AsyncOp) __wt_async_op; %rename(Cursor) __wt_cursor; %rename(Session) __wt_session; %rename(Connection) __wt_connection; %include "wiredtiger.h" /* Add event handler support. */ %{ /* Write to and flush the stream. */ static int writeToPythonStream(const char *streamname, const char *message) { PyObject *sys, *se, *write_method, *flush_method, *written, *arglist, *arglist2; char *msg; int ret; size_t msglen; sys = NULL; se = NULL; write_method = flush_method = NULL; written = NULL; arglist = arglist2 = NULL; msglen = strlen(message); msg = malloc(msglen + 2); strcpy(msg, message); strcpy(&msg[msglen], "\n"); /* Acquire python Global Interpreter Lock. Otherwise can segfault. */ SWIG_PYTHON_THREAD_BEGIN_BLOCK; ret = 1; if ((sys = PyImport_ImportModule("sys")) == NULL) goto err; if ((se = PyObject_GetAttrString(sys, streamname)) == NULL) goto err; if ((write_method = PyObject_GetAttrString(se, "write")) == NULL) goto err; if ((flush_method = PyObject_GetAttrString(se, "flush")) == NULL) goto err; if ((arglist = Py_BuildValue("(s)", msg)) == NULL) goto err; if ((arglist2 = Py_BuildValue("()")) == NULL) goto err; written = PyObject_CallObject(write_method, arglist); (void)PyObject_CallObject(flush_method, arglist2); ret = 0; err: Py_XDECREF(arglist2); Py_XDECREF(arglist); Py_XDECREF(flush_method); Py_XDECREF(write_method); Py_XDECREF(se); Py_XDECREF(sys); Py_XDECREF(written); /* Release python Global Interpreter Lock */ SWIG_PYTHON_THREAD_END_BLOCK; if (msg) free(msg); return (ret); } static int pythonErrorCallback(WT_EVENT_HANDLER *handler, WT_SESSION *session, int err, const char *message) { return writeToPythonStream("stderr", message); } static int pythonMessageCallback(WT_EVENT_HANDLER *handler, WT_SESSION *session, const char *message) { return writeToPythonStream("stdout", message); } /* Zero out SWIG's pointer to the C object, * equivalent to 'pyobj.this = None' in Python. */ static int pythonClose(PY_CALLBACK *pcb) { int ret; /* * Ensure the global interpreter lock is held - so that Python * doesn't shut down threads while we use them. */ SWIG_PYTHON_THREAD_BEGIN_BLOCK; ret = 0; if (PyObject_SetAttrString(pcb->pyobj, "this", Py_None) == -1) { SWIG_Error(SWIG_RuntimeError, "WT SetAttr failed"); ret = EINVAL; /* any non-zero value will do. */ } Py_XDECREF(pcb->pyobj); Py_XDECREF(pcb->pyasynccb); SWIG_PYTHON_THREAD_END_BLOCK; return (ret); } /* Session specific close handler. */ static int sessionCloseHandler(WT_SESSION *session_arg) { int ret; PY_CALLBACK *pcb; WT_SESSION_IMPL *session; ret = 0; session = (WT_SESSION_IMPL *)session_arg; pcb = (PY_CALLBACK *)session->lang_private; session->lang_private = NULL; if (pcb != NULL) ret = pythonClose(pcb); __wt_free(session, pcb); return (ret); } /* Cursor specific close handler. */ static int cursorCloseHandler(WT_CURSOR *cursor) { int ret; PY_CALLBACK *pcb; ret = 0; pcb = (PY_CALLBACK *)cursor->lang_private; cursor->lang_private = NULL; if (pcb != NULL) ret = pythonClose(pcb); __wt_free((WT_SESSION_IMPL *)cursor->session, pcb); return (ret); } /* Session specific close handler. */ static int sessionFreeHandler(WT_SESSION *session_arg) { PY_CALLBACK *pcb; WT_SESSION_IMPL *session; session = (WT_SESSION_IMPL *)session_arg; pcb = (PY_CALLBACK *)session->lang_private; session->lang_private = NULL; __wt_free(session, pcb); return (0); } /* Cursor specific close handler. */ static int cursorFreeHandler(WT_CURSOR *cursor) { PY_CALLBACK *pcb; pcb = (PY_CALLBACK *)cursor->lang_private; cursor->lang_private = NULL; __wt_free((WT_SESSION_IMPL *)cursor->session, pcb); return (0); } static int pythonCloseCallback(WT_EVENT_HANDLER *handler, WT_SESSION *session, WT_CURSOR *cursor) { int ret; WT_UNUSED(handler); if (cursor != NULL) ret = cursorCloseHandler(cursor); else ret = sessionCloseHandler(session); return (ret); } static WT_EVENT_HANDLER pyApiEventHandler = { pythonErrorCallback, pythonMessageCallback, NULL, pythonCloseCallback }; %} /* Add async callback support. */ %{ static int pythonAsyncCallback(WT_ASYNC_CALLBACK *cb, WT_ASYNC_OP *asyncop, int opret, uint32_t flags) { int ret, t_ret; PY_CALLBACK *pcb; PyObject *arglist, *notify_method, *pyresult; WT_ASYNC_OP_IMPL *op; WT_SESSION_IMPL *session; /* * Ensure the global interpreter lock is held since we'll be * making Python calls now. */ SWIG_PYTHON_THREAD_BEGIN_BLOCK; op = (WT_ASYNC_OP_IMPL *)asyncop; session = O2S(op); pcb = (PY_CALLBACK *)asyncop->c.lang_private; asyncop->c.lang_private = NULL; ret = 0; if (pcb->pyasynccb == NULL) goto err; if ((arglist = Py_BuildValue("(Oii)", pcb->pyobj, opret, flags)) == NULL) goto err; if ((notify_method = PyObject_GetAttrString(pcb->pyasynccb, "notify")) == NULL) goto err; pyresult = PyEval_CallObject(notify_method, arglist); if (pyresult == NULL || !PyArg_Parse(pyresult, "i", &ret)) goto err; if (0) { if (ret == 0) ret = EINVAL; err: __wt_err(session, ret, "python async callback error"); } Py_XDECREF(pyresult); Py_XDECREF(notify_method); Py_XDECREF(arglist); SWIG_PYTHON_THREAD_END_BLOCK; if (pcb != NULL) { if ((t_ret = pythonClose(pcb) != 0) && ret == 0) ret = t_ret; } __wt_free(session, pcb); if (ret == 0 && (opret == 0 || opret == WT_NOTFOUND)) return (0); else return (1); } static WT_ASYNC_CALLBACK pyApiAsyncCallback = { pythonAsyncCallback }; %} %pythoncode %{ class stat: '''keys for statistics cursors''' class conn: '''keys for cursors on connection statistics''' pass class dsrc: '''keys for cursors on data source statistics''' pass ## @} import sys # All names starting with 'WT_STAT_DSRC_' are renamed to # the wiredtiger.stat.dsrc class, those starting with 'WT_STAT_CONN' are # renamed to wiredtiger.stat.conn class. def _rename_with_prefix(prefix, toclass): curmodule = sys.modules[__name__] for name in dir(curmodule): if name.startswith(prefix): shortname = name[len(prefix):].lower() setattr(toclass, shortname, getattr(curmodule, name)) delattr(curmodule, name) _rename_with_prefix('WT_STAT_CONN_', stat.conn) _rename_with_prefix('WT_STAT_DSRC_', stat.dsrc) del _rename_with_prefix %}