Author: phunt Date: Thu Apr 22 06:06:18 2010 New Revision: 936624 URL: http://svn.apache.org/viewvc?rev=936624&view=rev Log: ZOOKEEPER-631. zkpython's C code could do with a style clean-up Modified: hadoop/zookeeper/trunk/CHANGES.txt hadoop/zookeeper/trunk/src/contrib/zkpython/src/c/zookeeper.c Modified: hadoop/zookeeper/trunk/CHANGES.txt URL: http://svn.apache.org/viewvc/hadoop/zookeeper/trunk/CHANGES.txt?rev=936624&r1=936623&r2=936624&view=diff ============================================================================== --- hadoop/zookeeper/trunk/CHANGES.txt (original) +++ hadoop/zookeeper/trunk/CHANGES.txt Thu Apr 22 06:06:18 2010 @@ -20,6 +20,9 @@ BUGFIXES: ZOOKEEPER-741. root level create on REST proxy fails (phunt) + ZOOKEEPER-631. zkpython's C code could do with a style clean-up + (henry robinson via phunt) + IMPROVEMENTS: ZOOKEEPER-724. Improve junit test integration - log harness information (phunt via mahadev) Modified: hadoop/zookeeper/trunk/src/contrib/zkpython/src/c/zookeeper.c URL: http://svn.apache.org/viewvc/hadoop/zookeeper/trunk/src/contrib/zkpython/src/c/zookeeper.c?rev=936624&r1=936623&r2=936624&view=diff ============================================================================== --- hadoop/zookeeper/trunk/src/contrib/zkpython/src/c/zookeeper.c (original) +++ hadoop/zookeeper/trunk/src/contrib/zkpython/src/c/zookeeper.c Thu Apr 22 06:06:18 2010 @@ -19,7 +19,7 @@ #include #include #include - + ////////////////////////////////////////////// // EXCEPTIONS PyObject *ZooKeeperException = NULL; @@ -50,324 +50,385 @@ PyObject *NothingException; PyObject *err_to_exception(int errcode) { switch (errcode) { - case ZSYSTEMERROR: - return SystemErrorException; - case ZRUNTIMEINCONSISTENCY: - return RuntimeInconsistencyException; - case ZDATAINCONSISTENCY: - return DataInconsistencyException; - case ZCONNECTIONLOSS: - return ConnectionLossException; - case ZMARSHALLINGERROR: - return MarshallingErrorException; - case ZUNIMPLEMENTED: - return UnimplementedException; - case ZOPERATIONTIMEOUT: - return OperationTimeoutException; - case ZBADARGUMENTS: - return BadArgumentsException; - case ZAPIERROR: - return ApiErrorException; - case ZNONODE: - return NoNodeException; - case ZNOAUTH: - return NoAuthException; - case ZBADVERSION: - return BadVersionException; - case ZNOCHILDRENFOREPHEMERALS: - return NoChildrenForEphemeralsException; - case ZNODEEXISTS: - return NodeExistsException; - case ZINVALIDACL: - return InvalidACLException; - case ZAUTHFAILED: - return AuthFailedException; - case ZNOTEMPTY: - return NotEmptyException; - case ZSESSIONEXPIRED: - return SessionExpiredException; - case ZINVALIDCALLBACK: - return InvalidCallbackException; - case ZSESSIONMOVED: - return SessionMovedException; + case ZSYSTEMERROR: + return SystemErrorException; + case ZRUNTIMEINCONSISTENCY: + return RuntimeInconsistencyException; + case ZDATAINCONSISTENCY: + return DataInconsistencyException; + case ZCONNECTIONLOSS: + return ConnectionLossException; + case ZMARSHALLINGERROR: + return MarshallingErrorException; + case ZUNIMPLEMENTED: + return UnimplementedException; + case ZOPERATIONTIMEOUT: + return OperationTimeoutException; + case ZBADARGUMENTS: + return BadArgumentsException; + case ZAPIERROR: + return ApiErrorException; + case ZNONODE: + return NoNodeException; + case ZNOAUTH: + return NoAuthException; + case ZBADVERSION: + return BadVersionException; + case ZNOCHILDRENFOREPHEMERALS: + return NoChildrenForEphemeralsException; + case ZNODEEXISTS: + return NodeExistsException; + case ZINVALIDACL: + return InvalidACLException; + case ZAUTHFAILED: + return AuthFailedException; + case ZNOTEMPTY: + return NotEmptyException; + case ZSESSIONEXPIRED: + return SessionExpiredException; + case ZINVALIDCALLBACK: + return InvalidCallbackException; + case ZSESSIONMOVED: + return SessionMovedException; - case ZOK: - default: - return NULL; - } + case ZOK: + default: + return NULL; + } } -#define CHECK_ZHANDLE(z) if ( (z) < 0 || (z) >= num_zhandles) { \ - PyErr_SetString( ZooKeeperException, "zhandle out of range" ); \ -return NULL; \ -} else if ( zhandles[(z)] == NULL ) { \ - PyErr_SetString(ZooKeeperException, "zhandle already freed"); \ - return NULL; \ - } +#define CHECK_ZHANDLE(z) if ( (z) < 0 || (z) >= num_zhandles) { \ + PyErr_SetString( ZooKeeperException, "zhandle out of range" ); \ +return NULL; \ +} else if ( zhandles[(z)] == NULL ) { \ + PyErr_SetString(ZooKeeperException, "zhandle already freed"); \ + return NULL; \ + } -///////////////////////////////////////////// -// HELPER FUNCTIONS +/* Contains all the state required for a watcher callback - these are + passed to the *dispatch functions as void*, cast to pywatcher_t and + then their callback member is invoked if not NULL */ typedef struct { int zhandle; PyObject *callback; int permanent; }pywatcher_t; -// This array exists because we need to ref. count -// the global watchers for each connection - but they're -// inaccessible without pulling in zk_adaptor.h, which I'm -// trying to avoid. +/* This array exists because we need to ref. count the global watchers + for each connection - but they're inaccessible without pulling in + zk_adaptor.h, which I'm trying to avoid. */ static pywatcher_t **watchers; -// We keep an array of zhandles available for use. -// When a zhandle is correctly closed, the C client -// frees the memory so we set the zhandles[i] entry to NULL. -// This entry can then be re-used +/* We keep an array of zhandles available for use. When a zhandle is + correctly closed, the C client frees the memory so we set the + zhandles[i] entry to NULL. This entry can then be re-used. */ static zhandle_t** zhandles = NULL; static int num_zhandles = 0; static int max_zhandles = 0; #define REAL_MAX_ZHANDLES 32768 -// Allocates an initial zhandle and watcher array -void init_zhandles(int num) { - zhandles = malloc(sizeof(zhandle_t*)*num); - watchers = malloc(sizeof(pywatcher_t*)*num); - max_zhandles = num; - num_zhandles = 0; - memset(zhandles, 0, sizeof(zhandle_t*)*max_zhandles); -} - -// Note that the following zhandle functions are not -// thread-safe. The C-Python runtime does not seem to -// pre-empt a thread that is in a C module, so there's -// no need for synchronisation. - -// Doubles the size of the zhandle / watcher array -// Returns 0 if the new array would be >= REAL_MAX_ZHANDLES -// in size. -int resize_zhandles() { - zhandle_t **tmp = zhandles; - pywatcher_t ** wtmp = watchers; - if (max_zhandles >= REAL_MAX_ZHANDLES >> 1) { - return -1; - } - max_zhandles *= 2; - zhandles = malloc(sizeof(zhandle_t*)*max_zhandles); - memset(zhandles, 0, sizeof(zhandle_t*)*max_zhandles); - memcpy(zhandles, tmp, sizeof(zhandle_t*)*max_zhandles/2); - - watchers = malloc(sizeof(pywatcher_t*)*max_zhandles); - memset(watchers, 0, sizeof(pywatcher_t*)*max_zhandles); - memcpy(watchers, wtmp, sizeof(pywatcher_t*)*max_zhandles/2); - - free(wtmp); - free(tmp); - return 0; -} - -// Find a free zhandle - this is expensive, but we -// expect it to be infrequently called. -// There are optimisations that can be made if this turns out -// to be problematic. -// Returns -1 if no free handle is found. -unsigned int next_zhandle() { - int i = 0; - for (i=0;i= REAL_MAX_ZHANDLES in size. Called when zhandles + is full. Returns 0 if allocation failed or if max num zhandles + exceeded. */ +int resize_zhandles(void) { + zhandle_t **tmp = zhandles; + pywatcher_t ** wtmp = watchers; + if (max_zhandles >= REAL_MAX_ZHANDLES >> 1) { + return 0; + } + max_zhandles *= 2; + zhandles = malloc(sizeof(zhandle_t*)*max_zhandles); + if (zhandles == NULL) { + PyErr_SetString(PyExc_MemoryError, "malloc for new zhandles failed"); + return 0; + } + memset(zhandles, 0, sizeof(zhandle_t*)*max_zhandles); + memcpy(zhandles, tmp, sizeof(zhandle_t*)*max_zhandles/2); + + watchers = malloc(sizeof(pywatcher_t*)*max_zhandles); + if (watchers == NULL) { + PyErr_SetString(PyExc_MemoryError, "malloc for new watchers failed"); + return 0; + } + memset(watchers, 0, sizeof(pywatcher_t*)*max_zhandles); + memcpy(watchers, wtmp, sizeof(pywatcher_t*)*max_zhandles/2); - return -1; + free(wtmp); + free(tmp); + return 1; +} + +/* Find a free zhandle - this iterates through the list of open + zhandles, but we expect it to be infrequently called. There are + optimisations that can be made if this turns out to be problematic. + Returns -1 if no free handle is found - resize_handles() can be + called in that case. */ +unsigned int next_zhandle(void) { + int i = 0; + for (i=0;izhandle = zh; ret->callback = cb; ret->permanent = permanent; return ret; } -void free_pywatcher( pywatcher_t *pw) +/* Releases the reference taken in create_pywatcher to the callback, + then frees the allocated pywatcher_t* */ +void free_pywatcher(pywatcher_t *pw) { + if (pw == NULL) { + return; + } Py_DECREF(pw->callback); + free(pw); } - +/* Constructs a new stat object. Returns Py_None if stat == NULL or a + dictionary containing all the stat information otherwise. In either + case, takes a reference to the returned object. */ PyObject *build_stat( const struct Stat *stat ) { - if (stat == NULL) { - return Py_None; - } + if (stat == NULL) { + Py_INCREF(Py_None); + return Py_None; + } return Py_BuildValue( "{s:K, s:K, s:K, s:K," - "s:i, s:i, s:i, s:K," - "s:i, s:i, s:K}", - "czxid", stat->czxid, - "mzxid", stat->mzxid, - "ctime", stat->ctime, - "mtime", stat->mtime, - "version", stat->version, - "cversion", stat->cversion, - "aversion", stat->aversion, - "ephemeralOwner", stat->ephemeralOwner, - "dataLength", stat->dataLength, - "numChildren", stat->numChildren, - "pzxid", stat->pzxid ); -} - + "s:i, s:i, s:i, s:K," + "s:i, s:i, s:K}", + "czxid", stat->czxid, + "mzxid", stat->mzxid, + "ctime", stat->ctime, + "mtime", stat->mtime, + "version", stat->version, + "cversion", stat->cversion, + "aversion", stat->aversion, + "ephemeralOwner", stat->ephemeralOwner, + "dataLength", stat->dataLength, + "numChildren", stat->numChildren, + "pzxid", stat->pzxid ); +} + +/* Creates a new list of strings from a String_vector. Returns the + empty list if the String_vector is NULL. Takes a reference to the + returned PyObject and gives that reference to the caller. */ PyObject *build_string_vector(const struct String_vector *sv) { PyObject *ret; - if (!sv) - { - ret = PyList_New(0); - } - else - { - ret = PyList_New(sv->count); - if (ret) - { - int i; - for (i=0;icount;++i) - { - PyObject *s = PyString_FromString(sv->data[i]); - if (!s) - { - Py_DECREF(ret); - ret = NULL; - break; - } - PyList_SetItem(ret, i, s); - } + if (!sv) { + return PyList_New(0); + } + + ret = PyList_New(sv->count); + if (ret) { + int i; + for (i=0;icount;++i) { + PyObject *s = PyString_FromString(sv->data[i]); + if (!s) { + if (ret != Py_None) { + Py_DECREF(ret); } + ret = NULL; + break; + } + PyList_SetItem(ret, i, s); } + } return ret; } +/* Returns 1 if the PyObject is a valid representation of an ACL, and + 0 otherwise. */ +int check_is_acl(PyObject *o) { + int i; + if (o == NULL) { + return 0; + } + if (!PyList_Check(o)) { + return 0; + } + for (i=0;icount ); + if (acls == NULL) { + return PyList_New(0); + } + + PyObject *ret = PyList_New(acls->count); int i; - for (i=0;icount;++i) - { - PyObject *acl = Py_BuildValue( "{s:i, s:s, s:s}", - "perms", acls->data[i].perms, - "scheme", acls->data[i].id.scheme, - "id", acls->data[i].id.id ); - PyList_SetItem(ret, i, acl); - } + for (i=0;icount;++i) { + PyObject *acl = Py_BuildValue( "{s:i, s:s, s:s}", + "perms", acls->data[i].perms, + "scheme", acls->data[i].id.scheme, + "id", acls->data[i].id.id ); + PyList_SetItem(ret, i, acl); + } return ret; } -void parse_acls( struct ACL_vector *acls, PyObject *pyacls ) +/* Parse the Python representation of an ACL list into an ACL_vector + (which needs subsequent freeing) */ +int parse_acls(struct ACL_vector *acls, PyObject *pyacls) { PyObject *a; - acls->count = PyList_Size( pyacls ); - acls->data = (struct ACL *)calloc( acls->count, sizeof(struct ACL) ); int i; - for (i=0;icount;++i) - { - a = PyList_GetItem( pyacls, i ); - // a is now a dictionary - PyObject *perms = PyDict_GetItemString( a, "perms" ); - acls->data[i].perms = (int32_t)(PyInt_AsLong(perms)); - acls->data[i].id.id = strdup( PyString_AsString( PyDict_GetItemString( a, "id" ) ) ); - acls->data[i].id.scheme = strdup( PyString_AsString( PyDict_GetItemString( a, "scheme" ) ) ); - } + if (acls == NULL || pyacls == NULL) { + PyErr_SetString(PyExc_ValueError, "acls or pyacls NULL in parse_acls"); + return 0; + } + + acls->count = PyList_Size( pyacls ); + + // Is this a list? If not, we can't do anything + if (PyList_Check(pyacls) == 0) { + PyErr_SetString(InvalidACLException, "List of ACLs required in parse_acls"); + return 0; + } + + acls->data = (struct ACL *)calloc(acls->count, sizeof(struct ACL)); + if (acls->data == NULL) { + PyErr_SetString(PyExc_MemoryError, "calloc failed in parse_acls"); + return 0; + } + + for (i=0;icount;++i) { + a = PyList_GetItem(pyacls, i); + // a is now a dictionary + PyObject *perms = PyDict_GetItemString( a, "perms" ); + acls->data[i].perms = (int32_t)(PyInt_AsLong(perms)); + acls->data[i].id.id = strdup( PyString_AsString( PyDict_GetItemString( a, "id" ) ) ); + acls->data[i].id.scheme = strdup( PyString_AsString( PyDict_GetItemString( a, "scheme" ) ) ); + } + return 1; } +/* Deallocates the memory allocated inside an ACL_vector, but not the + ACL_vector itself */ void free_acls( struct ACL_vector *acls ) { + if (acls == NULL) { + return; + } int i; - for (i=0;icount;++i) - { - free(acls->data[i].id.id); - free(acls->data[i].id.scheme); - } + for (i=0;icount;++i) { + free(acls->data[i].id.id); + free(acls->data[i].id.scheme); + } free(acls->data); } -///////////////////////////////////////////// +/* -------------------------------------------------------------------------- */ +/* Watcher and callback implementation */ +/* -------------------------------------------------------------------------- */ + /* Every watcher invocation goes through this dispatch point, which a) acquires the global interpreter lock - b) unpacks the PyObject to call from the passed context pointer, which - handily includes the index of the relevant zookeeper handle to pass back to Python. - c) Makes the call into Python, checking for error conditions which we are responsible for - detecting and doing something about (we just print the error and plough right on) - d) releases the lock after freeing up the context object, which is only used for one - watch invocation (watches are one-shot) + + b) unpacks the PyObject to call from the passed context pointer, + which handily includes the index of the relevant zookeeper handle + to pass back to Python. + + c) Makes the call into Python, checking for error conditions which + we are responsible for detecting and doing something about (we just + print the error and plough right on) + + d) releases the lock after freeing up the context object, which is + only used for one watch invocation (watches are one-shot, unless + 'permanent' != 0) */ -void watcher_dispatch(zhandle_t *zzh, int type, int state, const char *path, void *context) +void watcher_dispatch(zhandle_t *zzh, int type, int state, + const char *path, void *context) { PyGILState_STATE gstate; pywatcher_t *pyw = (pywatcher_t*)context; PyObject *callback = pyw->callback; + if (callback == NULL) { + // This is unexpected + char msg[256]; + sprintf(msg, "pywatcher: %d %p %d", pyw->zhandle, pyw->callback, pyw->permanent); + PyErr_SetString(PyExc_ValueError, msg); + return; + } + gstate = PyGILState_Ensure(); PyObject *arglist = Py_BuildValue("(i,i,i,s)", pyw->zhandle,type, state, path); - if (PyObject_CallObject((PyObject*)callback, arglist) == NULL) + if (PyObject_CallObject((PyObject*)callback, arglist) == NULL) { PyErr_Print(); - if (pyw->permanent == 0) - free_pywatcher(pyw); - PyGILState_Release(gstate); -} - -static PyObject *pyzookeeper_init(PyObject *self, PyObject *args) -{ - const char *host; - PyObject *watcherfn = Py_None; - int recv_timeout = 10000; - // int clientid = -1; - clientid_t cid; - cid.client_id = -1; - const char *passwd; - int handle = next_zhandle(); - if (handle == -1) { - resize_zhandles(); - handle = next_zhandle(); - } - - if (handle == -1) { - PyErr_SetString(ZooKeeperException,"Couldn't find a free zhandle, something is very wrong"); - return NULL; - } - - if (!PyArg_ParseTuple(args, "s|Oi(Ls)", &host, &watcherfn, &recv_timeout, &cid.client_id, &passwd)) - return NULL; - - if (cid.client_id != -1) { - strncpy(cid.passwd, passwd, 16*sizeof(char)); } - pywatcher_t *pyw = NULL; - if (watcherfn != Py_None) { - pyw = create_pywatcher(handle, watcherfn,1); + if (pyw->permanent == 0) { + free_pywatcher(pyw); } - watchers[handle] = pyw; - zhandle_t *zh = zookeeper_init( host, watcherfn != Py_None ? watcher_dispatch : NULL, - recv_timeout, cid.client_id == -1 ? 0 : &cid, - pyw, - 0 ); - - if (zh == NULL) - { - PyErr_SetString( ZooKeeperException, "Could not internally obtain zookeeper handle" ); - return NULL; - } - - zhandles[handle] = zh; - return Py_BuildValue( "i", handle); + PyGILState_Release(gstate); } -/////////////////////////////////////////////////////// -// Similar kind of mechanisms for various completion -// types +/* The completion callbacks (from asynchronous calls) are implemented similarly */ +/* Called when an asynchronous call that returns void completes and + dispatches user provided callback */ void void_completion_dispatch(int rc, const void *data) { PyGILState_STATE gstate; @@ -383,6 +444,8 @@ void void_completion_dispatch(int rc, co PyGILState_Release(gstate); } +/* Called when an asynchronous call that returns a stat structure + completes and dispatches user provided callback */ void stat_completion_dispatch(int rc, const struct Stat *stat, const void *data) { PyGILState_STATE gstate; @@ -391,13 +454,19 @@ void stat_completion_dispatch(int rc, co return; PyObject *callback = pyw->callback; gstate = PyGILState_Ensure(); - PyObject *arglist = Py_BuildValue("(i,i,N)", pyw->zhandle,rc, build_stat(stat)); + PyObject *pystat = build_stat(stat); + PyObject *arglist = Py_BuildValue("(i,i,O)", pyw->zhandle,rc, pystat); + Py_DECREF(pystat); + if (PyObject_CallObject((PyObject*)callback, arglist) == NULL) PyErr_Print(); free_pywatcher(pyw); PyGILState_Release(gstate); } +/* Called when an asynchronous call that returns a stat structure and + some untyped data completes and dispatches user provided + callback (used by aget) */ void data_completion_dispatch(int rc, const char *value, int value_len, const struct Stat *stat, const void *data) { PyGILState_STATE gstate; @@ -406,13 +475,18 @@ void data_completion_dispatch(int rc, co return; PyObject *callback = pyw->callback; gstate = PyGILState_Ensure(); - PyObject *arglist = Py_BuildValue("(i,i,s#,O)", pyw->zhandle,rc, value,value_len, build_stat(stat)); + PyObject *pystat = build_stat(stat); + PyObject *arglist = Py_BuildValue("(i,i,s#,O)", pyw->zhandle,rc, value,value_len, pystat); + Py_DECREF(pystat); + if (PyObject_CallObject((PyObject*)callback, arglist) == NULL) PyErr_Print(); free_pywatcher(pyw); PyGILState_Release(gstate); } +/* Called when an asynchronous call that returns a list of strings + completes and dispatches user provided callback */ void strings_completion_dispatch(int rc, const struct String_vector *strings, const void *data) { PyGILState_STATE gstate; @@ -424,22 +498,26 @@ void strings_completion_dispatch(int rc, PyObject *pystrings = build_string_vector(strings); if (pystrings) { - PyObject *arglist = Py_BuildValue("(i,i,O)", pyw->zhandle, rc, pystrings); + PyObject *arglist = Py_BuildValue("(i,i,O)", pyw->zhandle, rc, pystrings); if (arglist == NULL || PyObject_CallObject((PyObject*)callback, arglist) == NULL) PyErr_Print(); } else PyErr_Print(); + Py_DECREF(pystrings); free_pywatcher(pyw); PyGILState_Release(gstate); } +/* Called when an asynchronous call that returns a single string + completes and dispatches user provided callback */ void string_completion_dispatch(int rc, const char *value, const void *data) { PyGILState_STATE gstate; pywatcher_t *pyw = (pywatcher_t*)data; - if (pyw == NULL) + if (pyw == NULL) { return; + } PyObject *callback = pyw->callback; gstate = PyGILState_Ensure(); PyObject *arglist = Py_BuildValue("(i,i,s)", pyw->zhandle,rc, value); @@ -449,52 +527,115 @@ void string_completion_dispatch(int rc, PyGILState_Release(gstate); } +/* Called when an asynchronous call that returns a list of ACLs + completes and dispatches user provided callback */ void acl_completion_dispatch(int rc, struct ACL_vector *acl, struct Stat *stat, const void *data) { PyGILState_STATE gstate; pywatcher_t *pyw = (pywatcher_t*)data; - if (pyw == NULL) + if (pyw == NULL) { return; + } PyObject *callback = pyw->callback; gstate = PyGILState_Ensure(); - PyObject *arglist = Py_BuildValue("(i,i,O,O)", pyw->zhandle,rc, build_acls(acl), build_stat(stat)); - if (PyObject_CallObject((PyObject*)callback, arglist) == NULL) + PyObject *pystat = build_stat(stat); + PyObject *pyacls = build_acls(acl); + PyObject *arglist = Py_BuildValue("(i,i,O,O)", pyw->zhandle,rc, pyacls, pystat); + + Py_DECREF(pystat); + Py_DECREF(pyacls); + + if (PyObject_CallObject((PyObject*)callback, arglist) == NULL) { PyErr_Print(); + } free_pywatcher(pyw); PyGILState_Release(gstate); } -int check_is_acl(PyObject *o) { - int i; - if (!PyList_Check(o)) { - return 0; - } - for (i=0;iclient_id, cid->passwd); + return Py_BuildValue("(L,s)", cid->client_id, cid->passwd); } +/* DO NOT USE - context is used internally. This method is not exposed + in the Python module */ PyObject *pyzoo_get_context(PyObject *self, PyObject *args) { int zkhid; @@ -996,60 +1274,76 @@ PyObject *pyzoo_get_context(PyObject *se PyObject *context = NULL; context = (PyObject*)zoo_get_context(zhandles[zkhid]); if (context) return context; + Py_INCREF(Py_None); return Py_None; } +/* DO NOT USE - context is used internally. This method is not exposed + in the Python module */ PyObject *pyzoo_set_context(PyObject *self, PyObject *args) { int zkhid; PyObject *context; - if (!PyArg_ParseTuple(args, "iO", &zkhid, &context)) + if (!PyArg_ParseTuple(args, "iO", &zkhid, &context)) { return NULL; + } CHECK_ZHANDLE(zkhid); PyObject *py_context = (PyObject*)zoo_get_context(zhandles[zkhid]); - if (py_context != NULL) { + if (py_context != NULL && py_context != Py_None) { Py_DECREF(py_context); } Py_INCREF(context); zoo_set_context(zhandles[zkhid], (void*)context); + Py_INCREF(Py_None); return Py_None; } -/////////////////////////////////////////////////////// -// misc + +/* -------------------------------------------------------------------------- */ +/* Miscellaneous methods */ +/* -------------------------------------------------------------------------- */ + +/* Sets the global watcher. Returns None */ PyObject *pyzoo_set_watcher(PyObject *self, PyObject *args) { int zkhid; PyObject *watcherfn; - if (!PyArg_ParseTuple(args, "iO", &zkhid, &watcherfn)) + if (!PyArg_ParseTuple(args, "iO", &zkhid, &watcherfn)) { return NULL; + } CHECK_ZHANDLE(zkhid); pywatcher_t *pyw = watchers[zkhid]; if (pyw != NULL) { free_pywatcher( pyw ); } + // Create a *permanent* watcher object, not deallocated when called pyw = create_pywatcher(zkhid, watcherfn,1); + if (pyw == NULL) { + return NULL; + } watchers[zkhid] = pyw; zoo_set_watcher(zhandles[zkhid], watcher_dispatch); zoo_set_context(zhandles[zkhid], pyw); - + Py_INCREF(Py_None); return Py_None; } +/* Returns an integer code representing the current connection + state */ PyObject *pyzoo_state(PyObject *self, PyObject *args) { int zkhid; - if (!PyArg_ParseTuple(args,"i",&zkhid)) + if (!PyArg_ParseTuple(args,"i",&zkhid)) { return NULL; + } CHECK_ZHANDLE(zkhid); int state = zoo_state(zhandles[zkhid]); return Py_BuildValue("i",state); } -// Synchronous calls return ZOK or throw an exception, but async calls get -// an integer so should use this. This could perhaps use standardising. +/* Convert an integer error code into a string */ PyObject *pyzerror(PyObject *self, PyObject *args) { int rc; @@ -1058,6 +1352,7 @@ PyObject *pyzerror(PyObject *self, PyObj return Py_BuildValue("s", zerror(rc)); } +/* Returns the integer receive timeout for a connection */ PyObject *pyzoo_recv_timeout(PyObject *self, PyObject *args) { int zkhid; @@ -1068,6 +1363,7 @@ PyObject *pyzoo_recv_timeout(PyObject *s return Py_BuildValue("i",recv_timeout); } +/* Returns > 0 if connection is unrecoverable, 0 otherwise */ PyObject *pyis_unrecoverable(PyObject *self, PyObject *args) { int zkhid; @@ -1078,43 +1374,58 @@ PyObject *pyis_unrecoverable(PyObject *s return Py_BuildValue("i",ret); // TODO: make this a boolean } +/* Set the debug level for logging, returns None */ PyObject *pyzoo_set_debug_level(PyObject *self, PyObject *args) { int loglevel; if (!PyArg_ParseTuple(args, "i", &loglevel)) - return NULL; + return NULL; zoo_set_debug_level((ZooLogLevel)loglevel); + Py_INCREF(Py_None); return Py_None; } + static PyObject *log_stream = NULL; +/* Set the output file-like object for logging output. Returns Py_None */ PyObject *pyzoo_set_log_stream(PyObject *self, PyObject *args) { PyObject *pystream = NULL; - if (!PyArg_ParseTuple(args,"O",&pystream)) + if (!PyArg_ParseTuple(args,"O",&pystream)) { + PyErr_SetString(PyExc_ValueError, "Must supply a Python object to set_log_stream"); return NULL; - if (!PyFile_Check(pystream)) + } + if (!PyFile_Check(pystream)) { + PyErr_SetString(PyExc_ValueError, "Must supply a file object to set_log_stream"); return NULL; + } + /* Release the previous reference to log_stream that we took */ if (log_stream != NULL) { Py_DECREF(log_stream); } + log_stream = pystream; Py_INCREF(log_stream); zoo_set_log_stream(PyFile_AsFile(log_stream)); + Py_INCREF(Py_None); return Py_None; } +/* Set the connection order - randomized or in-order. Returns None. */ PyObject *pyzoo_deterministic_conn_order(PyObject *self, PyObject *args) { int yesOrNo; if (!PyArg_ParseTuple(args, "i",&yesOrNo)) return NULL; zoo_deterministic_conn_order( yesOrNo ); + Py_INCREF(Py_None); return Py_None; } -/////////////////////////////////////////////////// +/* -------------------------------------------------------------------------- */ +/* Module setup */ +/* -------------------------------------------------------------------------- */ #include "pyzk_docstrings.h" @@ -1131,11 +1442,6 @@ static PyMethodDef ZooKeeperMethods[] = {"set_acl", pyzoo_set_acl, METH_VARARGS, pyzk_set_acl_doc }, {"close", pyzoo_close, METH_VARARGS, pyzk_close_doc }, {"client_id", pyzoo_client_id, METH_VARARGS, pyzk_client_id_doc }, - // DO NOT USE get / set_context. Context is used internally - // to pass the python watcher to a dispatch function. If you want - // context, set it through set_watcher. - // {"get_context", pyzoo_get_context, METH_VARARGS, "" }, - // {"set_context", pyzoo_set_context, METH_VARARGS, "" }, {"set_watcher", pyzoo_set_watcher, METH_VARARGS }, {"state", pyzoo_state, METH_VARARGS, pyzk_state_doc }, {"recv_timeout",pyzoo_recv_timeout, METH_VARARGS }, @@ -1154,29 +1460,34 @@ static PyMethodDef ZooKeeperMethods[] = {"aset_acl", pyzoo_aset_acl, METH_VARARGS, pyzk_aset_acl_doc }, {"zerror", pyzerror, METH_VARARGS, pyzk_zerror_doc }, {"add_auth", pyzoo_add_auth, METH_VARARGS, pyzk_add_auth_doc }, + /* DO NOT USE get / set_context. Context is used internally to pass + the python watcher to a dispatch function. If you want context, set + it through set_watcher. */ + // {"get_context", pyzoo_get_context, METH_VARARGS, "" }, + // {"set_context", pyzoo_set_context, METH_VARARGS, "" }, {NULL, NULL} }; #define ADD_INTCONSTANT(x) PyModule_AddIntConstant(module, #x, ZOO_##x) #define ADD_INTCONSTANTZ(x) PyModule_AddIntConstant(module, #x, Z##x) - - #define ADD_EXCEPTION(x) x = PyErr_NewException("zookeeper."#x, ZooKeeperException, NULL); \ - Py_INCREF(x); \ + Py_INCREF(x); \ PyModule_AddObject(module, #x, x); -PyMODINIT_FUNC initzookeeper() { +PyMODINIT_FUNC initzookeeper(void) { PyEval_InitThreads(); PyObject *module = Py_InitModule("zookeeper", ZooKeeperMethods ); - init_zhandles(32); + if (init_zhandles(32) == 0) { + return; // TODO: Is there any way to raise an exception here? + } ZooKeeperException = PyErr_NewException("zookeeper.ZooKeeperException", - PyExc_Exception, - NULL); + PyExc_Exception, + NULL); - PyModule_AddObject(module, "ZooKeeperException", ZooKeeperException); + PyModule_AddObject(module, "ZooKeeperException", ZooKeeperException); Py_INCREF(ZooKeeperException); ADD_INTCONSTANT(PERM_READ); @@ -1258,11 +1569,3 @@ PyMODINIT_FUNC initzookeeper() { ADD_EXCEPTION(NothingException); ADD_EXCEPTION(SessionMovedException); } - -int main(int argc, char **argv) -{ - Py_SetProgramName(argv[0]); - Py_Initialize(); - initzookeeper(); - return 0; -}