diff options
author | Oleg Pudeyev <oleg@bsdpower.com> | 2014-05-23 16:15:08 -0400 |
---|---|---|
committer | Oleg Pudeyev <oleg@bsdpower.com> | 2014-05-23 16:19:15 -0400 |
commit | 79a9675f63d4818124d1ce326bb2729728e2c90c (patch) | |
tree | 1ed87a8b524320fc42c7d2e90d68ac7117c10221 /src | |
parent | 58d5b6b6d69ccc38b8c3da3142599c5a5beaee5f (diff) | |
download | pycurl-79a9675f63d4818124d1ce326bb2729728e2c90c.tar.gz |
Split pycurl.c into separate files for easy, multi and share objects
Diffstat (limited to 'src')
-rw-r--r-- | src/easy.c (renamed from src/pycurl.c) | 1150 | ||||
-rw-r--r-- | src/multi.c | 822 | ||||
-rw-r--r-- | src/pycurl.h | 35 | ||||
-rw-r--r-- | src/pythoncompat.c | 84 | ||||
-rw-r--r-- | src/share.c | 261 |
5 files changed, 1217 insertions, 1135 deletions
diff --git a/src/pycurl.c b/src/easy.c index 7f39f51..5c72b6e 100644 --- a/src/pycurl.c +++ b/src/easy.c @@ -1,24 +1,8 @@ -/* PycURL -- cURL Python module - */ - #include "pycurl.h" -/* Raise exception based on return value `res' and `self->error' */ -#define CURLERROR_RETVAL() do {\ - PyObject *v; \ - self->error[sizeof(self->error) - 1] = 0; \ - v = Py_BuildValue("(is)", (int) (res), self->error); \ - if (v != NULL) { PyErr_SetObject(ErrorObject, v); Py_DECREF(v); } \ - return NULL; \ -} while (0) - -/* Raise exception based on return value `res' and custom message */ -#define CURLERROR_MSG(msg) do {\ - PyObject *v; const char *m = (msg); \ - v = Py_BuildValue("(is)", (int) (res), (m)); \ - if (v != NULL) { PyErr_SetObject(ErrorObject, v); Py_DECREF(v); } \ - return NULL; \ -} while (0) +/************************************************************************* +// static utility functions +**************************************************************************/ /* Convert a curl slist (a list of strings) to a Python list. @@ -58,6 +42,7 @@ error: return NULL; } + #ifdef HAVE_CURLOPT_CERTINFO /* Convert a struct curl_certinfo into a Python data structure. * In case of error return NULL with an exception set. @@ -124,24 +109,8 @@ static PyObject *convert_certinfo(struct curl_certinfo *cinfo) } #endif -/************************************************************************* -// static utility functions -**************************************************************************/ - -/* assert some CurlShareObject invariants */ -static void -assert_share_state(const CurlShareObject *self) -{ - assert(self != NULL); - assert(Py_TYPE(self) == p_CurlShare_Type); -#ifdef WITH_THREAD - assert(self->lock != NULL); -#endif -} - - /* assert some CurlObject invariants */ -static void +PYCURL_INTERNAL void assert_curl_state(const CurlObject *self) { assert(self != NULL); @@ -152,20 +121,6 @@ assert_curl_state(const CurlObject *self) } -/* assert some CurlMultiObject invariants */ -static void -assert_multi_state(const CurlMultiObject *self) -{ - assert(self != NULL); - assert(Py_TYPE(self) == p_CurlMulti_Type); -#ifdef WITH_THREAD - if (self->state != NULL) { - assert(self->multi_handle != NULL); - } -#endif -} - - /* check state for methods */ static int check_curl_state(const CurlObject *self, int flags, const char *name) @@ -184,195 +139,6 @@ check_curl_state(const CurlObject *self, int flags, const char *name) return 0; } -static int -check_multi_state(const CurlMultiObject *self, int flags, const char *name) -{ - assert_multi_state(self); - if ((flags & 1) && self->multi_handle == NULL) { - PyErr_Format(ErrorObject, "cannot invoke %s() - no multi handle", name); - return -1; - } -#ifdef WITH_THREAD - if ((flags & 2) && self->state != NULL) { - PyErr_Format(ErrorObject, "cannot invoke %s() - multi_perform() is currently running", name); - return -1; - } -#endif - return 0; -} - -static int -check_share_state(const CurlShareObject *self, int flags, const char *name) -{ - assert_share_state(self); - return 0; -} - -/* constructor - this is a module-level function returning a new instance */ -PYCURL_INTERNAL CurlShareObject * -do_share_new(PyObject *dummy) -{ - int res; - CurlShareObject *self; -#ifdef WITH_THREAD - const curl_lock_function lock_cb = share_lock_callback; - const curl_unlock_function unlock_cb = share_unlock_callback; -#endif - - UNUSED(dummy); - - /* Allocate python curl-share object */ - self = (CurlShareObject *) PyObject_GC_New(CurlShareObject, p_CurlShare_Type); - if (self) { - PyObject_GC_Track(self); - } - else { - return NULL; - } - - /* Initialize object attributes */ - self->dict = NULL; -#ifdef WITH_THREAD - self->lock = share_lock_new(); - assert(self->lock != NULL); -#endif - - /* Allocate libcurl share handle */ - self->share_handle = curl_share_init(); - if (self->share_handle == NULL) { - Py_DECREF(self); - PyErr_SetString(ErrorObject, "initializing curl-share failed"); - return NULL; - } - -#ifdef WITH_THREAD - /* Set locking functions and data */ - res = curl_share_setopt(self->share_handle, CURLSHOPT_LOCKFUNC, lock_cb); - assert(res == CURLE_OK); - res = curl_share_setopt(self->share_handle, CURLSHOPT_USERDATA, self); - assert(res == CURLE_OK); - res = curl_share_setopt(self->share_handle, CURLSHOPT_UNLOCKFUNC, unlock_cb); - assert(res == CURLE_OK); -#endif - - return self; -} - - -PYCURL_INTERNAL int -do_share_traverse(CurlShareObject *self, visitproc visit, void *arg) -{ - int err; -#undef VISIT -#define VISIT(v) if ((v) != NULL && ((err = visit(v, arg)) != 0)) return err - - VISIT(self->dict); - - return 0; -#undef VISIT -} - - -/* Drop references that may have created reference cycles. */ -PYCURL_INTERNAL int -do_share_clear(CurlShareObject *self) -{ - Py_CLEAR(self->dict); - return 0; -} - - -static void -util_share_close(CurlShareObject *self){ - if (self->share_handle != NULL) { - CURLSH *share_handle = self->share_handle; - self->share_handle = NULL; - curl_share_cleanup(share_handle); - } -} - - -PYCURL_INTERNAL void -do_share_dealloc(CurlShareObject *self) -{ - PyObject_GC_UnTrack(self); - Py_TRASHCAN_SAFE_BEGIN(self); - - Py_CLEAR(self->dict); - util_share_close(self); - -#ifdef WITH_THREAD - share_lock_destroy(self->lock); -#endif - - PyObject_GC_Del(self); - Py_TRASHCAN_SAFE_END(self) -} - - -static PyObject * -do_share_close(CurlShareObject *self) -{ - if (check_share_state(self, 2, "close") != 0) { - return NULL; - } - util_share_close(self); - Py_RETURN_NONE; -} - - -/* setopt, unsetopt*/ -/* --------------- unsetopt/setopt/getinfo --------------- */ - -static PyObject * -do_curlshare_setopt(CurlShareObject *self, PyObject *args) -{ - int option; - PyObject *obj; - - if (!PyArg_ParseTuple(args, "iO:setopt", &option, &obj)) - return NULL; - if (check_share_state(self, 1 | 2, "sharesetopt") != 0) - return NULL; - - /* early checks of option value */ - if (option <= 0) - goto error; - if (option >= (int)CURLOPTTYPE_OFF_T + OPTIONS_SIZE) - goto error; - if (option % 10000 >= OPTIONS_SIZE) - goto error; - -#if 0 /* XXX - should we ??? */ - /* Handle the case of None */ - if (obj == Py_None) { - return util_curl_unsetopt(self, option); - } -#endif - - /* Handle the case of integer arguments */ - if (PyInt_Check(obj)) { - long d = PyInt_AsLong(obj); - if (d != CURL_LOCK_DATA_COOKIE && d != CURL_LOCK_DATA_DNS && d != CURL_LOCK_DATA_SSL_SESSION) { - goto error; - } - switch(option) { - case CURLSHOPT_SHARE: - case CURLSHOPT_UNSHARE: - curl_share_setopt(self->share_handle, option, d); - break; - default: - PyErr_SetString(PyExc_TypeError, "integers are not supported for this option"); - return NULL; - } - Py_RETURN_NONE; - } - /* Failed to match any of the function signatures -- return error */ -error: - PyErr_SetString(PyExc_TypeError, "invalid arguments to setopt"); - return NULL; -} - /************************************************************************* // CurlObject @@ -2305,699 +2071,6 @@ do_curl_pause(CurlObject *self, PyObject *args) } } -static const char co_pause_doc [] = - "pause(bitmask) -> None. " - "Pauses or unpauses a curl handle. Bitmask should be a value such as PAUSE_RECV or PAUSE_CONT. " - "Raises pycurl.error exception upon failure.\n"; - -/************************************************************************* -// CurlMultiObject -**************************************************************************/ - -/* --------------- construct/destruct (i.e. open/close) --------------- */ - -/* constructor - this is a module-level function returning a new instance */ -PYCURL_INTERNAL CurlMultiObject * -do_multi_new(PyObject *dummy) -{ - CurlMultiObject *self; - - UNUSED(dummy); - - /* Allocate python curl-multi object */ - self = (CurlMultiObject *) PyObject_GC_New(CurlMultiObject, p_CurlMulti_Type); - if (self) { - PyObject_GC_Track(self); - } - else { - return NULL; - } - - /* Initialize object attributes */ - self->dict = NULL; -#ifdef WITH_THREAD - self->state = NULL; -#endif - self->t_cb = NULL; - self->s_cb = NULL; - - /* Allocate libcurl multi handle */ - self->multi_handle = curl_multi_init(); - if (self->multi_handle == NULL) { - Py_DECREF(self); - PyErr_SetString(ErrorObject, "initializing curl-multi failed"); - return NULL; - } - return self; -} - -static void -util_multi_close(CurlMultiObject *self) -{ - assert(self != NULL); -#ifdef WITH_THREAD - self->state = NULL; -#endif - if (self->multi_handle != NULL) { - CURLM *multi_handle = self->multi_handle; - self->multi_handle = NULL; - curl_multi_cleanup(multi_handle); - } -} - - -PYCURL_INTERNAL void -do_multi_dealloc(CurlMultiObject *self) -{ - PyObject_GC_UnTrack(self); - Py_TRASHCAN_SAFE_BEGIN(self) - - Py_CLEAR(self->dict); - util_multi_close(self); - - PyObject_GC_Del(self); - Py_TRASHCAN_SAFE_END(self) -} - - -static PyObject * -do_multi_close(CurlMultiObject *self) -{ - if (check_multi_state(self, 2, "close") != 0) { - return NULL; - } - util_multi_close(self); - Py_RETURN_NONE; -} - - -/* --------------- GC support --------------- */ - -/* Drop references that may have created reference cycles. */ -PYCURL_INTERNAL int -do_multi_clear(CurlMultiObject *self) -{ - Py_CLEAR(self->dict); - return 0; -} - -PYCURL_INTERNAL int -do_multi_traverse(CurlMultiObject *self, visitproc visit, void *arg) -{ - int err; -#undef VISIT -#define VISIT(v) if ((v) != NULL && ((err = visit(v, arg)) != 0)) return err - - VISIT(self->dict); - - return 0; -#undef VISIT -} - - -/* --------------- setopt --------------- */ - -static int -multi_socket_callback(CURL *easy, - curl_socket_t s, - int what, - void *userp, - void *socketp) -{ - CurlMultiObject *self; - PyObject *arglist; - PyObject *result = NULL; - PYCURL_DECLARE_THREAD_STATE; - - /* acquire thread */ - self = (CurlMultiObject *)userp; - if (!PYCURL_ACQUIRE_THREAD_MULTI()) - return 0; - - /* check args */ - if (self->s_cb == NULL) - goto silent_error; - - if (socketp == NULL) { - Py_INCREF(Py_None); - socketp = Py_None; - } - - /* run callback */ - arglist = Py_BuildValue("(iiOO)", what, s, userp, (PyObject *)socketp); - if (arglist == NULL) - goto verbose_error; - result = PyEval_CallObject(self->s_cb, arglist); - Py_DECREF(arglist); - if (result == NULL) - goto verbose_error; - - /* return values from socket callbacks should be ignored */ - -silent_error: - Py_XDECREF(result); - PYCURL_RELEASE_THREAD(); - return 0; -verbose_error: - PyErr_Print(); - goto silent_error; - return 0; -} - - -static int -multi_timer_callback(CURLM *multi, - long timeout_ms, - void *userp) -{ - CurlMultiObject *self; - PyObject *arglist; - PyObject *result = NULL; - int ret = 0; /* always success */ - PYCURL_DECLARE_THREAD_STATE; - - UNUSED(multi); - - /* acquire thread */ - self = (CurlMultiObject *)userp; - if (!PYCURL_ACQUIRE_THREAD_MULTI()) - return ret; - - /* check args */ - if (self->t_cb == NULL) - goto silent_error; - - /* run callback */ - arglist = Py_BuildValue("(i)", timeout_ms); - if (arglist == NULL) - goto verbose_error; - result = PyEval_CallObject(self->t_cb, arglist); - Py_DECREF(arglist); - if (result == NULL) - goto verbose_error; - - /* return values from timer callbacks should be ignored */ - -silent_error: - Py_XDECREF(result); - PYCURL_RELEASE_THREAD(); - return ret; -verbose_error: - PyErr_Print(); - goto silent_error; - - return 0; -} - - -static PyObject * -do_multi_setopt(CurlMultiObject *self, PyObject *args) -{ - int option; - PyObject *obj; - - if (!PyArg_ParseTuple(args, "iO:setopt", &option, &obj)) - return NULL; - if (check_multi_state(self, 1 | 2, "setopt") != 0) - return NULL; - - /* Early checks of option value */ - if (option <= 0) - goto error; - if (option >= (int)CURLOPTTYPE_OFF_T + MOPTIONS_SIZE) - goto error; - if (option % 10000 >= MOPTIONS_SIZE) - goto error; - - /* Handle the case of integer arguments */ - if (PyInt_Check(obj)) { - long d = PyInt_AsLong(obj); - switch(option) { - case CURLMOPT_MAXCONNECTS: - case CURLMOPT_PIPELINING: -#ifdef HAVE_CURL_7_30_0_PIPELINE_OPTS - case CURLMOPT_MAX_HOST_CONNECTIONS: - case CURLMOPT_MAX_TOTAL_CONNECTIONS: - case CURLMOPT_MAX_PIPELINE_LENGTH: - case CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE: - case CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE: -#endif - curl_multi_setopt(self->multi_handle, option, d); - break; - default: - PyErr_SetString(PyExc_TypeError, "integers are not supported for this option"); - return NULL; - } - Py_RETURN_NONE; - } - if (PyFunction_Check(obj) || PyCFunction_Check(obj) || - PyCallable_Check(obj) || PyMethod_Check(obj)) { - /* We use function types here to make sure that our callback - * definitions exactly match the <curl/multi.h> interface. - */ - const curl_multi_timer_callback t_cb = multi_timer_callback; - const curl_socket_callback s_cb = multi_socket_callback; - - switch(option) { - case CURLMOPT_SOCKETFUNCTION: - curl_multi_setopt(self->multi_handle, CURLMOPT_SOCKETFUNCTION, s_cb); - curl_multi_setopt(self->multi_handle, CURLMOPT_SOCKETDATA, self); - Py_INCREF(obj); - self->s_cb = obj; - break; - case CURLMOPT_TIMERFUNCTION: - curl_multi_setopt(self->multi_handle, CURLMOPT_TIMERFUNCTION, t_cb); - curl_multi_setopt(self->multi_handle, CURLMOPT_TIMERDATA, self); - Py_INCREF(obj); - self->t_cb = obj; - break; - default: - PyErr_SetString(PyExc_TypeError, "callables are not supported for this option"); - return NULL; - } - Py_RETURN_NONE; - } - /* Failed to match any of the function signatures -- return error */ -error: - PyErr_SetString(PyExc_TypeError, "invalid arguments to setopt"); - return NULL; -} - - -/* --------------- timeout --------------- */ - -static PyObject * -do_multi_timeout(CurlMultiObject *self) -{ - CURLMcode res; - long timeout; - - if (check_multi_state(self, 1 | 2, "timeout") != 0) { - return NULL; - } - - res = curl_multi_timeout(self->multi_handle, &timeout); - if (res != CURLM_OK) { - CURLERROR_MSG("timeout failed"); - } - - /* Return number of millisecs until timeout */ - return Py_BuildValue("l", timeout); -} - - -/* --------------- assign --------------- */ - -static PyObject * -do_multi_assign(CurlMultiObject *self, PyObject *args) -{ - CURLMcode res; - curl_socket_t socket; - PyObject *obj; - - if (!PyArg_ParseTuple(args, "iO:assign", &socket, &obj)) - return NULL; - if (check_multi_state(self, 1 | 2, "assign") != 0) { - return NULL; - } - Py_INCREF(obj); - - res = curl_multi_assign(self->multi_handle, socket, obj); - if (res != CURLM_OK) { - CURLERROR_MSG("assign failed"); - } - - Py_RETURN_NONE; -} - - -/* --------------- socket_action --------------- */ -static PyObject * -do_multi_socket_action(CurlMultiObject *self, PyObject *args) -{ - CURLMcode res; - curl_socket_t socket; - int ev_bitmask; - int running = -1; - - if (!PyArg_ParseTuple(args, "ii:socket_action", &socket, &ev_bitmask)) - return NULL; - if (check_multi_state(self, 1 | 2, "socket_action") != 0) { - return NULL; - } - - PYCURL_BEGIN_ALLOW_THREADS - res = curl_multi_socket_action(self->multi_handle, socket, ev_bitmask, &running); - PYCURL_END_ALLOW_THREADS - - if (res != CURLM_OK) { - CURLERROR_MSG("multi_socket_action failed"); - } - /* Return a tuple with the result and the number of running handles */ - return Py_BuildValue("(ii)", (int)res, running); -} - -/* --------------- socket_all --------------- */ - -static PyObject * -do_multi_socket_all(CurlMultiObject *self) -{ - CURLMcode res; - int running = -1; - - if (check_multi_state(self, 1 | 2, "socket_all") != 0) { - return NULL; - } - - PYCURL_BEGIN_ALLOW_THREADS - res = curl_multi_socket_all(self->multi_handle, &running); - PYCURL_END_ALLOW_THREADS - - /* We assume these errors are ok, otherwise raise exception */ - if (res != CURLM_OK && res != CURLM_CALL_MULTI_PERFORM) { - CURLERROR_MSG("perform failed"); - } - - /* Return a tuple with the result and the number of running handles */ - return Py_BuildValue("(ii)", (int)res, running); -} - - -/* --------------- perform --------------- */ - -static PyObject * -do_multi_perform(CurlMultiObject *self) -{ - CURLMcode res; - int running = -1; - - if (check_multi_state(self, 1 | 2, "perform") != 0) { - return NULL; - } - - PYCURL_BEGIN_ALLOW_THREADS - res = curl_multi_perform(self->multi_handle, &running); - PYCURL_END_ALLOW_THREADS - - /* We assume these errors are ok, otherwise raise exception */ - if (res != CURLM_OK && res != CURLM_CALL_MULTI_PERFORM) { - CURLERROR_MSG("perform failed"); - } - - /* Return a tuple with the result and the number of running handles */ - return Py_BuildValue("(ii)", (int)res, running); -} - - -/* --------------- add_handle/remove_handle --------------- */ - -/* static utility function */ -static int -check_multi_add_remove(const CurlMultiObject *self, const CurlObject *obj) -{ - /* check CurlMultiObject status */ - assert_multi_state(self); - if (self->multi_handle == NULL) { - PyErr_SetString(ErrorObject, "cannot add/remove handle - multi-stack is closed"); - return -1; - } -#ifdef WITH_THREAD - if (self->state != NULL) { - PyErr_SetString(ErrorObject, "cannot add/remove handle - multi_perform() already running"); - return -1; - } -#endif - /* check CurlObject status */ - assert_curl_state(obj); -#ifdef WITH_THREAD - if (obj->state != NULL) { - PyErr_SetString(ErrorObject, "cannot add/remove handle - perform() of curl object already running"); - return -1; - } -#endif - if (obj->multi_stack != NULL && obj->multi_stack != self) { - PyErr_SetString(ErrorObject, "cannot add/remove handle - curl object already on another multi-stack"); - return -1; - } - return 0; -} - - -static PyObject * -do_multi_add_handle(CurlMultiObject *self, PyObject *args) -{ - CurlObject *obj; - CURLMcode res; - - if (!PyArg_ParseTuple(args, "O!:add_handle", p_Curl_Type, &obj)) { - return NULL; - } - if (check_multi_add_remove(self, obj) != 0) { - return NULL; - } - if (obj->handle == NULL) { - PyErr_SetString(ErrorObject, "curl object already closed"); - return NULL; - } - if (obj->multi_stack == self) { - PyErr_SetString(ErrorObject, "curl object already on this multi-stack"); - return NULL; - } - assert(obj->multi_stack == NULL); - res = curl_multi_add_handle(self->multi_handle, obj->handle); - if (res != CURLM_OK) { - CURLERROR_MSG("curl_multi_add_handle() failed due to internal errors"); - } - obj->multi_stack = self; - Py_INCREF(self); - Py_RETURN_NONE; -} - - -static PyObject * -do_multi_remove_handle(CurlMultiObject *self, PyObject *args) -{ - CurlObject *obj; - CURLMcode res; - - if (!PyArg_ParseTuple(args, "O!:remove_handle", p_Curl_Type, &obj)) { - return NULL; - } - if (check_multi_add_remove(self, obj) != 0) { - return NULL; - } - if (obj->handle == NULL) { - /* CurlObject handle already closed -- ignore */ - goto done; - } - if (obj->multi_stack != self) { - PyErr_SetString(ErrorObject, "curl object not on this multi-stack"); - return NULL; - } - res = curl_multi_remove_handle(self->multi_handle, obj->handle); - if (res != CURLM_OK) { - CURLERROR_MSG("curl_multi_remove_handle() failed due to internal errors"); - } - assert(obj->multi_stack == self); - obj->multi_stack = NULL; - Py_DECREF(self); -done: - Py_RETURN_NONE; -} - - -/* --------------- fdset ---------------------- */ - -static PyObject * -do_multi_fdset(CurlMultiObject *self) -{ - CURLMcode res; - int max_fd = -1, fd; - PyObject *ret = NULL; - PyObject *read_list = NULL, *write_list = NULL, *except_list = NULL; - PyObject *py_fd = NULL; - - if (check_multi_state(self, 1 | 2, "fdset") != 0) { - return NULL; - } - - /* Clear file descriptor sets */ - FD_ZERO(&self->read_fd_set); - FD_ZERO(&self->write_fd_set); - FD_ZERO(&self->exc_fd_set); - - /* Don't bother releasing the gil as this is just a data structure operation */ - res = curl_multi_fdset(self->multi_handle, &self->read_fd_set, - &self->write_fd_set, &self->exc_fd_set, &max_fd); - if (res != CURLM_OK) { - CURLERROR_MSG("curl_multi_fdset() failed due to internal errors"); - } - - /* Allocate lists. */ - if ((read_list = PyList_New((Py_ssize_t)0)) == NULL) goto error; - if ((write_list = PyList_New((Py_ssize_t)0)) == NULL) goto error; - if ((except_list = PyList_New((Py_ssize_t)0)) == NULL) goto error; - - /* Populate lists */ - for (fd = 0; fd < max_fd + 1; fd++) { - if (FD_ISSET(fd, &self->read_fd_set)) { - if ((py_fd = PyInt_FromLong((long)fd)) == NULL) goto error; - if (PyList_Append(read_list, py_fd) != 0) goto error; - Py_DECREF(py_fd); - py_fd = NULL; - } - if (FD_ISSET(fd, &self->write_fd_set)) { - if ((py_fd = PyInt_FromLong((long)fd)) == NULL) goto error; - if (PyList_Append(write_list, py_fd) != 0) goto error; - Py_DECREF(py_fd); - py_fd = NULL; - } - if (FD_ISSET(fd, &self->exc_fd_set)) { - if ((py_fd = PyInt_FromLong((long)fd)) == NULL) goto error; - if (PyList_Append(except_list, py_fd) != 0) goto error; - Py_DECREF(py_fd); - py_fd = NULL; - } - } - - /* Return a tuple with the 3 lists */ - ret = Py_BuildValue("(OOO)", read_list, write_list, except_list); -error: - Py_XDECREF(py_fd); - Py_XDECREF(except_list); - Py_XDECREF(write_list); - Py_XDECREF(read_list); - return ret; -} - - -/* --------------- info_read --------------- */ - -static PyObject * -do_multi_info_read(CurlMultiObject *self, PyObject *args) -{ - PyObject *ret = NULL; - PyObject *ok_list = NULL, *err_list = NULL; - CURLMsg *msg; - int in_queue = 0, num_results = INT_MAX; - - /* Sanity checks */ - if (!PyArg_ParseTuple(args, "|i:info_read", &num_results)) { - return NULL; - } - if (num_results <= 0) { - PyErr_SetString(ErrorObject, "argument to info_read must be greater than zero"); - return NULL; - } - if (check_multi_state(self, 1 | 2, "info_read") != 0) { - return NULL; - } - - if ((ok_list = PyList_New((Py_ssize_t)0)) == NULL) goto error; - if ((err_list = PyList_New((Py_ssize_t)0)) == NULL) goto error; - - /* Loop through all messages */ - while ((msg = curl_multi_info_read(self->multi_handle, &in_queue)) != NULL) { - CURLcode res; - CurlObject *co = NULL; - - /* Check for termination as specified by the user */ - if (num_results-- <= 0) { - break; - } - - /* Fetch the curl object that corresponds to the curl handle in the message */ - res = curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &co); - if (res != CURLE_OK || co == NULL) { - Py_DECREF(err_list); - Py_DECREF(ok_list); - CURLERROR_MSG("Unable to fetch curl handle from curl object"); - } - assert(Py_TYPE(co) == p_Curl_Type); - if (msg->msg != CURLMSG_DONE) { - /* FIXME: what does this mean ??? */ - } - if (msg->data.result == CURLE_OK) { - /* Append curl object to list of objects which succeeded */ - if (PyList_Append(ok_list, (PyObject *)co) != 0) { - goto error; - } - } - else { - /* Create a result tuple that will get added to err_list. */ - PyObject *v = Py_BuildValue("(Ois)", (PyObject *)co, (int)msg->data.result, co->error); - /* Append curl object to list of objects which failed */ - if (v == NULL || PyList_Append(err_list, v) != 0) { - Py_XDECREF(v); - goto error; - } - Py_DECREF(v); - } - } - /* Return (number of queued messages, [ok_objects], [error_objects]) */ - ret = Py_BuildValue("(iOO)", in_queue, ok_list, err_list); -error: - Py_XDECREF(err_list); - Py_XDECREF(ok_list); - return ret; -} - - -/* --------------- select --------------- */ - -static PyObject * -do_multi_select(CurlMultiObject *self, PyObject *args) -{ - int max_fd = -1, n; - double timeout = -1.0; - struct timeval tv, *tvp; - CURLMcode res; - - if (!PyArg_ParseTuple(args, "d:select", &timeout)) { - return NULL; - } - if (check_multi_state(self, 1 | 2, "select") != 0) { - return NULL; - } - - if (timeout < 0 || timeout >= 365 * 24 * 60 * 60) { - PyErr_SetString(PyExc_OverflowError, "invalid timeout period"); - return NULL; - } else { - long seconds = (long)timeout; - timeout = timeout - (double)seconds; - assert(timeout >= 0.0); assert(timeout < 1.0); - tv.tv_sec = seconds; - tv.tv_usec = (long)(timeout*1000000.0); - tvp = &tv; - } - - FD_ZERO(&self->read_fd_set); - FD_ZERO(&self->write_fd_set); - FD_ZERO(&self->exc_fd_set); - - res = curl_multi_fdset(self->multi_handle, &self->read_fd_set, - &self->write_fd_set, &self->exc_fd_set, &max_fd); - if (res != CURLM_OK) { - CURLERROR_MSG("multi_fdset failed"); - } - - if (max_fd < 0) { - n = 0; - } - else { - Py_BEGIN_ALLOW_THREADS - n = select(max_fd + 1, &self->read_fd_set, &self->write_fd_set, &self->exc_fd_set, tvp); - Py_END_ALLOW_THREADS - /* info: like Python's socketmodule.c we do not raise an exception - * if select() fails - we'll leave it to the actual libcurl - * socket code to report any errors. - */ - } - - return PyInt_FromLong(n); -} - /************************************************************************* // type definitions @@ -3005,13 +2078,6 @@ do_multi_select(CurlMultiObject *self, PyObject *args) /* --------------- methods --------------- */ -static const char cso_close_doc [] = - "close() -> None. " - "Close shared handle.\n"; -static const char cso_setopt_doc [] = - "setopt(option, parameter) -> None. " - "Set curl share option. Raises pycurl.error exception upon failure.\n"; - static const char co_close_doc [] = "close() -> None. " "Close handle and end curl session.\n"; @@ -3033,35 +2099,10 @@ static const char co_unsetopt_doc [] = static const char co_reset_doc [] = "reset() -> None. " "Reset all options set on curl handle to default values, but preserves live connections, session ID cache, DNS cache, cookies, and shares.\n"; - -static const char co_multi_close_doc [] = "\ -close() -> None\n\ -\n\ -Corresponds to `curl_multi_cleanup`_ in libcurl. This method is\n\ -automatically called by pycurl when a CurlMulti object no longer has any\n\ -references to it, but can also be called explicitly.\ -"; -static const char co_multi_fdset_doc [] = - "fdset() -> Tuple. " - "Returns a tuple of three lists that can be passed to the select.select() method .\n"; -static const char co_multi_info_read_doc [] = - "info_read([max_objects]) -> Tuple. " - "Returns a tuple (number of queued handles, [curl objects]).\n"; -static const char co_multi_select_doc [] = - "select([timeout]) -> Int. " - "Returns result from doing a select() on the curl multi file descriptor with the given timeout.\n"; -static const char co_multi_socket_action_doc [] = - "socket_action(sockfd, ev_bitmask) -> Tuple. " - "Returns result from doing a socket_action() on the curl multi file descriptor with the given timeout.\n"; -static const char co_multi_socket_all_doc [] = - "socket_all() -> Tuple. " - "Returns result from doing a socket_all() on the curl multi file descriptor with the given timeout.\n"; - -PYCURL_INTERNAL PyMethodDef curlshareobject_methods[] = { - {"close", (PyCFunction)do_share_close, METH_NOARGS, cso_close_doc}, - {"setopt", (PyCFunction)do_curlshare_setopt, METH_VARARGS, cso_setopt_doc}, - {NULL, NULL, 0, 0} -}; +static const char co_pause_doc [] = + "pause(bitmask) -> None. " + "Pauses or unpauses a curl handle. Bitmask should be a value such as PAUSE_RECV or PAUSE_CONT. " + "Raises pycurl.error exception upon failure.\n"; PYCURL_INTERNAL PyMethodDef curlobject_methods[] = { {"close", (PyCFunction)do_curl_close, METH_NOARGS, co_close_doc}, @@ -3075,66 +2116,11 @@ PYCURL_INTERNAL PyMethodDef curlobject_methods[] = { {NULL, NULL, 0, NULL} }; -PYCURL_INTERNAL PyMethodDef curlmultiobject_methods[] = { - {"add_handle", (PyCFunction)do_multi_add_handle, METH_VARARGS, NULL}, - {"close", (PyCFunction)do_multi_close, METH_NOARGS, co_multi_close_doc}, - {"fdset", (PyCFunction)do_multi_fdset, METH_NOARGS, co_multi_fdset_doc}, - {"info_read", (PyCFunction)do_multi_info_read, METH_VARARGS, co_multi_info_read_doc}, - {"perform", (PyCFunction)do_multi_perform, METH_NOARGS, NULL}, - {"socket_action", (PyCFunction)do_multi_socket_action, METH_VARARGS, co_multi_socket_action_doc}, - {"socket_all", (PyCFunction)do_multi_socket_all, METH_NOARGS, co_multi_socket_all_doc}, - {"setopt", (PyCFunction)do_multi_setopt, METH_VARARGS, NULL}, - {"timeout", (PyCFunction)do_multi_timeout, METH_NOARGS, NULL}, - {"assign", (PyCFunction)do_multi_assign, METH_VARARGS, NULL}, - {"remove_handle", (PyCFunction)do_multi_remove_handle, METH_VARARGS, NULL}, - {"select", (PyCFunction)do_multi_select, METH_VARARGS, co_multi_select_doc}, - {NULL, NULL, 0, NULL} -}; - /* --------------- setattr/getattr --------------- */ #if PY_MAJOR_VERSION >= 3 -static PyObject * -my_getattro(PyObject *co, PyObject *name, PyObject *dict1, PyObject *dict2, PyMethodDef *m) -{ - PyObject *v = NULL; - if( dict1 != NULL ) - v = PyDict_GetItem(dict1, name); - if( v == NULL && dict2 != NULL ) - v = PyDict_GetItem(dict2, name); - if( v != NULL ) - { - Py_INCREF(v); - return v; - } - PyErr_SetString(PyExc_AttributeError, "trying to obtain a non-existing attribute"); - return NULL; -} - -static int -my_setattro(PyObject **dict, PyObject *name, PyObject *v) -{ - if( *dict == NULL ) - { - *dict = PyDict_New(); - if( *dict == NULL ) - return -1; - } - if (v != NULL) - return PyDict_SetItem(*dict, name, v); - else { - int v = PyDict_DelItem(*dict, name); - if (v != 0) { - /* need to convert KeyError to AttributeError */ - if (PyErr_ExceptionMatches(PyExc_KeyError)) { - PyErr_SetString(PyExc_AttributeError, "trying to delete a non-existing attribute"); - } - } - return v; - } -} PYCURL_INTERNAL PyObject * do_curl_getattro(PyObject *o, PyObject *n) @@ -3156,90 +2142,14 @@ do_curl_setattro(PyObject *o, PyObject *name, PyObject *v) return my_setattro(&((CurlObject *)o)->dict, name, v); } -PYCURL_INTERNAL PyObject * -do_multi_getattro(PyObject *o, PyObject *n) -{ - PyObject *v; - assert_multi_state((CurlMultiObject *)o); - v = PyObject_GenericGetAttr(o, n); - if( !v && PyErr_ExceptionMatches(PyExc_AttributeError) ) - { - PyErr_Clear(); - v = my_getattro(o, n, ((CurlMultiObject *)o)->dict, - curlmultiobject_constants, curlmultiobject_methods); - } - return v; -} - -PYCURL_INTERNAL int -do_multi_setattro(PyObject *o, PyObject *n, PyObject *v) -{ - assert_multi_state((CurlMultiObject *)o); - return my_setattro(&((CurlMultiObject *)o)->dict, n, v); -} +#else /* PY_MAJOR_VERSION >= 3 */ PYCURL_INTERNAL PyObject * -do_share_getattro(PyObject *o, PyObject *n) -{ - PyObject *v; - assert_share_state((CurlShareObject *)o); - v = PyObject_GenericGetAttr(o, n); - if( !v && PyErr_ExceptionMatches(PyExc_AttributeError) ) - { - PyErr_Clear(); - v = my_getattro(o, n, ((CurlShareObject *)o)->dict, - curlshareobject_constants, curlshareobject_methods); - } - return v; -} - -PYCURL_INTERNAL int -do_share_setattro(PyObject *o, PyObject *n, PyObject *v) -{ - assert_share_state((CurlShareObject *)o); - return my_setattro(&((CurlShareObject *)o)->dict, n, v); -} - -#else -static int -my_setattr(PyObject **dict, char *name, PyObject *v) -{ - if (v == NULL) { - int rv = -1; - if (*dict != NULL) - rv = PyDict_DelItemString(*dict, name); - if (rv < 0) - PyErr_SetString(PyExc_AttributeError, "delete non-existing attribute"); - return rv; - } - if (*dict == NULL) { - *dict = PyDict_New(); - if (*dict == NULL) - return -1; - } - return PyDict_SetItemString(*dict, name, v); -} - -static PyObject * -my_getattr(PyObject *co, char *name, PyObject *dict1, PyObject *dict2, PyMethodDef *m) -{ - PyObject *v = NULL; - if (v == NULL && dict1 != NULL) - v = PyDict_GetItemString(dict1, name); - if (v == NULL && dict2 != NULL) - v = PyDict_GetItemString(dict2, name); - if (v != NULL) { - Py_INCREF(v); - return v; - } - return Py_FindMethod(m, co, name); -} - -PYCURL_INTERNAL int -do_share_setattr(CurlShareObject *so, char *name, PyObject *v) +do_curl_getattr(CurlObject *co, char *name) { - assert_share_state(so); - return my_setattr(&so->dict, name, v); + assert_curl_state(co); + return my_getattr((PyObject *)co, name, co->dict, + curlobject_constants, curlobject_methods); } PYCURL_INTERNAL int @@ -3249,37 +2159,7 @@ do_curl_setattr(CurlObject *co, char *name, PyObject *v) return my_setattr(&co->dict, name, v); } -PYCURL_INTERNAL int -do_multi_setattr(CurlMultiObject *co, char *name, PyObject *v) -{ - assert_multi_state(co); - return my_setattr(&co->dict, name, v); -} - -PYCURL_INTERNAL PyObject * -do_share_getattr(CurlShareObject *cso, char *name) -{ - assert_share_state(cso); - return my_getattr((PyObject *)cso, name, cso->dict, - curlshareobject_constants, curlshareobject_methods); -} - -PYCURL_INTERNAL PyObject * -do_curl_getattr(CurlObject *co, char *name) -{ - assert_curl_state(co); - return my_getattr((PyObject *)co, name, co->dict, - curlobject_constants, curlobject_methods); -} - -PYCURL_INTERNAL PyObject * -do_multi_getattr(CurlMultiObject *co, char *name) -{ - assert_multi_state(co); - return my_getattr((PyObject *)co, name, co->dict, - curlmultiobject_constants, curlmultiobject_methods); -} -#endif +#endif /* PY_MAJOR_VERSION >= 3 */ /* vi:ts=4:et:nowrap */ diff --git a/src/multi.c b/src/multi.c new file mode 100644 index 0000000..43d2017 --- /dev/null +++ b/src/multi.c @@ -0,0 +1,822 @@ +#include "pycurl.h" + +/************************************************************************* +// static utility functions +**************************************************************************/ + + +/* assert some CurlMultiObject invariants */ +static void +assert_multi_state(const CurlMultiObject *self) +{ + assert(self != NULL); + assert(Py_TYPE(self) == p_CurlMulti_Type); +#ifdef WITH_THREAD + if (self->state != NULL) { + assert(self->multi_handle != NULL); + } +#endif +} + + +static int +check_multi_state(const CurlMultiObject *self, int flags, const char *name) +{ + assert_multi_state(self); + if ((flags & 1) && self->multi_handle == NULL) { + PyErr_Format(ErrorObject, "cannot invoke %s() - no multi handle", name); + return -1; + } +#ifdef WITH_THREAD + if ((flags & 2) && self->state != NULL) { + PyErr_Format(ErrorObject, "cannot invoke %s() - multi_perform() is currently running", name); + return -1; + } +#endif + return 0; +} + + +/************************************************************************* +// CurlMultiObject +**************************************************************************/ + +/* --------------- construct/destruct (i.e. open/close) --------------- */ + +/* constructor - this is a module-level function returning a new instance */ +PYCURL_INTERNAL CurlMultiObject * +do_multi_new(PyObject *dummy) +{ + CurlMultiObject *self; + + UNUSED(dummy); + + /* Allocate python curl-multi object */ + self = (CurlMultiObject *) PyObject_GC_New(CurlMultiObject, p_CurlMulti_Type); + if (self) { + PyObject_GC_Track(self); + } + else { + return NULL; + } + + /* Initialize object attributes */ + self->dict = NULL; +#ifdef WITH_THREAD + self->state = NULL; +#endif + self->t_cb = NULL; + self->s_cb = NULL; + + /* Allocate libcurl multi handle */ + self->multi_handle = curl_multi_init(); + if (self->multi_handle == NULL) { + Py_DECREF(self); + PyErr_SetString(ErrorObject, "initializing curl-multi failed"); + return NULL; + } + return self; +} + +static void +util_multi_close(CurlMultiObject *self) +{ + assert(self != NULL); +#ifdef WITH_THREAD + self->state = NULL; +#endif + if (self->multi_handle != NULL) { + CURLM *multi_handle = self->multi_handle; + self->multi_handle = NULL; + curl_multi_cleanup(multi_handle); + } +} + + +PYCURL_INTERNAL void +do_multi_dealloc(CurlMultiObject *self) +{ + PyObject_GC_UnTrack(self); + Py_TRASHCAN_SAFE_BEGIN(self) + + Py_CLEAR(self->dict); + util_multi_close(self); + + PyObject_GC_Del(self); + Py_TRASHCAN_SAFE_END(self) +} + + +static PyObject * +do_multi_close(CurlMultiObject *self) +{ + if (check_multi_state(self, 2, "close") != 0) { + return NULL; + } + util_multi_close(self); + Py_RETURN_NONE; +} + + +/* --------------- GC support --------------- */ + +/* Drop references that may have created reference cycles. */ +PYCURL_INTERNAL int +do_multi_clear(CurlMultiObject *self) +{ + Py_CLEAR(self->dict); + return 0; +} + +PYCURL_INTERNAL int +do_multi_traverse(CurlMultiObject *self, visitproc visit, void *arg) +{ + int err; +#undef VISIT +#define VISIT(v) if ((v) != NULL && ((err = visit(v, arg)) != 0)) return err + + VISIT(self->dict); + + return 0; +#undef VISIT +} + + +/* --------------- setopt --------------- */ + +static int +multi_socket_callback(CURL *easy, + curl_socket_t s, + int what, + void *userp, + void *socketp) +{ + CurlMultiObject *self; + PyObject *arglist; + PyObject *result = NULL; + PYCURL_DECLARE_THREAD_STATE; + + /* acquire thread */ + self = (CurlMultiObject *)userp; + if (!PYCURL_ACQUIRE_THREAD_MULTI()) + return 0; + + /* check args */ + if (self->s_cb == NULL) + goto silent_error; + + if (socketp == NULL) { + Py_INCREF(Py_None); + socketp = Py_None; + } + + /* run callback */ + arglist = Py_BuildValue("(iiOO)", what, s, userp, (PyObject *)socketp); + if (arglist == NULL) + goto verbose_error; + result = PyEval_CallObject(self->s_cb, arglist); + Py_DECREF(arglist); + if (result == NULL) + goto verbose_error; + + /* return values from socket callbacks should be ignored */ + +silent_error: + Py_XDECREF(result); + PYCURL_RELEASE_THREAD(); + return 0; +verbose_error: + PyErr_Print(); + goto silent_error; + return 0; +} + + +static int +multi_timer_callback(CURLM *multi, + long timeout_ms, + void *userp) +{ + CurlMultiObject *self; + PyObject *arglist; + PyObject *result = NULL; + int ret = 0; /* always success */ + PYCURL_DECLARE_THREAD_STATE; + + UNUSED(multi); + + /* acquire thread */ + self = (CurlMultiObject *)userp; + if (!PYCURL_ACQUIRE_THREAD_MULTI()) + return ret; + + /* check args */ + if (self->t_cb == NULL) + goto silent_error; + + /* run callback */ + arglist = Py_BuildValue("(i)", timeout_ms); + if (arglist == NULL) + goto verbose_error; + result = PyEval_CallObject(self->t_cb, arglist); + Py_DECREF(arglist); + if (result == NULL) + goto verbose_error; + + /* return values from timer callbacks should be ignored */ + +silent_error: + Py_XDECREF(result); + PYCURL_RELEASE_THREAD(); + return ret; +verbose_error: + PyErr_Print(); + goto silent_error; + + return 0; +} + + +static PyObject * +do_multi_setopt(CurlMultiObject *self, PyObject *args) +{ + int option; + PyObject *obj; + + if (!PyArg_ParseTuple(args, "iO:setopt", &option, &obj)) + return NULL; + if (check_multi_state(self, 1 | 2, "setopt") != 0) + return NULL; + + /* Early checks of option value */ + if (option <= 0) + goto error; + if (option >= (int)CURLOPTTYPE_OFF_T + MOPTIONS_SIZE) + goto error; + if (option % 10000 >= MOPTIONS_SIZE) + goto error; + + /* Handle the case of integer arguments */ + if (PyInt_Check(obj)) { + long d = PyInt_AsLong(obj); + switch(option) { + case CURLMOPT_MAXCONNECTS: + case CURLMOPT_PIPELINING: +#ifdef HAVE_CURL_7_30_0_PIPELINE_OPTS + case CURLMOPT_MAX_HOST_CONNECTIONS: + case CURLMOPT_MAX_TOTAL_CONNECTIONS: + case CURLMOPT_MAX_PIPELINE_LENGTH: + case CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE: + case CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE: +#endif + curl_multi_setopt(self->multi_handle, option, d); + break; + default: + PyErr_SetString(PyExc_TypeError, "integers are not supported for this option"); + return NULL; + } + Py_RETURN_NONE; + } + if (PyFunction_Check(obj) || PyCFunction_Check(obj) || + PyCallable_Check(obj) || PyMethod_Check(obj)) { + /* We use function types here to make sure that our callback + * definitions exactly match the <curl/multi.h> interface. + */ + const curl_multi_timer_callback t_cb = multi_timer_callback; + const curl_socket_callback s_cb = multi_socket_callback; + + switch(option) { + case CURLMOPT_SOCKETFUNCTION: + curl_multi_setopt(self->multi_handle, CURLMOPT_SOCKETFUNCTION, s_cb); + curl_multi_setopt(self->multi_handle, CURLMOPT_SOCKETDATA, self); + Py_INCREF(obj); + self->s_cb = obj; + break; + case CURLMOPT_TIMERFUNCTION: + curl_multi_setopt(self->multi_handle, CURLMOPT_TIMERFUNCTION, t_cb); + curl_multi_setopt(self->multi_handle, CURLMOPT_TIMERDATA, self); + Py_INCREF(obj); + self->t_cb = obj; + break; + default: + PyErr_SetString(PyExc_TypeError, "callables are not supported for this option"); + return NULL; + } + Py_RETURN_NONE; + } + /* Failed to match any of the function signatures -- return error */ +error: + PyErr_SetString(PyExc_TypeError, "invalid arguments to setopt"); + return NULL; +} + + +/* --------------- timeout --------------- */ + +static PyObject * +do_multi_timeout(CurlMultiObject *self) +{ + CURLMcode res; + long timeout; + + if (check_multi_state(self, 1 | 2, "timeout") != 0) { + return NULL; + } + + res = curl_multi_timeout(self->multi_handle, &timeout); + if (res != CURLM_OK) { + CURLERROR_MSG("timeout failed"); + } + + /* Return number of millisecs until timeout */ + return Py_BuildValue("l", timeout); +} + + +/* --------------- assign --------------- */ + +static PyObject * +do_multi_assign(CurlMultiObject *self, PyObject *args) +{ + CURLMcode res; + curl_socket_t socket; + PyObject *obj; + + if (!PyArg_ParseTuple(args, "iO:assign", &socket, &obj)) + return NULL; + if (check_multi_state(self, 1 | 2, "assign") != 0) { + return NULL; + } + Py_INCREF(obj); + + res = curl_multi_assign(self->multi_handle, socket, obj); + if (res != CURLM_OK) { + CURLERROR_MSG("assign failed"); + } + + Py_RETURN_NONE; +} + + +/* --------------- socket_action --------------- */ +static PyObject * +do_multi_socket_action(CurlMultiObject *self, PyObject *args) +{ + CURLMcode res; + curl_socket_t socket; + int ev_bitmask; + int running = -1; + + if (!PyArg_ParseTuple(args, "ii:socket_action", &socket, &ev_bitmask)) + return NULL; + if (check_multi_state(self, 1 | 2, "socket_action") != 0) { + return NULL; + } + + PYCURL_BEGIN_ALLOW_THREADS + res = curl_multi_socket_action(self->multi_handle, socket, ev_bitmask, &running); + PYCURL_END_ALLOW_THREADS + + if (res != CURLM_OK) { + CURLERROR_MSG("multi_socket_action failed"); + } + /* Return a tuple with the result and the number of running handles */ + return Py_BuildValue("(ii)", (int)res, running); +} + +/* --------------- socket_all --------------- */ + +static PyObject * +do_multi_socket_all(CurlMultiObject *self) +{ + CURLMcode res; + int running = -1; + + if (check_multi_state(self, 1 | 2, "socket_all") != 0) { + return NULL; + } + + PYCURL_BEGIN_ALLOW_THREADS + res = curl_multi_socket_all(self->multi_handle, &running); + PYCURL_END_ALLOW_THREADS + + /* We assume these errors are ok, otherwise raise exception */ + if (res != CURLM_OK && res != CURLM_CALL_MULTI_PERFORM) { + CURLERROR_MSG("perform failed"); + } + + /* Return a tuple with the result and the number of running handles */ + return Py_BuildValue("(ii)", (int)res, running); +} + + +/* --------------- perform --------------- */ + +static PyObject * +do_multi_perform(CurlMultiObject *self) +{ + CURLMcode res; + int running = -1; + + if (check_multi_state(self, 1 | 2, "perform") != 0) { + return NULL; + } + + PYCURL_BEGIN_ALLOW_THREADS + res = curl_multi_perform(self->multi_handle, &running); + PYCURL_END_ALLOW_THREADS + + /* We assume these errors are ok, otherwise raise exception */ + if (res != CURLM_OK && res != CURLM_CALL_MULTI_PERFORM) { + CURLERROR_MSG("perform failed"); + } + + /* Return a tuple with the result and the number of running handles */ + return Py_BuildValue("(ii)", (int)res, running); +} + + +/* --------------- add_handle/remove_handle --------------- */ + +/* static utility function */ +static int +check_multi_add_remove(const CurlMultiObject *self, const CurlObject *obj) +{ + /* check CurlMultiObject status */ + assert_multi_state(self); + if (self->multi_handle == NULL) { + PyErr_SetString(ErrorObject, "cannot add/remove handle - multi-stack is closed"); + return -1; + } +#ifdef WITH_THREAD + if (self->state != NULL) { + PyErr_SetString(ErrorObject, "cannot add/remove handle - multi_perform() already running"); + return -1; + } +#endif + /* check CurlObject status */ + assert_curl_state(obj); +#ifdef WITH_THREAD + if (obj->state != NULL) { + PyErr_SetString(ErrorObject, "cannot add/remove handle - perform() of curl object already running"); + return -1; + } +#endif + if (obj->multi_stack != NULL && obj->multi_stack != self) { + PyErr_SetString(ErrorObject, "cannot add/remove handle - curl object already on another multi-stack"); + return -1; + } + return 0; +} + + +static PyObject * +do_multi_add_handle(CurlMultiObject *self, PyObject *args) +{ + CurlObject *obj; + CURLMcode res; + + if (!PyArg_ParseTuple(args, "O!:add_handle", p_Curl_Type, &obj)) { + return NULL; + } + if (check_multi_add_remove(self, obj) != 0) { + return NULL; + } + if (obj->handle == NULL) { + PyErr_SetString(ErrorObject, "curl object already closed"); + return NULL; + } + if (obj->multi_stack == self) { + PyErr_SetString(ErrorObject, "curl object already on this multi-stack"); + return NULL; + } + assert(obj->multi_stack == NULL); + res = curl_multi_add_handle(self->multi_handle, obj->handle); + if (res != CURLM_OK) { + CURLERROR_MSG("curl_multi_add_handle() failed due to internal errors"); + } + obj->multi_stack = self; + Py_INCREF(self); + Py_RETURN_NONE; +} + + +static PyObject * +do_multi_remove_handle(CurlMultiObject *self, PyObject *args) +{ + CurlObject *obj; + CURLMcode res; + + if (!PyArg_ParseTuple(args, "O!:remove_handle", p_Curl_Type, &obj)) { + return NULL; + } + if (check_multi_add_remove(self, obj) != 0) { + return NULL; + } + if (obj->handle == NULL) { + /* CurlObject handle already closed -- ignore */ + goto done; + } + if (obj->multi_stack != self) { + PyErr_SetString(ErrorObject, "curl object not on this multi-stack"); + return NULL; + } + res = curl_multi_remove_handle(self->multi_handle, obj->handle); + if (res != CURLM_OK) { + CURLERROR_MSG("curl_multi_remove_handle() failed due to internal errors"); + } + assert(obj->multi_stack == self); + obj->multi_stack = NULL; + Py_DECREF(self); +done: + Py_RETURN_NONE; +} + + +/* --------------- fdset ---------------------- */ + +static PyObject * +do_multi_fdset(CurlMultiObject *self) +{ + CURLMcode res; + int max_fd = -1, fd; + PyObject *ret = NULL; + PyObject *read_list = NULL, *write_list = NULL, *except_list = NULL; + PyObject *py_fd = NULL; + + if (check_multi_state(self, 1 | 2, "fdset") != 0) { + return NULL; + } + + /* Clear file descriptor sets */ + FD_ZERO(&self->read_fd_set); + FD_ZERO(&self->write_fd_set); + FD_ZERO(&self->exc_fd_set); + + /* Don't bother releasing the gil as this is just a data structure operation */ + res = curl_multi_fdset(self->multi_handle, &self->read_fd_set, + &self->write_fd_set, &self->exc_fd_set, &max_fd); + if (res != CURLM_OK) { + CURLERROR_MSG("curl_multi_fdset() failed due to internal errors"); + } + + /* Allocate lists. */ + if ((read_list = PyList_New((Py_ssize_t)0)) == NULL) goto error; + if ((write_list = PyList_New((Py_ssize_t)0)) == NULL) goto error; + if ((except_list = PyList_New((Py_ssize_t)0)) == NULL) goto error; + + /* Populate lists */ + for (fd = 0; fd < max_fd + 1; fd++) { + if (FD_ISSET(fd, &self->read_fd_set)) { + if ((py_fd = PyInt_FromLong((long)fd)) == NULL) goto error; + if (PyList_Append(read_list, py_fd) != 0) goto error; + Py_DECREF(py_fd); + py_fd = NULL; + } + if (FD_ISSET(fd, &self->write_fd_set)) { + if ((py_fd = PyInt_FromLong((long)fd)) == NULL) goto error; + if (PyList_Append(write_list, py_fd) != 0) goto error; + Py_DECREF(py_fd); + py_fd = NULL; + } + if (FD_ISSET(fd, &self->exc_fd_set)) { + if ((py_fd = PyInt_FromLong((long)fd)) == NULL) goto error; + if (PyList_Append(except_list, py_fd) != 0) goto error; + Py_DECREF(py_fd); + py_fd = NULL; + } + } + + /* Return a tuple with the 3 lists */ + ret = Py_BuildValue("(OOO)", read_list, write_list, except_list); +error: + Py_XDECREF(py_fd); + Py_XDECREF(except_list); + Py_XDECREF(write_list); + Py_XDECREF(read_list); + return ret; +} + + +/* --------------- info_read --------------- */ + +static PyObject * +do_multi_info_read(CurlMultiObject *self, PyObject *args) +{ + PyObject *ret = NULL; + PyObject *ok_list = NULL, *err_list = NULL; + CURLMsg *msg; + int in_queue = 0, num_results = INT_MAX; + + /* Sanity checks */ + if (!PyArg_ParseTuple(args, "|i:info_read", &num_results)) { + return NULL; + } + if (num_results <= 0) { + PyErr_SetString(ErrorObject, "argument to info_read must be greater than zero"); + return NULL; + } + if (check_multi_state(self, 1 | 2, "info_read") != 0) { + return NULL; + } + + if ((ok_list = PyList_New((Py_ssize_t)0)) == NULL) goto error; + if ((err_list = PyList_New((Py_ssize_t)0)) == NULL) goto error; + + /* Loop through all messages */ + while ((msg = curl_multi_info_read(self->multi_handle, &in_queue)) != NULL) { + CURLcode res; + CurlObject *co = NULL; + + /* Check for termination as specified by the user */ + if (num_results-- <= 0) { + break; + } + + /* Fetch the curl object that corresponds to the curl handle in the message */ + res = curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &co); + if (res != CURLE_OK || co == NULL) { + Py_DECREF(err_list); + Py_DECREF(ok_list); + CURLERROR_MSG("Unable to fetch curl handle from curl object"); + } + assert(Py_TYPE(co) == p_Curl_Type); + if (msg->msg != CURLMSG_DONE) { + /* FIXME: what does this mean ??? */ + } + if (msg->data.result == CURLE_OK) { + /* Append curl object to list of objects which succeeded */ + if (PyList_Append(ok_list, (PyObject *)co) != 0) { + goto error; + } + } + else { + /* Create a result tuple that will get added to err_list. */ + PyObject *v = Py_BuildValue("(Ois)", (PyObject *)co, (int)msg->data.result, co->error); + /* Append curl object to list of objects which failed */ + if (v == NULL || PyList_Append(err_list, v) != 0) { + Py_XDECREF(v); + goto error; + } + Py_DECREF(v); + } + } + /* Return (number of queued messages, [ok_objects], [error_objects]) */ + ret = Py_BuildValue("(iOO)", in_queue, ok_list, err_list); +error: + Py_XDECREF(err_list); + Py_XDECREF(ok_list); + return ret; +} + + +/* --------------- select --------------- */ + +static PyObject * +do_multi_select(CurlMultiObject *self, PyObject *args) +{ + int max_fd = -1, n; + double timeout = -1.0; + struct timeval tv, *tvp; + CURLMcode res; + + if (!PyArg_ParseTuple(args, "d:select", &timeout)) { + return NULL; + } + if (check_multi_state(self, 1 | 2, "select") != 0) { + return NULL; + } + + if (timeout < 0 || timeout >= 365 * 24 * 60 * 60) { + PyErr_SetString(PyExc_OverflowError, "invalid timeout period"); + return NULL; + } else { + long seconds = (long)timeout; + timeout = timeout - (double)seconds; + assert(timeout >= 0.0); assert(timeout < 1.0); + tv.tv_sec = seconds; + tv.tv_usec = (long)(timeout*1000000.0); + tvp = &tv; + } + + FD_ZERO(&self->read_fd_set); + FD_ZERO(&self->write_fd_set); + FD_ZERO(&self->exc_fd_set); + + res = curl_multi_fdset(self->multi_handle, &self->read_fd_set, + &self->write_fd_set, &self->exc_fd_set, &max_fd); + if (res != CURLM_OK) { + CURLERROR_MSG("multi_fdset failed"); + } + + if (max_fd < 0) { + n = 0; + } + else { + Py_BEGIN_ALLOW_THREADS + n = select(max_fd + 1, &self->read_fd_set, &self->write_fd_set, &self->exc_fd_set, tvp); + Py_END_ALLOW_THREADS + /* info: like Python's socketmodule.c we do not raise an exception + * if select() fails - we'll leave it to the actual libcurl + * socket code to report any errors. + */ + } + + return PyInt_FromLong(n); +} + + +/************************************************************************* +// type definitions +**************************************************************************/ + +/* --------------- methods --------------- */ + +static const char co_multi_close_doc [] = "\ +close() -> None\n\ +\n\ +Corresponds to `curl_multi_cleanup`_ in libcurl. This method is\n\ +automatically called by pycurl when a CurlMulti object no longer has any\n\ +references to it, but can also be called explicitly.\ +"; +static const char co_multi_fdset_doc [] = + "fdset() -> Tuple. " + "Returns a tuple of three lists that can be passed to the select.select() method .\n"; +static const char co_multi_info_read_doc [] = + "info_read([max_objects]) -> Tuple. " + "Returns a tuple (number of queued handles, [curl objects]).\n"; +static const char co_multi_select_doc [] = + "select([timeout]) -> Int. " + "Returns result from doing a select() on the curl multi file descriptor with the given timeout.\n"; +static const char co_multi_socket_action_doc [] = + "socket_action(sockfd, ev_bitmask) -> Tuple. " + "Returns result from doing a socket_action() on the curl multi file descriptor with the given timeout.\n"; +static const char co_multi_socket_all_doc [] = + "socket_all() -> Tuple. " + "Returns result from doing a socket_all() on the curl multi file descriptor with the given timeout.\n"; + +PYCURL_INTERNAL PyMethodDef curlmultiobject_methods[] = { + {"add_handle", (PyCFunction)do_multi_add_handle, METH_VARARGS, NULL}, + {"close", (PyCFunction)do_multi_close, METH_NOARGS, co_multi_close_doc}, + {"fdset", (PyCFunction)do_multi_fdset, METH_NOARGS, co_multi_fdset_doc}, + {"info_read", (PyCFunction)do_multi_info_read, METH_VARARGS, co_multi_info_read_doc}, + {"perform", (PyCFunction)do_multi_perform, METH_NOARGS, NULL}, + {"socket_action", (PyCFunction)do_multi_socket_action, METH_VARARGS, co_multi_socket_action_doc}, + {"socket_all", (PyCFunction)do_multi_socket_all, METH_NOARGS, co_multi_socket_all_doc}, + {"setopt", (PyCFunction)do_multi_setopt, METH_VARARGS, NULL}, + {"timeout", (PyCFunction)do_multi_timeout, METH_NOARGS, NULL}, + {"assign", (PyCFunction)do_multi_assign, METH_VARARGS, NULL}, + {"remove_handle", (PyCFunction)do_multi_remove_handle, METH_VARARGS, NULL}, + {"select", (PyCFunction)do_multi_select, METH_VARARGS, co_multi_select_doc}, + {NULL, NULL, 0, NULL} +}; + + +/* --------------- setattr/getattr --------------- */ + + +#if PY_MAJOR_VERSION >= 3 + +PYCURL_INTERNAL PyObject * +do_multi_getattro(PyObject *o, PyObject *n) +{ + PyObject *v; + assert_multi_state((CurlMultiObject *)o); + v = PyObject_GenericGetAttr(o, n); + if( !v && PyErr_ExceptionMatches(PyExc_AttributeError) ) + { + PyErr_Clear(); + v = my_getattro(o, n, ((CurlMultiObject *)o)->dict, + curlmultiobject_constants, curlmultiobject_methods); + } + return v; +} + +PYCURL_INTERNAL int +do_multi_setattro(PyObject *o, PyObject *n, PyObject *v) +{ + assert_multi_state((CurlMultiObject *)o); + return my_setattro(&((CurlMultiObject *)o)->dict, n, v); +} + +#else /* PY_MAJOR_VERSION >= 3 */ + +PYCURL_INTERNAL PyObject * +do_multi_getattr(CurlMultiObject *co, char *name) +{ + assert_multi_state(co); + return my_getattr((PyObject *)co, name, co->dict, + curlmultiobject_constants, curlmultiobject_methods); +} + +PYCURL_INTERNAL int +do_multi_setattr(CurlMultiObject *co, char *name, PyObject *v) +{ + assert_multi_state(co); + return my_setattr(&co->dict, name, v); +} + +#endif /* PY_MAJOR_VERSION >= 3 */ + +/* vi:ts=4:et:nowrap + */ diff --git a/src/pycurl.h b/src/pycurl.h index ece2bb2..0133ba3 100644 --- a/src/pycurl.h +++ b/src/pycurl.h @@ -200,6 +200,25 @@ PyText_AsString_NoNUL(PyObject *obj, PyObject **encoded_obj); PYCURL_INTERNAL int PyText_Check(PyObject *o); + +/* Raise exception based on return value `res' and `self->error' */ +#define CURLERROR_RETVAL() do {\ + PyObject *v; \ + self->error[sizeof(self->error) - 1] = 0; \ + v = Py_BuildValue("(is)", (int) (res), self->error); \ + if (v != NULL) { PyErr_SetObject(ErrorObject, v); Py_DECREF(v); } \ + return NULL; \ +} while (0) + +/* Raise exception based on return value `res' and custom message */ +#define CURLERROR_MSG(msg) do {\ + PyObject *v; const char *m = (msg); \ + v = Py_BuildValue("(is)", (int) (res), (m)); \ + if (v != NULL) { PyErr_SetObject(ErrorObject, v); Py_DECREF(v); } \ + return NULL; \ +} while (0) + + /* Calculate the number of OBJECTPOINT options we need to store */ #define OPTIONS_SIZE ((int)CURLOPT_LASTENTRY % 10000) #define MOPTIONS_SIZE ((int)CURLMOPT_LASTENTRY % 10000) @@ -331,6 +350,22 @@ PYCURL_INTERNAL void pycurl_ssl_cleanup(void); #endif +#if PY_MAJOR_VERSION >= 3 +PYCURL_INTERNAL PyObject * +my_getattro(PyObject *co, PyObject *name, PyObject *dict1, PyObject *dict2, PyMethodDef *m); +PYCURL_INTERNAL int +my_setattro(PyObject **dict, PyObject *name, PyObject *v); +#else /* PY_MAJOR_VERSION >= 3 */ +PYCURL_INTERNAL int +my_setattr(PyObject **dict, char *name, PyObject *v); +PYCURL_INTERNAL PyObject * +my_getattr(PyObject *co, char *name, PyObject *dict1, PyObject *dict2, PyMethodDef *m); +#endif /* PY_MAJOR_VERSION >= 3 */ + +/* used by multi object */ +PYCURL_INTERNAL void +assert_curl_state(const CurlObject *self); + PYCURL_INTERNAL CurlShareObject * do_share_new(PyObject *dummy); PYCURL_INTERNAL int diff --git a/src/pythoncompat.c b/src/pythoncompat.c new file mode 100644 index 0000000..329e528 --- /dev/null +++ b/src/pythoncompat.c @@ -0,0 +1,84 @@ +#include "pycurl.h" + +#if PY_MAJOR_VERSION >= 3 + +PYCURL_INTERNAL PyObject * +my_getattro(PyObject *co, PyObject *name, PyObject *dict1, PyObject *dict2, PyMethodDef *m) +{ + PyObject *v = NULL; + if( dict1 != NULL ) + v = PyDict_GetItem(dict1, name); + if( v == NULL && dict2 != NULL ) + v = PyDict_GetItem(dict2, name); + if( v != NULL ) + { + Py_INCREF(v); + return v; + } + PyErr_SetString(PyExc_AttributeError, "trying to obtain a non-existing attribute"); + return NULL; +} + +PYCURL_INTERNAL int +my_setattro(PyObject **dict, PyObject *name, PyObject *v) +{ + if( *dict == NULL ) + { + *dict = PyDict_New(); + if( *dict == NULL ) + return -1; + } + if (v != NULL) + return PyDict_SetItem(*dict, name, v); + else { + int v = PyDict_DelItem(*dict, name); + if (v != 0) { + /* need to convert KeyError to AttributeError */ + if (PyErr_ExceptionMatches(PyExc_KeyError)) { + PyErr_SetString(PyExc_AttributeError, "trying to delete a non-existing attribute"); + } + } + return v; + } +} + +#else /* PY_MAJOR_VERSION >= 3 */ + +PYCURL_INTERNAL int +my_setattr(PyObject **dict, char *name, PyObject *v) +{ + if (v == NULL) { + int rv = -1; + if (*dict != NULL) + rv = PyDict_DelItemString(*dict, name); + if (rv < 0) + PyErr_SetString(PyExc_AttributeError, "delete non-existing attribute"); + return rv; + } + if (*dict == NULL) { + *dict = PyDict_New(); + if (*dict == NULL) + return -1; + } + return PyDict_SetItemString(*dict, name, v); +} + +PYCURL_INTERNAL PyObject * +my_getattr(PyObject *co, char *name, PyObject *dict1, PyObject *dict2, PyMethodDef *m) +{ + PyObject *v = NULL; + if (v == NULL && dict1 != NULL) + v = PyDict_GetItemString(dict1, name); + if (v == NULL && dict2 != NULL) + v = PyDict_GetItemString(dict2, name); + if (v != NULL) { + Py_INCREF(v); + return v; + } + return Py_FindMethod(m, co, name); +} + +#endif /* PY_MAJOR_VERSION >= 3 */ + +/* vi:ts=4:et:nowrap + */ diff --git a/src/share.c b/src/share.c new file mode 100644 index 0000000..98524f6 --- /dev/null +++ b/src/share.c @@ -0,0 +1,261 @@ +#include "pycurl.h" + +/************************************************************************* +// static utility functions +**************************************************************************/ + + +/* assert some CurlShareObject invariants */ +static void +assert_share_state(const CurlShareObject *self) +{ + assert(self != NULL); + assert(Py_TYPE(self) == p_CurlShare_Type); +#ifdef WITH_THREAD + assert(self->lock != NULL); +#endif +} + + +static int +check_share_state(const CurlShareObject *self, int flags, const char *name) +{ + assert_share_state(self); + return 0; +} + + +/* constructor - this is a module-level function returning a new instance */ +PYCURL_INTERNAL CurlShareObject * +do_share_new(PyObject *dummy) +{ + int res; + CurlShareObject *self; +#ifdef WITH_THREAD + const curl_lock_function lock_cb = share_lock_callback; + const curl_unlock_function unlock_cb = share_unlock_callback; +#endif + + UNUSED(dummy); + + /* Allocate python curl-share object */ + self = (CurlShareObject *) PyObject_GC_New(CurlShareObject, p_CurlShare_Type); + if (self) { + PyObject_GC_Track(self); + } + else { + return NULL; + } + + /* Initialize object attributes */ + self->dict = NULL; +#ifdef WITH_THREAD + self->lock = share_lock_new(); + assert(self->lock != NULL); +#endif + + /* Allocate libcurl share handle */ + self->share_handle = curl_share_init(); + if (self->share_handle == NULL) { + Py_DECREF(self); + PyErr_SetString(ErrorObject, "initializing curl-share failed"); + return NULL; + } + +#ifdef WITH_THREAD + /* Set locking functions and data */ + res = curl_share_setopt(self->share_handle, CURLSHOPT_LOCKFUNC, lock_cb); + assert(res == CURLE_OK); + res = curl_share_setopt(self->share_handle, CURLSHOPT_USERDATA, self); + assert(res == CURLE_OK); + res = curl_share_setopt(self->share_handle, CURLSHOPT_UNLOCKFUNC, unlock_cb); + assert(res == CURLE_OK); +#endif + + return self; +} + + +PYCURL_INTERNAL int +do_share_traverse(CurlShareObject *self, visitproc visit, void *arg) +{ + int err; +#undef VISIT +#define VISIT(v) if ((v) != NULL && ((err = visit(v, arg)) != 0)) return err + + VISIT(self->dict); + + return 0; +#undef VISIT +} + + +/* Drop references that may have created reference cycles. */ +PYCURL_INTERNAL int +do_share_clear(CurlShareObject *self) +{ + Py_CLEAR(self->dict); + return 0; +} + + +static void +util_share_close(CurlShareObject *self){ + if (self->share_handle != NULL) { + CURLSH *share_handle = self->share_handle; + self->share_handle = NULL; + curl_share_cleanup(share_handle); + } +} + + +PYCURL_INTERNAL void +do_share_dealloc(CurlShareObject *self) +{ + PyObject_GC_UnTrack(self); + Py_TRASHCAN_SAFE_BEGIN(self); + + Py_CLEAR(self->dict); + util_share_close(self); + +#ifdef WITH_THREAD + share_lock_destroy(self->lock); +#endif + + PyObject_GC_Del(self); + Py_TRASHCAN_SAFE_END(self) +} + + +static PyObject * +do_share_close(CurlShareObject *self) +{ + if (check_share_state(self, 2, "close") != 0) { + return NULL; + } + util_share_close(self); + Py_RETURN_NONE; +} + + +/* setopt, unsetopt*/ +/* --------------- unsetopt/setopt/getinfo --------------- */ + +static PyObject * +do_curlshare_setopt(CurlShareObject *self, PyObject *args) +{ + int option; + PyObject *obj; + + if (!PyArg_ParseTuple(args, "iO:setopt", &option, &obj)) + return NULL; + if (check_share_state(self, 1 | 2, "sharesetopt") != 0) + return NULL; + + /* early checks of option value */ + if (option <= 0) + goto error; + if (option >= (int)CURLOPTTYPE_OFF_T + OPTIONS_SIZE) + goto error; + if (option % 10000 >= OPTIONS_SIZE) + goto error; + +#if 0 /* XXX - should we ??? */ + /* Handle the case of None */ + if (obj == Py_None) { + return util_curl_unsetopt(self, option); + } +#endif + + /* Handle the case of integer arguments */ + if (PyInt_Check(obj)) { + long d = PyInt_AsLong(obj); + if (d != CURL_LOCK_DATA_COOKIE && d != CURL_LOCK_DATA_DNS && d != CURL_LOCK_DATA_SSL_SESSION) { + goto error; + } + switch(option) { + case CURLSHOPT_SHARE: + case CURLSHOPT_UNSHARE: + curl_share_setopt(self->share_handle, option, d); + break; + default: + PyErr_SetString(PyExc_TypeError, "integers are not supported for this option"); + return NULL; + } + Py_RETURN_NONE; + } + /* Failed to match any of the function signatures -- return error */ +error: + PyErr_SetString(PyExc_TypeError, "invalid arguments to setopt"); + return NULL; +} + + +/************************************************************************* +// type definitions +**************************************************************************/ + +/* --------------- methods --------------- */ + +static const char cso_close_doc [] = + "close() -> None. " + "Close shared handle.\n"; +static const char cso_setopt_doc [] = + "setopt(option, parameter) -> None. " + "Set curl share option. Raises pycurl.error exception upon failure.\n"; + +PYCURL_INTERNAL PyMethodDef curlshareobject_methods[] = { + {"close", (PyCFunction)do_share_close, METH_NOARGS, cso_close_doc}, + {"setopt", (PyCFunction)do_curlshare_setopt, METH_VARARGS, cso_setopt_doc}, + {NULL, NULL, 0, 0} +}; + + +/* --------------- setattr/getattr --------------- */ + + +#if PY_MAJOR_VERSION >= 3 + +PYCURL_INTERNAL PyObject * +do_share_getattro(PyObject *o, PyObject *n) +{ + PyObject *v; + assert_share_state((CurlShareObject *)o); + v = PyObject_GenericGetAttr(o, n); + if( !v && PyErr_ExceptionMatches(PyExc_AttributeError) ) + { + PyErr_Clear(); + v = my_getattro(o, n, ((CurlShareObject *)o)->dict, + curlshareobject_constants, curlshareobject_methods); + } + return v; +} + +PYCURL_INTERNAL int +do_share_setattro(PyObject *o, PyObject *n, PyObject *v) +{ + assert_share_state((CurlShareObject *)o); + return my_setattro(&((CurlShareObject *)o)->dict, n, v); +} + +#else /* PY_MAJOR_VERSION >= 3 */ + +PYCURL_INTERNAL PyObject * +do_share_getattr(CurlShareObject *cso, char *name) +{ + assert_share_state(cso); + return my_getattr((PyObject *)cso, name, cso->dict, + curlshareobject_constants, curlshareobject_methods); +} + +PYCURL_INTERNAL int +do_share_setattr(CurlShareObject *so, char *name, PyObject *v) +{ + assert_share_state(so); + return my_setattr(&so->dict, name, v); +} + +#endif /* PY_MAJOR_VERSION >= 3 */ + +/* vi:ts=4:et:nowrap + */ |