From ba7171614a7639571eddb7998a0a657afabeaabc Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Jan 21 2014 13:38:50 +0000 Subject: update to 7.19.3 --- diff --git a/.gitignore b/.gitignore index eefd185..90e6917 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ pycurl-7.19.0.tar.gz +/pycurl-7.19.3.tar.gz diff --git a/0000-pycurl-7.19.7-8d654296.patch b/0000-pycurl-7.19.7-8d654296.patch deleted file mode 100644 index c802a64..0000000 --- a/0000-pycurl-7.19.7-8d654296.patch +++ /dev/null @@ -1,12436 +0,0 @@ -From 9a426178c83f756ec248e9fd0010030ae7c62f2a Mon Sep 17 00:00:00 2001 -From: Kjetil Jacobsen -Date: Tue, 9 Sep 2008 18:49:59 +0000 -Subject: [PATCH 001/149] whitespace - -Signed-off-by: Kamil Dudka ---- - ChangeLog | 2 +- - 1 files changed, 1 insertions(+), 1 deletions(-) - -diff --git a/ChangeLog b/ChangeLog -index 0fb7f8c..3d68424 100644 ---- a/ChangeLog -+++ b/ChangeLog -@@ -1,7 +1,7 @@ - Version 7.19.0 [requires libcurl-7.19.0 or better] - -------------- - -- * Added CURLFILE, ADDRESS_SCOPE and ISSUERCERT options, -+ * Added CURLFILE, ADDRESS_SCOPE and ISSUERCERT options, - as well as the APPCONNECT_TIME info. - - * Added PRIMARY_IP info (patch by --- -1.7.1 - - -From b5f6dd9fd839e54db567d0451483a400edad60a4 Mon Sep 17 00:00:00 2001 -From: Kjetil Jacobsen -Date: Mon, 29 Sep 2008 10:56:57 +0000 -Subject: [PATCH 002/149] No longer keep copies of string options since this is managed by libcurl - -Signed-off-by: Kamil Dudka ---- - ChangeLog | 10 +++++++++- - src/pycurl.c | 58 +--------------------------------------------------------- - 2 files changed, 10 insertions(+), 58 deletions(-) - -diff --git a/ChangeLog b/ChangeLog -index 3d68424..618654d 100644 ---- a/ChangeLog -+++ b/ChangeLog -@@ -1,4 +1,12 @@ --Version 7.19.0 [requires libcurl-7.19.0 or better] -+Version 7.19.1 [requires libcurl-7.19.0 or better] -+-------------- -+ -+ * No longer keep string options copies in the -+ Curl Python objects, since string options are -+ now managed by libcurl. -+ -+ -+Version 7.19.0 - -------------- - - * Added CURLFILE, ADDRESS_SCOPE and ISSUERCERT options, -diff --git a/src/pycurl.c b/src/pycurl.c -index 50ec9ce..189f5d6 100644 ---- a/src/pycurl.c -+++ b/src/pycurl.c -@@ -97,12 +97,6 @@ static void pycurl_ssl_cleanup(void); - /* Calculate the number of OBJECTPOINT options we need to store */ - #define OPTIONS_SIZE ((int)CURLOPT_LASTENTRY % 10000) - #define MOPTIONS_SIZE ((int)CURLMOPT_LASTENTRY % 10000) --static int OPT_INDEX(int o) --{ -- assert(o >= CURLOPTTYPE_OBJECTPOINT); -- assert(o < CURLOPTTYPE_OBJECTPOINT + OPTIONS_SIZE); -- return o - CURLOPTTYPE_OBJECTPOINT; --} - - /* Type objects */ - static PyObject *ErrorObject = NULL; -@@ -161,7 +155,6 @@ typedef struct { - PyObject *writedata_fp; - PyObject *writeheader_fp; - /* misc */ -- void *options[OPTIONS_SIZE]; /* for OBJECTPOINT options */ - char error[CURL_ERROR_SIZE+1]; - } CurlObject; - -@@ -741,7 +734,6 @@ util_curl_new(void) - self->writeheader_fp = NULL; - - /* Zero string pointer memory buffer used by setopt */ -- memset(self->options, 0, sizeof(self->options)); - memset(self->error, 0, sizeof(self->error)); - - return self; -@@ -804,7 +796,6 @@ do_curl_new(PyObject *dummy) - free(s); - goto error; - } -- self->options[ OPT_INDEX(CURLOPT_USERAGENT) ] = s; s = NULL; - - /* Success - return new object */ - return self; -@@ -872,7 +863,6 @@ static void - util_curl_close(CurlObject *self) - { - CURL *handle; -- int i; - - /* Zero handle and thread-state to disallow any operations to be run - * from now on */ -@@ -916,16 +906,6 @@ util_curl_close(CurlObject *self) - SFREE(self->postquote); - SFREE(self->prequote); - #undef SFREE -- -- /* Last, free the options. This must be done after the curl handle -- * is closed since libcurl assumes that some options are valid when -- * invoking curl_easy_cleanup(). */ -- for (i = 0; i < OPTIONS_SIZE; i++) { -- if (self->options[i] != NULL) { -- free(self->options[i]); -- self->options[i] = NULL; -- } -- } - } - - -@@ -1424,8 +1404,6 @@ verbose_error: - static PyObject* - do_curl_reset(CurlObject *self) - { -- unsigned int i; -- - curl_easy_reset(self->handle); - - /* Decref callbacks and file handles */ -@@ -1443,15 +1421,6 @@ do_curl_reset(CurlObject *self) - SFREE(self->postquote); - SFREE(self->prequote); - #undef SFREE -- -- /* Last, free the options */ -- for (i = 0; i < OPTIONS_SIZE; i++) { -- if (self->options[i] != NULL) { -- free(self->options[i]); -- self->options[i] = NULL; -- } -- } -- - return Py_None; - } - -@@ -1461,7 +1430,6 @@ static PyObject * - util_curl_unsetopt(CurlObject *self, int option) - { - int res; -- int opt_index = -1; - - #define SETOPT2(o,x) \ - if ((res = curl_easy_setopt(self->handle, (o), (x))) != CURLE_OK) goto error -@@ -1502,7 +1470,6 @@ util_curl_unsetopt(CurlObject *self, int option) - case CURLOPT_SSL_CIPHER_LIST: - case CURLOPT_USERPWD: - SETOPT((char *) 0); -- opt_index = OPT_INDEX(option); - break; - - /* info: we explicitly list unsupported options here */ -@@ -1512,11 +1479,6 @@ util_curl_unsetopt(CurlObject *self, int option) - return NULL; - } - -- if (opt_index >= 0 && self->options[opt_index] != NULL) { -- free(self->options[opt_index]); -- self->options[opt_index] = NULL; -- } -- - Py_INCREF(Py_None); - return Py_None; - -@@ -1587,8 +1549,6 @@ do_curl_setopt(CurlObject *self, PyObject *args) - if (PyString_Check(obj)) { - char *str = NULL; - Py_ssize_t len = -1; -- char *buf; -- int opt_index; - - /* Check that the option specified a string as well as the input */ - switch (option) { -@@ -1651,28 +1611,12 @@ do_curl_setopt(CurlObject *self, PyObject *args) - } - /* Allocate memory to hold the string */ - assert(str != NULL); -- if (len <= 0) -- buf = strdup(str); -- else { -- buf = (char *) malloc(len); -- if (buf) memcpy(buf, str, len); -- } -- if (buf == NULL) -- return PyErr_NoMemory(); - /* Call setopt */ -- res = curl_easy_setopt(self->handle, (CURLoption)option, buf); -+ res = curl_easy_setopt(self->handle, (CURLoption)option, str); - /* Check for errors */ - if (res != CURLE_OK) { -- free(buf); - CURLERROR_RETVAL(); - } -- /* Save allocated option buffer */ -- opt_index = OPT_INDEX(option); -- if (self->options[opt_index] != NULL) { -- free(self->options[opt_index]); -- self->options[opt_index] = NULL; -- } -- self->options[opt_index] = buf; - Py_INCREF(Py_None); - return Py_None; - } --- -1.7.1 - - -From f241a60acc67cc8cbf3d36a2f3d55e1abf88bc33 Mon Sep 17 00:00:00 2001 -From: Christopher Warner -Date: Fri, 23 Apr 2010 16:06:41 +0000 -Subject: [PATCH 003/149] Fixes https://sourceforge.net/tracker/?func=detail&aid=2812016&group_id=28236&atid=392777 with applied patch from sourceforge user dbprice1. - -Signed-off-by: Kamil Dudka ---- - setup.py | 21 +++++++++++++++++---- - 1 files changed, 17 insertions(+), 4 deletions(-) - -diff --git a/setup.py b/setup.py -index 1c3831b..632399d 100644 ---- a/setup.py -+++ b/setup.py -@@ -9,7 +9,7 @@ PACKAGE = "pycurl" - PY_PACKAGE = "curl" - VERSION = "7.19.0" - --import glob, os, re, sys, string -+import glob, os, re, sys, string, subprocess - import distutils - from distutils.core import setup - from distutils.extension import Extension -@@ -96,9 +96,22 @@ else: - include_dirs.append(e[2:]) - else: - extra_compile_args.append(e) -- libs = split_quoted( -- os.popen("'%s' --libs" % CURL_CONFIG).read()+\ -- os.popen("'%s' --static-libs" % CURL_CONFIG).read()) -+ -+ # Run curl-config --libs and --static-libs. Some platforms may not -+ # support one or the other of these curl-config options, so gracefully -+ # tolerate failure of either, but not both. -+ optbuf = "" -+ for option in ["--libs", "--static-libs"]: -+ p = subprocess.Popen("'%s' %s" % (CURL_CONFIG, option), shell=True, -+ stdout=subprocess.PIPE) -+ (stdout, stderr) = p.communicate() -+ if p.wait() == 0: -+ optbuf += stdout -+ if optbuf == "": -+ raise Exception, ("Neither of curl-config --libs or --static-libs" + -+ "produced output") -+ libs = split_quoted(optbuf) -+ - for e in libs: - if e[:2] == "-l": - libraries.append(e[2:]) --- -1.7.1 - - -From 70114226c76ee06c6bacb488734796551ac9b2f9 Mon Sep 17 00:00:00 2001 -From: Christopher Warner -Date: Wed, 28 Apr 2010 16:02:41 +0000 -Subject: [PATCH 004/149] Fixes refcount bug and provides better organization of PyCurl object. Submitted by dbprice1. - -https://sourceforge.net/tracker/?func=detail&aid=2893665&group_id=28236&atid=392777 - -Signed-off-by: Kamil Dudka ---- - src/pycurl.c | 87 ++++++++++++++++++++++++++++++++++++++-------------------- - 1 files changed, 57 insertions(+), 30 deletions(-) - -diff --git a/src/pycurl.c b/src/pycurl.c -index 189f5d6..47de850 100644 ---- a/src/pycurl.c -+++ b/src/pycurl.c -@@ -739,64 +739,80 @@ util_curl_new(void) - return self; - } - -- --/* constructor - this is a module-level function returning a new instance */ --static CurlObject * --do_curl_new(PyObject *dummy) -+/* initializer - used to intialize curl easy handles for use with pycurl */ -+static int -+util_curl_init(CurlObject *self) - { -- CurlObject *self = NULL; - int res; - char *s = NULL; - -- UNUSED(dummy); -- -- /* Allocate python curl object */ -- self = util_curl_new(); -- if (self == NULL) -- return NULL; -- -- /* Initialize curl handle */ -- self->handle = curl_easy_init(); -- if (self->handle == NULL) -- goto error; -- - /* Set curl error buffer and zero it */ - res = curl_easy_setopt(self->handle, CURLOPT_ERRORBUFFER, self->error); -- if (res != CURLE_OK) -- goto error; -+ if (res != CURLE_OK) { -+ return (-1); -+ } - memset(self->error, 0, sizeof(self->error)); - - /* Set backreference */ - res = curl_easy_setopt(self->handle, CURLOPT_PRIVATE, (char *) self); -- if (res != CURLE_OK) -- goto error; -+ if (res != CURLE_OK) { -+ return (-1); -+ } - - /* Enable NOPROGRESS by default, i.e. no progress output */ - res = curl_easy_setopt(self->handle, CURLOPT_NOPROGRESS, (long)1); -- if (res != CURLE_OK) -- goto error; -+ if (res != CURLE_OK) { -+ return (-1); -+ } - - /* Disable VERBOSE by default, i.e. no verbose output */ - res = curl_easy_setopt(self->handle, CURLOPT_VERBOSE, (long)0); -- if (res != CURLE_OK) -- goto error; -+ if (res != CURLE_OK) { -+ return (-1); -+ } - - /* Set FTP_ACCOUNT to NULL by default */ - res = curl_easy_setopt(self->handle, CURLOPT_FTP_ACCOUNT, NULL); -- if (res != CURLE_OK) -- goto error; -+ if (res != CURLE_OK) { -+ return (-1); -+ } - - /* Set default USERAGENT */ - s = (char *) malloc(7 + strlen(LIBCURL_VERSION) + 1); -- if (s == NULL) -- goto error; -+ if (s == NULL) { -+ return (-1); -+ } - strcpy(s, "PycURL/"); strcpy(s+7, LIBCURL_VERSION); - res = curl_easy_setopt(self->handle, CURLOPT_USERAGENT, (char *) s); - if (res != CURLE_OK) { - free(s); -- goto error; -+ return (-1); - } -+ return (0); -+} -+ -+/* constructor - this is a module-level function returning a new instance */ -+static CurlObject * -+do_curl_new(PyObject *dummy) -+{ -+ CurlObject *self = NULL; -+ int res; -+ -+ UNUSED(dummy); -+ -+ /* Allocate python curl object */ -+ self = util_curl_new(); -+ if (self == NULL) -+ return NULL; -+ -+ /* Initialize curl handle */ -+ self->handle = curl_easy_init(); -+ if (self->handle == NULL) -+ goto error; - -+ res = util_curl_init(self); -+ if (res < 0) -+ goto error; - /* Success - return new object */ - return self; - -@@ -1404,6 +1420,8 @@ verbose_error: - static PyObject* - do_curl_reset(CurlObject *self) - { -+ int res; -+ - curl_easy_reset(self->handle); - - /* Decref callbacks and file handles */ -@@ -1421,10 +1439,19 @@ do_curl_reset(CurlObject *self) - SFREE(self->postquote); - SFREE(self->prequote); - #undef SFREE -+ res = util_curl_init(self); -+ if (res < 0) { -+ Py_DECREF(self); /* this also closes self->handle */ -+ PyErr_SetString(ErrorObject, "resetting curl failed"); -+ return NULL; -+ } -+ -+ Py_INCREF(Py_None); - return Py_None; - } - - /* --------------- unsetopt/setopt/getinfo --------------- */ -+ int res; - - static PyObject * - util_curl_unsetopt(CurlObject *self, int option) --- -1.7.1 - - -From a607b0c9f4676858fcf6335e9fb0c9ed6a9c3069 Mon Sep 17 00:00:00 2001 -From: Christopher Warner -Date: Wed, 28 Apr 2010 16:03:40 +0000 -Subject: [PATCH 005/149] Test for reset fixes refcount bug - -Signed-off-by: Kamil Dudka ---- - tests/test_reset.py | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++ - 1 files changed, 75 insertions(+), 0 deletions(-) - create mode 100644 tests/test_reset.py - -diff --git a/tests/test_reset.py b/tests/test_reset.py -new file mode 100644 -index 0000000..1addcfe ---- /dev/null -+++ b/tests/test_reset.py -@@ -0,0 +1,75 @@ -+#!/usr/bin/python -+ -+import sys -+import pycurl -+ -+saw_error = 1 -+ -+def main(): -+ global saw_error -+ -+ pycurl.global_init(pycurl.GLOBAL_DEFAULT) -+ -+ outf = file("/dev/null", "rb+") -+ cm = pycurl.CurlMulti() -+ -+ # Set multi handle's options -+ cm.setopt(pycurl.M_PIPELINING, 1) -+ -+ eh = pycurl.Curl() -+ -+ for x in range(1, 20): -+ -+ eh.setopt(pycurl.WRITEDATA, outf) -+ eh.setopt(pycurl.URL, sys.argv[1]) -+ cm.add_handle(eh) -+ -+ while 1: -+ ret, active_handles = cm.perform() -+ if ret != pycurl.E_CALL_MULTI_PERFORM: -+ break -+ -+ while active_handles: -+ ret = cm.select(1.0) -+ if ret == -1: -+ continue -+ while 1: -+ ret, active_handles = cm.perform() -+ if ret != pycurl.E_CALL_MULTI_PERFORM: -+ break -+ -+ count, good, bad = cm.info_read() -+ -+ for h, en, em in bad: -+ print "Transfer to %s failed with %d, %s\n" % \ -+ (h.getinfo(pycurl.EFFECTIVE_URL), en, em) -+ raise RuntimeError -+ -+ for h in good: -+ httpcode = h.getinfo(pycurl.RESPONSE_CODE) -+ if httpcode != 200: -+ print "Transfer to %s failed with code %d\n" %\ -+ (h.getinfo(pycurl.EFFECTIVE_URL), httpcode) -+ raise RuntimeError -+ -+ else: -+ print "Recd %d bytes from %s" % \ -+ (h.getinfo(pycurl.SIZE_DOWNLOAD), -+ h.getinfo(pycurl.EFFECTIVE_URL)) -+ -+ cm.remove_handle(eh) -+ eh.reset() -+ -+ eh.close() -+ cm.close() -+ outf.close() -+ -+ pycurl.global_cleanup() -+ -+ -+if __name__ == '__main__': -+ if len(sys.argv) != 2: -+ print "Usage: %s " % sys.argv[0] -+ sys.exit(2) -+ main() -+ --- -1.7.1 - - -From f773340ec40d5988d4a5b75e64b898dbe4a89800 Mon Sep 17 00:00:00 2001 -From: Christopher Warner -Date: Wed, 28 Apr 2010 16:06:35 +0000 -Subject: [PATCH 006/149] Added myself to the list of maintainers - -Signed-off-by: Kamil Dudka ---- - setup.py | 4 ++-- - 1 files changed, 2 insertions(+), 2 deletions(-) - -diff --git a/setup.py b/setup.py -index 632399d..33704ef 100644 ---- a/setup.py -+++ b/setup.py -@@ -197,8 +197,8 @@ setup_args = get_kw( - description="PycURL -- cURL library module for Python", - author="Kjetil Jacobsen, Markus F.X.J. Oberhumer", - author_email="kjetilja at gmail.com, markus at oberhumer.com", -- maintainer="Kjetil Jacobsen, Markus F.X.J. Oberhumer", -- maintainer_email="kjetilja at gmail.com, markus at oberhumer.com", -+ maintainer="Kjetil Jacobsen, Markus F.X.J. Oberhumer, Christopher Warner", -+ maintainer_email="kjetilja at gmail.com, markus at oberhumer.com, cwarner at kernelcode.com", - url="http://pycurl.sourceforge.net/", - license="LGPL/MIT", - data_files=get_data_files(), --- -1.7.1 - - -From 67e0b9ac7c521b39d7dc779a79d21009f5619fa1 Mon Sep 17 00:00:00 2001 -From: Christopher Warner -Date: Tue, 4 May 2010 18:47:08 +0000 -Subject: [PATCH 007/149] Updating ChangeLog with relevant changes - -Signed-off-by: Kamil Dudka ---- - ChangeLog | 11 +++++++++++ - 1 files changed, 11 insertions(+), 0 deletions(-) - -diff --git a/ChangeLog b/ChangeLog -index 618654d..885c8b0 100644 ---- a/ChangeLog -+++ b/ChangeLog -@@ -1,3 +1,14 @@ -+Version 7.19.2 -+-------------- -+ -+ * Cleaned up website -+ -+ * Fix pycurl.reset() (patch by ). -+ -+ * Fix install routine in setup.py where -+ certain platforms (Solaris, Mac OSX, etc) -+ would search for a static copy of libcurl (dbp) -+ - Version 7.19.1 [requires libcurl-7.19.0 or better] - -------------- - --- -1.7.1 - - -From 310638226dbe77b71a8eb9f8da58cf15d236337a Mon Sep 17 00:00:00 2001 -From: Christopher Warner -Date: Wed, 13 Oct 2010 15:53:40 +0000 -Subject: [PATCH 008/149] Added CURLOPT_SEEKFUNCTION, CURLOPT_SEEKDATA - -Signed-off-by: Kamil Dudka ---- - src/pycurl.c | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++- - 1 files changed, 88 insertions(+), 1 deletions(-) - -diff --git a/src/pycurl.c b/src/pycurl.c -index 47de850..ac448b0 100644 ---- a/src/pycurl.c -+++ b/src/pycurl.c -@@ -150,6 +150,7 @@ typedef struct { - PyObject *debug_cb; - PyObject *ioctl_cb; - PyObject *opensocket_cb; -+ PyObject *seek_cb; - /* file objects */ - PyObject *readdata_fp; - PyObject *writedata_fp; -@@ -727,6 +728,7 @@ util_curl_new(void) - self->debug_cb = NULL; - self->ioctl_cb = NULL; - self->opensocket_cb = NULL; -+ self->seek_cb = NULL; - - /* Set file object pointers to NULL by default */ - self->readdata_fp = NULL; -@@ -1181,6 +1183,82 @@ verbose_error: - goto silent_error; - } - -+static int -+seek_callback(void *stream, curl_off_t offset, int origin) -+{ -+ CurlObject *self; -+ PyThreadState *tmp_state; -+ PyObject *arglist; -+ PyObject *result = NULL; -+ int ret = 2; /* assume error 2 (can't seek, libcurl free to work around). */ -+ PyObject *cb; -+ int source = 0; /* assume beginning */ -+ -+ /* acquire thread */ -+ self = (CurlObject *)stream; -+ tmp_state = get_thread_state(self); -+ if (tmp_state == NULL) -+ return ret; -+ PyEval_AcquireThread(tmp_state); -+ -+ /* check arguments */ -+ switch (origin) -+ { -+ case SEEK_SET: -+ source = 0; -+ break; -+ case SEEK_CUR: -+ source = 1; -+ break; -+ case SEEK_END: -+ source = 2; -+ break; -+ default: -+ source = origin; -+ break; -+ } -+ -+ /* run callback */ -+ cb = self->seek_cb; -+ if (cb == NULL) -+ goto silent_error; -+ arglist = Py_BuildValue("(i,i)", offset, source); -+ if (arglist == NULL) -+ goto verbose_error; -+ result = PyEval_CallObject(cb, arglist); -+ Py_DECREF(arglist); -+ if (result == NULL) -+ goto verbose_error; -+ -+ /* handle result */ -+ if (result == Py_None) { -+ ret = 0; /* None means success */ -+ } -+ else if (PyInt_Check(result)) { -+ int ret_code = PyInt_AsLong(result); -+ if (ret_code < 0 || ret_code > 2) { -+ PyErr_Format(ErrorObject, "invalid return value for seek callback %d not in (0, 1, 2)", ret_code); -+ goto verbose_error; -+ } -+ ret = ret_code; /* pass the return code from the callback */ -+ } -+ else { -+ PyErr_SetString(ErrorObject, "seek callback must return 0 (CURL_SEEKFUNC_OK), 1 (CURL_SEEKFUNC_FAIL), 2 (CURL_SEEKFUNC_CANTSEEK) or None"); -+ goto verbose_error; -+ } -+ -+silent_error: -+ Py_XDECREF(result); -+ PyEval_ReleaseThread(tmp_state); -+ return ret; -+verbose_error: -+ PyErr_Print(); -+ goto silent_error; -+} -+ -+ -+ -+ - static size_t - read_callback(char *ptr, size_t size, size_t nmemb, void *stream) - { -@@ -1988,7 +2066,8 @@ do_curl_setopt(CurlObject *self, PyObject *args) - const curl_progress_callback pro_cb = progress_callback; - const curl_debug_callback debug_cb = debug_callback; - const curl_ioctl_callback ioctl_cb = ioctl_callback; -- const curl_opensocket_callback opensocket_cb = opensocket_callback; -+ const curl_opensocket_callback opensocket_cb = opensocket_callback; -+ const curl_seek_callback seek_cb = seek_callback; - - switch(option) { - case CURLOPT_WRITEFUNCTION: -@@ -2046,6 +2125,13 @@ do_curl_setopt(CurlObject *self, PyObject *args) - curl_easy_setopt(self->handle, CURLOPT_OPENSOCKETFUNCTION, opensocket_cb); - curl_easy_setopt(self->handle, CURLOPT_OPENSOCKETDATA, self); - break; -+ case CURLOPT_SEEKFUNCTION: -+ Py_INCREF(obj); -+ ZAP(self->seek_cb); -+ self->seek_cb = obj; -+ curl_easy_setopt(self->handle, CURLOPT_SEEKFUNCTION, seek_cb); -+ curl_easy_setopt(self->handle, CURLOPT_SEEKDATA, self); -+ break; - - default: - /* None of the function options were recognized, throw exception */ -@@ -3616,6 +3702,7 @@ initpycurl(void) - insint_c(d, "PREQUOTE", CURLOPT_PREQUOTE); - insint_c(d, "WRITEHEADER", CURLOPT_WRITEHEADER); - insint_c(d, "HEADERFUNCTION", CURLOPT_HEADERFUNCTION); -+ insint_c(d, "SEEKFUNCTION", CURLOPT_SEEKFUNCTION); - insint_c(d, "COOKIEFILE", CURLOPT_COOKIEFILE); - insint_c(d, "SSLVERSION", CURLOPT_SSLVERSION); - insint_c(d, "TIMECONDITION", CURLOPT_TIMECONDITION); --- -1.7.1 - - -From bd8a7fa3279613f41c503f5946d2576d4df70865 Mon Sep 17 00:00:00 2001 -From: Kamil Dudka -Date: Tue, 26 Feb 2013 09:13:24 +0100 -Subject: [PATCH 009/149] pycurl.c: remove a left-over global variable introduced in r150 - -Signed-off-by: Kamil Dudka ---- - src/pycurl.c | 1 - - 1 files changed, 0 insertions(+), 1 deletions(-) - -diff --git a/src/pycurl.c b/src/pycurl.c -index ac448b0..094bc60 100644 ---- a/src/pycurl.c -+++ b/src/pycurl.c -@@ -1529,7 +1529,6 @@ do_curl_reset(CurlObject *self) - } - - /* --------------- unsetopt/setopt/getinfo --------------- */ -- int res; - - static PyObject * - util_curl_unsetopt(CurlObject *self, int option) --- -1.7.1 - - -From de45f3fc04323c619a879e835f8f212c8c887364 Mon Sep 17 00:00:00 2001 -From: Kamil Dudka -Date: Mon, 25 Feb 2013 19:50:18 +0100 -Subject: [PATCH 010/149] test_internals.py: add a test for ref-counting in reset() - -Signed-off-by: Kamil Dudka ---- - tests/test_internals.py | 5 +++++ - 1 files changed, 5 insertions(+), 0 deletions(-) - -diff --git a/tests/test_internals.py b/tests/test_internals.py -index a1a6533..3f5eefd 100644 ---- a/tests/test_internals.py -+++ b/tests/test_internals.py -@@ -245,6 +245,11 @@ if 1 and gc: - if opts.verbose >= 1: - print "Tracked objects:", len(gc.get_objects()) - -+if 1: -+ # Ensure that the refcounting error in "reset" is fixed: -+ for i in xrange(100000): -+ c = Curl() -+ c.reset() - - # /*********************************************************************** - # // done --- -1.7.1 - - -From be80a76e848ea77764da165d517ae0a8aca64604 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Wed, 6 Mar 2013 01:15:00 -0500 -Subject: [PATCH 011/149] Add gitignore - -Signed-off-by: Kamil Dudka ---- - .gitignore | 1 + - 1 files changed, 1 insertions(+), 0 deletions(-) - create mode 100644 .gitignore - -diff --git a/.gitignore b/.gitignore -new file mode 100644 -index 0000000..796b96d ---- /dev/null -+++ b/.gitignore -@@ -0,0 +1 @@ -+/build --- -1.7.1 - - -From c0bee23c728e61874fc2f89d800685c32b331f80 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Fri, 1 Mar 2013 00:11:47 -0500 -Subject: [PATCH 012/149] A much simpler request test, in unittest format - -Signed-off-by: Kamil Dudka ---- - tests/request_test.py | 31 +++++++++++++++++++++++++++++++ - 1 files changed, 31 insertions(+), 0 deletions(-) - create mode 100644 tests/request_test.py - -diff --git a/tests/request_test.py b/tests/request_test.py -new file mode 100644 -index 0000000..0761d6c ---- /dev/null -+++ b/tests/request_test.py -@@ -0,0 +1,31 @@ -+#! /usr/bin/env python -+# -*- coding: iso-8859-1 -*- -+# vi:ts=4:et -+ -+import pycurl -+import unittest -+try: -+ from cStringIO import StringIO -+except ImportError: -+ try: -+ from StringIO import StringIO -+ except ImportError: -+ from io import StringIO -+ -+class RequestTest(unittest.TestCase): -+ def setUp(self): -+ self.curl = pycurl.Curl() -+ -+ def tearDown(self): -+ self.curl.close() -+ -+ def test_perform_get(self): -+ self.curl.setopt(pycurl.URL, 'http://localhost') -+ self.curl.perform() -+ -+ def test_perform_get_with_write_function(self): -+ self.curl.setopt(pycurl.URL, 'http://localhost') -+ sio = StringIO() -+ self.curl.setopt(pycurl.WRITEFUNCTION, sio.write) -+ self.curl.perform() -+ print(sio.getvalue()) --- -1.7.1 - - -From c178c8f0b6ca9a839cd344b4ebe9e561d804d297 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Fri, 1 Mar 2013 13:35:59 -0500 -Subject: [PATCH 013/149] init.py for test package - -Signed-off-by: Kamil Dudka ---- - 0 files changed, 0 insertions(+), 0 deletions(-) - create mode 100644 tests/__init__.py - -diff --git a/tests/__init__.py b/tests/__init__.py -new file mode 100644 -index 0000000..e69de29 --- -1.7.1 - - -From a2a23e3e89e9d4cf7e61bf0259534fb0117ddf09 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Fri, 1 Mar 2013 13:36:08 -0500 -Subject: [PATCH 014/149] wsgi application runner - -Signed-off-by: Kamil Dudka ---- - tests/runwsgi.py | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ - 1 files changed, 82 insertions(+), 0 deletions(-) - create mode 100644 tests/runwsgi.py - -diff --git a/tests/runwsgi.py b/tests/runwsgi.py -new file mode 100644 -index 0000000..d658614 ---- /dev/null -+++ b/tests/runwsgi.py -@@ -0,0 +1,82 @@ -+# Run a WSGI application in a daemon thread -+ -+import bottle -+import threading -+import socket -+import time as _time -+ -+class Server(bottle.WSGIRefServer): -+ def run(self, handler): # pragma: no cover -+ from wsgiref.simple_server import make_server, WSGIRequestHandler -+ if self.quiet: -+ base = self.options.get('handler_class', WSGIRequestHandler) -+ class QuietHandler(base): -+ def log_request(*args, **kw): pass -+ self.options['handler_class'] = QuietHandler -+ self.srv = make_server(self.host, self.port, handler, **self.options) -+ self.srv.serve_forever(poll_interval=0.1) -+ -+def start_bottle_server(app, port, **kwargs): -+ server_thread = ServerThread(app, port, kwargs) -+ server_thread.daemon = True -+ server_thread.start() -+ -+ ok = False -+ for i in range(10): -+ try: -+ conn = socket.create_connection(('127.0.0.1', port), 0.1) -+ except socket.error as e: -+ _time.sleep(0.1) -+ else: -+ conn.close() -+ ok = True -+ break -+ if not ok: -+ import warnings -+ warnings.warn('Server did not start after 1 second') -+ -+ return server_thread.server -+ -+class ServerThread(threading.Thread): -+ def __init__(self, app, port, server_kwargs): -+ threading.Thread.__init__(self) -+ self.app = app -+ self.port = port -+ self.server_kwargs = server_kwargs -+ self.server = Server(host='localhost', port=self.port, **self.server_kwargs) -+ -+ def run(self): -+ bottle.run(self.app, server=self.server, quiet=True) -+ -+def app_runner_setup(*specs): -+ '''Returns setup and teardown methods for running a list of WSGI -+ applications in a daemon thread. -+ -+ Each argument is an (app, port) pair. -+ -+ Return value is a (setup, teardown) function pair. -+ -+ The setup and teardown functions expect to be called with an argument -+ on which server state will be stored. -+ -+ Example usage with nose: -+ -+ >>> setup_module, teardown_module = \ -+ runwsgi.app_runner_setup((app_module.app, 8050)) -+ ''' -+ -+ def setup(self): -+ self.servers = [] -+ for spec in specs: -+ if len(spec) == 2: -+ app, port = spec -+ kwargs = {} -+ else: -+ app, port, kwargs = spec -+ self.servers.append(start_bottle_server(app, port, **kwargs)) -+ -+ def teardown(self): -+ for server in self.servers: -+ server.srv.shutdown() -+ -+ return [setup, teardown] --- -1.7.1 - - -From 72ac92b738b9709e84ebd56fd055286c173e5fee Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Fri, 1 Mar 2013 14:06:42 -0500 -Subject: [PATCH 015/149] Test application to be on the server side - -Signed-off-by: Kamil Dudka ---- - tests/app.py | 7 +++++++ - 1 files changed, 7 insertions(+), 0 deletions(-) - create mode 100644 tests/app.py - -diff --git a/tests/app.py b/tests/app.py -new file mode 100644 -index 0000000..b173fd6 ---- /dev/null -+++ b/tests/app.py -@@ -0,0 +1,7 @@ -+import bottle -+ -+app = bottle.Bottle() -+ -+@app.route('/success') -+def ok(): -+ return 'success' --- -1.7.1 - - -From 83482f1c46d57074fa985def4f0aa62cc0df15d6 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Fri, 1 Mar 2013 14:07:04 -0500 -Subject: [PATCH 016/149] Use the test application in request test - -Signed-off-by: Kamil Dudka ---- - tests/request_test.py | 59 ++++++++++++++++++++++++++++++++++++++++++++++-- - 1 files changed, 56 insertions(+), 3 deletions(-) - -diff --git a/tests/request_test.py b/tests/request_test.py -index 0761d6c..127fbeb 100644 ---- a/tests/request_test.py -+++ b/tests/request_test.py -@@ -2,8 +2,12 @@ - # -*- coding: iso-8859-1 -*- - # vi:ts=4:et - -+import os -+import sys -+import tempfile - import pycurl - import unittest -+import io - try: - from cStringIO import StringIO - except ImportError: -@@ -12,6 +16,11 @@ except ImportError: - except ImportError: - from io import StringIO - -+from . import app -+from . import runwsgi -+ -+setup_module, teardown_module = runwsgi.app_runner_setup((app.app, 8380)) -+ - class RequestTest(unittest.TestCase): - def setUp(self): - self.curl = pycurl.Curl() -@@ -20,12 +29,56 @@ class RequestTest(unittest.TestCase): - self.curl.close() - - def test_perform_get(self): -- self.curl.setopt(pycurl.URL, 'http://localhost') -+ # This test performs a GET request without doing anything else. -+ # Unfortunately, the default curl behavior is to print response -+ # body to standard output, which spams test output. -+ # As a result this test is commented out. Uncomment for debugging. -+ # test_perform_get_with_default_write_function is the test -+ # which exercises default curl write handler. -+ return -+ -+ self.curl.setopt(pycurl.URL, 'http://localhost:8380/success') - self.curl.perform() - - def test_perform_get_with_write_function(self): -- self.curl.setopt(pycurl.URL, 'http://localhost') -+ self.curl.setopt(pycurl.URL, 'http://localhost:8380/success') - sio = StringIO() - self.curl.setopt(pycurl.WRITEFUNCTION, sio.write) - self.curl.perform() -- print(sio.getvalue()) -+ self.assertEqual('success', sio.getvalue()) -+ -+ def test_perform_get_with_default_write_function(self): -+ self.curl.setopt(pycurl.URL, 'http://localhost:8380/success') -+ #with tempfile.NamedTemporaryFile() as f: -+ with open('w', 'w+') as f: -+ # nose output capture plugin replaces sys.stdout with a StringIO -+ # instance. We want to redirect the underlying file descriptor -+ # anyway because underlying C code uses it. -+ # But keep track of whether we replace sys.stdout. -+ perform_dup = False -+ if hasattr(sys.stdout, 'fileno'): -+ try: -+ sys.stdout.fileno() -+ perform_dup = True -+ except io.UnsupportedOperation: -+ # stdout is a StringIO -+ pass -+ if perform_dup: -+ saved_stdout_fd = os.dup(sys.stdout.fileno()) -+ os.dup2(f.fileno(), sys.stdout.fileno()) -+ else: -+ saved_stdout = sys.stdout -+ sys.stdout = f -+ try: -+ self.curl.perform() -+ finally: -+ sys.stdout.flush() -+ if perform_dup: -+ os.fsync(sys.stdout.fileno()) -+ os.dup2(saved_stdout_fd, sys.stdout.fileno()) -+ os.close(saved_stdout_fd) -+ else: -+ sys.stdout = saved_stdout -+ f.seek(0) -+ body = f.read() -+ self.assertEqual('success', body) --- -1.7.1 - - -From 9f92a0e68c031e26012db508c2a9482f8a466f6c Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Sat, 2 Mar 2013 02:23:27 -0500 -Subject: [PATCH 017/149] Put stringio import into util - -This way all tests can simply import stringio from util in one line. - -Signed-off-by: Kamil Dudka ---- - tests/util.py | 8 ++++++++ - 1 files changed, 8 insertions(+), 0 deletions(-) - -diff --git a/tests/util.py b/tests/util.py -index a1a9978..891da44 100644 ---- a/tests/util.py -+++ b/tests/util.py -@@ -4,6 +4,14 @@ - - import os, sys - -+try: -+ from cStringIO import StringIO -+except ImportError: -+ try: -+ from StringIO import StringIO -+ except ImportError: -+ from io import StringIO -+ - # - # prepare sys.path in case we are still in the build directory - # see also: distutils/command/build.py (build_platlib) --- -1.7.1 - - -From 0289b4917a8510e174fe2237c28bdd219be3ef3f Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Sat, 2 Mar 2013 02:24:11 -0500 -Subject: [PATCH 018/149] Debug test, ported from the old debug test - -Signed-off-by: Kamil Dudka ---- - tests/debug_test.py | 46 ++++++++++++++++++++++++++++++++++++++++++++++ - 1 files changed, 46 insertions(+), 0 deletions(-) - create mode 100644 tests/debug_test.py - -diff --git a/tests/debug_test.py b/tests/debug_test.py -new file mode 100644 -index 0000000..dd00719 ---- /dev/null -+++ b/tests/debug_test.py -@@ -0,0 +1,46 @@ -+#! /usr/bin/env python -+# -*- coding: iso-8859-1 -*- -+# vi:ts=4:et -+ -+import pycurl -+import unittest -+ -+from . import app -+from . import runwsgi -+ -+setup_module, teardown_module = runwsgi.app_runner_setup((app.app, 8380)) -+ -+class DebugTest(unittest.TestCase): -+ def setUp(self): -+ self.curl = pycurl.Curl() -+ self.debug_entries = [] -+ -+ def tearDown(self): -+ self.curl.close() -+ -+ def debug_function(self, t, b): -+ self.debug_entries.append((t, b)) -+ -+ def test_perform_get_with_debug_function(self): -+ self.curl.setopt(pycurl.VERBOSE, 1) -+ self.curl.setopt(pycurl.DEBUGFUNCTION, self.debug_function) -+ self.curl.setopt(pycurl.URL, 'http://localhost:8380/success') -+ self.curl.perform() -+ -+ # Some checks with no particular intent -+ self.check(0, 'About to connect') -+ self.check(0, 'Connected to localhost') -+ self.check(0, 'port 8380') -+ # request -+ self.check(2, 'GET /success HTTP/1.1') -+ # response -+ self.check(1, 'HTTP/1.0 200 OK') -+ self.check(1, 'Content-Length: 7') -+ # result -+ self.check(3, 'success') -+ -+ def check(self, wanted_t, wanted_b): -+ for t, b in self.debug_entries: -+ if t == wanted_t and wanted_b in b: -+ return -+ assert False, "%d: %s not found in debug entries" % (wanted_t, wanted_b) --- -1.7.1 - - -From 972cd4d7322aaa68074e44d5c9200a220fbb2b7e Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Sat, 2 Mar 2013 02:43:57 -0500 -Subject: [PATCH 019/149] Write output to a stringio to avoid stdout spam - -Signed-off-by: Kamil Dudka ---- - tests/debug_test.py | 3 +++ - 1 files changed, 3 insertions(+), 0 deletions(-) - -diff --git a/tests/debug_test.py b/tests/debug_test.py -index dd00719..8005239 100644 ---- a/tests/debug_test.py -+++ b/tests/debug_test.py -@@ -7,6 +7,7 @@ import unittest - - from . import app - from . import runwsgi -+from . import util - - setup_module, teardown_module = runwsgi.app_runner_setup((app.app, 8380)) - -@@ -25,6 +26,8 @@ class DebugTest(unittest.TestCase): - self.curl.setopt(pycurl.VERBOSE, 1) - self.curl.setopt(pycurl.DEBUGFUNCTION, self.debug_function) - self.curl.setopt(pycurl.URL, 'http://localhost:8380/success') -+ sio = util.StringIO() -+ self.curl.setopt(pycurl.WRITEFUNCTION, sio.write) - self.curl.perform() - - # Some checks with no particular intent --- -1.7.1 - - -From ece77e7257bea16a9a9d4dd5efd948358974bc4a Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Sat, 2 Mar 2013 02:44:24 -0500 -Subject: [PATCH 020/149] Use stringio from util in request test - -Signed-off-by: Kamil Dudka ---- - tests/request_test.py | 10 ++-------- - 1 files changed, 2 insertions(+), 8 deletions(-) - -diff --git a/tests/request_test.py b/tests/request_test.py -index 127fbeb..afd8566 100644 ---- a/tests/request_test.py -+++ b/tests/request_test.py -@@ -8,16 +8,10 @@ import tempfile - import pycurl - import unittest - import io --try: -- from cStringIO import StringIO --except ImportError: -- try: -- from StringIO import StringIO -- except ImportError: -- from io import StringIO - - from . import app - from . import runwsgi -+from . import util - - setup_module, teardown_module = runwsgi.app_runner_setup((app.app, 8380)) - -@@ -42,7 +36,7 @@ class RequestTest(unittest.TestCase): - - def test_perform_get_with_write_function(self): - self.curl.setopt(pycurl.URL, 'http://localhost:8380/success') -- sio = StringIO() -+ sio = util.StringIO() - self.curl.setopt(pycurl.WRITEFUNCTION, sio.write) - self.curl.perform() - self.assertEqual('success', sio.getvalue()) --- -1.7.1 - - -From aa359d2da90735e06d9c5dcf387e472e4b11f60d Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Sat, 2 Mar 2013 04:06:00 -0500 -Subject: [PATCH 021/149] Add a test that writes response to a file - -Signed-off-by: Kamil Dudka ---- - tests/request_test.py | 9 +++++++++ - 1 files changed, 9 insertions(+), 0 deletions(-) - -diff --git a/tests/request_test.py b/tests/request_test.py -index afd8566..7386a51 100644 ---- a/tests/request_test.py -+++ b/tests/request_test.py -@@ -41,6 +41,15 @@ class RequestTest(unittest.TestCase): - self.curl.perform() - self.assertEqual('success', sio.getvalue()) - -+ def test_write_to_file(self): -+ self.curl.setopt(pycurl.URL, 'http://localhost:8380/success') -+ with tempfile.NamedTemporaryFile() as f: -+ self.curl.setopt(pycurl.WRITEFUNCTION, f.write) -+ self.curl.perform() -+ f.seek(0) -+ body = f.read() -+ self.assertEqual('success', body) -+ - def test_perform_get_with_default_write_function(self): - self.curl.setopt(pycurl.URL, 'http://localhost:8380/success') - #with tempfile.NamedTemporaryFile() as f: --- -1.7.1 - - -From 517c8f2dd4dc519fd733e049abab704e052866b6 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Sun, 3 Mar 2013 00:14:26 -0500 -Subject: [PATCH 022/149] Account for the case of nose running no tests - -Signed-off-by: Kamil Dudka ---- - tests/runwsgi.py | 4 +++- - 1 files changed, 3 insertions(+), 1 deletions(-) - -diff --git a/tests/runwsgi.py b/tests/runwsgi.py -index d658614..7b39358 100644 ---- a/tests/runwsgi.py -+++ b/tests/runwsgi.py -@@ -77,6 +77,8 @@ def app_runner_setup(*specs): - - def teardown(self): - for server in self.servers: -- server.srv.shutdown() -+ # if no tests from module were run, there is no server to shut down -+ if hasattr(server, 'srv'): -+ server.srv.shutdown() - - return [setup, teardown] --- -1.7.1 - - -From cc68fffc8365c4f8e761a586d5e74beb5fd71473 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Sun, 3 Mar 2013 00:14:41 -0500 -Subject: [PATCH 023/149] Split request test - -Signed-off-by: Kamil Dudka ---- - tests/default_write_function_test.py | 70 +++++++++++++++++++++++++++ - tests/request_test.py | 87 ---------------------------------- - tests/write_to_file_test.py | 29 +++++++++++ - tests/write_to_stringio_test.py | 30 ++++++++++++ - 4 files changed, 129 insertions(+), 87 deletions(-) - create mode 100644 tests/default_write_function_test.py - delete mode 100644 tests/request_test.py - create mode 100644 tests/write_to_file_test.py - create mode 100644 tests/write_to_stringio_test.py - -diff --git a/tests/default_write_function_test.py b/tests/default_write_function_test.py -new file mode 100644 -index 0000000..2efc579 ---- /dev/null -+++ b/tests/default_write_function_test.py -@@ -0,0 +1,70 @@ -+#! /usr/bin/env python -+# -*- coding: iso-8859-1 -*- -+# vi:ts=4:et -+ -+import unittest -+import pycurl -+import sys -+import tempfile -+import io -+import os -+ -+from . import app -+from . import runwsgi -+from . import util -+ -+setup_module, teardown_module = runwsgi.app_runner_setup((app.app, 8380)) -+ -+class RequestTest(unittest.TestCase): -+ def setUp(self): -+ self.curl = pycurl.Curl() -+ -+ def tearDown(self): -+ self.curl.close() -+ -+ def test_perform_get(self): -+ # This test performs a GET request without doing anything else. -+ # Unfortunately, the default curl behavior is to print response -+ # body to standard output, which spams test output. -+ # As a result this test is commented out. Uncomment for debugging. -+ # test_perform_get_with_default_write_function is the test -+ # which exercises default curl write handler. -+ -+ self.curl.setopt(pycurl.URL, 'http://localhost:8380/success') -+ self.curl.perform() -+ -+ def test_perform_get_with_default_write_function(self): -+ self.curl.setopt(pycurl.URL, 'http://localhost:8380/success') -+ #with tempfile.NamedTemporaryFile() as f: -+ with open('w', 'w+') as f: -+ # nose output capture plugin replaces sys.stdout with a StringIO -+ # instance. We want to redirect the underlying file descriptor -+ # anyway because underlying C code uses it. -+ # But keep track of whether we replace sys.stdout. -+ perform_dup = False -+ if hasattr(sys.stdout, 'fileno'): -+ try: -+ sys.stdout.fileno() -+ perform_dup = True -+ except io.UnsupportedOperation: -+ # stdout is a StringIO -+ pass -+ if perform_dup: -+ saved_stdout_fd = os.dup(sys.stdout.fileno()) -+ os.dup2(f.fileno(), sys.stdout.fileno()) -+ else: -+ saved_stdout = sys.stdout -+ sys.stdout = f -+ try: -+ self.curl.perform() -+ finally: -+ sys.stdout.flush() -+ if perform_dup: -+ os.fsync(sys.stdout.fileno()) -+ os.dup2(saved_stdout_fd, sys.stdout.fileno()) -+ os.close(saved_stdout_fd) -+ else: -+ sys.stdout = saved_stdout -+ f.seek(0) -+ body = f.read() -+ self.assertEqual('success', body) -diff --git a/tests/request_test.py b/tests/request_test.py -deleted file mode 100644 -index 7386a51..0000000 ---- a/tests/request_test.py -+++ /dev/null -@@ -1,87 +0,0 @@ --#! /usr/bin/env python --# -*- coding: iso-8859-1 -*- --# vi:ts=4:et -- --import os --import sys --import tempfile --import pycurl --import unittest --import io -- --from . import app --from . import runwsgi --from . import util -- --setup_module, teardown_module = runwsgi.app_runner_setup((app.app, 8380)) -- --class RequestTest(unittest.TestCase): -- def setUp(self): -- self.curl = pycurl.Curl() -- -- def tearDown(self): -- self.curl.close() -- -- def test_perform_get(self): -- # This test performs a GET request without doing anything else. -- # Unfortunately, the default curl behavior is to print response -- # body to standard output, which spams test output. -- # As a result this test is commented out. Uncomment for debugging. -- # test_perform_get_with_default_write_function is the test -- # which exercises default curl write handler. -- return -- -- self.curl.setopt(pycurl.URL, 'http://localhost:8380/success') -- self.curl.perform() -- -- def test_perform_get_with_write_function(self): -- self.curl.setopt(pycurl.URL, 'http://localhost:8380/success') -- sio = util.StringIO() -- self.curl.setopt(pycurl.WRITEFUNCTION, sio.write) -- self.curl.perform() -- self.assertEqual('success', sio.getvalue()) -- -- def test_write_to_file(self): -- self.curl.setopt(pycurl.URL, 'http://localhost:8380/success') -- with tempfile.NamedTemporaryFile() as f: -- self.curl.setopt(pycurl.WRITEFUNCTION, f.write) -- self.curl.perform() -- f.seek(0) -- body = f.read() -- self.assertEqual('success', body) -- -- def test_perform_get_with_default_write_function(self): -- self.curl.setopt(pycurl.URL, 'http://localhost:8380/success') -- #with tempfile.NamedTemporaryFile() as f: -- with open('w', 'w+') as f: -- # nose output capture plugin replaces sys.stdout with a StringIO -- # instance. We want to redirect the underlying file descriptor -- # anyway because underlying C code uses it. -- # But keep track of whether we replace sys.stdout. -- perform_dup = False -- if hasattr(sys.stdout, 'fileno'): -- try: -- sys.stdout.fileno() -- perform_dup = True -- except io.UnsupportedOperation: -- # stdout is a StringIO -- pass -- if perform_dup: -- saved_stdout_fd = os.dup(sys.stdout.fileno()) -- os.dup2(f.fileno(), sys.stdout.fileno()) -- else: -- saved_stdout = sys.stdout -- sys.stdout = f -- try: -- self.curl.perform() -- finally: -- sys.stdout.flush() -- if perform_dup: -- os.fsync(sys.stdout.fileno()) -- os.dup2(saved_stdout_fd, sys.stdout.fileno()) -- os.close(saved_stdout_fd) -- else: -- sys.stdout = saved_stdout -- f.seek(0) -- body = f.read() -- self.assertEqual('success', body) -diff --git a/tests/write_to_file_test.py b/tests/write_to_file_test.py -new file mode 100644 -index 0000000..6528f81 ---- /dev/null -+++ b/tests/write_to_file_test.py -@@ -0,0 +1,29 @@ -+#! /usr/bin/env python -+# -*- coding: iso-8859-1 -*- -+# vi:ts=4:et -+ -+import unittest -+import pycurl -+import tempfile -+ -+from . import app -+from . import runwsgi -+from . import util -+ -+setup_module, teardown_module = runwsgi.app_runner_setup((app.app, 8380)) -+ -+class RequestTest(unittest.TestCase): -+ def setUp(self): -+ self.curl = pycurl.Curl() -+ -+ def tearDown(self): -+ self.curl.close() -+ -+ def test_get_to_file(self): -+ self.curl.setopt(pycurl.URL, 'http://localhost:8380/success') -+ with tempfile.NamedTemporaryFile() as f: -+ self.curl.setopt(pycurl.WRITEFUNCTION, f.write) -+ self.curl.perform() -+ f.seek(0) -+ body = f.read() -+ self.assertEqual('success', body) -diff --git a/tests/write_to_stringio_test.py b/tests/write_to_stringio_test.py -new file mode 100644 -index 0000000..fd1c28d ---- /dev/null -+++ b/tests/write_to_stringio_test.py -@@ -0,0 +1,30 @@ -+#! /usr/bin/env python -+# -*- coding: iso-8859-1 -*- -+# vi:ts=4:et -+ -+import os -+import sys -+import tempfile -+import pycurl -+import unittest -+import io -+ -+from . import app -+from . import runwsgi -+from . import util -+ -+setup_module, teardown_module = runwsgi.app_runner_setup((app.app, 8380)) -+ -+class WriteToStringioTest(unittest.TestCase): -+ def setUp(self): -+ self.curl = pycurl.Curl() -+ -+ def tearDown(self): -+ self.curl.close() -+ -+ def test_get(self): -+ self.curl.setopt(pycurl.URL, 'http://localhost:8380/success') -+ sio = util.StringIO() -+ self.curl.setopt(pycurl.WRITEFUNCTION, sio.write) -+ self.curl.perform() -+ self.assertEqual('success', sio.getvalue()) --- -1.7.1 - - -From f7055358a57d0fa9bcef3f0ad38a63ff5422178c Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Sun, 3 Mar 2013 00:22:05 -0500 -Subject: [PATCH 024/149] Need to flush when using default write function - -Signed-off-by: Kamil Dudka ---- - tests/default_write_function_test.py | 5 ++++- - 1 files changed, 4 insertions(+), 1 deletions(-) - -diff --git a/tests/default_write_function_test.py b/tests/default_write_function_test.py -index 2efc579..afbba33 100644 ---- a/tests/default_write_function_test.py -+++ b/tests/default_write_function_test.py -@@ -32,6 +32,9 @@ class RequestTest(unittest.TestCase): - - self.curl.setopt(pycurl.URL, 'http://localhost:8380/success') - self.curl.perform() -+ # If this flush is not done, stdout output bleeds into the next test -+ # that is executed -+ sys.stdout.flush() - - def test_perform_get_with_default_write_function(self): - self.curl.setopt(pycurl.URL, 'http://localhost:8380/success') -@@ -57,8 +60,8 @@ class RequestTest(unittest.TestCase): - sys.stdout = f - try: - self.curl.perform() -- finally: - sys.stdout.flush() -+ finally: - if perform_dup: - os.fsync(sys.stdout.fileno()) - os.dup2(saved_stdout_fd, sys.stdout.fileno()) --- -1.7.1 - - -From bb6edcd11b75949aa4c7b7513ce70f8a16a8a857 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Sun, 3 Mar 2013 00:34:39 -0500 -Subject: [PATCH 025/149] Trying to get the default write function test to work with nose output capture, but no luck so far - -Signed-off-by: Kamil Dudka ---- - tests/default_write_function_test.py | 52 +++++++++++++++++---------------- - 1 files changed, 27 insertions(+), 25 deletions(-) - -diff --git a/tests/default_write_function_test.py b/tests/default_write_function_test.py -index afbba33..853992b 100644 ---- a/tests/default_write_function_test.py -+++ b/tests/default_write_function_test.py -@@ -15,6 +15,8 @@ from . import util - - setup_module, teardown_module = runwsgi.app_runner_setup((app.app, 8380)) - -+STDOUT_FD_NUM = 1 -+ - class RequestTest(unittest.TestCase): - def setUp(self): - self.curl = pycurl.Curl() -@@ -33,41 +35,41 @@ class RequestTest(unittest.TestCase): - self.curl.setopt(pycurl.URL, 'http://localhost:8380/success') - self.curl.perform() - # If this flush is not done, stdout output bleeds into the next test -- # that is executed -+ # that is executed (without nose output capture) - sys.stdout.flush() -+ os.fsync(STDOUT_FD_NUM) - -- def test_perform_get_with_default_write_function(self): -+ # I have a really hard time getting this to work with nose output capture -+ def skip_test_perform_get_with_default_write_function(self): - self.curl.setopt(pycurl.URL, 'http://localhost:8380/success') -- #with tempfile.NamedTemporaryFile() as f: -- with open('w', 'w+') as f: -+ with tempfile.NamedTemporaryFile() as f: -+ #with open('w', 'w+') as f: - # nose output capture plugin replaces sys.stdout with a StringIO - # instance. We want to redirect the underlying file descriptor - # anyway because underlying C code uses it. -- # But keep track of whether we replace sys.stdout. -- perform_dup = False -- if hasattr(sys.stdout, 'fileno'): -- try: -- sys.stdout.fileno() -- perform_dup = True -- except io.UnsupportedOperation: -- # stdout is a StringIO -- pass -- if perform_dup: -- saved_stdout_fd = os.dup(sys.stdout.fileno()) -- os.dup2(f.fileno(), sys.stdout.fileno()) -- else: -- saved_stdout = sys.stdout -- sys.stdout = f -+ # Therefore: -+ # 1. Use file descriptor 1 rather than sys.stdout.fileno() to -+ # reference the standard output file descriptor. -+ # 2. We do not touch sys.stdout. This means anything written to -+ # sys.stdout will be captured by nose, and not make it to our code. -+ # But the output we care about happens at libcurl level, below -+ # nose, therefore this is fine. -+ saved_stdout_fd = os.dup(STDOUT_FD_NUM) -+ os.dup2(f.fileno(), STDOUT_FD_NUM) -+ #os.dup2(1, 100) -+ #os.dup2(2, 1) -+ # We also need to flush the output that libcurl wrote to stdout. -+ # Since sys.stdout might be nose's StringIO instance, open the -+ # stdout file descriptor manually. -+ - try: - self.curl.perform() - sys.stdout.flush() - finally: -- if perform_dup: -- os.fsync(sys.stdout.fileno()) -- os.dup2(saved_stdout_fd, sys.stdout.fileno()) -- os.close(saved_stdout_fd) -- else: -- sys.stdout = saved_stdout -+ os.fsync(STDOUT_FD_NUM) -+ os.dup2(saved_stdout_fd, STDOUT_FD_NUM) -+ os.close(saved_stdout_fd) -+ #os.dup2(100, 1) - f.seek(0) - body = f.read() - self.assertEqual('success', body) --- -1.7.1 - - -From 83a7d49e7e27f499e9d400a1af09e07fcc595c2f Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Sun, 3 Mar 2013 00:35:25 -0500 -Subject: [PATCH 026/149] Fix test class names - -Signed-off-by: Kamil Dudka ---- - tests/default_write_function_test.py | 2 +- - tests/write_to_file_test.py | 2 +- - 2 files changed, 2 insertions(+), 2 deletions(-) - -diff --git a/tests/default_write_function_test.py b/tests/default_write_function_test.py -index 853992b..a0de66e 100644 ---- a/tests/default_write_function_test.py -+++ b/tests/default_write_function_test.py -@@ -17,7 +17,7 @@ setup_module, teardown_module = runwsgi.app_runner_setup((app.app, 8380)) - - STDOUT_FD_NUM = 1 - --class RequestTest(unittest.TestCase): -+class DefaultWriteFunctionTest(unittest.TestCase): - def setUp(self): - self.curl = pycurl.Curl() - -diff --git a/tests/write_to_file_test.py b/tests/write_to_file_test.py -index 6528f81..67c9c63 100644 ---- a/tests/write_to_file_test.py -+++ b/tests/write_to_file_test.py -@@ -12,7 +12,7 @@ from . import util - - setup_module, teardown_module = runwsgi.app_runner_setup((app.app, 8380)) - --class RequestTest(unittest.TestCase): -+class WriteToFileTest(unittest.TestCase): - def setUp(self): - self.curl = pycurl.Curl() - --- -1.7.1 - - -From 44ff45d8644789a098ea68b196b5f5cea5fbfd35 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Sun, 3 Mar 2013 00:36:34 -0500 -Subject: [PATCH 027/149] Delete unused imports - -Signed-off-by: Kamil Dudka ---- - tests/write_to_stringio_test.py | 3 --- - 1 files changed, 0 insertions(+), 3 deletions(-) - -diff --git a/tests/write_to_stringio_test.py b/tests/write_to_stringio_test.py -index fd1c28d..018800d 100644 ---- a/tests/write_to_stringio_test.py -+++ b/tests/write_to_stringio_test.py -@@ -2,9 +2,6 @@ - # -*- coding: iso-8859-1 -*- - # vi:ts=4:et - --import os --import sys --import tempfile - import pycurl - import unittest - import io --- -1.7.1 - - -From f14cdea639d6976f8dd8077be28c4452d4c8d1cf Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Sun, 3 Mar 2013 00:41:35 -0500 -Subject: [PATCH 028/149] Test for header function - -Signed-off-by: Kamil Dudka ---- - tests/header_function_test.py | 50 +++++++++++++++++++++++++++++++++++++++++ - 1 files changed, 50 insertions(+), 0 deletions(-) - create mode 100644 tests/header_function_test.py - -diff --git a/tests/header_function_test.py b/tests/header_function_test.py -new file mode 100644 -index 0000000..bfe7173 ---- /dev/null -+++ b/tests/header_function_test.py -@@ -0,0 +1,50 @@ -+#! /usr/bin/env python -+# -*- coding: iso-8859-1 -*- -+# vi:ts=4:et -+ -+import pycurl -+import unittest -+import io -+import time as _time -+ -+from . import app -+from . import runwsgi -+from . import util -+ -+setup_module, teardown_module = runwsgi.app_runner_setup((app.app, 8380)) -+ -+class HeaderFunctionTest(unittest.TestCase): -+ def setUp(self): -+ self.curl = pycurl.Curl() -+ self.header_lines = [] -+ -+ def tearDown(self): -+ self.curl.close() -+ -+ def header_function(self, line): -+ self.header_lines.append(line) -+ -+ def test_get(self): -+ self.curl.setopt(pycurl.URL, 'http://localhost:8380/success') -+ sio = util.StringIO() -+ self.curl.setopt(pycurl.WRITEFUNCTION, sio.write) -+ self.curl.setopt(pycurl.HEADERFUNCTION, self.header_function) -+ self.curl.perform() -+ self.assertEqual('success', sio.getvalue()) -+ -+ assert len(self.header_lines) > 0 -+ self.assertEqual("HTTP/1.0 200 OK\r\n", self.header_lines[0]) -+ # day of week -+ todays_day = _time.strftime('%a') -+ # Date: Sun, 03 Mar 2013 05:38:12 GMT\r\n -+ self.check('Date: %s' % todays_day) -+ # Server: WSGIServer/0.1 Python/2.7.3\r\n -+ self.check('Server: WSGIServer') -+ self.check('Content-Length: 7') -+ self.check('Content-Type: text/html') -+ -+ def check(self, wanted_text): -+ for line in self.header_lines: -+ if wanted_text in line: -+ return -+ assert False, "%s not found in header lines" % wanted_text --- -1.7.1 - - -From 2491ddd3f028059b4e86338ad64413de75db28af Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Sun, 3 Mar 2013 01:35:21 -0500 -Subject: [PATCH 029/149] FTP test - -Signed-off-by: Kamil Dudka ---- - tests/ftp_test.py | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ - 1 files changed, 48 insertions(+), 0 deletions(-) - create mode 100644 tests/ftp_test.py - -diff --git a/tests/ftp_test.py b/tests/ftp_test.py -new file mode 100644 -index 0000000..d215b6e ---- /dev/null -+++ b/tests/ftp_test.py -@@ -0,0 +1,48 @@ -+#! /usr/bin/env python -+# -*- coding: iso-8859-1 -*- -+# vi:ts=4:et -+ -+import pycurl -+import unittest -+ -+from . import util -+ -+class FtpTest(unittest.TestCase): -+ def setUp(self): -+ self.curl = pycurl.Curl() -+ -+ def tearDown(self): -+ self.curl.close() -+ -+ def test_get_ftp(self): -+ self.curl.setopt(pycurl.URL, 'ftp://localhost:8921') -+ sio = util.StringIO() -+ self.curl.setopt(pycurl.WRITEFUNCTION, sio.write) -+ self.curl.perform() -+ -+ result = sio.getvalue() -+ assert 'README' in result -+ assert 'bin -> usr/bin' in result -+ -+ # XXX this test needs to be fixed -+ def test_quote(self): -+ self.curl.setopt(pycurl.URL, 'ftp://localhost:8921') -+ sio = util.StringIO() -+ self.curl.setopt(pycurl.WRITEFUNCTION, sio.write) -+ self.curl.setopt(pycurl.QUOTE, ['CWD pub']) -+ self.curl.perform() -+ -+ result = sio.getvalue() -+ assert 'README' in result -+ assert 'bin -> usr/bin' in result -+ -+ def test_epsv(self): -+ self.curl.setopt(pycurl.URL, 'ftp://localhost:8921') -+ sio = util.StringIO() -+ self.curl.setopt(pycurl.WRITEFUNCTION, sio.write) -+ self.curl.setopt(pycurl.FTP_USE_EPSV, 1) -+ self.curl.perform() -+ -+ result = sio.getvalue() -+ assert 'README' in result -+ assert 'bin -> usr/bin' in result --- -1.7.1 - - -From ead7f2743a7bd6b497d994ca11dc823a144d50d7 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Sun, 3 Mar 2013 01:44:22 -0500 -Subject: [PATCH 030/149] Getinfo test - -Signed-off-by: Kamil Dudka ---- - tests/getinfo_test.py | 43 +++++++++++++++++++++++++++++++++++++++++++ - 1 files changed, 43 insertions(+), 0 deletions(-) - create mode 100644 tests/getinfo_test.py - -diff --git a/tests/getinfo_test.py b/tests/getinfo_test.py -new file mode 100644 -index 0000000..39dff2f ---- /dev/null -+++ b/tests/getinfo_test.py -@@ -0,0 +1,43 @@ -+#! /usr/bin/env python -+# -*- coding: iso-8859-1 -*- -+# vi:ts=4:et -+ -+import pycurl -+import unittest -+ -+from . import app -+from . import runwsgi -+from . import util -+ -+setup_module, teardown_module = runwsgi.app_runner_setup((app.app, 8380)) -+ -+class GetinfoTest(unittest.TestCase): -+ def setUp(self): -+ self.curl = pycurl.Curl() -+ -+ def tearDown(self): -+ self.curl.close() -+ -+ def test_getinfo(self): -+ self.curl.setopt(pycurl.URL, 'http://localhost:8380/success') -+ sio = util.StringIO() -+ self.curl.setopt(pycurl.WRITEFUNCTION, sio.write) -+ self.curl.perform() -+ self.assertEqual('success', sio.getvalue()) -+ -+ self.assertEqual(200, self.curl.getinfo(pycurl.HTTP_CODE)) -+ assert type(self.curl.getinfo(pycurl.TOTAL_TIME)) is float -+ assert self.curl.getinfo(pycurl.TOTAL_TIME) > 0 -+ assert self.curl.getinfo(pycurl.TOTAL_TIME) < 1 -+ assert type(self.curl.getinfo(pycurl.SPEED_DOWNLOAD)) is float -+ assert self.curl.getinfo(pycurl.SPEED_DOWNLOAD) > 0 -+ self.assertEqual(7, self.curl.getinfo(pycurl.SIZE_DOWNLOAD)) -+ self.assertEqual('http://localhost:8380/success', self.curl.getinfo(pycurl.EFFECTIVE_URL)) -+ self.assertEqual('text/html; charset=utf-8', self.curl.getinfo(pycurl.CONTENT_TYPE).lower()) -+ assert type(self.curl.getinfo(pycurl.NAMELOOKUP_TIME)) is float -+ assert self.curl.getinfo(pycurl.NAMELOOKUP_TIME) > 0 -+ assert self.curl.getinfo(pycurl.NAMELOOKUP_TIME) < 1 -+ self.assertEqual(0, self.curl.getinfo(pycurl.REDIRECT_TIME)) -+ self.assertEqual(0, self.curl.getinfo(pycurl.REDIRECT_COUNT)) -+ # time not requested -+ self.assertEqual(-1, self.curl.getinfo(pycurl.INFO_FILETIME)) --- -1.7.1 - - -From 7c48535bef19bf13a9d8aef377d4465490a588f2 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Sun, 3 Mar 2013 02:05:30 -0500 -Subject: [PATCH 031/149] Ported internals test - -Signed-off-by: Kamil Dudka ---- - tests/internals_test.py | 224 +++++++++++++++++++++++++++++++++++++++++++++++ - 1 files changed, 224 insertions(+), 0 deletions(-) - create mode 100644 tests/internals_test.py - -diff --git a/tests/internals_test.py b/tests/internals_test.py -new file mode 100644 -index 0000000..34d4eb8 ---- /dev/null -+++ b/tests/internals_test.py -@@ -0,0 +1,224 @@ -+#! /usr/bin/env python -+# -*- coding: iso-8859-1 -*- -+# vi:ts=4:et -+ -+import pycurl -+import unittest -+from .util import StringIO -+try: -+ import cPickle -+except ImportError: -+ cPickle = None -+import pickle -+import gc -+import copy -+ -+class InternalsTest(unittest.TestCase): -+ def setUp(self): -+ self.curl = pycurl.Curl() -+ -+ def tearDown(self): -+ self.curl.close() -+ del self.curl -+ -+ # /*********************************************************************** -+ # // test misc -+ # ************************************************************************/ -+ -+ def test_constant_aliasing(self): -+ assert self.curl.URL is pycurl.URL -+ -+ # /*********************************************************************** -+ # // test handles -+ # ************************************************************************/ -+ -+ def test_remove_invalid_handle(self): -+ m = pycurl.CurlMulti() -+ try: -+ m.remove_handle(self.curl) -+ except pycurl.error: -+ pass -+ else: -+ assert False, "No exception when trying to remove a handle that is not in CurlMulti" -+ del m -+ -+ def test_remove_invalid_closed_handle(self): -+ m = pycurl.CurlMulti() -+ c = pycurl.Curl() -+ c.close() -+ m.remove_handle(c) -+ del m, c -+ -+ def test_add_closed_handle(self): -+ m = pycurl.CurlMulti() -+ c = pycurl.Curl() -+ c.close() -+ try: -+ m.add_handle(c) -+ except pycurl.error: -+ pass -+ else: -+ assert 0, "No exception when trying to add a close handle to CurlMulti" -+ m.close() -+ del m, c -+ -+ def test_add_handle_twice(self): -+ m = pycurl.CurlMulti() -+ m.add_handle(self.curl) -+ try: -+ m.add_handle(self.curl) -+ except pycurl.error: -+ pass -+ else: -+ assert 0, "No exception when trying to add the same handle twice" -+ del m -+ -+ def test_add_handle_on_multiple_stacks(self): -+ m1 = pycurl.CurlMulti() -+ m2 = pycurl.CurlMulti() -+ m1.add_handle(self.curl) -+ try: -+ m2.add_handle(self.curl) -+ except pycurl.error: -+ pass -+ else: -+ assert 0, "No exception when trying to add the same handle on multiple stacks" -+ del m1, m2 -+ -+ def test_move_handle(self): -+ m1 = pycurl.CurlMulti() -+ m2 = pycurl.CurlMulti() -+ m1.add_handle(self.curl) -+ m1.remove_handle(self.curl) -+ m2.add_handle(self.curl) -+ del m1, m2 -+ -+ # /*********************************************************************** -+ # // test copying and pickling - copying and pickling of -+ # // instances of Curl and CurlMulti is not allowed -+ # ************************************************************************/ -+ -+ def test_copy_curl(self): -+ try: -+ copy.copy(self.curl) -+ # python 2 raises copy.Error, python 3 raises TypeError -+ except (copy.Error, TypeError): -+ pass -+ else: -+ assert False, "No exception when trying to copy a Curl handle" -+ -+ def test_copy_multi(self): -+ m = pycurl.CurlMulti() -+ try: -+ copy.copy(m) -+ except (copy.Error, TypeError): -+ pass -+ else: -+ assert False, "No exception when trying to copy a CurlMulti handle" -+ -+ def test_pickle_curl(self): -+ fp = StringIO() -+ p = pickle.Pickler(fp, 1) -+ try: -+ p.dump(self.curl) -+ # python 2 raises pickle.PicklingError, python 3 raises TypeError -+ except (pickle.PicklingError, TypeError): -+ pass -+ else: -+ assert 0, "No exception when trying to pickle a Curl handle" -+ del fp, p -+ -+ def test_pickle_multi(self): -+ m = pycurl.CurlMulti() -+ fp = StringIO() -+ p = pickle.Pickler(fp, 1) -+ try: -+ p.dump(m) -+ except (pickle.PicklingError, TypeError): -+ pass -+ else: -+ assert 0, "No exception when trying to pickle a CurlMulti handle" -+ del m, fp, p -+ -+ if cPickle is not None: -+ def test_cpickle_curl(self): -+ fp = StringIO() -+ p = cPickle.Pickler(fp, 1) -+ try: -+ p.dump(self.curl) -+ except cPickle.PicklingError: -+ pass -+ else: -+ assert 0, "No exception when trying to pickle a Curl handle via cPickle" -+ del fp, p -+ -+ def test_cpickle_multi(self): -+ m = pycurl.CurlMulti() -+ fp = StringIO() -+ p = cPickle.Pickler(fp, 1) -+ try: -+ p.dump(m) -+ except cPickle.PicklingError: -+ pass -+ else: -+ assert 0, "No exception when trying to pickle a CurlMulti handle via cPickle" -+ del m, fp, p -+ -+ # /*********************************************************************** -+ # // test refcounts -+ # ************************************************************************/ -+ -+ # basic check of reference counting (use a memory checker like valgrind) -+ def test_reference_counting(self): -+ c = pycurl.Curl() -+ m = pycurl.CurlMulti() -+ m.add_handle(c) -+ del m -+ m = pycurl.CurlMulti() -+ c.close() -+ del m, c -+ -+ def test_cyclic_gc(self): -+ gc.collect() -+ c = pycurl.Curl() -+ c.m = pycurl.CurlMulti() -+ c.m.add_handle(c) -+ # create some nasty cyclic references -+ c.c = c -+ c.c.c1 = c -+ c.c.c2 = c -+ c.c.c3 = c.c -+ c.c.c4 = c.m -+ c.m.c = c -+ c.m.m = c.m -+ c.m.c = c -+ # delete -+ gc.collect() -+ flags = gc.DEBUG_COLLECTABLE | gc.DEBUG_UNCOLLECTABLE -+ # python 3 has no DEBUG_OBJECTS -+ #if hasattr(gc, 'DEBUG_OBJECTS'): -+ #flags |= gc.DEBUG_OBJECTS -+ #if opts.verbose >= 1: -+ #flags = flags | gc.DEBUG_STATS -+ gc.set_debug(flags) -+ gc.collect() -+ ##print gc.get_referrers(c) -+ ##print gc.get_objects() -+ #if opts.verbose >= 1: -+ #print("Tracked objects:", len(gc.get_objects())) -+ # The `del' below should delete these 4 objects: -+ # Curl + internal dict, CurlMulti + internal dict -+ del c -+ gc.collect() -+ #if opts.verbose >= 1: -+ #print("Tracked objects:", len(gc.get_objects())) -+ -+ def test_refcounting_bug_in_reset(self): -+ try: -+ range_generator = xrange -+ except NameError: -+ range_generator = range -+ # Ensure that the refcounting error in "reset" is fixed: -+ for i in range_generator(100000): -+ c = pycurl.Curl() -+ c.reset() --- -1.7.1 - - -From c7b8f39437e953deb8af1b17f366a6242db28d5d Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Sun, 3 Mar 2013 02:16:10 -0500 -Subject: [PATCH 032/149] Make cyclic gc test actually check that cycles are collected - -Signed-off-by: Kamil Dudka ---- - tests/internals_test.py | 9 +++++++++ - 1 files changed, 9 insertions(+), 0 deletions(-) - -diff --git a/tests/internals_test.py b/tests/internals_test.py -index 34d4eb8..fb451df 100644 ---- a/tests/internals_test.py -+++ b/tests/internals_test.py -@@ -12,6 +12,7 @@ except ImportError: - import pickle - import gc - import copy -+import re - - class InternalsTest(unittest.TestCase): - def setUp(self): -@@ -179,6 +180,7 @@ class InternalsTest(unittest.TestCase): - del m, c - - def test_cyclic_gc(self): -+ regexp = re.compile(r'at (0x\d+)') - gc.collect() - c = pycurl.Curl() - c.m = pycurl.CurlMulti() -@@ -206,10 +208,17 @@ class InternalsTest(unittest.TestCase): - ##print gc.get_objects() - #if opts.verbose >= 1: - #print("Tracked objects:", len(gc.get_objects())) -+ match = regexp.search(repr(c)) -+ assert match is not None -+ address = match.group(1) - # The `del' below should delete these 4 objects: - # Curl + internal dict, CurlMulti + internal dict - del c - gc.collect() -+ objects = gc.get_objects() -+ search = 'at %s' % address -+ for object in objects: -+ assert search not in repr(object) - #if opts.verbose >= 1: - #print("Tracked objects:", len(gc.get_objects())) - --- -1.7.1 - - -From 6fa36d3a070e784fc679427a6c56725ee30375e8 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Sun, 3 Mar 2013 03:00:20 -0500 -Subject: [PATCH 033/149] Ported memleak test - -Signed-off-by: Kamil Dudka ---- - tests/memleak_test.py | 59 +++++++++++++++++++++++++++++++++++++++++++++++++ - 1 files changed, 59 insertions(+), 0 deletions(-) - create mode 100644 tests/memleak_test.py - -diff --git a/tests/memleak_test.py b/tests/memleak_test.py -new file mode 100644 -index 0000000..6e9f76c ---- /dev/null -+++ b/tests/memleak_test.py -@@ -0,0 +1,59 @@ -+#! /usr/bin/env python -+# -*- coding: iso-8859-1 -*- -+# vi:ts=4:et -+ -+import pycurl -+import unittest -+import gc -+import re -+ -+class MemleakTest(unittest.TestCase): -+ def test_collection(self): -+ regexp = re.compile(r'at (0x\d+)') -+ -+ gc.collect() -+ flags = gc.DEBUG_COLLECTABLE | gc.DEBUG_UNCOLLECTABLE -+ # python 3 has no DEBUG_OBJECTS -+ #if hasattr(gc, 'DEBUG_OBJECTS'): -+ #flags |= gc.DEBUG_OBJECTS -+ #if 1: -+ #flags = flags | gc.DEBUG_STATS -+ #gc.set_debug(flags) -+ gc.collect() -+ -+ #print("Tracked objects:", len(gc.get_objects())) -+ -+ multi = pycurl.CurlMulti() -+ t = [] -+ searches = [] -+ for a in range(100): -+ curl = pycurl.Curl() -+ multi.add_handle(curl) -+ t.append(curl) -+ -+ match = regexp.search(repr(curl)) -+ assert match is not None -+ searches.append(match.group(1)) -+ match = regexp.search(repr(multi)) -+ assert match -+ searches.append(match.group(1)) -+ -+ #print("Tracked objects:", len(gc.get_objects())) -+ -+ for curl in t: -+ curl.close() -+ multi.remove_handle(curl) -+ -+ #print("Tracked objects:", len(gc.get_objects())) -+ -+ del curl -+ del t -+ del multi -+ -+ #print("Tracked objects:", len(gc.get_objects())) -+ gc.collect() -+ #print("Tracked objects:", len(gc.get_objects())) -+ -+ objects = gc.get_objects() -+ for search in searches: -+ assert 'at %s' % search not in repr(object) --- -1.7.1 - - -From cef807dfde5e1ca931947ec37d0171227899e72f Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Sun, 3 Mar 2013 03:06:26 -0500 -Subject: [PATCH 034/149] Ported the first multi test - -Signed-off-by: Kamil Dudka ---- - tests/multi_test.py | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++ - 1 files changed, 56 insertions(+), 0 deletions(-) - create mode 100644 tests/multi_test.py - -diff --git a/tests/multi_test.py b/tests/multi_test.py -new file mode 100644 -index 0000000..4c0bdaf ---- /dev/null -+++ b/tests/multi_test.py -@@ -0,0 +1,56 @@ -+#! /usr/bin/env python -+# -*- coding: iso-8859-1 -*- -+# vi:ts=4:et -+ -+import pycurl -+import unittest -+ -+from . import app -+from . import runwsgi -+from . import util -+ -+setup_module_1, teardown_module_1 = runwsgi.app_runner_setup((app.app, 8380)) -+setup_module_2, teardown_module_2 = runwsgi.app_runner_setup((app.app, 8381)) -+ -+def setup_module(mod): -+ setup_module_1(mod) -+ setup_module_2(mod) -+ -+def teardown_module(mod): -+ teardown_module_2(mod) -+ teardown_module_1(mod) -+ -+class MultiTest(unittest.TestCase): -+ def test_multi(self): -+ io1 = util.StringIO() -+ io2 = util.StringIO() -+ m = pycurl.CurlMulti() -+ m.handles = [] -+ c1 = pycurl.Curl() -+ c2 = pycurl.Curl() -+ c1.setopt(c1.URL, 'http://localhost:8380/success') -+ c1.setopt(c1.WRITEFUNCTION, io1.write) -+ c2.setopt(c2.URL, 'http://localhost:8381/success') -+ c2.setopt(c1.WRITEFUNCTION, io2.write) -+ m.add_handle(c1) -+ m.add_handle(c2) -+ m.handles.append(c1) -+ m.handles.append(c2) -+ -+ num_handles = len(m.handles) -+ while num_handles: -+ while 1: -+ ret, num_handles = m.perform() -+ if ret != pycurl.E_CALL_MULTI_PERFORM: -+ break -+ m.select(1.0) -+ -+ m.remove_handle(c2) -+ m.remove_handle(c1) -+ del m.handles -+ m.close() -+ c1.close() -+ c2.close() -+ -+ self.assertEqual('success', io1.getvalue()) -+ self.assertEqual('success', io2.getvalue()) --- -1.7.1 - - -From 5a9488e0e5f75e70faf37b419819ce18878f29ed Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Sun, 3 Mar 2013 12:55:42 -0500 -Subject: [PATCH 035/149] Port multi2 test - -Signed-off-by: Kamil Dudka ---- - tests/app.py | 8 +++++++ - tests/multi_test.py | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++ - 2 files changed, 62 insertions(+), 0 deletions(-) - -diff --git a/tests/app.py b/tests/app.py -index b173fd6..3b9303b 100644 ---- a/tests/app.py -+++ b/tests/app.py -@@ -5,3 +5,11 @@ app = bottle.Bottle() - @app.route('/success') - def ok(): - return 'success' -+ -+@app.route('/status/403') -+def forbidden(): -+ bottle.abort(403, 'forbidden') -+ -+@app.route('/status/404') -+def not_found(): -+ bottle.abort(404, 'not found') -diff --git a/tests/multi_test.py b/tests/multi_test.py -index 4c0bdaf..fd96e51 100644 ---- a/tests/multi_test.py -+++ b/tests/multi_test.py -@@ -11,12 +11,15 @@ from . import util - - setup_module_1, teardown_module_1 = runwsgi.app_runner_setup((app.app, 8380)) - setup_module_2, teardown_module_2 = runwsgi.app_runner_setup((app.app, 8381)) -+setup_module_3, teardown_module_3 = runwsgi.app_runner_setup((app.app, 8382)) - - def setup_module(mod): - setup_module_1(mod) - setup_module_2(mod) -+ setup_module_3(mod) - - def teardown_module(mod): -+ teardown_module_3(mod) - teardown_module_2(mod) - teardown_module_1(mod) - -@@ -54,3 +57,54 @@ class MultiTest(unittest.TestCase): - - self.assertEqual('success', io1.getvalue()) - self.assertEqual('success', io2.getvalue()) -+ -+ def test_multi_status_codes(self): -+ # init -+ m = pycurl.CurlMulti() -+ m.handles = [] -+ urls = [ -+ 'http://localhost:8380/success', -+ 'http://localhost:8381/status/403', -+ 'http://localhost:8382/status/404', -+ ] -+ for url in urls: -+ c = pycurl.Curl() -+ # save info in standard Python attributes -+ c.url = url.rstrip() -+ c.body = util.StringIO() -+ c.http_code = -1 -+ m.handles.append(c) -+ # pycurl API calls -+ c.setopt(c.URL, c.url) -+ c.setopt(c.WRITEFUNCTION, c.body.write) -+ m.add_handle(c) -+ -+ # get data -+ num_handles = len(m.handles) -+ while num_handles: -+ while 1: -+ ret, num_handles = m.perform() -+ if ret != pycurl.E_CALL_MULTI_PERFORM: -+ break -+ # currently no more I/O is pending, could do something in the meantime -+ # (display a progress bar, etc.) -+ m.select(0.1) -+ -+ # close handles -+ for c in m.handles: -+ # save info in standard Python attributes -+ c.http_code = c.getinfo(c.HTTP_CODE) -+ # pycurl API calls -+ m.remove_handle(c) -+ c.close() -+ m.close() -+ -+ # check result -+ self.assertEqual('success', m.handles[0].body.getvalue()) -+ self.assertEqual(200, m.handles[0].http_code) -+ # bottle generated response body -+ assert 'Error 403: Forbidden' in m.handles[1].body.getvalue() -+ self.assertEqual(403, m.handles[1].http_code) -+ # bottle generated response body -+ assert 'Error 404: Not Found' in m.handles[2].body.getvalue() -+ self.assertEqual(404, m.handles[2].http_code) --- -1.7.1 - - -From ddeba20f213aea07a2491df171b899b2453de70b Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Sun, 3 Mar 2013 14:05:49 -0500 -Subject: [PATCH 036/149] Porting multi3 test - -Signed-off-by: Kamil Dudka ---- - tests/multi_test.py | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++ - 1 files changed, 71 insertions(+), 0 deletions(-) - -diff --git a/tests/multi_test.py b/tests/multi_test.py -index fd96e51..1c13f70 100644 ---- a/tests/multi_test.py -+++ b/tests/multi_test.py -@@ -108,3 +108,74 @@ class MultiTest(unittest.TestCase): - # bottle generated response body - assert 'Error 404: Not Found' in m.handles[2].body.getvalue() - self.assertEqual(404, m.handles[2].http_code) -+ -+ def test_adding_closed_handle(self): -+ # init -+ m = pycurl.CurlMulti() -+ m.handles = [] -+ urls = [ -+ 'http://localhost:8380/success', -+ 'http://localhost:8381/status/403', -+ 'http://localhost:8382/status/404', -+ ] -+ for url in urls: -+ c = pycurl.Curl() -+ # save info in standard Python attributes -+ c.url = url -+ c.body = util.StringIO() -+ c.http_code = -1 -+ c.debug = 0 -+ m.handles.append(c) -+ # pycurl API calls -+ c.setopt(c.URL, c.url) -+ c.setopt(c.WRITEFUNCTION, c.body.write) -+ m.add_handle(c) -+ -+ # debug - close a handle -+ c = m.handles[2] -+ c.debug = 1 -+ c.close() -+ -+ # get data -+ num_handles = len(m.handles) -+ while num_handles: -+ while 1: -+ ret, num_handles = m.perform() -+ if ret != pycurl.E_CALL_MULTI_PERFORM: -+ break -+ # currently no more I/O is pending, could do something in the meantime -+ # (display a progress bar, etc.) -+ m.select(0.1) -+ -+ # close handles -+ for c in m.handles: -+ # save info in standard Python attributes -+ try: -+ c.http_code = c.getinfo(c.HTTP_CODE) -+ except pycurl.error: -+ # handle already closed - see debug above -+ assert c.debug -+ c.http_code = -1 -+ # pycurl API calls -+ if 0: -+ m.remove_handle(c) -+ c.close() -+ elif 0: -+ # in the C API this is the wrong calling order, but pycurl -+ # handles this automatically -+ c.close() -+ m.remove_handle(c) -+ else: -+ # actually, remove_handle is called automatically on close -+ c.close() -+ m.close() -+ -+ # check result -+ self.assertEqual('success', m.handles[0].body.getvalue()) -+ self.assertEqual(200, m.handles[0].http_code) -+ # bottle generated response body -+ assert 'Error 403: Forbidden' in m.handles[1].body.getvalue() -+ self.assertEqual(403, m.handles[1].http_code) -+ # bottle generated response body -+ self.assertEqual('', m.handles[2].body.getvalue()) -+ self.assertEqual(-1, m.handles[2].http_code) --- -1.7.1 - - -From bd01383371db110534ea39c4ce35563d63a21f73 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Sun, 3 Mar 2013 14:15:13 -0500 -Subject: [PATCH 037/149] Exercise all 3 possibilites in ported multi3 test - -Signed-off-by: Kamil Dudka ---- - tests/multi_test.py | 37 +++++++++++++++++++++++++------------ - 1 files changed, 25 insertions(+), 12 deletions(-) - -diff --git a/tests/multi_test.py b/tests/multi_test.py -index 1c13f70..e1fec05 100644 ---- a/tests/multi_test.py -+++ b/tests/multi_test.py -@@ -109,7 +109,7 @@ class MultiTest(unittest.TestCase): - assert 'Error 404: Not Found' in m.handles[2].body.getvalue() - self.assertEqual(404, m.handles[2].http_code) - -- def test_adding_closed_handle(self): -+ def check_adding_closed_handle(self, close_fn): - # init - m = pycurl.CurlMulti() - m.handles = [] -@@ -157,17 +157,7 @@ class MultiTest(unittest.TestCase): - assert c.debug - c.http_code = -1 - # pycurl API calls -- if 0: -- m.remove_handle(c) -- c.close() -- elif 0: -- # in the C API this is the wrong calling order, but pycurl -- # handles this automatically -- c.close() -- m.remove_handle(c) -- else: -- # actually, remove_handle is called automatically on close -- c.close() -+ close_fn(m, c) - m.close() - - # check result -@@ -179,3 +169,26 @@ class MultiTest(unittest.TestCase): - # bottle generated response body - self.assertEqual('', m.handles[2].body.getvalue()) - self.assertEqual(-1, m.handles[2].http_code) -+ -+ def _remove_then_close(self, m, c): -+ m.remove_handle(c) -+ c.close() -+ -+ def _close_then_remove(self, m, c): -+ # in the C API this is the wrong calling order, but pycurl -+ # handles this automatically -+ c.close() -+ m.remove_handle(c) -+ -+ def _close_without_removing(self, m, c): -+ # actually, remove_handle is called automatically on close -+ c.close -+ -+ def test_adding_closed_handle_remove_then_close(self): -+ self.check_adding_closed_handle(self._remove_then_close) -+ -+ def test_adding_closed_handle_close_then_remove(self): -+ self.check_adding_closed_handle(self._close_then_remove) -+ -+ def test_adding_closed_handle_close_without_removing(self): -+ self.check_adding_closed_handle(self._close_without_removing) --- -1.7.1 - - -From 587c4b3403bf16ce5ab8a72e738ac26dab4a31c8 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Sun, 3 Mar 2013 14:21:33 -0500 -Subject: [PATCH 038/149] Port mulit4 test - -Signed-off-by: Kamil Dudka ---- - tests/multi_test.py | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ - 1 files changed, 50 insertions(+), 0 deletions(-) - -diff --git a/tests/multi_test.py b/tests/multi_test.py -index e1fec05..a508809 100644 ---- a/tests/multi_test.py -+++ b/tests/multi_test.py -@@ -4,6 +4,7 @@ - - import pycurl - import unittest -+import select - - from . import app - from . import runwsgi -@@ -58,6 +59,55 @@ class MultiTest(unittest.TestCase): - self.assertEqual('success', io1.getvalue()) - self.assertEqual('success', io2.getvalue()) - -+ def test_multi_select_fdset(self): -+ c1 = pycurl.Curl() -+ c2 = pycurl.Curl() -+ c3 = pycurl.Curl() -+ c1.setopt(c1.URL, "http://localhost:8380/success") -+ c2.setopt(c2.URL, "http://localhost:8381/success") -+ c3.setopt(c3.URL, "http://localhost:8382/success") -+ c1.body = util.StringIO() -+ c2.body = util.StringIO() -+ c3.body = util.StringIO() -+ c1.setopt(c1.WRITEFUNCTION, c1.body.write) -+ c2.setopt(c2.WRITEFUNCTION, c2.body.write) -+ c3.setopt(c3.WRITEFUNCTION, c3.body.write) -+ -+ m = pycurl.CurlMulti() -+ m.add_handle(c1) -+ m.add_handle(c2) -+ m.add_handle(c3) -+ -+ # Number of seconds to wait for a timeout to happen -+ SELECT_TIMEOUT = 0.1 -+ -+ # Stir the state machine into action -+ while 1: -+ ret, num_handles = m.perform() -+ if ret != pycurl.E_CALL_MULTI_PERFORM: -+ break -+ -+ # Keep going until all the connections have terminated -+ while num_handles: -+ select.select(*m.fdset() + (SELECT_TIMEOUT,)) -+ while 1: -+ ret, num_handles = m.perform() -+ if ret != pycurl.E_CALL_MULTI_PERFORM: -+ break -+ -+ # Cleanup -+ m.remove_handle(c3) -+ m.remove_handle(c2) -+ m.remove_handle(c1) -+ m.close() -+ c1.close() -+ c2.close() -+ c3.close() -+ -+ self.assertEqual('success', c1.body.getvalue()) -+ self.assertEqual('success', c2.body.getvalue()) -+ self.assertEqual('success', c3.body.getvalue()) -+ - def test_multi_status_codes(self): - # init - m = pycurl.CurlMulti() --- -1.7.1 - - -From cd0e96615c24173e38177395931899aaa566c270 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Tue, 5 Mar 2013 00:24:44 -0500 -Subject: [PATCH 039/149] Port multit6 test - -Signed-off-by: Kamil Dudka ---- - tests/multi_test.py | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++ - 1 files changed, 55 insertions(+), 0 deletions(-) - -diff --git a/tests/multi_test.py b/tests/multi_test.py -index a508809..e83ce48 100644 ---- a/tests/multi_test.py -+++ b/tests/multi_test.py -@@ -242,3 +242,58 @@ class MultiTest(unittest.TestCase): - - def test_adding_closed_handle_close_without_removing(self): - self.check_adding_closed_handle(self._close_without_removing) -+ -+ def test_multi_info_read(self): -+ c1 = pycurl.Curl() -+ c2 = pycurl.Curl() -+ c3 = pycurl.Curl() -+ c1.setopt(c1.URL, "http://localhost:8380/success") -+ c2.setopt(c2.URL, "http://localhost:8381/success") -+ c3.setopt(c3.URL, "http://localhost:8382/success") -+ c1.body = util.StringIO() -+ c2.body = util.StringIO() -+ c3.body = util.StringIO() -+ c1.setopt(c1.WRITEFUNCTION, c1.body.write) -+ c2.setopt(c2.WRITEFUNCTION, c2.body.write) -+ c3.setopt(c3.WRITEFUNCTION, c3.body.write) -+ -+ m = pycurl.CurlMulti() -+ m.add_handle(c1) -+ m.add_handle(c2) -+ m.add_handle(c3) -+ -+ # Number of seconds to wait for a timeout to happen -+ SELECT_TIMEOUT = 1.0 -+ -+ # Stir the state machine into action -+ while 1: -+ ret, num_handles = m.perform() -+ if ret != pycurl.E_CALL_MULTI_PERFORM: -+ break -+ -+ # Keep going until all the connections have terminated -+ while num_handles: -+ # The select method uses fdset internally to determine which file descriptors -+ # to check. -+ m.select(SELECT_TIMEOUT) -+ while 1: -+ ret, num_handles = m.perform() -+ # Print the message, if any -+ while True: -+ info = m.info_read() -+ print info -+ if ret != pycurl.E_CALL_MULTI_PERFORM: -+ break -+ -+ # Cleanup -+ m.remove_handle(c3) -+ m.remove_handle(c2) -+ m.remove_handle(c1) -+ m.close() -+ c1.close() -+ c2.close() -+ c3.close() -+ -+ self.assertEqual('success', c1.body.getvalue()) -+ self.assertEqual('success', c2.body.getvalue()) -+ self.assertEqual('success', c3.body.getvalue()) --- -1.7.1 - - -From d59cc0dffc1f70545317c03307211559afe46b6d Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Tue, 5 Mar 2013 00:38:59 -0500 -Subject: [PATCH 040/149] Improve info_read test to test info_read - -Signed-off-by: Kamil Dudka ---- - tests/multi_test.py | 19 +++++++++++++++---- - 1 files changed, 15 insertions(+), 4 deletions(-) - -diff --git a/tests/multi_test.py b/tests/multi_test.py -index e83ce48..10af44c 100644 ---- a/tests/multi_test.py -+++ b/tests/multi_test.py -@@ -271,6 +271,7 @@ class MultiTest(unittest.TestCase): - if ret != pycurl.E_CALL_MULTI_PERFORM: - break - -+ infos = [] - # Keep going until all the connections have terminated - while num_handles: - # The select method uses fdset internally to determine which file descriptors -@@ -278,13 +279,23 @@ class MultiTest(unittest.TestCase): - m.select(SELECT_TIMEOUT) - while 1: - ret, num_handles = m.perform() -- # Print the message, if any -- while True: -- info = m.info_read() -- print info -+ info = m.info_read() -+ infos.append(info) - if ret != pycurl.E_CALL_MULTI_PERFORM: - break - -+ all_handles = [] -+ for info in infos: -+ handles = info[1] -+ # last info is an empty array -+ if handles: -+ all_handles.extend(handles) -+ -+ self.assertEqual(3, len(all_handles)) -+ assert c1 in all_handles -+ assert c2 in all_handles -+ assert c3 in all_handles -+ - # Cleanup - m.remove_handle(c3) - m.remove_handle(c2) --- -1.7.1 - - -From 9b239643fd425e600b467f91ce7ea0560dd88b79 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Tue, 5 Mar 2013 00:40:25 -0500 -Subject: [PATCH 041/149] Port multi5 test - -Signed-off-by: Kamil Dudka ---- - tests/multi_test.py | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ - 1 files changed, 51 insertions(+), 0 deletions(-) - -diff --git a/tests/multi_test.py b/tests/multi_test.py -index 10af44c..d9c6174 100644 ---- a/tests/multi_test.py -+++ b/tests/multi_test.py -@@ -243,6 +243,57 @@ class MultiTest(unittest.TestCase): - def test_adding_closed_handle_close_without_removing(self): - self.check_adding_closed_handle(self._close_without_removing) - -+ def test_multi_select(self): -+ c1 = pycurl.Curl() -+ c2 = pycurl.Curl() -+ c3 = pycurl.Curl() -+ c1.setopt(c1.URL, "http://localhost:8380/success") -+ c2.setopt(c2.URL, "http://localhost:8381/success") -+ c3.setopt(c3.URL, "http://localhost:8382/success") -+ c1.body = util.StringIO() -+ c2.body = util.StringIO() -+ c3.body = util.StringIO() -+ c1.setopt(c1.WRITEFUNCTION, c1.body.write) -+ c2.setopt(c2.WRITEFUNCTION, c2.body.write) -+ c3.setopt(c3.WRITEFUNCTION, c3.body.write) -+ -+ m = pycurl.CurlMulti() -+ m.add_handle(c1) -+ m.add_handle(c2) -+ m.add_handle(c3) -+ -+ # Number of seconds to wait for a timeout to happen -+ SELECT_TIMEOUT = 1.0 -+ -+ # Stir the state machine into action -+ while 1: -+ ret, num_handles = m.perform() -+ if ret != pycurl.E_CALL_MULTI_PERFORM: -+ break -+ -+ # Keep going until all the connections have terminated -+ while num_handles: -+ # The select method uses fdset internally to determine which file descriptors -+ # to check. -+ m.select(SELECT_TIMEOUT) -+ while 1: -+ ret, num_handles = m.perform() -+ if ret != pycurl.E_CALL_MULTI_PERFORM: -+ break -+ -+ # Cleanup -+ m.remove_handle(c3) -+ m.remove_handle(c2) -+ m.remove_handle(c1) -+ m.close() -+ c1.close() -+ c2.close() -+ c3.close() -+ -+ self.assertEqual('success', c1.body.getvalue()) -+ self.assertEqual('success', c2.body.getvalue()) -+ self.assertEqual('success', c3.body.getvalue()) -+ - def test_multi_info_read(self): - c1 = pycurl.Curl() - c2 = pycurl.Curl() --- -1.7.1 - - -From c16f8df53e9f97d2ef4c0acb19e4abcda5825260 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Tue, 5 Mar 2013 22:05:34 -0500 -Subject: [PATCH 042/149] Ported test_multi_socket.py - -Signed-off-by: Kamil Dudka ---- - tests/multi_socket_test.py | 110 ++++++++++++++++++++++++++++++++++++++++++++ - 1 files changed, 110 insertions(+), 0 deletions(-) - create mode 100644 tests/multi_socket_test.py - -diff --git a/tests/multi_socket_test.py b/tests/multi_socket_test.py -new file mode 100644 -index 0000000..2586569 ---- /dev/null -+++ b/tests/multi_socket_test.py -@@ -0,0 +1,110 @@ -+#! /usr/bin/env python -+# -*- coding: iso-8859-1 -*- -+# vi:ts=4:et -+ -+import pycurl -+import unittest -+import select -+ -+from . import app -+from . import runwsgi -+from . import util -+ -+setup_module_1, teardown_module_1 = runwsgi.app_runner_setup((app.app, 8380)) -+setup_module_2, teardown_module_2 = runwsgi.app_runner_setup((app.app, 8381)) -+setup_module_3, teardown_module_3 = runwsgi.app_runner_setup((app.app, 8382)) -+ -+def setup_module(mod): -+ setup_module_1(mod) -+ setup_module_2(mod) -+ setup_module_3(mod) -+ -+def teardown_module(mod): -+ teardown_module_3(mod) -+ teardown_module_2(mod) -+ teardown_module_1(mod) -+ -+class MultiSocketTest(unittest.TestCase): -+ def test_multi_socket(self): -+ urls = [ -+ 'http://localhost:8380/success', -+ 'http://localhost:8381/success', -+ 'http://localhost:8382/success', -+ ] -+ -+ timers = [] -+ -+ # timer callback -+ def timer(msecs): -+ #print('Timer callback msecs:', msecs) -+ timers.append(msecs) -+ -+ socket_events = [] -+ -+ # socket callback -+ def socket(event, socket, multi, data): -+ #print(event, socket, multi, data) -+ # multi.assign(socket, timer) -+ socket_events.append((event, multi)) -+ -+ # init -+ m = pycurl.CurlMulti() -+ m.setopt(pycurl.M_PIPELINING, 1) -+ m.setopt(pycurl.M_TIMERFUNCTION, timer) -+ m.setopt(pycurl.M_SOCKETFUNCTION, socket) -+ m.handles = [] -+ for url in urls: -+ c = pycurl.Curl() -+ # save info in standard Python attributes -+ c.url = url -+ c.body = util.StringIO() -+ c.http_code = -1 -+ m.handles.append(c) -+ # pycurl API calls -+ c.setopt(c.URL, c.url) -+ c.setopt(c.WRITEFUNCTION, c.body.write) -+ m.add_handle(c) -+ -+ # get data -+ num_handles = len(m.handles) -+ while num_handles: -+ while 1: -+ ret, num_handles = m.socket_all() -+ if ret != pycurl.E_CALL_MULTI_PERFORM: -+ break -+ # currently no more I/O is pending, could do something in the meantime -+ # (display a progress bar, etc.) -+ m.select(0.1) -+ -+ for c in m.handles: -+ # save info in standard Python attributes -+ c.http_code = c.getinfo(c.HTTP_CODE) -+ -+ # at least in and remove events per socket -+ assert len(socket_events) >= 6 -+ -+ # print result -+ for c in m.handles: -+ self.assertEqual('success', c.body.getvalue()) -+ self.assertEqual(200, c.http_code) -+ -+ # multi, not curl handle -+ self.check(pycurl.POLL_IN, m, socket_events) -+ self.check(pycurl.POLL_REMOVE, m, socket_events) -+ -+ assert len(timers) > 0 -+ assert timers[0] > 0 -+ self.assertEqual(-1, timers[-1]) -+ -+ # close handles -+ for c in m.handles: -+ # pycurl API calls -+ m.remove_handle(c) -+ c.close() -+ m.close() -+ -+ def check(self, event, multi, socket_events): -+ for event_, multi_ in socket_events: -+ if event == event_ and multi == multi_: -+ return -+ assert False, '%d %s not found in socket events' % (event, multi) --- -1.7.1 - - -From 9a0ae278c27b16fa7dc745c2add39101a13fc9ec Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Tue, 5 Mar 2013 22:29:45 -0500 -Subject: [PATCH 043/149] Ported test_multi_socket_select.py - -Signed-off-by: Kamil Dudka ---- - tests/multi_socket_select_test.py | 130 +++++++++++++++++++++++++++++++++++++ - 1 files changed, 130 insertions(+), 0 deletions(-) - create mode 100644 tests/multi_socket_select_test.py - -diff --git a/tests/multi_socket_select_test.py b/tests/multi_socket_select_test.py -new file mode 100644 -index 0000000..b9ba950 ---- /dev/null -+++ b/tests/multi_socket_select_test.py -@@ -0,0 +1,130 @@ -+#! /usr/bin/env python -+# -*- coding: iso-8859-1 -*- -+# vi:ts=4:et -+ -+import pycurl -+import unittest -+import select -+ -+from . import app -+from . import runwsgi -+from . import util -+ -+setup_module_1, teardown_module_1 = runwsgi.app_runner_setup((app.app, 8380)) -+setup_module_2, teardown_module_2 = runwsgi.app_runner_setup((app.app, 8381)) -+setup_module_3, teardown_module_3 = runwsgi.app_runner_setup((app.app, 8382)) -+ -+def setup_module(mod): -+ setup_module_1(mod) -+ setup_module_2(mod) -+ setup_module_3(mod) -+ -+def teardown_module(mod): -+ teardown_module_3(mod) -+ teardown_module_2(mod) -+ teardown_module_1(mod) -+ -+class MultiSocketSelectTest(unittest.TestCase): -+ def test_multi_socket_select(self): -+ sockets = set() -+ timeout = 0 -+ -+ urls = [ -+ 'http://localhost:8380/success', -+ 'http://localhost:8381/success', -+ 'http://localhost:8382/success', -+ ] -+ -+ timers = [] -+ -+ # timer callback -+ def timer(msecs): -+ #print('Timer callback msecs:', msecs) -+ timers.append(msecs) -+ -+ socket_events = [] -+ -+ # socket callback -+ def socket(event, socket, multi, data): -+ if event == pycurl.POLL_REMOVE: -+ #print("Remove Socket %d"%socket) -+ sockets.remove(socket) -+ else: -+ if socket not in sockets: -+ #print("Add socket %d"%socket) -+ sockets.add(socket) -+ socket_events.append((event, multi)) -+ -+ # init -+ m = pycurl.CurlMulti() -+ m.setopt(pycurl.M_PIPELINING, 1) -+ m.setopt(pycurl.M_TIMERFUNCTION, timer) -+ m.setopt(pycurl.M_SOCKETFUNCTION, socket) -+ m.handles = [] -+ for url in urls: -+ c = pycurl.Curl() -+ # save info in standard Python attributes -+ c.url = url -+ c.body = util.StringIO() -+ c.http_code = -1 -+ m.handles.append(c) -+ # pycurl API calls -+ c.setopt(c.URL, c.url) -+ c.setopt(c.WRITEFUNCTION, c.body.write) -+ m.add_handle(c) -+ -+ # get data -+ num_handles = len(m.handles) -+ -+ while (pycurl.E_CALL_MULTI_PERFORM==m.socket_all()[0]): -+ pass -+ -+ timeout = m.timeout() -+ -+ -+ while True: -+ (rr, wr, er) = select.select(sockets,sockets,sockets,timeout/1000.0) -+ socketSet = set(rr+wr+er) -+ if socketSet: -+ for s in socketSet: -+ while True: -+ (ret,running) = m.socket_action(s,0) -+ if ret!=pycurl.E_CALL_MULTI_PERFORM: -+ break -+ else: -+ (ret,running) = m.socket_action(pycurl.SOCKET_TIMEOUT,0) -+ if running==0: -+ break -+ -+ for c in m.handles: -+ # save info in standard Python attributes -+ c.http_code = c.getinfo(c.HTTP_CODE) -+ -+ # at least in and remove events per socket -+ assert len(socket_events) >= 6 -+ -+ # print result -+ for c in m.handles: -+ self.assertEqual('success', c.body.getvalue()) -+ self.assertEqual(200, c.http_code) -+ -+ # multi, not curl handle -+ self.check(pycurl.POLL_IN, m, socket_events) -+ self.check(pycurl.POLL_REMOVE, m, socket_events) -+ -+ assert len(timers) > 0 -+ assert timers[0] > 0 -+ self.assertEqual(-1, timers[-1]) -+ -+ # close handles -+ for c in m.handles: -+ # pycurl API calls -+ m.remove_handle(c) -+ c.close() -+ m.close() -+ -+ def check(self, event, multi, socket_events): -+ for event_, multi_ in socket_events: -+ if event == event_ and multi == multi_: -+ return -+ assert False, '%d %s not found in socket events' % (event, multi) --- -1.7.1 - - -From 7631c7c047354c1dacb09459fefd6ea9ca11a28b Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Tue, 5 Mar 2013 23:15:47 -0500 -Subject: [PATCH 044/149] Ported test_multi_timer.py - -Signed-off-by: Kamil Dudka ---- - tests/multi_timer_test.py | 88 +++++++++++++++++++++++++++++++++++++++++++++ - 1 files changed, 88 insertions(+), 0 deletions(-) - create mode 100644 tests/multi_timer_test.py - -diff --git a/tests/multi_timer_test.py b/tests/multi_timer_test.py -new file mode 100644 -index 0000000..e961780 ---- /dev/null -+++ b/tests/multi_timer_test.py -@@ -0,0 +1,88 @@ -+#! /usr/bin/env python -+# -*- coding: iso-8859-1 -*- -+# vi:ts=4:et -+ -+import pycurl -+import unittest -+import select -+ -+from . import app -+from . import runwsgi -+from . import util -+ -+setup_module_1, teardown_module_1 = runwsgi.app_runner_setup((app.app, 8380)) -+setup_module_2, teardown_module_2 = runwsgi.app_runner_setup((app.app, 8381)) -+setup_module_3, teardown_module_3 = runwsgi.app_runner_setup((app.app, 8382)) -+ -+def setup_module(mod): -+ setup_module_1(mod) -+ setup_module_2(mod) -+ setup_module_3(mod) -+ -+def teardown_module(mod): -+ teardown_module_3(mod) -+ teardown_module_2(mod) -+ teardown_module_1(mod) -+ -+class MultiSocketTest(unittest.TestCase): -+ def test_multi_timer(self): -+ urls = [ -+ 'http://localhost:8380/success', -+ 'http://localhost:8381/success', -+ 'http://localhost:8382/success', -+ ] -+ -+ timers = [] -+ -+ # timer callback -+ def timer(msecs): -+ #print('Timer callback msecs:', msecs) -+ timers.append(msecs) -+ -+ # init -+ m = pycurl.CurlMulti() -+ m.setopt(pycurl.M_PIPELINING, 1) -+ m.setopt(pycurl.M_TIMERFUNCTION, timer) -+ m.handles = [] -+ for url in urls: -+ c = pycurl.Curl() -+ # save info in standard Python attributes -+ c.url = url -+ c.body = util.StringIO() -+ c.http_code = -1 -+ m.handles.append(c) -+ # pycurl API calls -+ c.setopt(c.URL, c.url) -+ c.setopt(c.WRITEFUNCTION, c.body.write) -+ m.add_handle(c) -+ -+ # get data -+ num_handles = len(m.handles) -+ while num_handles: -+ while 1: -+ ret, num_handles = m.perform() -+ if ret != pycurl.E_CALL_MULTI_PERFORM: -+ break -+ # currently no more I/O is pending, could do something in the meantime -+ # (display a progress bar, etc.) -+ m.select(1.0) -+ -+ for c in m.handles: -+ # save info in standard Python attributes -+ c.http_code = c.getinfo(c.HTTP_CODE) -+ -+ # print result -+ for c in m.handles: -+ self.assertEqual('success', c.body.getvalue()) -+ self.assertEqual(200, c.http_code) -+ -+ assert len(timers) > 0 -+ assert timers[0] > 0 -+ self.assertEqual(-1, timers[-1]) -+ -+ # close handles -+ for c in m.handles: -+ # pycurl API calls -+ m.remove_handle(c) -+ c.close() -+ m.close() --- -1.7.1 - - -From eb523b0b4e3729d6613e1841d3d3715ad6e58109 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Tue, 5 Mar 2013 23:17:14 -0500 -Subject: [PATCH 045/149] Timers are tested in multi_timer test, delete them from other multi tests - -Signed-off-by: Kamil Dudka ---- - tests/multi_socket_select_test.py | 12 ------------ - tests/multi_socket_test.py | 13 ------------- - 2 files changed, 0 insertions(+), 25 deletions(-) - -diff --git a/tests/multi_socket_select_test.py b/tests/multi_socket_select_test.py -index b9ba950..0c472cf 100644 ---- a/tests/multi_socket_select_test.py -+++ b/tests/multi_socket_select_test.py -@@ -35,13 +35,6 @@ class MultiSocketSelectTest(unittest.TestCase): - 'http://localhost:8382/success', - ] - -- timers = [] -- -- # timer callback -- def timer(msecs): -- #print('Timer callback msecs:', msecs) -- timers.append(msecs) -- - socket_events = [] - - # socket callback -@@ -58,7 +51,6 @@ class MultiSocketSelectTest(unittest.TestCase): - # init - m = pycurl.CurlMulti() - m.setopt(pycurl.M_PIPELINING, 1) -- m.setopt(pycurl.M_TIMERFUNCTION, timer) - m.setopt(pycurl.M_SOCKETFUNCTION, socket) - m.handles = [] - for url in urls: -@@ -112,10 +104,6 @@ class MultiSocketSelectTest(unittest.TestCase): - self.check(pycurl.POLL_IN, m, socket_events) - self.check(pycurl.POLL_REMOVE, m, socket_events) - -- assert len(timers) > 0 -- assert timers[0] > 0 -- self.assertEqual(-1, timers[-1]) -- - # close handles - for c in m.handles: - # pycurl API calls -diff --git a/tests/multi_socket_test.py b/tests/multi_socket_test.py -index 2586569..2cce7ae 100644 ---- a/tests/multi_socket_test.py -+++ b/tests/multi_socket_test.py -@@ -32,25 +32,16 @@ class MultiSocketTest(unittest.TestCase): - 'http://localhost:8382/success', - ] - -- timers = [] -- -- # timer callback -- def timer(msecs): -- #print('Timer callback msecs:', msecs) -- timers.append(msecs) -- - socket_events = [] - - # socket callback - def socket(event, socket, multi, data): - #print(event, socket, multi, data) -- # multi.assign(socket, timer) - socket_events.append((event, multi)) - - # init - m = pycurl.CurlMulti() - m.setopt(pycurl.M_PIPELINING, 1) -- m.setopt(pycurl.M_TIMERFUNCTION, timer) - m.setopt(pycurl.M_SOCKETFUNCTION, socket) - m.handles = [] - for url in urls: -@@ -92,10 +83,6 @@ class MultiSocketTest(unittest.TestCase): - self.check(pycurl.POLL_IN, m, socket_events) - self.check(pycurl.POLL_REMOVE, m, socket_events) - -- assert len(timers) > 0 -- assert timers[0] > 0 -- self.assertEqual(-1, timers[-1]) -- - # close handles - for c in m.handles: - # pycurl API calls --- -1.7.1 - - -From c4ad7c4b2cf2107fb4dfcf468fef554bb8f1a38f Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Tue, 5 Mar 2013 23:30:48 -0500 -Subject: [PATCH 046/149] Ported post test - -Signed-off-by: Kamil Dudka ---- - tests/app.py | 5 +++++ - tests/post_test.py | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ - 2 files changed, 54 insertions(+), 0 deletions(-) - create mode 100644 tests/post_test.py - -diff --git a/tests/app.py b/tests/app.py -index 3b9303b..1fa09a6 100644 ---- a/tests/app.py -+++ b/tests/app.py -@@ -1,4 +1,5 @@ - import bottle -+import json - - app = bottle.Bottle() - -@@ -13,3 +14,7 @@ def forbidden(): - @app.route('/status/404') - def not_found(): - bottle.abort(404, 'not found') -+ -+@app.route('/postfields', method='post') -+def postfields(): -+ return json.dumps(dict(bottle.request.forms)) -diff --git a/tests/post_test.py b/tests/post_test.py -new file mode 100644 -index 0000000..183c4c9 ---- /dev/null -+++ b/tests/post_test.py -@@ -0,0 +1,49 @@ -+#! /usr/bin/env python -+# -*- coding: iso-8859-1 -*- -+# vi:ts=4:et -+ -+import pycurl -+import unittest -+import io -+import json -+try: -+ import urllib.parse as urllib_parse -+except ImportError: -+ import urllib as urllib_parse -+ -+from . import app -+from . import runwsgi -+from . import util -+ -+setup_module, teardown_module = runwsgi.app_runner_setup((app.app, 8380)) -+ -+class PostTest(unittest.TestCase): -+ def setUp(self): -+ self.curl = pycurl.Curl() -+ -+ def tearDown(self): -+ self.curl.close() -+ -+ def test_post_single_field(self): -+ pf = {'field1': 'value1'} -+ self.check(pf) -+ -+ def test_post_multiple_fields(self): -+ pf = {'field1':'value1', 'field2':'value2 with blanks', 'field3':'value3'} -+ self.check(pf) -+ -+ def test_post_fields_with_ampersand(self): -+ pf = {'field1':'value1', 'field2':'value2 with blanks and & chars', -+ 'field3':'value3'} -+ self.check(pf) -+ -+ def check(self, pf): -+ self.curl.setopt(pycurl.URL, 'http://localhost:8380/postfields') -+ self.curl.setopt(pycurl.POSTFIELDS, urllib_parse.urlencode(pf)) -+ #self.curl.setopt(pycurl.VERBOSE, 1) -+ sio = util.StringIO() -+ self.curl.setopt(pycurl.WRITEFUNCTION, sio.write) -+ self.curl.perform() -+ body = sio.getvalue() -+ returned_fields = json.loads(body) -+ self.assertEqual(pf, returned_fields) --- -1.7.1 - - -From 789b647eff13181fa527cb218dd487695abf3515 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Tue, 5 Mar 2013 23:35:02 -0500 -Subject: [PATCH 047/149] Port test_post2.py null byte test - -Signed-off-by: Kamil Dudka ---- - tests/post_test.py | 28 ++++++++++++++++++++++++---- - 1 files changed, 24 insertions(+), 4 deletions(-) - -diff --git a/tests/post_test.py b/tests/post_test.py -index 183c4c9..6f9cf80 100644 ---- a/tests/post_test.py -+++ b/tests/post_test.py -@@ -26,18 +26,18 @@ class PostTest(unittest.TestCase): - - def test_post_single_field(self): - pf = {'field1': 'value1'} -- self.check(pf) -+ self.urlencode_and_check(pf) - - def test_post_multiple_fields(self): - pf = {'field1':'value1', 'field2':'value2 with blanks', 'field3':'value3'} -- self.check(pf) -+ self.urlencode_and_check(pf) - - def test_post_fields_with_ampersand(self): - pf = {'field1':'value1', 'field2':'value2 with blanks and & chars', - 'field3':'value3'} -- self.check(pf) -+ self.urlencode_and_check(pf) - -- def check(self, pf): -+ def urlencode_and_check(self, pf): - self.curl.setopt(pycurl.URL, 'http://localhost:8380/postfields') - self.curl.setopt(pycurl.POSTFIELDS, urllib_parse.urlencode(pf)) - #self.curl.setopt(pycurl.VERBOSE, 1) -@@ -47,3 +47,23 @@ class PostTest(unittest.TestCase): - body = sio.getvalue() - returned_fields = json.loads(body) - self.assertEqual(pf, returned_fields) -+ -+ def test_post_with_null_byte(self): -+ send = [ -+ ('field3', (pycurl.FORM_CONTENTS, 'this is wei\000rd, but null-bytes are okay')) -+ ] -+ expect = { -+ 'field3': 'this is wei\000rd, but null-bytes are okay', -+ } -+ self.check_post(send, expect) -+ -+ def check_post(self, send, expect): -+ self.curl.setopt(pycurl.URL, 'http://localhost:8380/postfields') -+ self.curl.setopt(pycurl.HTTPPOST, send) -+ #self.curl.setopt(pycurl.VERBOSE, 1) -+ sio = util.StringIO() -+ self.curl.setopt(pycurl.WRITEFUNCTION, sio.write) -+ self.curl.perform() -+ body = sio.getvalue() -+ returned_fields = json.loads(body) -+ self.assertEqual(expect, returned_fields) --- -1.7.1 - - -From 2f56e4844e78280410f781b04d43ac49ce08129e Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Tue, 5 Mar 2013 23:50:51 -0500 -Subject: [PATCH 048/149] Port test_post2.py file upload test - -Signed-off-by: Kamil Dudka ---- - tests/app.py | 24 ++++++++++++++++++++++++ - tests/post_test.py | 22 +++++++++++++++++++--- - 2 files changed, 43 insertions(+), 3 deletions(-) - -diff --git a/tests/app.py b/tests/app.py -index 1fa09a6..a83e628 100644 ---- a/tests/app.py -+++ b/tests/app.py -@@ -18,3 +18,27 @@ def not_found(): - @app.route('/postfields', method='post') - def postfields(): - return json.dumps(dict(bottle.request.forms)) -+ -+# XXX file is not a bottle FileUpload instance, but FieldStorage? -+def convert_file(key, file): -+ return { -+ 'key': key, -+ 'name': file.name, -+ 'raw_filename': file.raw_filename, -+ 'headers': file.headers, -+ 'content_type': file.content_type, -+ 'content_length': file.content_length, -+ 'data': file.read(), -+ } -+ -+def convert_file(key, file): -+ return { -+ 'name': file.name, -+ 'filename': file.filename, -+ 'data': file.file.read(), -+ } -+ -+@app.route('/files', method='post') -+def files(): -+ files = [convert_file(key, bottle.request.files[key]) for key in bottle.request.files] -+ return json.dumps(files) -diff --git a/tests/post_test.py b/tests/post_test.py -index 6f9cf80..7df0f3b 100644 ---- a/tests/post_test.py -+++ b/tests/post_test.py -@@ -2,6 +2,7 @@ - # -*- coding: iso-8859-1 -*- - # vi:ts=4:et - -+import os.path - import pycurl - import unittest - import io -@@ -55,10 +56,25 @@ class PostTest(unittest.TestCase): - expect = { - 'field3': 'this is wei\000rd, but null-bytes are okay', - } -- self.check_post(send, expect) -+ self.check_post(send, expect, 'http://localhost:8380/postfields') - -- def check_post(self, send, expect): -- self.curl.setopt(pycurl.URL, 'http://localhost:8380/postfields') -+ def test_post_file(self): -+ path = os.path.join(os.path.dirname(__file__), '..', 'README') -+ with open(path) as f: -+ contents = f.read() -+ send = [ -+ #('field2', (pycurl.FORM_FILE, 'test_post.py', pycurl.FORM_FILE, 'test_post2.py')), -+ ('field2', (pycurl.FORM_FILE, path)), -+ ] -+ expect = [{ -+ 'name': 'field2', -+ 'filename': 'README', -+ 'data': contents, -+ }] -+ self.check_post(send, expect, 'http://localhost:8380/files') -+ -+ def check_post(self, send, expect, endpoint): -+ self.curl.setopt(pycurl.URL, endpoint) - self.curl.setopt(pycurl.HTTPPOST, send) - #self.curl.setopt(pycurl.VERBOSE, 1) - sio = util.StringIO() --- -1.7.1 - - -From c8f91c64ad1eb067907e9b97bd5f8bfa27c29062 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Tue, 5 Mar 2013 23:51:45 -0500 -Subject: [PATCH 049/149] Add a note that this test takes forever to run - -Signed-off-by: Kamil Dudka ---- - tests/post_test.py | 1 + - 1 files changed, 1 insertions(+), 0 deletions(-) - -diff --git a/tests/post_test.py b/tests/post_test.py -index 7df0f3b..564d043 100644 ---- a/tests/post_test.py -+++ b/tests/post_test.py -@@ -73,6 +73,7 @@ class PostTest(unittest.TestCase): - }] - self.check_post(send, expect, 'http://localhost:8380/files') - -+ # XXX this test takes about a second to run, check keep-alives? - def check_post(self, send, expect, endpoint): - self.curl.setopt(pycurl.URL, endpoint) - self.curl.setopt(pycurl.HTTPPOST, send) --- -1.7.1 - - -From 9ac9dce97226d34e6bb077a0357063fd23056158 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Tue, 5 Mar 2013 23:59:04 -0500 -Subject: [PATCH 050/149] Ported test_post3.py - -Signed-off-by: Kamil Dudka ---- - tests/post_with_read_callback_test.py | 60 +++++++++++++++++++++++++++++++++ - 1 files changed, 60 insertions(+), 0 deletions(-) - create mode 100644 tests/post_with_read_callback_test.py - -diff --git a/tests/post_with_read_callback_test.py b/tests/post_with_read_callback_test.py -new file mode 100644 -index 0000000..a09e83a ---- /dev/null -+++ b/tests/post_with_read_callback_test.py -@@ -0,0 +1,60 @@ -+#! /usr/bin/env python -+# -*- coding: iso-8859-1 -*- -+# vi:ts=4:et -+ -+import os.path -+import pycurl -+import unittest -+import io -+import json -+try: -+ import urllib.parse as urllib_parse -+except ImportError: -+ import urllib as urllib_parse -+ -+from . import app -+from . import runwsgi -+from . import util -+ -+setup_module, teardown_module = runwsgi.app_runner_setup((app.app, 8380)) -+ -+POSTFIELDS = { -+ 'field1':'value1', -+ 'field2':'value2 with blanks', -+ 'field3':'value3', -+} -+POSTSTRING = urllib_parse.urlencode(POSTFIELDS) -+ -+class DataProvider(object): -+ def __init__(self): -+ self.finished = False -+ -+ def read_cb(self, size): -+ assert len(POSTSTRING) <= size -+ if not self.finished: -+ self.finished = True -+ return POSTSTRING -+ else: -+ # Nothing more to read -+ return "" -+ -+class PostWithReadCallbackTest(unittest.TestCase): -+ def setUp(self): -+ self.curl = pycurl.Curl() -+ -+ def tearDown(self): -+ self.curl.close() -+ -+ def test_post_with_read_callback(self): -+ d = DataProvider() -+ self.curl.setopt(self.curl.URL, 'http://localhost:8380/postfields') -+ self.curl.setopt(self.curl.POST, 1) -+ self.curl.setopt(self.curl.POSTFIELDSIZE, len(POSTSTRING)) -+ self.curl.setopt(self.curl.READFUNCTION, d.read_cb) -+ #self.curl.setopt(self.curl.VERBOSE, 1) -+ sio = util.StringIO() -+ self.curl.setopt(pycurl.WRITEFUNCTION, sio.write) -+ self.curl.perform() -+ -+ actual = json.loads(sio.getvalue()) -+ self.assertEqual(POSTFIELDS, actual) --- -1.7.1 - - -From 8a4babacffc0bbeacdfcfb3f9888f26efd6c072c Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Wed, 6 Mar 2013 00:10:41 -0500 -Subject: [PATCH 051/149] Ported test_socketopen.py - -Signed-off-by: Kamil Dudka ---- - tests/socket_open_test.py | 45 +++++++++++++++++++++++++++++++++++++++++++++ - 1 files changed, 45 insertions(+), 0 deletions(-) - create mode 100644 tests/socket_open_test.py - -diff --git a/tests/socket_open_test.py b/tests/socket_open_test.py -new file mode 100644 -index 0000000..dff3f65 ---- /dev/null -+++ b/tests/socket_open_test.py -@@ -0,0 +1,45 @@ -+#! /usr/bin/env python -+# -*- coding: iso-8859-1 -*- -+# vi:ts=4:et -+ -+import socket -+import pycurl -+import unittest -+try: -+ import urllib.parse as urllib_parse -+except ImportError: -+ import urllib as urllib_parse -+ -+from . import app -+from . import runwsgi -+from . import util -+ -+setup_module, teardown_module = runwsgi.app_runner_setup((app.app, 8380)) -+ -+socket_open_called = False -+ -+def socket_open(family, socktype, protocol): -+ global socket_open_called -+ socket_open_called = True -+ -+ #print(family, socktype, protocol) -+ s = socket.socket(family, socktype, protocol) -+ s.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) -+ return s -+ -+class SocketOpenTest(unittest.TestCase): -+ def setUp(self): -+ self.curl = pycurl.Curl() -+ -+ def tearDown(self): -+ self.curl.close() -+ -+ def test_socket_open(self): -+ self.curl.setopt(pycurl.OPENSOCKETFUNCTION, socket_open) -+ self.curl.setopt(self.curl.URL, 'http://localhost:8380/success') -+ sio = util.StringIO() -+ self.curl.setopt(pycurl.WRITEFUNCTION, sio.write) -+ self.curl.perform() -+ -+ assert socket_open_called -+ self.assertEqual('success', sio.getvalue()) --- -1.7.1 - - -From bf65a3bac3446950421927da8230216acefebf86 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Wed, 6 Mar 2013 00:13:46 -0500 -Subject: [PATCH 052/149] Ported test_share.py - -Signed-off-by: Kamil Dudka ---- - tests/share_test.py | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ - 1 files changed, 51 insertions(+), 0 deletions(-) - create mode 100644 tests/share_test.py - -diff --git a/tests/share_test.py b/tests/share_test.py -new file mode 100644 -index 0000000..7b5da77 ---- /dev/null -+++ b/tests/share_test.py -@@ -0,0 +1,51 @@ -+#! /usr/bin/env python -+# -*- coding: iso-8859-1 -*- -+# vi:ts=4:et -+ -+import threading -+import pycurl -+import unittest -+try: -+ import urllib.parse as urllib_parse -+except ImportError: -+ import urllib as urllib_parse -+ -+from . import app -+from . import runwsgi -+from . import util -+ -+setup_module, teardown_module = runwsgi.app_runner_setup((app.app, 8380)) -+ -+class WorkerThread(threading.Thread): -+ -+ def __init__(self, share): -+ threading.Thread.__init__(self) -+ self.curl = pycurl.Curl() -+ self.curl.setopt(pycurl.URL, 'http://localhost:8380/success') -+ self.curl.setopt(pycurl.SHARE, share) -+ self.sio = util.StringIO() -+ self.curl.setopt(pycurl.WRITEFUNCTION, self.sio.write) -+ -+ def run(self): -+ self.curl.perform() -+ self.curl.close() -+ -+class ShareTest(unittest.TestCase): -+ def test_share(self): -+ s = pycurl.CurlShare() -+ s.setopt(pycurl.SH_SHARE, pycurl.LOCK_DATA_COOKIE) -+ s.setopt(pycurl.SH_SHARE, pycurl.LOCK_DATA_DNS) -+ -+ t1 = WorkerThread(s) -+ t2 = WorkerThread(s) -+ -+ t1.start() -+ t2.start() -+ -+ t1.join() -+ t2.join() -+ -+ del s -+ -+ self.assertEqual('success', t1.sio.getvalue()) -+ self.assertEqual('success', t2.sio.getvalue()) --- -1.7.1 - - -From c5f46d8247e3cbf3d2a0a2cf74c7daeade97c1d8 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Wed, 6 Mar 2013 00:26:45 -0500 -Subject: [PATCH 053/149] Port test_reset.py, which appears to be broken; skip the ported version for now - -Signed-off-by: Kamil Dudka ---- - tests/reset_test.py | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++ - 1 files changed, 76 insertions(+), 0 deletions(-) - create mode 100644 tests/reset_test.py - -diff --git a/tests/reset_test.py b/tests/reset_test.py -new file mode 100644 -index 0000000..66b7108 ---- /dev/null -+++ b/tests/reset_test.py -@@ -0,0 +1,76 @@ -+#! /usr/bin/env python -+# -*- coding: iso-8859-1 -*- -+# vi:ts=4:et -+ -+import sys -+import pycurl -+import unittest -+try: -+ import urllib.parse as urllib_parse -+except ImportError: -+ import urllib as urllib_parse -+ -+from . import app -+from . import runwsgi -+from . import util -+ -+setup_module, teardown_module = runwsgi.app_runner_setup((app.app, 8380)) -+ -+class ResetTest(unittest.TestCase): -+ # XXX this test was broken when it was test_reset.py -+ def skip_test_reset(self): -+ outf = util.StringIO() -+ cm = pycurl.CurlMulti() -+ -+ # Set multi handle's options -+ cm.setopt(pycurl.M_PIPELINING, 1) -+ -+ eh = pycurl.Curl() -+ -+ for x in range(1, 20): -+ -+ eh.setopt(pycurl.WRITEFUNCTION, outf.write) -+ eh.setopt(pycurl.URL, 'http://localhost:8380/success') -+ cm.add_handle(eh) -+ -+ while 1: -+ ret, active_handles = cm.perform() -+ if ret != pycurl.E_CALL_MULTI_PERFORM: -+ break -+ -+ while active_handles: -+ ret = cm.select(1.0) -+ if ret == -1: -+ continue -+ while 1: -+ ret, active_handles = cm.perform() -+ if ret != pycurl.E_CALL_MULTI_PERFORM: -+ break -+ -+ count, good, bad = cm.info_read() -+ -+ for h, en, em in bad: -+ print("Transfer to %s failed with %d, %s\n" % \ -+ (h.getinfo(pycurl.EFFECTIVE_URL), en, em)) -+ raise RuntimeError -+ -+ for h in good: -+ httpcode = h.getinfo(pycurl.RESPONSE_CODE) -+ if httpcode != 200: -+ print("Transfer to %s failed with code %d\n" %\ -+ (h.getinfo(pycurl.EFFECTIVE_URL), httpcode)) -+ raise RuntimeError -+ -+ else: -+ print("Recd %d bytes from %s" % \ -+ (h.getinfo(pycurl.SIZE_DOWNLOAD), -+ h.getinfo(pycurl.EFFECTIVE_URL))) -+ -+ cm.remove_handle(eh) -+ eh.reset() -+ -+ eh.close() -+ cm.close() -+ outf.close() -+ -+ pycurl.global_cleanup() --- -1.7.1 - - -From 4aba306862de76d584af790bf2dd90f0d55fda0e Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Wed, 6 Mar 2013 00:55:41 -0500 -Subject: [PATCH 054/149] Start a server on each port once, different servers should go on different ports - -Signed-off-by: Kamil Dudka ---- - tests/runwsgi.py | 9 ++++++++- - 1 files changed, 8 insertions(+), 1 deletions(-) - -diff --git a/tests/runwsgi.py b/tests/runwsgi.py -index 7b39358..6d3b69f 100644 ---- a/tests/runwsgi.py -+++ b/tests/runwsgi.py -@@ -48,6 +48,8 @@ class ServerThread(threading.Thread): - def run(self): - bottle.run(self.app, server=self.server, quiet=True) - -+started_servers = {} -+ - def app_runner_setup(*specs): - '''Returns setup and teardown methods for running a list of WSGI - applications in a daemon thread. -@@ -73,9 +75,14 @@ def app_runner_setup(*specs): - kwargs = {} - else: - app, port, kwargs = spec -- self.servers.append(start_bottle_server(app, port, **kwargs)) -+ if port in started_servers: -+ assert started_servers[port] == (app, kwargs) -+ else: -+ self.servers.append(start_bottle_server(app, port, **kwargs)) -+ started_servers[port] = (app, kwargs) - - def teardown(self): -+ return - for server in self.servers: - # if no tests from module were run, there is no server to shut down - if hasattr(server, 'srv'): --- -1.7.1 - - -From 8fc24d93e2188d68c738e602c75753cec503de7d Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Wed, 6 Mar 2013 00:56:44 -0500 -Subject: [PATCH 055/149] Need to skip more aggressively it would seem - -Signed-off-by: Kamil Dudka ---- - tests/default_write_function_test.py | 2 +- - tests/reset_test.py | 2 +- - 2 files changed, 2 insertions(+), 2 deletions(-) - -diff --git a/tests/default_write_function_test.py b/tests/default_write_function_test.py -index a0de66e..c0d256b 100644 ---- a/tests/default_write_function_test.py -+++ b/tests/default_write_function_test.py -@@ -40,7 +40,7 @@ class DefaultWriteFunctionTest(unittest.TestCase): - os.fsync(STDOUT_FD_NUM) - - # I have a really hard time getting this to work with nose output capture -- def skip_test_perform_get_with_default_write_function(self): -+ def skip_perform_get_with_default_write_function(self): - self.curl.setopt(pycurl.URL, 'http://localhost:8380/success') - with tempfile.NamedTemporaryFile() as f: - #with open('w', 'w+') as f: -diff --git a/tests/reset_test.py b/tests/reset_test.py -index 66b7108..cc55f86 100644 ---- a/tests/reset_test.py -+++ b/tests/reset_test.py -@@ -18,7 +18,7 @@ setup_module, teardown_module = runwsgi.app_runner_setup((app.app, 8380)) - - class ResetTest(unittest.TestCase): - # XXX this test was broken when it was test_reset.py -- def skip_test_reset(self): -+ def skip_reset(self): - outf = util.StringIO() - cm = pycurl.CurlMulti() - --- -1.7.1 - - -From d3a77eabe019b1707a7bdf8cbae32d1eddd12f9c Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Wed, 6 Mar 2013 01:29:59 -0500 -Subject: [PATCH 056/149] Store urlencode result in a local variable, otherwise things break in a highly weird way - -Signed-off-by: Kamil Dudka ---- - tests/post_test.py | 24 +++++++++++++++++++++++- - 1 files changed, 23 insertions(+), 1 deletions(-) - -diff --git a/tests/post_test.py b/tests/post_test.py -index 564d043..e8b0675 100644 ---- a/tests/post_test.py -+++ b/tests/post_test.py -@@ -40,11 +40,33 @@ class PostTest(unittest.TestCase): - - def urlencode_and_check(self, pf): - self.curl.setopt(pycurl.URL, 'http://localhost:8380/postfields') -- self.curl.setopt(pycurl.POSTFIELDS, urllib_parse.urlencode(pf)) -+ postfields = urllib_parse.urlencode(pf) -+ self.curl.setopt(pycurl.POSTFIELDS, postfields) -+ -+ # But directly passing urlencode result into setopt call: -+ #self.curl.setopt(pycurl.POSTFIELDS, urllib_parse.urlencode(pf)) -+ # produces: -+ # {'\x00\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00': ''} -+ # Traceback (most recent call last): -+ # File "/usr/local/bin/bottle.py", line 744, in _handle -+ # return route.call(**args) -+ # File "/usr/local/bin/bottle.py", line 1479, in wrapper -+ # rv = callback(*a, **ka) -+ # File "/home/pie/apps/pycurl/tests/app.py", line 21, in postfields -+ # return json.dumps(dict(bottle.request.forms)) -+ # File "/usr/local/lib/python2.7/json/__init__.py", line 231, in dumps -+ # return _default_encoder.encode(obj) -+ # File "/usr/local/lib/python2.7/json/encoder.py", line 201, in encode -+ # chunks = self.iterencode(o, _one_shot=True) -+ # File "/usr/local/lib/python2.7/json/encoder.py", line 264, in iterencode -+ # return _iterencode(o, 0) -+ # UnicodeDecodeError: 'utf8' codec can't decode byte 0x80 in position 4: invalid start byte -+ - #self.curl.setopt(pycurl.VERBOSE, 1) - sio = util.StringIO() - self.curl.setopt(pycurl.WRITEFUNCTION, sio.write) - self.curl.perform() -+ self.assertEqual(200, self.curl.getinfo(pycurl.HTTP_CODE)) - body = sio.getvalue() - returned_fields = json.loads(body) - self.assertEqual(pf, returned_fields) --- -1.7.1 - - -From 3ea332ffec21dfe6ddf3e0bc66a101259f88f216 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Wed, 6 Mar 2013 01:32:33 -0500 -Subject: [PATCH 057/149] Fix regular expression to allow all hex chars like it should - -Signed-off-by: Kamil Dudka ---- - tests/internals_test.py | 2 +- - 1 files changed, 1 insertions(+), 1 deletions(-) - -diff --git a/tests/internals_test.py b/tests/internals_test.py -index fb451df..f628ab2 100644 ---- a/tests/internals_test.py -+++ b/tests/internals_test.py -@@ -180,7 +180,7 @@ class InternalsTest(unittest.TestCase): - del m, c - - def test_cyclic_gc(self): -- regexp = re.compile(r'at (0x\d+)') -+ regexp = re.compile(r'at (0x[\da-f]+)') - gc.collect() - c = pycurl.Curl() - c.m = pycurl.CurlMulti() --- -1.7.1 - - -From 990569ee36eac37c03f28c86b2a8cfc7a29752ab Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Wed, 6 Mar 2013 01:36:48 -0500 -Subject: [PATCH 058/149] Rewrite cyclic gc test to use id() - -Signed-off-by: Kamil Dudka ---- - tests/internals_test.py | 9 ++------- - 1 files changed, 2 insertions(+), 7 deletions(-) - -diff --git a/tests/internals_test.py b/tests/internals_test.py -index f628ab2..0133da0 100644 ---- a/tests/internals_test.py -+++ b/tests/internals_test.py -@@ -12,7 +12,6 @@ except ImportError: - import pickle - import gc - import copy --import re - - class InternalsTest(unittest.TestCase): - def setUp(self): -@@ -180,7 +179,6 @@ class InternalsTest(unittest.TestCase): - del m, c - - def test_cyclic_gc(self): -- regexp = re.compile(r'at (0x[\da-f]+)') - gc.collect() - c = pycurl.Curl() - c.m = pycurl.CurlMulti() -@@ -208,17 +206,14 @@ class InternalsTest(unittest.TestCase): - ##print gc.get_objects() - #if opts.verbose >= 1: - #print("Tracked objects:", len(gc.get_objects())) -- match = regexp.search(repr(c)) -- assert match is not None -- address = match.group(1) -+ c_id = id(c) - # The `del' below should delete these 4 objects: - # Curl + internal dict, CurlMulti + internal dict - del c - gc.collect() - objects = gc.get_objects() -- search = 'at %s' % address - for object in objects: -- assert search not in repr(object) -+ assert id(object) != c_id - #if opts.verbose >= 1: - #print("Tracked objects:", len(gc.get_objects())) - --- -1.7.1 - - -From 3b7bb39d6b33031ff1efd03abdc174caa32ef3bc Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Wed, 6 Mar 2013 03:25:34 -0500 -Subject: [PATCH 059/149] Adjust ftp test to work with vsftpd started from project root - -Signed-off-by: Kamil Dudka ---- - tests/ftp_test.py | 18 ++++++++++-------- - 1 files changed, 10 insertions(+), 8 deletions(-) - -diff --git a/tests/ftp_test.py b/tests/ftp_test.py -index d215b6e..1d382ed 100644 ---- a/tests/ftp_test.py -+++ b/tests/ftp_test.py -@@ -2,6 +2,8 @@ - # -*- coding: iso-8859-1 -*- - # vi:ts=4:et - -+# Note: this test is meant to be run from pycurl project root. -+ - import pycurl - import unittest - -@@ -15,29 +17,29 @@ class FtpTest(unittest.TestCase): - self.curl.close() - - def test_get_ftp(self): -- self.curl.setopt(pycurl.URL, 'ftp://localhost:8921') -+ self.curl.setopt(pycurl.URL, 'ftp://localhost:8321') - sio = util.StringIO() - self.curl.setopt(pycurl.WRITEFUNCTION, sio.write) - self.curl.perform() - - result = sio.getvalue() - assert 'README' in result -- assert 'bin -> usr/bin' in result -+ assert 'INSTALL' in result - - # XXX this test needs to be fixed - def test_quote(self): -- self.curl.setopt(pycurl.URL, 'ftp://localhost:8921') -+ self.curl.setopt(pycurl.URL, 'ftp://localhost:8321') - sio = util.StringIO() - self.curl.setopt(pycurl.WRITEFUNCTION, sio.write) -- self.curl.setopt(pycurl.QUOTE, ['CWD pub']) -+ self.curl.setopt(pycurl.QUOTE, ['CWD tests']) - self.curl.perform() - - result = sio.getvalue() -- assert 'README' in result -- assert 'bin -> usr/bin' in result -+ assert 'README' not in result -+ assert 'ftp_test.py' in result - - def test_epsv(self): -- self.curl.setopt(pycurl.URL, 'ftp://localhost:8921') -+ self.curl.setopt(pycurl.URL, 'ftp://localhost:8321') - sio = util.StringIO() - self.curl.setopt(pycurl.WRITEFUNCTION, sio.write) - self.curl.setopt(pycurl.FTP_USE_EPSV, 1) -@@ -45,4 +47,4 @@ class FtpTest(unittest.TestCase): - - result = sio.getvalue() - assert 'README' in result -- assert 'bin -> usr/bin' in result -+ assert 'INSTALL' in result --- -1.7.1 - - -From ce0c221f9db95029982619afd5cc154d68a97334 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Wed, 6 Mar 2013 04:01:13 -0500 -Subject: [PATCH 060/149] Execute vsftpd as test ftp server - -Signed-off-by: Kamil Dudka ---- - tests/ftp_test.py | 3 ++ - tests/procmgr.py | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++ - tests/runwsgi.py | 20 ++++++++------ - tests/vsftpd.conf | 10 +++++++ - 4 files changed, 98 insertions(+), 8 deletions(-) - create mode 100644 tests/procmgr.py - create mode 100644 tests/vsftpd.conf - -diff --git a/tests/ftp_test.py b/tests/ftp_test.py -index 1d382ed..fa2ef79 100644 ---- a/tests/ftp_test.py -+++ b/tests/ftp_test.py -@@ -8,6 +8,9 @@ import pycurl - import unittest - - from . import util -+from . import procmgr -+ -+setup_module, teardown_module = procmgr.vsftpd_setup() - - class FtpTest(unittest.TestCase): - def setUp(self): -diff --git a/tests/procmgr.py b/tests/procmgr.py -new file mode 100644 -index 0000000..8d5c0cc ---- /dev/null -+++ b/tests/procmgr.py -@@ -0,0 +1,73 @@ -+import threading -+import subprocess -+import os -+import signal -+ -+from . import runwsgi -+ -+class ProcessManager(object): -+ def __init__(self, cmd): -+ self.cmd = cmd -+ -+ def start(self): -+ self.process = subprocess.Popen(self.cmd) -+ -+ self.thread = threading.Thread(target=self.run) -+ self.thread.daemon = True -+ self.thread.start() -+ -+ def run(self): -+ self.process.communicate() -+ -+managers = {} -+ -+def start(cmd): -+ if str(cmd) in managers: -+ # already started -+ return -+ -+ manager = ProcessManager(cmd) -+ managers[str(cmd)] = manager -+ manager.start() -+ -+def start_setup(cmd): -+ def do_start(): -+ start(cmd) -+ return do_start -+ -+# Example on FreeBSD: -+# PYCURL_VSFTPD_PATH=/usr/local/libexec/vsftpd nosetests -+ -+if 'PYCURL_VSFTPD_PATH' in os.environ: -+ vsftpd_path = os.environ['PYCURL_VSFTPD_PATH'] -+else: -+ vsftpd_path = 'vsftpd' -+ -+def vsftpd_setup(): -+ config_file_path = os.path.join(os.path.dirname(__file__), 'vsftpd.conf') -+ root_path = os.path.join(os.path.dirname(__file__), '..') -+ cmd = [ -+ vsftpd_path, -+ config_file_path, -+ '-oanon_root=%s' % root_path, -+ ] -+ setup_module = start_setup(cmd) -+ def do_setup_module(): -+ setup_module() -+ ok = runwsgi.wait_for_network_service(('127.0.0.1', 8321), 0.1, 10) -+ if not ok: -+ import warnings -+ warnings.warn('vsftpd did not start after 1 second') -+ -+ def teardown_module(): -+ try: -+ manager = managers[str(cmd)] -+ except KeyError: -+ pass -+ else: -+ try: -+ os.kill(manager.process.pid, signal.SIGTERM) -+ except OSError: -+ pass -+ -+ return do_setup_module, teardown_module -diff --git a/tests/runwsgi.py b/tests/runwsgi.py -index 6d3b69f..5217a3f 100644 ---- a/tests/runwsgi.py -+++ b/tests/runwsgi.py -@@ -16,21 +16,25 @@ class Server(bottle.WSGIRefServer): - self.srv = make_server(self.host, self.port, handler, **self.options) - self.srv.serve_forever(poll_interval=0.1) - --def start_bottle_server(app, port, **kwargs): -- server_thread = ServerThread(app, port, kwargs) -- server_thread.daemon = True -- server_thread.start() -- -+def wait_for_network_service(netloc, check_interval, num_attempts): - ok = False -- for i in range(10): -+ for i in range(num_attempts): - try: -- conn = socket.create_connection(('127.0.0.1', port), 0.1) -+ conn = socket.create_connection(netloc, check_interval) - except socket.error as e: -- _time.sleep(0.1) -+ _time.sleep(check_interval) - else: - conn.close() - ok = True - break -+ return ok -+ -+def start_bottle_server(app, port, **kwargs): -+ server_thread = ServerThread(app, port, kwargs) -+ server_thread.daemon = True -+ server_thread.start() -+ -+ ok = wait_for_network_service(('127.0.0.1', port), 0.1, 10) - if not ok: - import warnings - warnings.warn('Server did not start after 1 second') -diff --git a/tests/vsftpd.conf b/tests/vsftpd.conf -new file mode 100644 -index 0000000..0abb39f ---- /dev/null -+++ b/tests/vsftpd.conf -@@ -0,0 +1,10 @@ -+anon_world_readable_only=yes -+anonymous_enable=yes -+# currently we only list files -+download_enable=no -+listen=yes -+run_as_launching_user=yes -+write_enable=no -+listen_port=8321 -+# should be supplied on command line -+anon_root=/var/empty --- -1.7.1 - - -From 0457d78a5bd77c11ac67698f125c596f16f05da6 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Wed, 6 Mar 2013 00:37:19 -0500 -Subject: [PATCH 061/149] Move gtk and xmlrpc tests to examples as they do not test anything not already tested by the test suite - -Signed-off-by: Kamil Dudka ---- - examples/tests/test_gtk.py | 98 +++++++++++++++++++++++++++++++++++++++++ - examples/tests/test_xmlrpc.py | 29 ++++++++++++ - tests/test_gtk.py | 98 ----------------------------------------- - tests/test_xmlrpc.py | 29 ------------ - 4 files changed, 127 insertions(+), 127 deletions(-) - create mode 100644 examples/tests/test_gtk.py - create mode 100644 examples/tests/test_xmlrpc.py - delete mode 100644 tests/test_gtk.py - delete mode 100644 tests/test_xmlrpc.py - -diff --git a/examples/tests/test_gtk.py b/examples/tests/test_gtk.py -new file mode 100644 -index 0000000..7104439 ---- /dev/null -+++ b/examples/tests/test_gtk.py -@@ -0,0 +1,98 @@ -+#! /usr/bin/env python -+# -*- coding: iso-8859-1 -*- -+# vi:ts=4:et -+# $Id$ -+ -+import sys, threading -+import pycurl -+import pygtk -+pygtk.require('2.0') -+import gtk -+ -+# We should ignore SIGPIPE when using pycurl.NOSIGNAL - see -+# the libcurl tutorial for more info. -+try: -+ import signal -+ from signal import SIGPIPE, SIG_IGN -+ signal.signal(signal.SIGPIPE, signal.SIG_IGN) -+except ImportError: -+ pass -+ -+ -+class ProgressBar: -+ def __init__(self, uri): -+ self.round = 0.0 -+ win = gtk.Window(gtk.WINDOW_TOPLEVEL) -+ win.set_title("PycURL progress") -+ win.show() -+ vbox = gtk.VBox(spacing=5) -+ vbox.set_border_width(10) -+ win.add(vbox) -+ win.set_default_size(200, 20) -+ vbox.show() -+ label = gtk.Label("Downloading %s" % uri) -+ label.set_alignment(0, 0.5) -+ vbox.pack_start(label) -+ label.show() -+ pbar = gtk.ProgressBar() -+ pbar.show() -+ self.pbar = pbar -+ vbox.pack_start(pbar) -+ win.connect("destroy", self.close_app) -+ -+ def progress(self, download_t, download_d, upload_t, upload_d): -+ if download_t == 0: -+ self.round = self.round + 0.1 -+ if self.round >= 1.0: self.round = 0.0 -+ else: -+ self.round = float(download_d) / float(download_t) -+ gtk.threads_enter() -+ self.pbar.set_fraction(self.round) -+ gtk.threads_leave() -+ -+ def mainloop(self): -+ gtk.threads_enter() -+ gtk.main() -+ gtk.threads_leave() -+ -+ def close_app(self, *args): -+ args[0].destroy() -+ gtk.main_quit() -+ -+ -+class Test(threading.Thread): -+ def __init__(self, url, target_file, progress): -+ threading.Thread.__init__(self) -+ self.target_file = target_file -+ self.progress = progress -+ self.curl = pycurl.Curl() -+ self.curl.setopt(pycurl.URL, url) -+ self.curl.setopt(pycurl.WRITEDATA, self.target_file) -+ self.curl.setopt(pycurl.FOLLOWLOCATION, 1) -+ self.curl.setopt(pycurl.NOPROGRESS, 0) -+ self.curl.setopt(pycurl.PROGRESSFUNCTION, self.progress) -+ self.curl.setopt(pycurl.MAXREDIRS, 5) -+ self.curl.setopt(pycurl.NOSIGNAL, 1) -+ -+ def run(self): -+ self.curl.perform() -+ self.curl.close() -+ self.target_file.close() -+ self.progress(1.0, 1.0, 0, 0) -+ -+ -+# Check command line args -+if len(sys.argv) < 3: -+ print "Usage: %s " % sys.argv[0] -+ raise SystemExit -+ -+# Make a progress bar window -+p = ProgressBar(sys.argv[1]) -+# Start thread for fetching url -+Test(sys.argv[1], open(sys.argv[2], 'wb'), p.progress).start() -+# Enter the GTK mainloop -+gtk.threads_init() -+try: -+ p.mainloop() -+except KeyboardInterrupt: -+ pass -diff --git a/examples/tests/test_xmlrpc.py b/examples/tests/test_xmlrpc.py -new file mode 100644 -index 0000000..bc5953e ---- /dev/null -+++ b/examples/tests/test_xmlrpc.py -@@ -0,0 +1,29 @@ -+#! /usr/bin/env python -+# -*- coding: iso-8859-1 -*- -+# vi:ts=4:et -+# $Id$ -+ -+## XML-RPC lib included in python2.2 -+import xmlrpclib -+import pycurl -+ -+# Header fields passed in request -+xmlrpc_header = [ -+ "User-Agent: PycURL XML-RPC Test", "Content-Type: text/xml" -+ ] -+ -+# XML-RPC request template -+xmlrpc_template = """ -+%s%s -+""" -+ -+# Engage -+c = pycurl.Curl() -+c.setopt(c.URL, 'http://betty.userland.com/RPC2') -+c.setopt(c.POST, 1) -+c.setopt(c.HTTPHEADER, xmlrpc_header) -+c.setopt(c.POSTFIELDS, xmlrpc_template % ("examples.getStateName", xmlrpclib.dumps((5,)))) -+ -+print 'Response from http://betty.userland.com/' -+c.perform() -+c.close() -diff --git a/tests/test_gtk.py b/tests/test_gtk.py -deleted file mode 100644 -index 7104439..0000000 ---- a/tests/test_gtk.py -+++ /dev/null -@@ -1,98 +0,0 @@ --#! /usr/bin/env python --# -*- coding: iso-8859-1 -*- --# vi:ts=4:et --# $Id$ -- --import sys, threading --import pycurl --import pygtk --pygtk.require('2.0') --import gtk -- --# We should ignore SIGPIPE when using pycurl.NOSIGNAL - see --# the libcurl tutorial for more info. --try: -- import signal -- from signal import SIGPIPE, SIG_IGN -- signal.signal(signal.SIGPIPE, signal.SIG_IGN) --except ImportError: -- pass -- -- --class ProgressBar: -- def __init__(self, uri): -- self.round = 0.0 -- win = gtk.Window(gtk.WINDOW_TOPLEVEL) -- win.set_title("PycURL progress") -- win.show() -- vbox = gtk.VBox(spacing=5) -- vbox.set_border_width(10) -- win.add(vbox) -- win.set_default_size(200, 20) -- vbox.show() -- label = gtk.Label("Downloading %s" % uri) -- label.set_alignment(0, 0.5) -- vbox.pack_start(label) -- label.show() -- pbar = gtk.ProgressBar() -- pbar.show() -- self.pbar = pbar -- vbox.pack_start(pbar) -- win.connect("destroy", self.close_app) -- -- def progress(self, download_t, download_d, upload_t, upload_d): -- if download_t == 0: -- self.round = self.round + 0.1 -- if self.round >= 1.0: self.round = 0.0 -- else: -- self.round = float(download_d) / float(download_t) -- gtk.threads_enter() -- self.pbar.set_fraction(self.round) -- gtk.threads_leave() -- -- def mainloop(self): -- gtk.threads_enter() -- gtk.main() -- gtk.threads_leave() -- -- def close_app(self, *args): -- args[0].destroy() -- gtk.main_quit() -- -- --class Test(threading.Thread): -- def __init__(self, url, target_file, progress): -- threading.Thread.__init__(self) -- self.target_file = target_file -- self.progress = progress -- self.curl = pycurl.Curl() -- self.curl.setopt(pycurl.URL, url) -- self.curl.setopt(pycurl.WRITEDATA, self.target_file) -- self.curl.setopt(pycurl.FOLLOWLOCATION, 1) -- self.curl.setopt(pycurl.NOPROGRESS, 0) -- self.curl.setopt(pycurl.PROGRESSFUNCTION, self.progress) -- self.curl.setopt(pycurl.MAXREDIRS, 5) -- self.curl.setopt(pycurl.NOSIGNAL, 1) -- -- def run(self): -- self.curl.perform() -- self.curl.close() -- self.target_file.close() -- self.progress(1.0, 1.0, 0, 0) -- -- --# Check command line args --if len(sys.argv) < 3: -- print "Usage: %s " % sys.argv[0] -- raise SystemExit -- --# Make a progress bar window --p = ProgressBar(sys.argv[1]) --# Start thread for fetching url --Test(sys.argv[1], open(sys.argv[2], 'wb'), p.progress).start() --# Enter the GTK mainloop --gtk.threads_init() --try: -- p.mainloop() --except KeyboardInterrupt: -- pass -diff --git a/tests/test_xmlrpc.py b/tests/test_xmlrpc.py -deleted file mode 100644 -index bc5953e..0000000 ---- a/tests/test_xmlrpc.py -+++ /dev/null -@@ -1,29 +0,0 @@ --#! /usr/bin/env python --# -*- coding: iso-8859-1 -*- --# vi:ts=4:et --# $Id$ -- --## XML-RPC lib included in python2.2 --import xmlrpclib --import pycurl -- --# Header fields passed in request --xmlrpc_header = [ -- "User-Agent: PycURL XML-RPC Test", "Content-Type: text/xml" -- ] -- --# XML-RPC request template --xmlrpc_template = """ --%s%s --""" -- --# Engage --c = pycurl.Curl() --c.setopt(c.URL, 'http://betty.userland.com/RPC2') --c.setopt(c.POST, 1) --c.setopt(c.HTTPHEADER, xmlrpc_header) --c.setopt(c.POSTFIELDS, xmlrpc_template % ("examples.getStateName", xmlrpclib.dumps((5,)))) -- --print 'Response from http://betty.userland.com/' --c.perform() --c.close() --- -1.7.1 - - -From 25e7d646b9d5c80e8987528bf1b0031622ab440b Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Wed, 6 Mar 2013 00:37:41 -0500 -Subject: [PATCH 062/149] Delete old tests - -Signed-off-by: Kamil Dudka ---- - tests/test.py | 74 ----------- - tests/test_cb.py | 28 ---- - tests/test_debug.py | 16 --- - tests/test_ftp.py | 13 -- - tests/test_getinfo.py | 49 ------- - tests/test_internals.py | 258 ------------------------------------ - tests/test_memleak.py | 53 -------- - tests/test_multi.py | 33 ----- - tests/test_multi2.py | 72 ---------- - tests/test_multi3.py | 87 ------------ - tests/test_multi4.py | 57 -------- - tests/test_multi5.py | 60 --------- - tests/test_multi6.py | 62 --------- - tests/test_multi_socket.py | 82 ------------ - tests/test_multi_socket_select.py | 105 --------------- - tests/test_multi_timer.py | 76 ----------- - tests/test_multi_vs_thread.py | 262 ------------------------------------- - tests/test_post.py | 24 ---- - tests/test_post2.py | 18 --- - tests/test_post3.py | 32 ----- - tests/test_reset.py | 75 ----------- - tests/test_share.py | 34 ----- - tests/test_socketopen.py | 17 --- - tests/test_stringio.py | 25 ---- - 24 files changed, 0 insertions(+), 1612 deletions(-) - delete mode 100644 tests/test.py - delete mode 100644 tests/test_cb.py - delete mode 100644 tests/test_debug.py - delete mode 100644 tests/test_ftp.py - delete mode 100644 tests/test_getinfo.py - delete mode 100644 tests/test_internals.py - delete mode 100644 tests/test_memleak.py - delete mode 100644 tests/test_multi.py - delete mode 100644 tests/test_multi2.py - delete mode 100644 tests/test_multi3.py - delete mode 100644 tests/test_multi4.py - delete mode 100644 tests/test_multi5.py - delete mode 100644 tests/test_multi6.py - delete mode 100644 tests/test_multi_socket.py - delete mode 100644 tests/test_multi_socket_select.py - delete mode 100644 tests/test_multi_timer.py - delete mode 100644 tests/test_multi_vs_thread.py - delete mode 100644 tests/test_post.py - delete mode 100644 tests/test_post2.py - delete mode 100644 tests/test_post3.py - delete mode 100644 tests/test_reset.py - delete mode 100644 tests/test_share.py - delete mode 100644 tests/test_socketopen.py - delete mode 100644 tests/test_stringio.py - -diff --git a/tests/test.py b/tests/test.py -deleted file mode 100644 -index 5cd9740..0000000 ---- a/tests/test.py -+++ /dev/null -@@ -1,74 +0,0 @@ --#! /usr/bin/env python --# -*- coding: iso-8859-1 -*- --# vi:ts=4:et --# $Id$ -- --import sys, threading, time --import pycurl -- --# We should ignore SIGPIPE when using pycurl.NOSIGNAL - see --# the libcurl tutorial for more info. --try: -- import signal -- from signal import SIGPIPE, SIG_IGN -- signal.signal(signal.SIGPIPE, signal.SIG_IGN) --except ImportError: -- pass -- -- --class Test(threading.Thread): -- def __init__(self, url, ofile): -- threading.Thread.__init__(self) -- self.curl = pycurl.Curl() -- self.curl.setopt(pycurl.URL, url) -- self.curl.setopt(pycurl.WRITEDATA, ofile) -- self.curl.setopt(pycurl.FOLLOWLOCATION, 1) -- self.curl.setopt(pycurl.MAXREDIRS, 5) -- self.curl.setopt(pycurl.NOSIGNAL, 1) -- -- def run(self): -- self.curl.perform() -- self.curl.close() -- sys.stdout.write(".") -- sys.stdout.flush() -- -- --# Read list of URIs from file specified on commandline --try: -- urls = open(sys.argv[1]).readlines() --except IndexError: -- # No file was specified, show usage string -- print "Usage: %s " % sys.argv[0] -- raise SystemExit -- --# Initialize thread array and the file number --threads = [] --fileno = 0 -- --# Start one thread per URI in parallel --t1 = time.time() --for url in urls: -- f = open(str(fileno), "wb") -- t = Test(url.rstrip(), f) -- t.start() -- threads.append((t, f)) -- fileno = fileno + 1 --# Wait for all threads to finish --for thread, file in threads: -- thread.join() -- file.close() --t2 = time.time() --print "\n** Multithreading, %d seconds elapsed for %d uris" % (int(t2-t1), len(urls)) -- --# Start one thread per URI in sequence --fileno = 0 --t1 = time.time() --for url in urls: -- f = open(str(fileno), "wb") -- t = Test(url.rstrip(), f) -- t.start() -- fileno = fileno + 1 -- t.join() -- f.close() --t2 = time.time() --print "\n** Singlethreading, %d seconds elapsed for %d uris" % (int(t2-t1), len(urls)) -diff --git a/tests/test_cb.py b/tests/test_cb.py -deleted file mode 100644 -index 1be305c..0000000 ---- a/tests/test_cb.py -+++ /dev/null -@@ -1,28 +0,0 @@ --#! /usr/bin/env python --# -*- coding: iso-8859-1 -*- --# vi:ts=4:et --# $Id$ -- --import sys --import pycurl -- --## Callback function invoked when body data is ready --def body(buf): -- # Print body data to stdout -- sys.stdout.write(buf) -- --## Callback function invoked when header data is ready --def header(buf): -- # Print header data to stderr -- sys.stderr.write(buf) -- --c = pycurl.Curl() --c.setopt(pycurl.URL, 'http://www.python.org/') --c.setopt(pycurl.WRITEFUNCTION, body) --c.setopt(pycurl.HEADERFUNCTION, header) --c.setopt(pycurl.FOLLOWLOCATION, 1) --c.setopt(pycurl.MAXREDIRS, 5) --c.perform() --c.setopt(pycurl.URL, 'http://curl.haxx.se/') --c.perform() --c.close() -diff --git a/tests/test_debug.py b/tests/test_debug.py -deleted file mode 100644 -index d439b16..0000000 ---- a/tests/test_debug.py -+++ /dev/null -@@ -1,16 +0,0 @@ --#! /usr/bin/env python --# -*- coding: iso-8859-1 -*- --# vi:ts=4:et --# $Id$ -- --import pycurl -- --def test(t, b): -- print "debug(%d): %s" % (t, b) -- --c = pycurl.Curl() --c.setopt(pycurl.URL, 'http://curl.haxx.se/') --c.setopt(pycurl.VERBOSE, 1) --c.setopt(pycurl.DEBUGFUNCTION, test) --c.perform() --c.close() -diff --git a/tests/test_ftp.py b/tests/test_ftp.py -deleted file mode 100644 -index 2d4d358..0000000 ---- a/tests/test_ftp.py -+++ /dev/null -@@ -1,13 +0,0 @@ --#! /usr/bin/env python --# -*- coding: iso-8859-1 -*- --# vi:ts=4:et --# $Id$ -- --import pycurl -- --c = pycurl.Curl() --c.setopt(c.URL, 'ftp://ftp.sunet.se/') --c.setopt(c.FTP_USE_EPSV, 1) --c.setopt(c.QUOTE, ['cwd pub', 'type i']) --c.perform() --c.close() -diff --git a/tests/test_getinfo.py b/tests/test_getinfo.py -deleted file mode 100644 -index ed64594..0000000 ---- a/tests/test_getinfo.py -+++ /dev/null -@@ -1,49 +0,0 @@ --#! /usr/bin/env python --# -*- coding: iso-8859-1 -*- --# vi:ts=4:et --# $Id$ -- --import time --import pycurl -- -- --## Callback function invoked when progress information is updated --def progress(download_t, download_d, upload_t, upload_d): -- print "Total to download %d bytes, have %d bytes so far" % \ -- (download_t, download_d) -- --url = "http://www.cnn.com" -- --print "Starting downloading", url --print --f = open("body", "wb") --h = open("header", "wb") --c = pycurl.Curl() --c.setopt(c.URL, url) --c.setopt(c.WRITEDATA, f) --c.setopt(c.NOPROGRESS, 0) --c.setopt(c.PROGRESSFUNCTION, progress) --c.setopt(c.FOLLOWLOCATION, 1) --c.setopt(c.MAXREDIRS, 5) --c.setopt(c.WRITEHEADER, h) --c.setopt(c.OPT_FILETIME, 1) --c.perform() -- --print --print "HTTP-code:", c.getinfo(c.HTTP_CODE) --print "Total-time:", c.getinfo(c.TOTAL_TIME) --print "Download speed: %.2f bytes/second" % c.getinfo(c.SPEED_DOWNLOAD) --print "Document size: %d bytes" % c.getinfo(c.SIZE_DOWNLOAD) --print "Effective URL:", c.getinfo(c.EFFECTIVE_URL) --print "Content-type:", c.getinfo(c.CONTENT_TYPE) --print "Namelookup-time:", c.getinfo(c.NAMELOOKUP_TIME) --print "Redirect-time:", c.getinfo(c.REDIRECT_TIME) --print "Redirect-count:", c.getinfo(c.REDIRECT_COUNT) --epoch = c.getinfo(c.INFO_FILETIME) --print "Filetime: %d (%s)" % (epoch, time.ctime(epoch)) --print --print "Header is in file 'header', body is in file 'body'" -- --c.close() --f.close() --h.close() -diff --git a/tests/test_internals.py b/tests/test_internals.py -deleted file mode 100644 -index 3f5eefd..0000000 ---- a/tests/test_internals.py -+++ /dev/null -@@ -1,258 +0,0 @@ --#! /usr/bin/env python --# -*- coding: iso-8859-1 -*- --# vi:ts=4:et --# $Id$ -- --# --# a simple self-test --# -- --try: -- # need Python 2.2 or better for garbage collection -- from gc import get_objects -- import gc -- del get_objects -- gc.enable() --except ImportError: -- gc = None --import copy, os, sys --from StringIO import StringIO --try: -- import cPickle --except ImportError: -- cPickle = None --try: -- import pickle --except ImportError: -- pickle = None -- --# update sys.path when running in the build directory --from util import get_sys_path --sys.path = get_sys_path() -- --import pycurl --from pycurl import Curl, CurlMulti -- -- --class opts: -- verbose = 1 -- --if "-q" in sys.argv: -- opts.verbose = opts.verbose - 1 -- -- --print "Python", sys.version --print "PycURL %s (compiled against 0x%x)" % (pycurl.version, pycurl.COMPILE_LIBCURL_VERSION_NUM) --print "PycURL version info", pycurl.version_info() --print " %s, compiled %s" % (pycurl.__file__, pycurl.COMPILE_DATE) -- -- --# /*********************************************************************** --# // test misc --# ************************************************************************/ -- --if 1: -- c = Curl() -- assert c.URL is pycurl.URL -- del c -- -- --# /*********************************************************************** --# // test handles --# ************************************************************************/ -- --# remove an invalid handle: this should fail --if 1: -- m = CurlMulti() -- c = Curl() -- try: -- m.remove_handle(c) -- except pycurl.error: -- pass -- else: -- assert 0, "internal error" -- del m, c -- -- --# remove an invalid but closed handle --if 1: -- m = CurlMulti() -- c = Curl() -- c.close() -- m.remove_handle(c) -- del m, c -- -- --# add a closed handle: this should fail --if 1: -- m = CurlMulti() -- c = Curl() -- c.close() -- try: -- m.add_handle(c) -- except pycurl.error: -- pass -- else: -- assert 0, "internal error" -- m.close() -- del m, c -- -- --# add a handle twice: this should fail --if 1: -- m = CurlMulti() -- c = Curl() -- m.add_handle(c) -- try: -- m.add_handle(c) -- except pycurl.error: -- pass -- else: -- assert 0, "internal error" -- del m, c -- -- --# add a handle on multiple stacks: this should fail --if 1: -- m1 = CurlMulti() -- m2 = CurlMulti() -- c = Curl() -- m1.add_handle(c) -- try: -- m2.add_handle(c) -- except pycurl.error: -- pass -- else: -- assert 0, "internal error" -- del m1, m2, c -- -- --# move a handle --if 1: -- m1 = CurlMulti() -- m2 = CurlMulti() -- c = Curl() -- m1.add_handle(c) -- m1.remove_handle(c) -- m2.add_handle(c) -- del m1, m2, c -- -- --# /*********************************************************************** --# // test copying and pickling - copying and pickling of --# // instances of Curl and CurlMulti is not allowed --# ************************************************************************/ -- --if 1 and copy: -- c = Curl() -- m = CurlMulti() -- try: -- copy.copy(c) -- except copy.Error: -- pass -- else: -- assert 0, "internal error - copying should fail" -- try: -- copy.copy(m) -- except copy.Error: -- pass -- else: -- assert 0, "internal error - copying should fail" -- --if 1 and pickle: -- c = Curl() -- m = CurlMulti() -- fp = StringIO() -- p = pickle.Pickler(fp, 1) -- try: -- p.dump(c) -- except pickle.PicklingError: -- pass -- else: -- assert 0, "internal error - pickling should fail" -- try: -- p.dump(m) -- except pickle.PicklingError: -- pass -- else: -- assert 0, "internal error - pickling should fail" -- del c, m, fp, p -- --if 1 and cPickle: -- c = Curl() -- m = CurlMulti() -- fp = StringIO() -- p = cPickle.Pickler(fp, 1) -- try: -- p.dump(c) -- except cPickle.PicklingError: -- pass -- else: -- assert 0, "internal error - pickling should fail" -- try: -- p.dump(m) -- except cPickle.PicklingError: -- pass -- else: -- assert 0, "internal error - pickling should fail" -- del c, m, fp, p -- -- --# /*********************************************************************** --# // test refcounts --# ************************************************************************/ -- --# basic check of reference counting (use a memory checker like valgrind) --if 1: -- c = Curl() -- m = CurlMulti() -- m.add_handle(c) -- del m -- m = CurlMulti() -- c.close() -- del m, c -- --# basic check of cyclic garbage collection --if 1 and gc: -- gc.collect() -- c = Curl() -- c.m = CurlMulti() -- c.m.add_handle(c) -- # create some nasty cyclic references -- c.c = c -- c.c.c1 = c -- c.c.c2 = c -- c.c.c3 = c.c -- c.c.c4 = c.m -- c.m.c = c -- c.m.m = c.m -- c.m.c = c -- # delete -- gc.collect() -- flags = gc.DEBUG_COLLECTABLE | gc.DEBUG_UNCOLLECTABLE | gc.DEBUG_OBJECTS -- if opts.verbose >= 1: -- flags = flags | gc.DEBUG_STATS -- gc.set_debug(flags) -- gc.collect() -- ##print gc.get_referrers(c) -- ##print gc.get_objects() -- if opts.verbose >= 1: -- print "Tracked objects:", len(gc.get_objects()) -- # The `del' below should delete these 4 objects: -- # Curl + internal dict, CurlMulti + internal dict -- del c -- gc.collect() -- if opts.verbose >= 1: -- print "Tracked objects:", len(gc.get_objects()) -- --if 1: -- # Ensure that the refcounting error in "reset" is fixed: -- for i in xrange(100000): -- c = Curl() -- c.reset() -- --# /*********************************************************************** --# // done --# ************************************************************************/ -- --print "All tests passed." -diff --git a/tests/test_memleak.py b/tests/test_memleak.py -deleted file mode 100644 -index 8577b97..0000000 ---- a/tests/test_memleak.py -+++ /dev/null -@@ -1,53 +0,0 @@ --#! /usr/bin/env python --# -*- coding: iso-8859-1 -*- --# vi:ts=4:et --# $Id$ -- --# --# just a simple self-test --# need Python 2.2 or better for garbage collection --# -- --import gc, pycurl, sys --gc.enable() -- -- --print "Python", sys.version --print "PycURL %s (compiled against 0x%x)" % (pycurl.version, pycurl.COMPILE_LIBCURL_VERSION_NUM) --##print "PycURL version info", pycurl.version_info() --print " %s, compiled %s" % (pycurl.__file__, pycurl.COMPILE_DATE) -- -- --gc.collect() --flags = gc.DEBUG_COLLECTABLE | gc.DEBUG_UNCOLLECTABLE | gc.DEBUG_OBJECTS --if 1: -- flags = flags | gc.DEBUG_STATS --gc.set_debug(flags) --gc.collect() -- --print "Tracked objects:", len(gc.get_objects()) -- --multi = pycurl.CurlMulti() --t = [] --for a in range(100): -- curl = pycurl.Curl() -- multi.add_handle(curl) -- t.append(curl) -- --print "Tracked objects:", len(gc.get_objects()) -- --for curl in t: -- curl.close() -- multi.remove_handle(curl) -- --print "Tracked objects:", len(gc.get_objects()) -- --del curl --del t --del multi -- --print "Tracked objects:", len(gc.get_objects()) --gc.collect() --print "Tracked objects:", len(gc.get_objects()) -- -- -diff --git a/tests/test_multi.py b/tests/test_multi.py -deleted file mode 100644 -index 9193986..0000000 ---- a/tests/test_multi.py -+++ /dev/null -@@ -1,33 +0,0 @@ --#! /usr/bin/env python --# -*- coding: iso-8859-1 -*- --# vi:ts=4:et --# $Id$ -- --import pycurl -- --m = pycurl.CurlMulti() --m.handles = [] --c1 = pycurl.Curl() --c2 = pycurl.Curl() --c1.setopt(c1.URL, 'http://curl.haxx.se') --c2.setopt(c2.URL, 'http://cnn.com') --c2.setopt(c2.FOLLOWLOCATION, 1) --m.add_handle(c1) --m.add_handle(c2) --m.handles.append(c1) --m.handles.append(c2) -- --num_handles = len(m.handles) --while num_handles: -- while 1: -- ret, num_handles = m.perform() -- if ret != pycurl.E_CALL_MULTI_PERFORM: -- break -- m.select(1.0) -- --m.remove_handle(c2) --m.remove_handle(c1) --del m.handles --m.close() --c1.close() --c2.close() -diff --git a/tests/test_multi2.py b/tests/test_multi2.py -deleted file mode 100644 -index 4b96789..0000000 ---- a/tests/test_multi2.py -+++ /dev/null -@@ -1,72 +0,0 @@ --#! /usr/bin/env python --# -*- coding: iso-8859-1 -*- --# vi:ts=4:et --# $Id$ -- --import os, sys --try: -- from cStringIO import StringIO --except ImportError: -- from StringIO import StringIO --import pycurl -- -- --urls = ( -- "http://curl.haxx.se", -- "http://www.python.org", -- "http://pycurl.sourceforge.net", -- "http://pycurl.sourceforge.net/tests/403_FORBIDDEN", # that actually exists ;-) -- "http://pycurl.sourceforge.net/tests/404_NOT_FOUND", --) -- --# Read list of URIs from file specified on commandline --try: -- urls = open(sys.argv[1], "rb").readlines() --except IndexError: -- # No file was specified -- pass -- --# init --m = pycurl.CurlMulti() --m.handles = [] --for url in urls: -- c = pycurl.Curl() -- # save info in standard Python attributes -- c.url = url.rstrip() -- c.body = StringIO() -- c.http_code = -1 -- m.handles.append(c) -- # pycurl API calls -- c.setopt(c.URL, c.url) -- c.setopt(c.WRITEFUNCTION, c.body.write) -- m.add_handle(c) -- --# get data --num_handles = len(m.handles) --while num_handles: -- while 1: -- ret, num_handles = m.perform() -- if ret != pycurl.E_CALL_MULTI_PERFORM: -- break -- # currently no more I/O is pending, could do something in the meantime -- # (display a progress bar, etc.) -- m.select(1.0) -- --# close handles --for c in m.handles: -- # save info in standard Python attributes -- c.http_code = c.getinfo(c.HTTP_CODE) -- # pycurl API calls -- m.remove_handle(c) -- c.close() --m.close() -- --# print result --for c in m.handles: -- data = c.body.getvalue() -- if 0: -- print "**********", c.url, "**********" -- print data -- else: -- print "%-53s http_code %3d, %6d bytes" % (c.url, c.http_code, len(data)) -- -diff --git a/tests/test_multi3.py b/tests/test_multi3.py -deleted file mode 100644 -index 5b7ea6e..0000000 ---- a/tests/test_multi3.py -+++ /dev/null -@@ -1,87 +0,0 @@ --#! /usr/bin/env python --# -*- coding: iso-8859-1 -*- --# vi:ts=4:et --# $Id$ -- --# same as test_multi2.py, but enforce some debugging and strange API-calls -- --import os, sys --try: -- from cStringIO import StringIO --except ImportError: -- from StringIO import StringIO --import pycurl -- -- --urls = ( -- "http://curl.haxx.se", -- "http://www.python.org", -- "http://pycurl.sourceforge.net", -- "http://pycurl.sourceforge.net/THIS_HANDLE_IS_CLOSED", --) -- --# init --m = pycurl.CurlMulti() --m.handles = [] --for url in urls: -- c = pycurl.Curl() -- # save info in standard Python attributes -- c.url = url -- c.body = StringIO() -- c.http_code = -1 -- c.debug = 0 -- m.handles.append(c) -- # pycurl API calls -- c.setopt(c.URL, c.url) -- c.setopt(c.WRITEFUNCTION, c.body.write) -- m.add_handle(c) -- --# debug - close a handle --if 1: -- c = m.handles[3] -- c.debug = 1 -- c.close() -- --# get data --num_handles = len(m.handles) --while num_handles: -- while 1: -- ret, num_handles = m.perform() -- if ret != pycurl.E_CALL_MULTI_PERFORM: -- break -- # currently no more I/O is pending, could do something in the meantime -- # (display a progress bar, etc.) -- m.select(1.0) -- --# close handles --for c in m.handles: -- # save info in standard Python attributes -- try: -- c.http_code = c.getinfo(c.HTTP_CODE) -- except pycurl.error: -- # handle already closed - see debug above -- assert c.debug -- c.http_code = -1 -- # pycurl API calls -- if 0: -- m.remove_handle(c) -- c.close() -- elif 0: -- # in the C API this is the wrong calling order, but pycurl -- # handles this automatically -- c.close() -- m.remove_handle(c) -- else: -- # actually, remove_handle is called automatically on close -- c.close() --m.close() -- --# print result --for c in m.handles: -- data = c.body.getvalue() -- if 0: -- print "**********", c.url, "**********" -- print data -- else: -- print "%-53s http_code %3d, %6d bytes" % (c.url, c.http_code, len(data)) -- -diff --git a/tests/test_multi4.py b/tests/test_multi4.py -deleted file mode 100644 -index f37ea26..0000000 ---- a/tests/test_multi4.py -+++ /dev/null -@@ -1,57 +0,0 @@ --#! /usr/bin/env python --# -*- coding: iso-8859-1 -*- --# vi:ts=4:et --# $Id$ -- --import sys, select, time --import pycurl -- --c1 = pycurl.Curl() --c2 = pycurl.Curl() --c3 = pycurl.Curl() --c1.setopt(c1.URL, "http://www.python.org") --c2.setopt(c2.URL, "http://curl.haxx.se") --c3.setopt(c3.URL, "http://slashdot.org") --c1.body = open("doc1", "wb") --c2.body = open("doc2", "wb") --c3.body = open("doc3", "wb") --c1.setopt(c1.WRITEFUNCTION, c1.body.write) --c2.setopt(c2.WRITEFUNCTION, c2.body.write) --c3.setopt(c3.WRITEFUNCTION, c3.body.write) -- --m = pycurl.CurlMulti() --m.add_handle(c1) --m.add_handle(c2) --m.add_handle(c3) -- --# Number of seconds to wait for a timeout to happen --SELECT_TIMEOUT = 1.0 -- --# Stir the state machine into action --while 1: -- ret, num_handles = m.perform() -- if ret != pycurl.E_CALL_MULTI_PERFORM: -- break -- --# Keep going until all the connections have terminated --while num_handles: -- apply(select.select, m.fdset() + (SELECT_TIMEOUT,)) -- while 1: -- ret, num_handles = m.perform() -- if ret != pycurl.E_CALL_MULTI_PERFORM: -- break -- --# Cleanup --m.remove_handle(c3) --m.remove_handle(c2) --m.remove_handle(c1) --m.close() --c1.body.close() --c2.body.close() --c3.body.close() --c1.close() --c2.close() --c3.close() --print "http://www.python.org is in file doc1" --print "http://curl.haxx.se is in file doc2" --print "http://slashdot.org is in file doc3" -diff --git a/tests/test_multi5.py b/tests/test_multi5.py -deleted file mode 100644 -index 3f0c8df..0000000 ---- a/tests/test_multi5.py -+++ /dev/null -@@ -1,60 +0,0 @@ --#! /usr/bin/env python --# -*- coding: iso-8859-1 -*- --# vi:ts=4:et --# $Id$ -- --import sys, select, time --import pycurl -- --c1 = pycurl.Curl() --c2 = pycurl.Curl() --c3 = pycurl.Curl() --c1.setopt(c1.URL, "http://www.python.org") --c2.setopt(c2.URL, "http://curl.haxx.se") --c3.setopt(c3.URL, "http://slashdot.org") --c1.body = open("doc1", "wb") --c2.body = open("doc2", "wb") --c3.body = open("doc3", "wb") --c1.setopt(c1.WRITEFUNCTION, c1.body.write) --c2.setopt(c2.WRITEFUNCTION, c2.body.write) --c3.setopt(c3.WRITEFUNCTION, c3.body.write) -- --m = pycurl.CurlMulti() --m.add_handle(c1) --m.add_handle(c2) --m.add_handle(c3) -- --# Number of seconds to wait for a timeout to happen --SELECT_TIMEOUT = 1.0 -- --# Stir the state machine into action --while 1: -- ret, num_handles = m.perform() -- if ret != pycurl.E_CALL_MULTI_PERFORM: -- break -- --# Keep going until all the connections have terminated --while num_handles: -- # The select method uses fdset internally to determine which file descriptors -- # to check. -- m.select(SELECT_TIMEOUT) -- while 1: -- ret, num_handles = m.perform() -- if ret != pycurl.E_CALL_MULTI_PERFORM: -- break -- --# Cleanup --m.remove_handle(c3) --m.remove_handle(c2) --m.remove_handle(c1) --m.close() --c1.body.close() --c2.body.close() --c3.body.close() --c1.close() --c2.close() --c3.close() --print "http://www.python.org is in file doc1" --print "http://curl.haxx.se is in file doc2" --print "http://slashdot.org is in file doc3" -- -diff --git a/tests/test_multi6.py b/tests/test_multi6.py -deleted file mode 100644 -index 35a284f..0000000 ---- a/tests/test_multi6.py -+++ /dev/null -@@ -1,62 +0,0 @@ --#! /usr/bin/env python --# -*- coding: iso-8859-1 -*- --# vi:ts=4:et --# $Id$ -- --import sys, select, time --import pycurl -- --c1 = pycurl.Curl() --c2 = pycurl.Curl() --c3 = pycurl.Curl() --c1.setopt(c1.URL, "http://www.python.org") --c2.setopt(c2.URL, "http://curl.haxx.se") --c3.setopt(c3.URL, "http://slashdot.org") --c1.body = open("doc1", "wb") --c2.body = open("doc2", "wb") --c3.body = open("doc3", "wb") --c1.setopt(c1.WRITEFUNCTION, c1.body.write) --c2.setopt(c2.WRITEFUNCTION, c2.body.write) --c3.setopt(c3.WRITEFUNCTION, c3.body.write) -- --m = pycurl.CurlMulti() --m.add_handle(c1) --m.add_handle(c2) --m.add_handle(c3) -- --# Number of seconds to wait for a timeout to happen --SELECT_TIMEOUT = 1.0 -- --# Stir the state machine into action --while 1: -- ret, num_handles = m.perform() -- if ret != pycurl.E_CALL_MULTI_PERFORM: -- break -- --# Keep going until all the connections have terminated --while num_handles: -- # The select method uses fdset internally to determine which file descriptors -- # to check. -- m.select(SELECT_TIMEOUT) -- while 1: -- ret, num_handles = m.perform() -- # Print the message, if any -- print m.info_read(1) -- if ret != pycurl.E_CALL_MULTI_PERFORM: -- break -- --# Cleanup --m.remove_handle(c3) --m.remove_handle(c2) --m.remove_handle(c1) --m.close() --c1.body.close() --c2.body.close() --c3.body.close() --c1.close() --c2.close() --c3.close() --print "http://www.python.org is in file doc1" --print "http://curl.haxx.se is in file doc2" --print "http://slashdot.org is in file doc3" -- -diff --git a/tests/test_multi_socket.py b/tests/test_multi_socket.py -deleted file mode 100644 -index 6768061..0000000 ---- a/tests/test_multi_socket.py -+++ /dev/null -@@ -1,82 +0,0 @@ --#! /usr/bin/env python --# -*- coding: iso-8859-1 -*- --# vi:ts=4:et --# $Id$ -- --import os, sys --try: -- from cStringIO import StringIO --except ImportError: -- from StringIO import StringIO --import pycurl -- -- --urls = ( -- "http://curl.haxx.se", -- "http://www.python.org", -- "http://pycurl.sourceforge.net", --) -- --# Read list of URIs from file specified on commandline --try: -- urls = open(sys.argv[1], "rb").readlines() --except IndexError: -- # No file was specified -- pass -- --# timer callback --def timer(msecs): -- print 'Timer callback msecs:', msecs -- --# socket callback --def socket(event, socket, multi, data): -- print event, socket, multi, data --# multi.assign(socket, timer) -- --# init --m = pycurl.CurlMulti() --m.setopt(pycurl.M_PIPELINING, 1) --m.setopt(pycurl.M_TIMERFUNCTION, timer) --m.setopt(pycurl.M_SOCKETFUNCTION, socket) --m.handles = [] --for url in urls: -- c = pycurl.Curl() -- # save info in standard Python attributes -- c.url = url -- c.body = StringIO() -- c.http_code = -1 -- m.handles.append(c) -- # pycurl API calls -- c.setopt(c.URL, c.url) -- c.setopt(c.WRITEFUNCTION, c.body.write) -- m.add_handle(c) -- --# get data --num_handles = len(m.handles) --while num_handles: -- while 1: -- ret, num_handles = m.socket_all() -- if ret != pycurl.E_CALL_MULTI_PERFORM: -- break -- # currently no more I/O is pending, could do something in the meantime -- # (display a progress bar, etc.) -- m.select(1.0) -- --# close handles --for c in m.handles: -- # save info in standard Python attributes -- c.http_code = c.getinfo(c.HTTP_CODE) -- # pycurl API calls -- m.remove_handle(c) -- c.close() --m.close() -- --# print result --for c in m.handles: -- data = c.body.getvalue() -- if 0: -- print "**********", c.url, "**********" -- print data -- else: -- print "%-53s http_code %3d, %6d bytes" % (c.url, c.http_code, len(data)) -- -diff --git a/tests/test_multi_socket_select.py b/tests/test_multi_socket_select.py -deleted file mode 100644 -index 1f56d1d..0000000 ---- a/tests/test_multi_socket_select.py -+++ /dev/null -@@ -1,105 +0,0 @@ --#! /usr/bin/env python --# -*- coding: iso-8859-1 -*- --# vi:ts=4:et --# $Id$ -- --import os, sys --try: -- from cStringIO import StringIO --except ImportError: -- from StringIO import StringIO --import pycurl --import select -- --sockets = set() --timeout = 0 -- --urls = ( -- "http://curl.haxx.se", -- "http://www.python.org", -- "http://pycurl.sourceforge.net", --) -- --# Read list of URIs from file specified on commandline --try: -- urls = open(sys.argv[1], "rb").readlines() --except IndexError: -- # No file was specified -- pass -- --# timer callback --def timer(msecs): -- global timeout -- timeout = msecs -- print 'Timer callback msecs:', msecs -- --# socket callback --def socket(event, socket, multi, data): -- if event == pycurl.POLL_REMOVE: -- print "Remove Socket %d"%socket -- sockets.remove(socket) -- else: -- if socket not in sockets: -- print "Add socket %d"%socket -- sockets.add(socket) -- print event, socket, multi, data -- --# init --m = pycurl.CurlMulti() --m.setopt(pycurl.M_PIPELINING, 1) --m.setopt(pycurl.M_TIMERFUNCTION, timer) --m.setopt(pycurl.M_SOCKETFUNCTION, socket) --m.handles = [] --for url in urls: -- c = pycurl.Curl() -- # save info in standard Python attributes -- c.url = url -- c.body = StringIO() -- c.http_code = -1 -- m.handles.append(c) -- # pycurl API calls -- c.setopt(c.URL, c.url) -- c.setopt(c.WRITEFUNCTION, c.body.write) -- m.add_handle(c) -- --# get data --num_handles = len(m.handles) -- --while (pycurl.E_CALL_MULTI_PERFORM==m.socket_all()[0]): -- pass -- --timeout = m.timeout() -- -- --while True: -- (rr, wr, er) = select.select(sockets,sockets,sockets,timeout/1000.0) -- socketSet = set(rr+wr+er) -- if socketSet: -- for s in socketSet: -- while True: -- (ret,running) = m.socket_action(s,0) -- if ret!=pycurl.E_CALL_MULTI_PERFORM: -- break -- else: -- (ret,running) = m.socket_action(pycurl.SOCKET_TIMEOUT,0) -- if running==0: -- break -- --# close handles --for c in m.handles: -- # save info in standard Python attributes -- c.http_code = c.getinfo(c.HTTP_CODE) -- # pycurl API calls -- m.remove_handle(c) -- c.close() --m.close() -- --# print result --for c in m.handles: -- data = c.body.getvalue() -- if 0: -- print "**********", c.url, "**********" -- print data -- else: -- print "%-53s http_code %3d, %6d bytes" % (c.url, c.http_code, len(data)) -- -diff --git a/tests/test_multi_timer.py b/tests/test_multi_timer.py -deleted file mode 100644 -index 17371d3..0000000 ---- a/tests/test_multi_timer.py -+++ /dev/null -@@ -1,76 +0,0 @@ --#! /usr/bin/env python --# -*- coding: iso-8859-1 -*- --# vi:ts=4:et --# $Id$ -- --import os, sys --try: -- from cStringIO import StringIO --except ImportError: -- from StringIO import StringIO --import pycurl -- -- --urls = ( -- "http://curl.haxx.se", -- "http://www.python.org", -- "http://pycurl.sourceforge.net", --) -- --# Read list of URIs from file specified on commandline --try: -- urls = open(sys.argv[1], "rb").readlines() --except IndexError: -- # No file was specified -- pass -- --# timer callback --def timer(msecs): -- print 'Timer callback msecs:', msecs -- --# init --m = pycurl.CurlMulti() --m.setopt(pycurl.M_PIPELINING, 1) --m.setopt(pycurl.M_TIMERFUNCTION, timer) --m.handles = [] --for url in urls: -- c = pycurl.Curl() -- # save info in standard Python attributes -- c.url = url -- c.body = StringIO() -- c.http_code = -1 -- m.handles.append(c) -- # pycurl API calls -- c.setopt(c.URL, c.url) -- c.setopt(c.WRITEFUNCTION, c.body.write) -- m.add_handle(c) -- --# get data --num_handles = len(m.handles) --while num_handles: -- while 1: -- ret, num_handles = m.perform() -- if ret != pycurl.E_CALL_MULTI_PERFORM: -- break -- # currently no more I/O is pending, could do something in the meantime -- # (display a progress bar, etc.) -- m.select(1.0) -- --# close handles --for c in m.handles: -- # save info in standard Python attributes -- c.http_code = c.getinfo(c.HTTP_CODE) -- # pycurl API calls -- m.remove_handle(c) -- c.close() --m.close() -- --# print result --for c in m.handles: -- data = c.body.getvalue() -- if 0: -- print "**********", c.url, "**********" -- print data -- else: -- print "%-53s http_code %3d, %6d bytes" % (c.url, c.http_code, len(data)) -- -diff --git a/tests/test_multi_vs_thread.py b/tests/test_multi_vs_thread.py -deleted file mode 100644 -index 0caed60..0000000 ---- a/tests/test_multi_vs_thread.py -+++ /dev/null -@@ -1,262 +0,0 @@ --#! /usr/bin/env python --# -*- coding: iso-8859-1 -*- --# vi:ts=4:et --# $Id$ -- --import os, sys, time --from threading import Thread, RLock --try: -- from cStringIO import StringIO --except ImportError: -- from StringIO import StringIO --import pycurl -- --# We should ignore SIGPIPE when using pycurl.NOSIGNAL - see --# the libcurl tutorial for more info. --try: -- import signal -- from signal import SIGPIPE, SIG_IGN -- signal.signal(signal.SIGPIPE, signal.SIG_IGN) --except ImportError: -- pass -- --# The conclusion is: the multi interface is fastest! -- --NUM_PAGES = 30 --NUM_THREADS = 10 --assert NUM_PAGES % NUM_THREADS == 0 -- --##URL = "http://pycurl.sourceforge.net/tests/testgetvars.php?%d" --URL = "http://pycurl.sourceforge.net/tests/teststaticpage.html?%d" -- -- --# --# util --# -- --class Curl: -- def __init__(self, url): -- self.url = url -- self.body = StringIO() -- self.http_code = -1 -- # pycurl API calls -- self._curl = pycurl.Curl() -- self._curl.setopt(pycurl.URL, self.url) -- self._curl.setopt(pycurl.WRITEFUNCTION, self.body.write) -- self._curl.setopt(pycurl.NOSIGNAL, 1) -- -- def perform(self): -- self._curl.perform() -- -- def close(self): -- self.http_code = self._curl.getinfo(pycurl.HTTP_CODE) -- self._curl.close() -- -- --def print_result(items): -- return # DO NOTHING -- # -- for c in items: -- data = c.body.getvalue() -- if 0: -- print "**********", c.url, "**********" -- print data -- elif 1: -- print "%-60s %3d %6d" % (c.url, c.http_code, len(data)) -- -- --### --### 1) multi --### -- --def test_multi(): -- clock1 = time.time() -- -- # init -- handles = [] -- m = pycurl.CurlMulti() -- for i in range(NUM_PAGES): -- c = Curl(URL %i) -- m.add_handle(c._curl) -- handles.append(c) -- -- clock2 = time.time() -- -- # stir state machine into action -- while 1: -- ret, num_handles = m.perform() -- if ret != pycurl.E_CALL_MULTI_PERFORM: -- break -- -- # get data -- while num_handles: -- m.select(1.0) -- while 1: -- ret, num_handles = m.perform() -- if ret != pycurl.E_CALL_MULTI_PERFORM: -- break -- -- clock3 = time.time() -- -- # close handles -- for c in handles: -- c.close() -- m.close() -- -- clock4 = time.time() -- print "multi interface: %d pages: perform %5.2f secs, total %5.2f secs" % (NUM_PAGES, clock3 - clock2, clock4 - clock1) -- -- # print result -- print_result(handles) -- -- -- --### --### 2) thread --### -- --class Test(Thread): -- def __init__(self, lock=None): -- Thread.__init__(self) -- self.lock = lock -- self.items = [] -- -- def run(self): -- if self.lock: -- self.lock.acquire() -- self.lock.release() -- for c in self.items: -- c.perform() -- -- --def test_threads(lock=None): -- clock1 = time.time() -- -- # create and start threads, but block them -- if lock: -- lock.acquire() -- -- # init (FIXME - this is ugly) -- threads = [] -- handles = [] -- t = None -- for i in range(NUM_PAGES): -- if i % (NUM_PAGES / NUM_THREADS) == 0: -- t = Test(lock) -- if lock: -- t.start() -- threads.append(t) -- c = Curl(URL % i) -- t.items.append(c) -- handles.append(c) -- assert len(handles) == NUM_PAGES -- assert len(threads) == NUM_THREADS -- -- clock2 = time.time() -- -- # -- if lock: -- # release lock to let the blocked threads run -- lock.release() -- else: -- # start threads -- for t in threads: -- t.start() -- # wait for threads to finish -- for t in threads: -- t.join() -- -- clock3 = time.time() -- -- # close handles -- for c in handles: -- c.close() -- -- clock4 = time.time() -- if lock: -- print "thread interface [lock]: %d pages: perform %5.2f secs, total %5.2f secs" % (NUM_PAGES, clock3 - clock2, clock4 - clock1) -- else: -- print "thread interface: %d pages: perform %5.2f secs, total %5.2f secs" % (NUM_PAGES, clock3 - clock2, clock4 - clock1) -- -- # print result -- print_result(handles) -- -- -- --### --### 3) thread - threads grab curl objects on demand from a shared pool --### -- --class TestPool(Thread): -- def __init__(self, lock, pool): -- Thread.__init__(self) -- self.lock = lock -- self.pool = pool -- -- def run(self): -- while 1: -- self.lock.acquire() -- c = None -- if self.pool: -- c = self.pool.pop() -- self.lock.release() -- if c is None: -- break -- c.perform() -- -- --def test_thread_pool(lock): -- clock1 = time.time() -- -- # init -- handles = [] -- for i in range(NUM_PAGES): -- c = Curl(URL %i) -- handles.append(c) -- -- # create and start threads, but block them -- lock.acquire() -- threads = [] -- pool = handles[:] # shallow copy of the list, shared for pop() -- for i in range(NUM_THREADS): -- t = TestPool(lock, pool) -- t.start() -- threads.append(t) -- assert len(pool) == NUM_PAGES -- assert len(threads) == NUM_THREADS -- -- clock2 = time.time() -- -- # release lock to let the blocked threads run -- lock.release() -- -- # wait for threads to finish -- for t in threads: -- t.join() -- -- clock3 = time.time() -- -- # close handles -- for c in handles: -- c.close() -- -- clock4 = time.time() -- print "thread interface [pool]: %d pages: perform %5.2f secs, total %5.2f secs" % (NUM_PAGES, clock3 - clock2, clock4 - clock1) -- -- # print result -- print_result(handles) -- -- -- --lock = RLock() --if 1: -- test_multi() -- test_threads() -- test_threads(lock) -- test_thread_pool(lock) --else: -- test_thread_pool(lock) -- test_threads(lock) -- test_threads() -- test_multi() -- -diff --git a/tests/test_post.py b/tests/test_post.py -deleted file mode 100644 -index f0a8ad0..0000000 ---- a/tests/test_post.py -+++ /dev/null -@@ -1,24 +0,0 @@ --#! /usr/bin/env python --# -*- coding: iso-8859-1 -*- --# vi:ts=4:et --# $Id$ -- --import urllib --import pycurl -- --# simple --pf = {'field1': 'value1'} -- --# multiple fields --pf = {'field1':'value1', 'field2':'value2 with blanks', 'field3':'value3'} -- --# multiple fields with & in field --pf = {'field1':'value1', 'field2':'value2 with blanks and & chars', -- 'field3':'value3'} -- --c = pycurl.Curl() --c.setopt(c.URL, 'http://pycurl.sourceforge.net/tests/testpostvars.php') --c.setopt(c.POSTFIELDS, urllib.urlencode(pf)) --c.setopt(c.VERBOSE, 1) --c.perform() --c.close() -diff --git a/tests/test_post2.py b/tests/test_post2.py -deleted file mode 100644 -index 74a6eca..0000000 ---- a/tests/test_post2.py -+++ /dev/null -@@ -1,18 +0,0 @@ --#! /usr/bin/env python --# -*- coding: iso-8859-1 -*- --# vi:ts=4:et --# $Id$ -- --import pycurl -- --pf = [('field1', 'this is a test using httppost & stuff'), -- ('field2', (pycurl.FORM_FILE, 'test_post.py', pycurl.FORM_FILE, 'test_post2.py')), -- ('field3', (pycurl.FORM_CONTENTS, 'this is wei\000rd, but null-bytes are okay')) -- ] -- --c = pycurl.Curl() --c.setopt(c.URL, 'http://www.contactor.se/~dast/postit.cgi') --c.setopt(c.HTTPPOST, pf) --c.setopt(c.VERBOSE, 1) --c.perform() --c.close() -diff --git a/tests/test_post3.py b/tests/test_post3.py -deleted file mode 100644 -index 617eba2..0000000 ---- a/tests/test_post3.py -+++ /dev/null -@@ -1,32 +0,0 @@ --#! /usr/bin/env python --# -*- coding: iso-8859-1 -*- --# vi:ts=4:et --# $Id$ -- --import urllib --POSTSTRING = urllib.urlencode({'field1':'value1', 'field2':'value2 with blanks', 'field3':'value3'}) -- --class test: -- -- def __init__(self): -- self.finished = False -- -- def read_cb(self, size): -- assert len(POSTSTRING) <= size -- if not self.finished: -- self.finished = True -- return POSTSTRING -- else: -- # Nothing more to read -- return "" -- --import pycurl --c = pycurl.Curl() --t = test() --c.setopt(c.URL, 'http://pycurl.sourceforge.net/tests/testpostvars.php') --c.setopt(c.POST, 1) --c.setopt(c.POSTFIELDSIZE, len(POSTSTRING)) --c.setopt(c.READFUNCTION, t.read_cb) --c.setopt(c.VERBOSE, 1) --c.perform() --c.close() -diff --git a/tests/test_reset.py b/tests/test_reset.py -deleted file mode 100644 -index 1addcfe..0000000 ---- a/tests/test_reset.py -+++ /dev/null -@@ -1,75 +0,0 @@ --#!/usr/bin/python -- --import sys --import pycurl -- --saw_error = 1 -- --def main(): -- global saw_error -- -- pycurl.global_init(pycurl.GLOBAL_DEFAULT) -- -- outf = file("/dev/null", "rb+") -- cm = pycurl.CurlMulti() -- -- # Set multi handle's options -- cm.setopt(pycurl.M_PIPELINING, 1) -- -- eh = pycurl.Curl() -- -- for x in range(1, 20): -- -- eh.setopt(pycurl.WRITEDATA, outf) -- eh.setopt(pycurl.URL, sys.argv[1]) -- cm.add_handle(eh) -- -- while 1: -- ret, active_handles = cm.perform() -- if ret != pycurl.E_CALL_MULTI_PERFORM: -- break -- -- while active_handles: -- ret = cm.select(1.0) -- if ret == -1: -- continue -- while 1: -- ret, active_handles = cm.perform() -- if ret != pycurl.E_CALL_MULTI_PERFORM: -- break -- -- count, good, bad = cm.info_read() -- -- for h, en, em in bad: -- print "Transfer to %s failed with %d, %s\n" % \ -- (h.getinfo(pycurl.EFFECTIVE_URL), en, em) -- raise RuntimeError -- -- for h in good: -- httpcode = h.getinfo(pycurl.RESPONSE_CODE) -- if httpcode != 200: -- print "Transfer to %s failed with code %d\n" %\ -- (h.getinfo(pycurl.EFFECTIVE_URL), httpcode) -- raise RuntimeError -- -- else: -- print "Recd %d bytes from %s" % \ -- (h.getinfo(pycurl.SIZE_DOWNLOAD), -- h.getinfo(pycurl.EFFECTIVE_URL)) -- -- cm.remove_handle(eh) -- eh.reset() -- -- eh.close() -- cm.close() -- outf.close() -- -- pycurl.global_cleanup() -- -- --if __name__ == '__main__': -- if len(sys.argv) != 2: -- print "Usage: %s " % sys.argv[0] -- sys.exit(2) -- main() -- -diff --git a/tests/test_share.py b/tests/test_share.py -deleted file mode 100644 -index 3332cda..0000000 ---- a/tests/test_share.py -+++ /dev/null -@@ -1,34 +0,0 @@ --#! /usr/bin/env python --# -*- coding: iso-8859-1 -*- --# vi:ts=4:et --# $Id$ -- --import sys --import pycurl --import threading -- --print >>sys.stderr, 'Testing', pycurl.version -- -- --class Test(threading.Thread): -- -- def __init__(self, share): -- threading.Thread.__init__(self) -- self.curl = pycurl.Curl() -- self.curl.setopt(pycurl.URL, 'http://curl.haxx.se') -- self.curl.setopt(pycurl.SHARE, share) -- -- def run(self): -- self.curl.perform() -- self.curl.close() -- --s = pycurl.CurlShare() --s.setopt(pycurl.SH_SHARE, pycurl.LOCK_DATA_COOKIE) --s.setopt(pycurl.SH_SHARE, pycurl.LOCK_DATA_DNS) -- --t1 = Test(s) --t2 = Test(s) -- --t1.start() --t2.start() --del s -diff --git a/tests/test_socketopen.py b/tests/test_socketopen.py -deleted file mode 100644 -index d3f0a62..0000000 ---- a/tests/test_socketopen.py -+++ /dev/null -@@ -1,17 +0,0 @@ --import pycurl --import StringIO --import socket -- --def socketopen(family, socktype, protocol): -- print family, socktype, protocol -- s = socket.socket(family, socktype, protocol) -- s.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) -- return s -- --sio = StringIO.StringIO() -- --c = pycurl.Curl() --c.setopt(pycurl.OPENSOCKETFUNCTION, socketopen) --c.setopt(pycurl.URL, 'http://camvine.com') --c.setopt(pycurl.WRITEFUNCTION, sio.write) --c.perform() -diff --git a/tests/test_stringio.py b/tests/test_stringio.py -deleted file mode 100644 -index 25e639b..0000000 ---- a/tests/test_stringio.py -+++ /dev/null -@@ -1,25 +0,0 @@ --#! /usr/bin/env python --# -*- coding: iso-8859-1 -*- --# vi:ts=4:et --# $Id$ -- --import sys --try: -- from cStringIO import StringIO --except ImportError: -- from StringIO import StringIO --import pycurl -- --url = "http://curl.haxx.se/dev/" -- --print "Testing", pycurl.version -- --body = StringIO() --c = pycurl.Curl() --c.setopt(c.URL, url) --c.setopt(c.WRITEFUNCTION, body.write) --c.perform() --c.close() -- --contents = body.getvalue() --print contents --- -1.7.1 - - -From f19bc32a8351b67ab3690b3ee0d4fe6843749a9b Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Wed, 6 Mar 2013 13:36:38 -0500 -Subject: [PATCH 063/149] Fix make test to run nosetests (closes #5) - -Signed-off-by: Kamil Dudka ---- - Makefile | 4 +++- - 1 files changed, 3 insertions(+), 1 deletions(-) - -diff --git a/Makefile b/Makefile -index 9b2369d..9475250 100644 ---- a/Makefile -+++ b/Makefile -@@ -7,6 +7,7 @@ SHELL = /bin/sh - - PYTHON = python2.3 - PYTHON = python -+NOSETESTS = nosetests - - all build: - $(PYTHON) setup.py build -@@ -15,7 +16,8 @@ build-7.10.8: - $(PYTHON) setup.py build --curl-config=/home/hosts/localhost/packages/curl-7.10.8/bin/curl-config - - test: build -- $(PYTHON) tests/test_internals.py -q -+ PYTHONPATH=$$(ls -d build/lib.*):$$PYTHONPATH \ -+ $(NOSETESTS) - - # (needs GNU binutils) - strip: build --- -1.7.1 - - -From 61d61649b7687c85bdcef3da3650bcce03c4735d Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Wed, 6 Mar 2013 04:25:47 -0500 -Subject: [PATCH 064/149] First stab at travis configuration - -Signed-off-by: Kamil Dudka ---- - .travis.yml | 11 +++++++++++ - requirements-dev.txt | 1 + - 2 files changed, 12 insertions(+), 0 deletions(-) - create mode 100644 .travis.yml - create mode 100644 requirements-dev.txt - -diff --git a/.travis.yml b/.travis.yml -new file mode 100644 -index 0000000..2350dfb ---- /dev/null -+++ b/.travis.yml -@@ -0,0 +1,11 @@ -+language: python -+python: -+ - "2.5" -+ - "2.6" -+ - "2.7" -+install: > -+ pip install -r requirements-dev.txt --use-mirrors && -+ sudo apt-get install vsftpd -+script: > -+ export PYCURL_VSFTPD_PATH=/usr/sbin/vsftpd && -+ nosetests -diff --git a/requirements-dev.txt b/requirements-dev.txt -new file mode 100644 -index 0000000..f3c7e8e ---- /dev/null -+++ b/requirements-dev.txt -@@ -0,0 +1 @@ -+nose --- -1.7.1 - - -From 8ceafd381dd7bca305686e33529a207b6544e2b4 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Wed, 6 Mar 2013 04:37:47 -0500 -Subject: [PATCH 065/149] Building the C module will help - -Signed-off-by: Kamil Dudka ---- - .travis.yml | 2 ++ - 1 files changed, 2 insertions(+), 0 deletions(-) - -diff --git a/.travis.yml b/.travis.yml -index 2350dfb..ae17929 100644 ---- a/.travis.yml -+++ b/.travis.yml -@@ -7,5 +7,7 @@ install: > - pip install -r requirements-dev.txt --use-mirrors && - sudo apt-get install vsftpd - script: > -+ make && -+ export PYTHONPATH=build/lib.* && - export PYCURL_VSFTPD_PATH=/usr/sbin/vsftpd && - nosetests --- -1.7.1 - - -From 1365d5ca2d70074d588cd23392862264494f0eeb Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Wed, 6 Mar 2013 04:45:46 -0500 -Subject: [PATCH 066/149] Go through more hoops (/bin/dash in play?) - -Signed-off-by: Kamil Dudka ---- - .travis.yml | 2 +- - 1 files changed, 1 insertions(+), 1 deletions(-) - -diff --git a/.travis.yml b/.travis.yml -index ae17929..4afdff9 100644 ---- a/.travis.yml -+++ b/.travis.yml -@@ -8,6 +8,6 @@ install: > - sudo apt-get install vsftpd - script: > - make && -- export PYTHONPATH=build/lib.* && -+ export PYTHONPATH=$(ls -d build/lib.*) && - export PYCURL_VSFTPD_PATH=/usr/sbin/vsftpd && - nosetests --- -1.7.1 - - -From 99896926cfdfc180f8c330cf2371c6cb023b4bac Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Wed, 6 Mar 2013 04:50:21 -0500 -Subject: [PATCH 067/149] Forgot about bottle - -Signed-off-by: Kamil Dudka ---- - requirements-dev.txt | 1 + - 1 files changed, 1 insertions(+), 0 deletions(-) - -diff --git a/requirements-dev.txt b/requirements-dev.txt -index f3c7e8e..36b0b24 100644 ---- a/requirements-dev.txt -+++ b/requirements-dev.txt -@@ -1 +1,2 @@ -+bottle - nose --- -1.7.1 - - -From ce37bf646c42352895ba83c6bf7364012666c5c7 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Wed, 6 Mar 2013 06:20:25 -0500 -Subject: [PATCH 068/149] Python 2.5 needs simplejson - -Signed-off-by: Kamil Dudka ---- - .travis.yml | 6 +++++- - requirements-dev-2.5.txt | 2 ++ - 2 files changed, 7 insertions(+), 1 deletions(-) - create mode 100644 requirements-dev-2.5.txt - -diff --git a/.travis.yml b/.travis.yml -index 4afdff9..4268895 100644 ---- a/.travis.yml -+++ b/.travis.yml -@@ -4,7 +4,11 @@ python: - - "2.6" - - "2.7" - install: > -- pip install -r requirements-dev.txt --use-mirrors && -+ if test -e requirements-dev-$TRAVIS_PYTHON_VERSION.txt; then -+ pip install -r requirements-dev-$TRAVIS_PYTHON_VERSION.txt --use-mirrors -+ else -+ pip install -r requirements-dev.txt --use-mirrors -+ fi && - sudo apt-get install vsftpd - script: > - make && -diff --git a/requirements-dev-2.5.txt b/requirements-dev-2.5.txt -new file mode 100644 -index 0000000..52e3460 ---- /dev/null -+++ b/requirements-dev-2.5.txt -@@ -0,0 +1,2 @@ -+-r requirements-dev.txt -+simplejson --- -1.7.1 - - -From bc7eb4aad84f8249356c9d221fd5f404943f6699 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Thu, 7 Mar 2013 04:23:52 -0500 -Subject: [PATCH 069/149] Expand readme (mostly borrowed from @Lispython's fork) - -Signed-off-by: Kamil Dudka ---- - README | 13 -------- - README.rst | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - 2 files changed, 93 insertions(+), 13 deletions(-) - delete mode 100644 README - create mode 100644 README.rst - -diff --git a/README b/README -deleted file mode 100644 -index 6b3e1d4..0000000 ---- a/README -+++ /dev/null -@@ -1,13 +0,0 @@ --License --------- -- --Copyright (C) 2001-2008 by Kjetil Jacobsen --Copyright (C) 2001-2008 by Markus F.X.J. Oberhumer -- --All rights reserved. -- --PycURL is dual licensed under the LGPL and an MIT/X derivative license --based on the cURL license. A full copy of the LGPL license is included --in the file COPYING. A full copy of the MIT/X derivative license is --included in the file COPYING2. You can redistribute and/or modify PycURL --according to the terms of either license. -diff --git a/README.rst b/README.rst -new file mode 100644 -index 0000000..3518d9d ---- /dev/null -+++ b/README.rst -@@ -0,0 +1,93 @@ -+PycURL: Python interface to libcurl -+==================================== -+ -+PycURL is a Python interface to `libcurl`_. PycURL can be used to fetch objects -+identified by a URL from a Python program, similar to the `urllib`_ Python module. -+PycURL is mature, very fast, and supports a lot of features. -+ -+Overview -+-------- -+ -+- libcurl is a free and easy-to-use client-side URL transfer library, supporting -+ FTP, FTPS, HTTP, HTTPS, SCP, SFTP, TFTP, TELNET, DICT, LDAP, LDAPS, FILE, IMAP, -+ SMTP, POP3 and RTSP. libcurl supports SSL certificates, HTTP POST, HTTP PUT, -+ FTP uploading, HTTP form based upload, proxies, cookies, user+password -+ authentication (Basic, Digest, NTLM, Negotiate, Kerberos4), file transfer -+ resume, http proxy tunneling and more! -+ -+- libcurl is highly portable, it builds and works identically on numerous -+ platforms, including Solaris, NetBSD, FreeBSD, OpenBSD, Darwin, HPUX, IRIX, -+ AIX, Tru64, Linux, UnixWare, HURD, Windows, Amiga, OS/2, BeOs, Mac OS X, -+ Ultrix, QNX, OpenVMS, RISC OS, Novell NetWare, DOS and more... -+ -+- libcurl is `free`_, `thread-safe`_, `IPv6 compatible`_, `feature rich`_, -+ `well supported`_, `fast`_, `thoroughly documented`_ and is already used by -+ many known, big and successful `companies`_ and numerous `applications`_. -+ -+.. _free: http://curl.haxx.se/docs/copyright.html -+.. _thread-safe: http://curl.haxx.se/libcurl/features.html#thread -+.. _`IPv6 compatible`: http://curl.haxx.se/libcurl/features.html#ipv6 -+.. _`feature rich`: http://curl.haxx.se/libcurl/features.html#features -+.. _`well supported`: http://curl.haxx.se/libcurl/features.html#support -+.. _`fast`: http://curl.haxx.se/libcurl/features.html#fast -+.. _`thoroughly documented`: http://curl.haxx.se/libcurl/features.html#docs -+.. _companies: http://curl.haxx.se/docs/companies.html -+.. _applications: http://curl.haxx.se/libcurl/using/apps.html -+ -+Installation -+------------ -+ -+You can install the most recent PycURL version using `easy_install`_:: -+ -+ easy_install pycurl -+ -+or `pip`_:: -+ -+ pip install pycurl -+ -+ -+.. _easy_install: http://peak.telecommunity.com/DevCenter/EasyInstall -+.. _pip: http://pypi.python.org/pypi/pip -+ -+Contribute -+---------- -+ -+For smaller changes: -+ -+#. Fork `the repository`_ on Github. -+#. Create a branch off **master**. -+#. Make your changes. -+#. Write a test which shows that the bug was fixed or that the feature -+ works as expected. -+#. Send a pull request. -+ -+For larger changes: -+ -+#. Join the `mailing list`_. -+#. Discuss your proposal on the mailing list. -+#. When consensus is reached, implement it as described above. -+ -+.. image:: https://api.travis-ci.org/p/pycurl.png -+ :target: https://travis-ci.org/p/pycurl -+ -+License -+------- -+ -+:: -+ -+ Copyright (C) 2001-2008 by Kjetil Jacobsen -+ Copyright (C) 2001-2008 by Markus F.X.J. Oberhumer -+ -+ All rights reserved. -+ -+ PycURL is dual licensed under the LGPL and an MIT/X derivative license -+ based on the cURL license. A full copy of the LGPL license is included -+ in the file COPYING. A full copy of the MIT/X derivative license is -+ included in the file COPYING2. You can redistribute and/or modify PycURL -+ according to the terms of either license. -+ -+.. _PycURL: http://pycurl.sourceforge.net/ -+.. _libcurl: http://curl.haxx.se/libcurl/ -+.. _urllib: http://docs.python.org/library/urllib.html -+.. _`the repository`: https://github.com/p/pycurl -+.. _`mailing list`: http://cool.haxx.se/mailman/listinfo/curl-and-python --- -1.7.1 - - -From 0d6217a0204fe48fae9b6db0b5b73f2a71cdb90c Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Thu, 7 Mar 2013 04:30:23 -0500 -Subject: [PATCH 070/149] Readme -> readme.rst elsewhere - -Signed-off-by: Kamil Dudka ---- - MANIFEST.in | 2 +- - setup.py | 2 +- - tests/ftp_test.py | 6 +++--- - tests/post_test.py | 4 ++-- - 4 files changed, 7 insertions(+), 7 deletions(-) - -diff --git a/MANIFEST.in b/MANIFEST.in -index 7d5aaf5..11ce1fe 100644 ---- a/MANIFEST.in -+++ b/MANIFEST.in -@@ -8,7 +8,7 @@ include COPYING - include COPYING2 - include INSTALL - include Makefile --include README -+include README.rst - include TODO - include MANIFEST.in - include src/Makefile -diff --git a/setup.py b/setup.py -index 33704ef..235e4c9 100644 ---- a/setup.py -+++ b/setup.py -@@ -166,7 +166,7 @@ def get_data_files(): - else: - datadir = os.path.join("share", "doc", PACKAGE) - # -- files = ["ChangeLog", "COPYING", "COPYING2", "INSTALL", "README", "TODO",] -+ files = ["ChangeLog", "COPYING", "COPYING2", "INSTALL", "README.rst", "TODO",] - if files: - data_files.append((os.path.join(datadir), files)) - files = glob.glob(os.path.join("doc", "*.html")) -diff --git a/tests/ftp_test.py b/tests/ftp_test.py -index fa2ef79..5ee380c 100644 ---- a/tests/ftp_test.py -+++ b/tests/ftp_test.py -@@ -26,7 +26,7 @@ class FtpTest(unittest.TestCase): - self.curl.perform() - - result = sio.getvalue() -- assert 'README' in result -+ assert 'README.rst' in result - assert 'INSTALL' in result - - # XXX this test needs to be fixed -@@ -38,7 +38,7 @@ class FtpTest(unittest.TestCase): - self.curl.perform() - - result = sio.getvalue() -- assert 'README' not in result -+ assert 'README.rst' not in result - assert 'ftp_test.py' in result - - def test_epsv(self): -@@ -49,5 +49,5 @@ class FtpTest(unittest.TestCase): - self.curl.perform() - - result = sio.getvalue() -- assert 'README' in result -+ assert 'README.rst' in result - assert 'INSTALL' in result -diff --git a/tests/post_test.py b/tests/post_test.py -index e8b0675..6f9d8d4 100644 ---- a/tests/post_test.py -+++ b/tests/post_test.py -@@ -81,7 +81,7 @@ class PostTest(unittest.TestCase): - self.check_post(send, expect, 'http://localhost:8380/postfields') - - def test_post_file(self): -- path = os.path.join(os.path.dirname(__file__), '..', 'README') -+ path = os.path.join(os.path.dirname(__file__), '..', 'README.rst') - with open(path) as f: - contents = f.read() - send = [ -@@ -90,7 +90,7 @@ class PostTest(unittest.TestCase): - ] - expect = [{ - 'name': 'field2', -- 'filename': 'README', -+ 'filename': 'README.rst', - 'data': contents, - }] - self.check_post(send, expect, 'http://localhost:8380/files') --- -1.7.1 - - -From 1bcb3684a9ba30411c675ddc2e75e9a5a5def311 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Wed, 6 Mar 2013 04:50:34 -0500 -Subject: [PATCH 071/149] Python 2.5 compatibility: with statement - -Signed-off-by: Kamil Dudka ---- - tests/default_write_function_test.py | 2 ++ - tests/post_test.py | 2 ++ - tests/write_to_file_test.py | 2 ++ - 3 files changed, 6 insertions(+), 0 deletions(-) - -diff --git a/tests/default_write_function_test.py b/tests/default_write_function_test.py -index c0d256b..1d31e97 100644 ---- a/tests/default_write_function_test.py -+++ b/tests/default_write_function_test.py -@@ -2,6 +2,8 @@ - # -*- coding: iso-8859-1 -*- - # vi:ts=4:et - -+from __future__ import with_statement -+ - import unittest - import pycurl - import sys -diff --git a/tests/post_test.py b/tests/post_test.py -index 6f9d8d4..804104e 100644 ---- a/tests/post_test.py -+++ b/tests/post_test.py -@@ -2,6 +2,8 @@ - # -*- coding: iso-8859-1 -*- - # vi:ts=4:et - -+from __future__ import with_statement -+ - import os.path - import pycurl - import unittest -diff --git a/tests/write_to_file_test.py b/tests/write_to_file_test.py -index 67c9c63..c3c8822 100644 ---- a/tests/write_to_file_test.py -+++ b/tests/write_to_file_test.py -@@ -2,6 +2,8 @@ - # -*- coding: iso-8859-1 -*- - # vi:ts=4:et - -+from __future__ import with_statement -+ - import unittest - import pycurl - import tempfile --- -1.7.1 - - -From 05d4fbe859d886ec0f0d104b5114a0f416021097 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Wed, 6 Mar 2013 06:10:22 -0500 -Subject: [PATCH 072/149] Python 2.5 compatibility: json/simplejson - -Signed-off-by: Kamil Dudka ---- - tests/app.py | 5 ++++- - tests/post_test.py | 5 ++++- - tests/post_with_read_callback_test.py | 5 ++++- - 3 files changed, 12 insertions(+), 3 deletions(-) - -diff --git a/tests/app.py b/tests/app.py -index a83e628..eb10668 100644 ---- a/tests/app.py -+++ b/tests/app.py -@@ -1,5 +1,8 @@ - import bottle --import json -+try: -+ import json -+except ImportError: -+ import simplejson as json - - app = bottle.Bottle() - -diff --git a/tests/post_test.py b/tests/post_test.py -index 804104e..a36a677 100644 ---- a/tests/post_test.py -+++ b/tests/post_test.py -@@ -8,7 +8,10 @@ import os.path - import pycurl - import unittest - import io --import json -+try: -+ import json -+except ImportError: -+ import simplejson as json - try: - import urllib.parse as urllib_parse - except ImportError: -diff --git a/tests/post_with_read_callback_test.py b/tests/post_with_read_callback_test.py -index a09e83a..4d8f261 100644 ---- a/tests/post_with_read_callback_test.py -+++ b/tests/post_with_read_callback_test.py -@@ -6,7 +6,10 @@ import os.path - import pycurl - import unittest - import io --import json -+try: -+ import json -+except ImportError: -+ import simplejson as json - try: - import urllib.parse as urllib_parse - except ImportError: --- -1.7.1 - - -From f29edfdd24ee3524a2674ba149c8a8d25741263c Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Wed, 6 Mar 2013 06:18:02 -0500 -Subject: [PATCH 073/149] Python 2.5 compatibility: except as - -Signed-off-by: Kamil Dudka ---- - tests/runwsgi.py | 4 +++- - 1 files changed, 3 insertions(+), 1 deletions(-) - -diff --git a/tests/runwsgi.py b/tests/runwsgi.py -index 5217a3f..8a978ec 100644 ---- a/tests/runwsgi.py -+++ b/tests/runwsgi.py -@@ -1,5 +1,6 @@ - # Run a WSGI application in a daemon thread - -+import sys - import bottle - import threading - import socket -@@ -21,7 +22,8 @@ def wait_for_network_service(netloc, check_interval, num_attempts): - for i in range(num_attempts): - try: - conn = socket.create_connection(netloc, check_interval) -- except socket.error as e: -+ except socket.error: -+ e = sys.exc_info()[1] - _time.sleep(check_interval) - else: - conn.close() --- -1.7.1 - - -From 28f459a96fcc6c74afa50b7b3c01a2dd75bd59f8 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Wed, 6 Mar 2013 06:23:08 -0500 -Subject: [PATCH 074/149] Delete unused io imports - -Signed-off-by: Kamil Dudka ---- - tests/default_write_function_test.py | 1 - - tests/header_function_test.py | 1 - - tests/post_test.py | 1 - - tests/post_with_read_callback_test.py | 1 - - tests/write_to_stringio_test.py | 1 - - 5 files changed, 0 insertions(+), 5 deletions(-) - -diff --git a/tests/default_write_function_test.py b/tests/default_write_function_test.py -index 1d31e97..27a3d04 100644 ---- a/tests/default_write_function_test.py -+++ b/tests/default_write_function_test.py -@@ -8,7 +8,6 @@ import unittest - import pycurl - import sys - import tempfile --import io - import os - - from . import app -diff --git a/tests/header_function_test.py b/tests/header_function_test.py -index bfe7173..00080ba 100644 ---- a/tests/header_function_test.py -+++ b/tests/header_function_test.py -@@ -4,7 +4,6 @@ - - import pycurl - import unittest --import io - import time as _time - - from . import app -diff --git a/tests/post_test.py b/tests/post_test.py -index a36a677..c11c3c8 100644 ---- a/tests/post_test.py -+++ b/tests/post_test.py -@@ -7,7 +7,6 @@ from __future__ import with_statement - import os.path - import pycurl - import unittest --import io - try: - import json - except ImportError: -diff --git a/tests/post_with_read_callback_test.py b/tests/post_with_read_callback_test.py -index 4d8f261..f0776ea 100644 ---- a/tests/post_with_read_callback_test.py -+++ b/tests/post_with_read_callback_test.py -@@ -5,7 +5,6 @@ - import os.path - import pycurl - import unittest --import io - try: - import json - except ImportError: -diff --git a/tests/write_to_stringio_test.py b/tests/write_to_stringio_test.py -index 018800d..e9ab0c7 100644 ---- a/tests/write_to_stringio_test.py -+++ b/tests/write_to_stringio_test.py -@@ -4,7 +4,6 @@ - - import pycurl - import unittest --import io - - from . import app - from . import runwsgi --- -1.7.1 - - -From 8e2ee4ec10741ae7373f3c490df6696267871c12 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Wed, 6 Mar 2013 04:54:35 -0500 -Subject: [PATCH 075/149] Ignore fsync of stdout failures on travis - -Signed-off-by: Kamil Dudka ---- - tests/default_write_function_test.py | 13 +++++++++++-- - 1 files changed, 11 insertions(+), 2 deletions(-) - -diff --git a/tests/default_write_function_test.py b/tests/default_write_function_test.py -index 27a3d04..1c8ec16 100644 ---- a/tests/default_write_function_test.py -+++ b/tests/default_write_function_test.py -@@ -18,6 +18,15 @@ setup_module, teardown_module = runwsgi.app_runner_setup((app.app, 8380)) - - STDOUT_FD_NUM = 1 - -+def try_fsync(fd): -+ try: -+ os.fsync(fd) -+ except OSError: -+ # On travis: -+ # OSError: [Errno 22] Invalid argument -+ # ignore -+ pass -+ - class DefaultWriteFunctionTest(unittest.TestCase): - def setUp(self): - self.curl = pycurl.Curl() -@@ -38,7 +47,7 @@ class DefaultWriteFunctionTest(unittest.TestCase): - # If this flush is not done, stdout output bleeds into the next test - # that is executed (without nose output capture) - sys.stdout.flush() -- os.fsync(STDOUT_FD_NUM) -+ try_fsync(STDOUT_FD_NUM) - - # I have a really hard time getting this to work with nose output capture - def skip_perform_get_with_default_write_function(self): -@@ -67,7 +76,7 @@ class DefaultWriteFunctionTest(unittest.TestCase): - self.curl.perform() - sys.stdout.flush() - finally: -- os.fsync(STDOUT_FD_NUM) -+ try_fsync(STDOUT_FD_NUM) - os.dup2(saved_stdout_fd, STDOUT_FD_NUM) - os.close(saved_stdout_fd) - #os.dup2(100, 1) --- -1.7.1 - - -From e7da1d864d955f21f31bdf880ed903abbee7c2ba Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Wed, 6 Mar 2013 04:58:42 -0500 -Subject: [PATCH 076/149] My vps says timeout might be -1 there - -Signed-off-by: Kamil Dudka ---- - tests/multi_socket_select_test.py | 5 +++-- - 1 files changed, 3 insertions(+), 2 deletions(-) - -diff --git a/tests/multi_socket_select_test.py b/tests/multi_socket_select_test.py -index 0c472cf..6db8b44 100644 ---- a/tests/multi_socket_select_test.py -+++ b/tests/multi_socket_select_test.py -@@ -73,8 +73,9 @@ class MultiSocketSelectTest(unittest.TestCase): - - timeout = m.timeout() - -- -- while True: -+ # timeout might be -1, indicating that all work is done -+ # XXX make sure there is always work to be done here? -+ while timeout >= 0: - (rr, wr, er) = select.select(sockets,sockets,sockets,timeout/1000.0) - socketSet = set(rr+wr+er) - if socketSet: --- -1.7.1 - - -From ee685a967f67e26ae32ec0c1d34fbad461fce2e7 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Wed, 6 Mar 2013 06:14:57 -0500 -Subject: [PATCH 077/149] Show pycurl versions in package setup - -Signed-off-by: Kamil Dudka ---- - tests/__init__.py | 4 ++++ - 1 files changed, 4 insertions(+), 0 deletions(-) - -diff --git a/tests/__init__.py b/tests/__init__.py -index e69de29..c1ff976 100644 ---- a/tests/__init__.py -+++ b/tests/__init__.py -@@ -0,0 +1,4 @@ -+import pycurl -+ -+def setup_package(): -+ print('Testing %s' % pycurl.version) --- -1.7.1 - - -From 359fc6e31a21620ea564288ae43ae23db35d1b2e Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Wed, 6 Mar 2013 06:07:19 -0500 -Subject: [PATCH 078/149] Show what the entries are if assertion fails - -Signed-off-by: Kamil Dudka ---- - tests/debug_test.py | 3 ++- - 1 files changed, 2 insertions(+), 1 deletions(-) - -diff --git a/tests/debug_test.py b/tests/debug_test.py -index 8005239..d74bed9 100644 ---- a/tests/debug_test.py -+++ b/tests/debug_test.py -@@ -46,4 +46,5 @@ class DebugTest(unittest.TestCase): - for t, b in self.debug_entries: - if t == wanted_t and wanted_b in b: - return -- assert False, "%d: %s not found in debug entries" % (wanted_t, wanted_b) -+ assert False, "%d: %s not found in debug entries\nEntries are:\n%s" % \ -+ (wanted_t, wanted_b, repr(self.debug_entries)) --- -1.7.1 - - -From 4c65d037fce349811d4954db137d0b3d5b3abe85 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Wed, 6 Mar 2013 06:09:22 -0500 -Subject: [PATCH 079/149] More informative failure message - -Signed-off-by: Kamil Dudka ---- - tests/multi_test.py | 5 ++++- - 1 files changed, 4 insertions(+), 1 deletions(-) - -diff --git a/tests/multi_test.py b/tests/multi_test.py -index d9c6174..8701649 100644 ---- a/tests/multi_test.py -+++ b/tests/multi_test.py -@@ -214,7 +214,10 @@ class MultiTest(unittest.TestCase): - self.assertEqual('success', m.handles[0].body.getvalue()) - self.assertEqual(200, m.handles[0].http_code) - # bottle generated response body -- assert 'Error 403: Forbidden' in m.handles[1].body.getvalue() -+ body = m.handles[1].body.getvalue() -+ search = 'Error 403: Forbidden' -+ if search not in body: -+ assert False, "'%s' not found in body:\n%s" % (search, body) - self.assertEqual(403, m.handles[1].http_code) - # bottle generated response body - self.assertEqual('', m.handles[2].body.getvalue()) --- -1.7.1 - - -From 831b7a919d47b55e787c8af457b6ec815fca2035 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Wed, 6 Mar 2013 06:34:39 -0500 -Subject: [PATCH 080/149] Return complete response bodies for 403 and 404 responses as it looks like exact wording varies between bottle versions - -Signed-off-by: Kamil Dudka ---- - tests/app.py | 4 ++-- - tests/multi_test.py | 9 +++------ - 2 files changed, 5 insertions(+), 8 deletions(-) - -diff --git a/tests/app.py b/tests/app.py -index eb10668..9b56ace 100644 ---- a/tests/app.py -+++ b/tests/app.py -@@ -12,11 +12,11 @@ def ok(): - - @app.route('/status/403') - def forbidden(): -- bottle.abort(403, 'forbidden') -+ return bottle.HTTPResponse('forbidden', 403) - - @app.route('/status/404') - def not_found(): -- bottle.abort(404, 'not found') -+ return bottle.HTTPResponse('not found', 404) - - @app.route('/postfields', method='post') - def postfields(): -diff --git a/tests/multi_test.py b/tests/multi_test.py -index 8701649..d540413 100644 ---- a/tests/multi_test.py -+++ b/tests/multi_test.py -@@ -153,10 +153,10 @@ class MultiTest(unittest.TestCase): - self.assertEqual('success', m.handles[0].body.getvalue()) - self.assertEqual(200, m.handles[0].http_code) - # bottle generated response body -- assert 'Error 403: Forbidden' in m.handles[1].body.getvalue() -+ self.assertEqual('forbidden', m.handles[1].body.getvalue()) - self.assertEqual(403, m.handles[1].http_code) - # bottle generated response body -- assert 'Error 404: Not Found' in m.handles[2].body.getvalue() -+ self.assertEqual('not found', m.handles[2].body.getvalue()) - self.assertEqual(404, m.handles[2].http_code) - - def check_adding_closed_handle(self, close_fn): -@@ -214,10 +214,7 @@ class MultiTest(unittest.TestCase): - self.assertEqual('success', m.handles[0].body.getvalue()) - self.assertEqual(200, m.handles[0].http_code) - # bottle generated response body -- body = m.handles[1].body.getvalue() -- search = 'Error 403: Forbidden' -- if search not in body: -- assert False, "'%s' not found in body:\n%s" % (search, body) -+ self.assertEqual('forbidden', m.handles[1].body.getvalue()) - self.assertEqual(403, m.handles[1].http_code) - # bottle generated response body - self.assertEqual('', m.handles[2].body.getvalue()) --- -1.7.1 - - -From 6730160a7ede5c1bb490836cb42269d06a497dbe Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Wed, 6 Mar 2013 22:15:47 -0500 -Subject: [PATCH 081/149] Debug messages originated by curl 7.22 are different - -Signed-off-by: Kamil Dudka ---- - tests/debug_test.py | 6 +++++- - 1 files changed, 5 insertions(+), 1 deletions(-) - -diff --git a/tests/debug_test.py b/tests/debug_test.py -index d74bed9..70e121c 100644 ---- a/tests/debug_test.py -+++ b/tests/debug_test.py -@@ -32,7 +32,11 @@ class DebugTest(unittest.TestCase): - - # Some checks with no particular intent - self.check(0, 'About to connect') -- self.check(0, 'Connected to localhost') -+ version = map(int, pycurl.version_info()[1].split('.')) -+ if version[0] < 7 or version[0] == 7 and version[1] <= 22: -+ self.check(0, 'connected') -+ else: -+ self.check(0, 'Connected to localhost') - self.check(0, 'port 8380') - # request - self.check(2, 'GET /success HTTP/1.1') --- -1.7.1 - - -From 283ebc888e8016f575457db70a81eb46f219467c Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Wed, 6 Mar 2013 22:17:48 -0500 -Subject: [PATCH 082/149] Times in http headers are in UTC - -Signed-off-by: Kamil Dudka ---- - tests/header_function_test.py | 3 ++- - 1 files changed, 2 insertions(+), 1 deletions(-) - -diff --git a/tests/header_function_test.py b/tests/header_function_test.py -index 00080ba..7ca564d 100644 ---- a/tests/header_function_test.py -+++ b/tests/header_function_test.py -@@ -34,7 +34,8 @@ class HeaderFunctionTest(unittest.TestCase): - assert len(self.header_lines) > 0 - self.assertEqual("HTTP/1.0 200 OK\r\n", self.header_lines[0]) - # day of week -- todays_day = _time.strftime('%a') -+ # important: must be in utc -+ todays_day = _time.strftime('%a', _time.gmtime()) - # Date: Sun, 03 Mar 2013 05:38:12 GMT\r\n - self.check('Date: %s' % todays_day) - # Server: WSGIServer/0.1 Python/2.7.3\r\n --- -1.7.1 - - -From 0034724dd8ec8526f361c067d72e87c930f6eeb3 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Wed, 6 Mar 2013 22:26:19 -0500 -Subject: [PATCH 083/149] Create a generalized function for curl version comparisons - -Signed-off-by: Kamil Dudka ---- - tests/util.py | 15 +++++++++++++++ - tests/version_comparison_test.py | 15 +++++++++++++++ - 2 files changed, 30 insertions(+), 0 deletions(-) - create mode 100644 tests/version_comparison_test.py - -diff --git a/tests/util.py b/tests/util.py -index 891da44..46ac59f 100644 ---- a/tests/util.py -+++ b/tests/util.py -@@ -3,6 +3,7 @@ - # $Id$ - - import os, sys -+import pycurl - - try: - from cStringIO import StringIO -@@ -12,6 +13,20 @@ except ImportError: - except ImportError: - from io import StringIO - -+def version_less_than_spec(version_tuple, spec_tuple): -+ # spec_tuple may have 2 elements, expect version_tuple to have 3 elements -+ assert len(version_tuple) >= len(spec_tuple) -+ for i in range(len(spec_tuple)): -+ if version_tuple[i] < spec_tuple[i]: -+ return True -+ if version_tuple[i] > spec_tuple[i]: -+ return False -+ return False -+ -+def pycurl_version_less_than(spec_tuple): -+ version = map(int, pycurl.version_info()[1].split('.')) -+ return version_less_than_spec(version, spec_tuple) -+ - # - # prepare sys.path in case we are still in the build directory - # see also: distutils/command/build.py (build_platlib) -diff --git a/tests/version_comparison_test.py b/tests/version_comparison_test.py -new file mode 100644 -index 0000000..80e780c ---- /dev/null -+++ b/tests/version_comparison_test.py -@@ -0,0 +1,15 @@ -+#! /usr/bin/env python -+# -*- coding: iso-8859-1 -*- -+# vi:ts=4:et -+ -+import unittest -+ -+from . import util -+ -+class VersionComparisonTest(unittest.TestCase): -+ def test_comparison(self): -+ assert util.version_less_than_spec((7, 22, 0), (7, 23, 0)) -+ assert util.version_less_than_spec((7, 22, 0), (7, 23)) -+ assert util.version_less_than_spec((7, 22, 0), (7, 22, 1)) -+ assert not util.version_less_than_spec((7, 22, 0), (7, 22, 0)) -+ assert not util.version_less_than_spec((7, 22, 0), (7, 22)) --- -1.7.1 - - -From d794edf43a119d4f600d3be8536c22419c9f4abb Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Wed, 6 Mar 2013 22:27:25 -0500 -Subject: [PATCH 084/149] Use version comparison helper in debug test - -Signed-off-by: Kamil Dudka ---- - tests/debug_test.py | 3 +-- - tests/util.py | 4 ++-- - 2 files changed, 3 insertions(+), 4 deletions(-) - -diff --git a/tests/debug_test.py b/tests/debug_test.py -index 70e121c..e81d653 100644 ---- a/tests/debug_test.py -+++ b/tests/debug_test.py -@@ -32,8 +32,7 @@ class DebugTest(unittest.TestCase): - - # Some checks with no particular intent - self.check(0, 'About to connect') -- version = map(int, pycurl.version_info()[1].split('.')) -- if version[0] < 7 or version[0] == 7 and version[1] <= 22: -+ if util.pycurl_version_less_than(7, 24): - self.check(0, 'connected') - else: - self.check(0, 'Connected to localhost') -diff --git a/tests/util.py b/tests/util.py -index 46ac59f..b8e22ec 100644 ---- a/tests/util.py -+++ b/tests/util.py -@@ -23,9 +23,9 @@ def version_less_than_spec(version_tuple, spec_tuple): - return False - return False - --def pycurl_version_less_than(spec_tuple): -+def pycurl_version_less_than(*spec): - version = map(int, pycurl.version_info()[1].split('.')) -- return version_less_than_spec(version, spec_tuple) -+ return version_less_than_spec(version, spec) - - # - # prepare sys.path in case we are still in the build directory --- -1.7.1 - - -From c1143004a0f27190fc4aec1228396db992f5c5de Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Wed, 6 Mar 2013 22:30:44 -0500 -Subject: [PATCH 085/149] libcurl 7.23.0 produces different results - -Signed-off-by: Kamil Dudka ---- - tests/multi_timer_test.py | 7 +++++-- - 1 files changed, 5 insertions(+), 2 deletions(-) - -diff --git a/tests/multi_timer_test.py b/tests/multi_timer_test.py -index e961780..c4b3df4 100644 ---- a/tests/multi_timer_test.py -+++ b/tests/multi_timer_test.py -@@ -77,8 +77,11 @@ class MultiSocketTest(unittest.TestCase): - self.assertEqual(200, c.http_code) - - assert len(timers) > 0 -- assert timers[0] > 0 -- self.assertEqual(-1, timers[-1]) -+ # libcurl 7.23.0 produces a 0 timer -+ assert timers[0] >= 0 -+ # this assertion does not appear to hold on older libcurls -+ if not util.pycurl_version_less_than(7, 24): -+ self.assertEqual(-1, timers[-1]) - - # close handles - for c in m.handles: --- -1.7.1 - - -From abc0199b4b2517a82c492cd3f64f10883a5ec257 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Thu, 7 Mar 2013 00:16:21 -0500 -Subject: [PATCH 086/149] Change memleak test to use id() rather than regexp match of addresses (closes #12) - -This commit also fixes the test to check objects that GC tracks -rather than the object type repeatedly. - -Signed-off-by: Kamil Dudka ---- - tests/memleak_test.py | 16 ++++++---------- - 1 files changed, 6 insertions(+), 10 deletions(-) - -diff --git a/tests/memleak_test.py b/tests/memleak_test.py -index 6e9f76c..1b1bbd5 100644 ---- a/tests/memleak_test.py -+++ b/tests/memleak_test.py -@@ -5,12 +5,9 @@ - import pycurl - import unittest - import gc --import re - - class MemleakTest(unittest.TestCase): - def test_collection(self): -- regexp = re.compile(r'at (0x\d+)') -- - gc.collect() - flags = gc.DEBUG_COLLECTABLE | gc.DEBUG_UNCOLLECTABLE - # python 3 has no DEBUG_OBJECTS -@@ -31,12 +28,10 @@ class MemleakTest(unittest.TestCase): - multi.add_handle(curl) - t.append(curl) - -- match = regexp.search(repr(curl)) -- assert match is not None -- searches.append(match.group(1)) -- match = regexp.search(repr(multi)) -- assert match -- searches.append(match.group(1)) -+ c_id = id(curl) -+ searches.append(c_id) -+ m_id = id(multi) -+ searches.append(m_id) - - #print("Tracked objects:", len(gc.get_objects())) - -@@ -56,4 +51,5 @@ class MemleakTest(unittest.TestCase): - - objects = gc.get_objects() - for search in searches: -- assert 'at %s' % search not in repr(object) -+ for object in objects: -+ assert search != id(object) --- -1.7.1 - - -From 14dd7fdbe5697f7e7349cbe44258f41f74455fc0 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Mon, 11 Mar 2013 22:19:14 -0400 -Subject: [PATCH 087/149] More informative exception message when vsftpd is missing or not in PATH (closes #6) - -Signed-off-by: Kamil Dudka ---- - tests/procmgr.py | 12 +++++++++++- - 1 files changed, 11 insertions(+), 1 deletions(-) - -diff --git a/tests/procmgr.py b/tests/procmgr.py -index 8d5c0cc..ce08da9 100644 ---- a/tests/procmgr.py -+++ b/tests/procmgr.py -@@ -1,6 +1,7 @@ - import threading - import subprocess - import os -+import sys - import signal - - from . import runwsgi -@@ -53,7 +54,16 @@ def vsftpd_setup(): - ] - setup_module = start_setup(cmd) - def do_setup_module(): -- setup_module() -+ try: -+ setup_module() -+ except OSError: -+ import errno -+ e = sys.exc_info()[1] -+ if e.errno == errno.ENOENT: -+ msg = "Tried to execute `%s`\nTry specifying path to vsftpd via PYCURL_VSFTPD_PATH environment variable\n" % vsftpd_path -+ raise OSError(e.errno, e.strerror + "\n" + msg) -+ else: -+ raise - ok = runwsgi.wait_for_network_service(('127.0.0.1', 8321), 0.1, 10) - if not ok: - import warnings --- -1.7.1 - - -From 1505197b9eb8d5ccbc3d039866e87d522455d9d0 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Mon, 11 Mar 2013 22:22:02 -0400 -Subject: [PATCH 088/149] Delete -1 timer assertion from multi timer test (closes #19) - -Signed-off-by: Kamil Dudka ---- - tests/multi_timer_test.py | 6 ++++-- - 1 files changed, 4 insertions(+), 2 deletions(-) - -diff --git a/tests/multi_timer_test.py b/tests/multi_timer_test.py -index c4b3df4..ff856d7 100644 ---- a/tests/multi_timer_test.py -+++ b/tests/multi_timer_test.py -@@ -80,8 +80,10 @@ class MultiSocketTest(unittest.TestCase): - # libcurl 7.23.0 produces a 0 timer - assert timers[0] >= 0 - # this assertion does not appear to hold on older libcurls -- if not util.pycurl_version_less_than(7, 24): -- self.assertEqual(-1, timers[-1]) -+ # or apparently on any linuxes, see -+ # https://github.com/p/pycurl/issues/19 -+ #if not util.pycurl_version_less_than(7, 24): -+ # self.assertEqual(-1, timers[-1]) - - # close handles - for c in m.handles: --- -1.7.1 - - -From 041b4666519f3fa2594f5ee919e62006cb8ae2f1 Mon Sep 17 00:00:00 2001 -From: Kamil Dudka -Date: Mon, 11 Mar 2013 14:00:04 +0100 -Subject: [PATCH 089/149] remove .cvsignore files, add *.pyc and *.pyo to .gitignore - -Signed-off-by: Kamil Dudka ---- - .gitignore | 2 ++ - 1 files changed, 2 insertions(+), 0 deletions(-) - -diff --git a/.gitignore b/.gitignore -index 796b96d..899daba 100644 ---- a/.gitignore -+++ b/.gitignore -@@ -1 +1,3 @@ -+*.pyc -+*.pyo - /build --- -1.7.1 - - -From 3dc27e280021512e84cd77f68c7ff3d4e5cb2ae0 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Mon, 11 Mar 2013 22:40:12 -0400 -Subject: [PATCH 090/149] Readd cvsignore contents and empty directories - -Signed-off-by: Kamil Dudka ---- - .gitignore | 6 ++++++ - 1 files changed, 6 insertions(+), 0 deletions(-) - -diff --git a/.gitignore b/.gitignore -index 899daba..c873e32 100644 ---- a/.gitignore -+++ b/.gitignore -@@ -1,3 +1,9 @@ - *.pyc - *.pyo -+/MANIFEST - /build -+/dist -+/www/htdocs/download/*.bz2 -+/www/htdocs/download/*.exe -+/www/htdocs/download/*.gz -+/www/upload/* --- -1.7.1 - - -From d6c800cdc963e349921cae8ac10df9f9f23cac79 Mon Sep 17 00:00:00 2001 -From: Kamil Dudka -Date: Mon, 11 Mar 2013 14:03:06 +0100 -Subject: [PATCH 091/149] vsftpd.conf: add background=no to allow for proper shutdown - -When the "background" directive in the vsftpd.conf configuration file is -set to "YES", the vsftpd startup script forks, creating a child process -(the vsftpd daemon) which immediately sends the SIGUSR1 signal to its -parent process, which exits upon receiving it. The teardown routine in -procmgr.py would then send the SIGTERM signal to a process that does not -exist anymore. - -Signed-off-by: Kamil Dudka ---- - tests/vsftpd.conf | 1 + - 1 files changed, 1 insertions(+), 0 deletions(-) - -diff --git a/tests/vsftpd.conf b/tests/vsftpd.conf -index 0abb39f..b4e4972 100644 ---- a/tests/vsftpd.conf -+++ b/tests/vsftpd.conf -@@ -1,5 +1,6 @@ - anon_world_readable_only=yes - anonymous_enable=yes -+background=no - # currently we only list files - download_enable=no - listen=yes --- -1.7.1 - - -From 8acdaaf5c311038a5dd7fcd81f13c0bb984b66c7 Mon Sep 17 00:00:00 2001 -From: Kamil Dudka -Date: Tue, 26 Feb 2013 14:49:47 +0100 -Subject: [PATCH 092/149] pycurl.c: eliminate duplicated code in util_write_callback() - -Suggested by Zdenek Pavlas . - -Signed-off-by: Kamil Dudka ---- - src/pycurl.c | 10 +--------- - 1 files changed, 1 insertions(+), 9 deletions(-) - -diff --git a/src/pycurl.c b/src/pycurl.c -index 094bc60..f701543 100644 ---- a/src/pycurl.c -+++ b/src/pycurl.c -@@ -1082,15 +1082,7 @@ util_write_callback(int flags, char *ptr, size_t size, size_t nmemb, void *strea - if (result == Py_None) { - ret = total_size; /* None means success */ - } -- else if (PyInt_Check(result)) { -- long obj_size = PyInt_AsLong(result); -- if (obj_size < 0 || obj_size > total_size) { -- PyErr_Format(ErrorObject, "invalid return value for write callback %ld %ld", (long)obj_size, (long)total_size); -- goto verbose_error; -- } -- ret = (size_t) obj_size; /* success */ -- } -- else if (PyLong_Check(result)) { -+ else if (PyInt_Check(result) || PyLong_Check(result)) { - long obj_size = PyLong_AsLong(result); - if (obj_size < 0 || obj_size > total_size) { - PyErr_Format(ErrorObject, "invalid return value for write callback %ld %ld", (long)obj_size, (long)total_size); --- -1.7.1 - - -From b55c2cab56733a86b4ebc5ccccdf8c5530bca85a Mon Sep 17 00:00:00 2001 -From: Kamil Dudka -Date: Tue, 26 Feb 2013 16:58:55 +0100 -Subject: [PATCH 093/149] pycurl.c: allow to return -1 from write callback - -... to abort the transfer and WRITEFUNC_PAUSE to pause the transfer - -Reported By: Zdenek Pavlas -Bug: https://bugzilla.redhat.com/857875 - -Signed-off-by: Kamil Dudka ---- - src/pycurl.c | 11 +++++------ - 1 files changed, 5 insertions(+), 6 deletions(-) - -diff --git a/src/pycurl.c b/src/pycurl.c -index f701543..a30c339 100644 ---- a/src/pycurl.c -+++ b/src/pycurl.c -@@ -1083,12 +1083,8 @@ util_write_callback(int flags, char *ptr, size_t size, size_t nmemb, void *strea - ret = total_size; /* None means success */ - } - else if (PyInt_Check(result) || PyLong_Check(result)) { -- long obj_size = PyLong_AsLong(result); -- if (obj_size < 0 || obj_size > total_size) { -- PyErr_Format(ErrorObject, "invalid return value for write callback %ld %ld", (long)obj_size, (long)total_size); -- goto verbose_error; -- } -- ret = (size_t) obj_size; /* success */ -+ /* if the cast to long fails, PyLong_AsLong() returns -1L */ -+ ret = (size_t) PyLong_AsLong(result); - } - else { - PyErr_SetString(ErrorObject, "write callback must return int or None"); -@@ -3509,6 +3505,9 @@ initpycurl(void) - /* Abort curl_read_callback(). */ - insint_c(d, "READFUNC_ABORT", CURL_READFUNC_ABORT); - -+ /* Pause curl_write_callback(). */ -+ insint_c(d, "WRITEFUNC_PAUSE", CURL_WRITEFUNC_PAUSE); -+ - /* constants for ioctl callback return values */ - insint_c(d, "IOE_OK", CURLIOE_OK); - insint_c(d, "IOE_UNKNOWNCMD", CURLIOE_UNKNOWNCMD); --- -1.7.1 - - -From 32664e552084fad471de98f219f58c52c95653f4 Mon Sep 17 00:00:00 2001 -From: Kamil Dudka -Date: Wed, 6 Mar 2013 14:38:01 +0100 -Subject: [PATCH 094/149] write_abort_test.py: test returning -1 from write callback - -Signed-off-by: Kamil Dudka ---- - tests/write_abort_test.py | 35 +++++++++++++++++++++++++++++++++++ - 1 files changed, 35 insertions(+), 0 deletions(-) - create mode 100755 tests/write_abort_test.py - -diff --git a/tests/write_abort_test.py b/tests/write_abort_test.py -new file mode 100755 -index 0000000..73e8245 ---- /dev/null -+++ b/tests/write_abort_test.py -@@ -0,0 +1,35 @@ -+#!/usr/bin/python -+# -*- coding: iso-8859-1 -*- -+# vi:ts=4:et -+ -+import os.path -+import pycurl -+import sys -+import unittest -+ -+class WriteAbortTest(unittest.TestCase): -+ def setUp(self): -+ pycurl.global_init(pycurl.GLOBAL_DEFAULT) -+ -+ def tearDown(self): -+ pycurl.global_cleanup() -+ -+ def test_write_abort(self): -+ def write_cb(_): -+ # this should cause pycurl.WRITEFUNCTION (without any range errors) -+ return -1 -+ -+ # download the script itself through the file:// protocol into write_cb -+ c = pycurl.Curl() -+ c.setopt(pycurl.URL, 'file://' + os.path.abspath(sys.argv[0])) -+ c.setopt(pycurl.WRITEFUNCTION, write_cb) -+ try: -+ c.perform() -+ except pycurl.error, (err, msg): -+ # we expect pycurl.E_WRITE_ERROR as the response -+ assert pycurl.E_WRITE_ERROR == err -+ -+ # no additional errors should be reported -+ assert not hasattr(sys, 'last_value') -+ -+ c.close() --- -1.7.1 - - -From 808c5b785ccd2f3bcca15146ae998a8609229b38 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Mon, 11 Mar 2013 23:48:10 -0400 -Subject: [PATCH 095/149] setup/teardown consistent with other tests - -Signed-off-by: Kamil Dudka ---- - tests/write_abort_test.py | 13 +++++-------- - 1 files changed, 5 insertions(+), 8 deletions(-) - -diff --git a/tests/write_abort_test.py b/tests/write_abort_test.py -index 73e8245..f720882 100755 ---- a/tests/write_abort_test.py -+++ b/tests/write_abort_test.py -@@ -9,10 +9,10 @@ import unittest - - class WriteAbortTest(unittest.TestCase): - def setUp(self): -- pycurl.global_init(pycurl.GLOBAL_DEFAULT) -+ self.curl = pycurl.Curl() - - def tearDown(self): -- pycurl.global_cleanup() -+ self.curl.close() - - def test_write_abort(self): - def write_cb(_): -@@ -20,16 +20,13 @@ class WriteAbortTest(unittest.TestCase): - return -1 - - # download the script itself through the file:// protocol into write_cb -- c = pycurl.Curl() -- c.setopt(pycurl.URL, 'file://' + os.path.abspath(sys.argv[0])) -- c.setopt(pycurl.WRITEFUNCTION, write_cb) -+ self.curl.setopt(pycurl.URL, 'file://' + os.path.abspath(sys.argv[0])) -+ self.curl.setopt(pycurl.WRITEFUNCTION, write_cb) - try: -- c.perform() -+ self.curl.perform() - except pycurl.error, (err, msg): - # we expect pycurl.E_WRITE_ERROR as the response - assert pycurl.E_WRITE_ERROR == err - - # no additional errors should be reported - assert not hasattr(sys, 'last_value') -- -- c.close() --- -1.7.1 - - -From c662b504c202ca434d723de3984c12a35358e9f8 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Mon, 11 Mar 2013 23:48:41 -0400 -Subject: [PATCH 096/149] Correct shebang - -Signed-off-by: Kamil Dudka ---- - tests/write_abort_test.py | 2 +- - 1 files changed, 1 insertions(+), 1 deletions(-) - -diff --git a/tests/write_abort_test.py b/tests/write_abort_test.py -index f720882..3272961 100755 ---- a/tests/write_abort_test.py -+++ b/tests/write_abort_test.py -@@ -1,4 +1,4 @@ --#!/usr/bin/python -+#! /usr/bin/env python - # -*- coding: iso-8859-1 -*- - # vi:ts=4:et - --- -1.7.1 - - -From 43d385231293a22fb65490db8348c9a18592c7b0 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Mon, 11 Mar 2013 23:56:36 -0400 -Subject: [PATCH 097/149] Check that bogus return values from write callback are correctly handled (still) - -Signed-off-by: Kamil Dudka ---- - tests/write_cb_bogus_test.py | 44 ++++++++++++++++++++++++++++++++++++++++++ - 1 files changed, 44 insertions(+), 0 deletions(-) - create mode 100644 tests/write_cb_bogus_test.py - -diff --git a/tests/write_cb_bogus_test.py b/tests/write_cb_bogus_test.py -new file mode 100644 -index 0000000..4bec2ad ---- /dev/null -+++ b/tests/write_cb_bogus_test.py -@@ -0,0 +1,44 @@ -+#! /usr/bin/env python -+# -*- coding: iso-8859-1 -*- -+# vi:ts=4:et -+ -+import os.path -+import pycurl -+import sys -+import unittest -+ -+class WriteAbortTest(unittest.TestCase): -+ def setUp(self): -+ self.curl = pycurl.Curl() -+ -+ def tearDown(self): -+ self.curl.close() -+ -+ def write_cb_returning_string(self, data): -+ return 'foo' -+ -+ def write_cb_returning_float(self, data): -+ return 0.5 -+ -+ def test_write_cb_returning_string(self): -+ self.check(self.write_cb_returning_string) -+ -+ def test_write_cb_returning_float(self): -+ self.check(self.write_cb_returning_float) -+ -+ def check(self, write_cb): -+ # download the script itself through the file:// protocol into write_cb -+ c = pycurl.Curl() -+ self.curl.setopt(pycurl.URL, 'file://' + os.path.abspath(sys.argv[0])) -+ self.curl.setopt(pycurl.WRITEFUNCTION, write_cb) -+ try: -+ self.curl.perform() -+ except pycurl.error, (err, msg): -+ # we expect pycurl.E_WRITE_ERROR as the response -+ assert pycurl.E_WRITE_ERROR == err -+ -+ # actual error -+ assert hasattr(sys, 'last_type') -+ self.assertEqual(pycurl.error, sys.last_type) -+ assert hasattr(sys, 'last_value') -+ self.assertEqual('write callback must return int or None', str(sys.last_value)) --- -1.7.1 - - -From c04e16173e67b91c47018a5f023d703f751ba23e Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Tue, 12 Mar 2013 00:00:40 -0400 -Subject: [PATCH 098/149] Fix mode on write abort test - -Signed-off-by: Kamil Dudka ---- - 0 files changed, 0 insertions(+), 0 deletions(-) - mode change 100755 => 100644 tests/write_abort_test.py - -diff --git a/tests/write_abort_test.py b/tests/write_abort_test.py -old mode 100755 -new mode 100644 --- -1.7.1 - - -From a14d3afd57c9a402e8256a53cdcc389008356f3e Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Tue, 12 Mar 2013 00:02:56 -0400 -Subject: [PATCH 099/149] This test fails intermittently, add diagnostics - -Signed-off-by: Kamil Dudka ---- - tests/multi_socket_select_test.py | 2 +- - 1 files changed, 1 insertions(+), 1 deletions(-) - -diff --git a/tests/multi_socket_select_test.py b/tests/multi_socket_select_test.py -index 6db8b44..9546169 100644 ---- a/tests/multi_socket_select_test.py -+++ b/tests/multi_socket_select_test.py -@@ -94,7 +94,7 @@ class MultiSocketSelectTest(unittest.TestCase): - c.http_code = c.getinfo(c.HTTP_CODE) - - # at least in and remove events per socket -- assert len(socket_events) >= 6 -+ assert len(socket_events) >= 6, 'Less than 6 socket events: %s' % repr(socket_events) - - # print result - for c in m.handles: --- -1.7.1 - - -From 2af85c62b266127bd9e58d1df2b1fb079a0e9654 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Tue, 12 Mar 2013 22:43:47 -0400 -Subject: [PATCH 100/149] Python 2.5 compatibility: socket.create_connection - -Signed-off-by: Kamil Dudka ---- - tests/runwsgi.py | 14 +++++++++++++- - 1 files changed, 13 insertions(+), 1 deletions(-) - -diff --git a/tests/runwsgi.py b/tests/runwsgi.py -index 8a978ec..be05dfa 100644 ---- a/tests/runwsgi.py -+++ b/tests/runwsgi.py -@@ -6,6 +6,18 @@ import threading - import socket - import time as _time - -+try: -+ create_connection = socket.create_connection -+except AttributeError: -+ # python 2.5 -+ def create_connection(netloc, timeout=None): -+ # XXX ipv4 only -+ s = socket.socket() -+ if timeout is not None: -+ s.settimeout(timeout) -+ s.connect(netloc) -+ return s -+ - class Server(bottle.WSGIRefServer): - def run(self, handler): # pragma: no cover - from wsgiref.simple_server import make_server, WSGIRequestHandler -@@ -21,7 +33,7 @@ def wait_for_network_service(netloc, check_interval, num_attempts): - ok = False - for i in range(num_attempts): - try: -- conn = socket.create_connection(netloc, check_interval) -+ conn = create_connection(netloc, check_interval) - except socket.error: - e = sys.exc_info()[1] - _time.sleep(check_interval) --- -1.7.1 - - -From d1c79723cecd7ee75181f29b46081fc49ea28790 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Tue, 12 Mar 2013 22:55:08 -0400 -Subject: [PATCH 101/149] Python 2.5 compatibility: poll_interval in SocketServer - -Signed-off-by: Kamil Dudka ---- - tests/runwsgi.py | 17 +++++++++++++++-- - 1 files changed, 15 insertions(+), 2 deletions(-) - -diff --git a/tests/runwsgi.py b/tests/runwsgi.py -index be05dfa..114ce42 100644 ---- a/tests/runwsgi.py -+++ b/tests/runwsgi.py -@@ -18,6 +18,8 @@ except AttributeError: - s.connect(netloc) - return s - -+global_stop = False -+ - class Server(bottle.WSGIRefServer): - def run(self, handler): # pragma: no cover - from wsgiref.simple_server import make_server, WSGIRequestHandler -@@ -27,7 +29,13 @@ class Server(bottle.WSGIRefServer): - def log_request(*args, **kw): pass - self.options['handler_class'] = QuietHandler - self.srv = make_server(self.host, self.port, handler, **self.options) -- self.srv.serve_forever(poll_interval=0.1) -+ if sys.version_info[0] == 2 and sys.version_info[1] < 6: -+ # python 2.5 has no poll_interval -+ # and thus no way to stop the server -+ while not global_stop: -+ self.srv.handle_request() -+ else: -+ self.srv.serve_forever(poll_interval=0.1) - - def wait_for_network_service(netloc, check_interval, num_attempts): - ok = False -@@ -104,6 +112,11 @@ def app_runner_setup(*specs): - for server in self.servers: - # if no tests from module were run, there is no server to shut down - if hasattr(server, 'srv'): -- server.srv.shutdown() -+ if hasattr(server.srv, 'shutdown'): -+ server.srv.shutdown() -+ else: -+ # python 2.5 -+ global global_stop -+ global_stop = True - - return [setup, teardown] --- -1.7.1 - - -From cb24d7050133cbed97c7f05ef6f649c7a7bfede3 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Fri, 15 Mar 2013 12:28:11 -0400 -Subject: [PATCH 102/149] Tests do not call global cleanup for now - -Signed-off-by: Kamil Dudka ---- - tests/reset_test.py | 2 -- - 1 files changed, 0 insertions(+), 2 deletions(-) - -diff --git a/tests/reset_test.py b/tests/reset_test.py -index cc55f86..61a692a 100644 ---- a/tests/reset_test.py -+++ b/tests/reset_test.py -@@ -72,5 +72,3 @@ class ResetTest(unittest.TestCase): - eh.close() - cm.close() - outf.close() -- -- pycurl.global_cleanup() --- -1.7.1 - - -From 7a74c1e39377d4220ffafe335a71487515bede8e Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Fri, 15 Mar 2013 17:01:35 -0400 -Subject: [PATCH 103/149] Python 3 compatibility: except syntax - -Signed-off-by: Kamil Dudka ---- - tests/write_abort_test.py | 3 ++- - tests/write_cb_bogus_test.py | 3 ++- - 2 files changed, 4 insertions(+), 2 deletions(-) - -diff --git a/tests/write_abort_test.py b/tests/write_abort_test.py -index 3272961..957fe78 100644 ---- a/tests/write_abort_test.py -+++ b/tests/write_abort_test.py -@@ -24,7 +24,8 @@ class WriteAbortTest(unittest.TestCase): - self.curl.setopt(pycurl.WRITEFUNCTION, write_cb) - try: - self.curl.perform() -- except pycurl.error, (err, msg): -+ except pycurl.error: -+ err, msg = sys.exc_info()[1] - # we expect pycurl.E_WRITE_ERROR as the response - assert pycurl.E_WRITE_ERROR == err - -diff --git a/tests/write_cb_bogus_test.py b/tests/write_cb_bogus_test.py -index 4bec2ad..ef709db 100644 ---- a/tests/write_cb_bogus_test.py -+++ b/tests/write_cb_bogus_test.py -@@ -33,7 +33,8 @@ class WriteAbortTest(unittest.TestCase): - self.curl.setopt(pycurl.WRITEFUNCTION, write_cb) - try: - self.curl.perform() -- except pycurl.error, (err, msg): -+ except pycurl.error: -+ err, msg = sys.exc_info()[1] - # we expect pycurl.E_WRITE_ERROR as the response - assert pycurl.E_WRITE_ERROR == err - --- -1.7.1 - - -From 84bb12176059f28c0cc030d6b620656a00fff3a4 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Fri, 15 Mar 2013 21:21:50 -0400 -Subject: [PATCH 104/149] Python 3 compatibility: map - -Signed-off-by: Kamil Dudka ---- - tests/util.py | 2 +- - 1 files changed, 1 insertions(+), 1 deletions(-) - -diff --git a/tests/util.py b/tests/util.py -index b8e22ec..1e9f406 100644 ---- a/tests/util.py -+++ b/tests/util.py -@@ -24,7 +24,7 @@ def version_less_than_spec(version_tuple, spec_tuple): - return False - - def pycurl_version_less_than(*spec): -- version = map(int, pycurl.version_info()[1].split('.')) -+ version = [int(part) for part in pycurl.version_info()[1].split('.')] - return version_less_than_spec(version, spec) - - # --- -1.7.1 - - -From d30ec2cf96c66cd176bb999fb7a3a39d13ddf0dd Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Fri, 15 Mar 2013 21:27:58 -0400 -Subject: [PATCH 105/149] Python 3 compatibility: apply no longer exists - -Signed-off-by: Kamil Dudka ---- - python/curl/__init__.py | 4 ++-- - setup.py | 3 +-- - setup_win32_ssl.py | 2 +- - 3 files changed, 4 insertions(+), 5 deletions(-) - -diff --git a/python/curl/__init__.py b/python/curl/__init__.py -index b002618..5b6b549 100644 ---- a/python/curl/__init__.py -+++ b/python/curl/__init__.py -@@ -63,7 +63,7 @@ class Curl: - - def set_option(self, *args): - "Set an option on the retrieval." -- apply(self.handle.setopt, args) -+ self.handle.setopt(*args) - - def set_verbosity(self, level): - "Set verbosity to 1 to see transactions." -@@ -103,7 +103,7 @@ class Curl: - - def get_info(self, *args): - "Get information about retrieval." -- return apply(self.handle.getinfo, args) -+ return self.handle.getinfo(*args) - - def info(self): - "Return a dictionary with all info on the last response." -diff --git a/setup.py b/setup.py -index 235e4c9..013bd11 100644 ---- a/setup.py -+++ b/setup.py -@@ -221,5 +221,4 @@ if LooseVersion(distutils.__version__) < LooseVersion("1.0.3"): - if __name__ == "__main__": - for o in ext.extra_objects: - assert os.path.isfile(o), o -- # We can live with the deprecationwarning for a while -- apply(setup, (), setup_args) -+ setup(**setup_args) -diff --git a/setup_win32_ssl.py b/setup_win32_ssl.py -index 332c04c..0ecc399 100644 ---- a/setup_win32_ssl.py -+++ b/setup_win32_ssl.py -@@ -32,5 +32,5 @@ ext.extra_objects.append(r"c:\src\pool\libidn-0.5.15" + pool + "idn.lib") - if __name__ == "__main__": - for o in ext.extra_objects: - assert os.path.isfile(o), o -- apply(setup, (), setup_args) -+ setup(**setup_args) - --- -1.7.1 - - -From c903dc9eebdf010bfc8142b5f47cd564e04baa19 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Tue, 26 Feb 2013 16:40:28 -0500 -Subject: [PATCH 106/149] Python 3 compatibility: exception raising syntax - -Signed-off-by: Kamil Dudka ---- - setup.py | 2 +- - 1 files changed, 1 insertions(+), 1 deletions(-) - -diff --git a/setup.py b/setup.py -index 013bd11..725007c 100644 ---- a/setup.py -+++ b/setup.py -@@ -108,7 +108,7 @@ else: - if p.wait() == 0: - optbuf += stdout - if optbuf == "": -- raise Exception, ("Neither of curl-config --libs or --static-libs" + -+ raise Exception("Neither of curl-config --libs or --static-libs" + - "produced output") - libs = split_quoted(optbuf) - --- -1.7.1 - - -From 2643ab0cd015f09b1a0fd928c8e1102eee8e203e Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Wed, 27 Feb 2013 20:23:28 -0500 -Subject: [PATCH 107/149] Python 3 compatibility: print syntax (src) - -Signed-off-by: Kamil Dudka ---- - python/curl/__init__.py | 8 ++++---- - 1 files changed, 4 insertions(+), 4 deletions(-) - -diff --git a/python/curl/__init__.py b/python/curl/__init__.py -index 5b6b549..5617262 100644 ---- a/python/curl/__init__.py -+++ b/python/curl/__init__.py -@@ -164,10 +164,10 @@ if __name__ == "__main__": - url = sys.argv[1] - c = Curl() - c.get(url) -- print c.body() -- print '='*74 + '\n' -+ print(c.body()) -+ print('='*74 + '\n') - import pprint - pprint.pprint(c.info()) -- print c.get_info(pycurl.OS_ERRNO) -- print c.info()['os-errno'] -+ print(c.get_info(pycurl.OS_ERRNO)) -+ print(c.info()['os-errno']) - c.close() --- -1.7.1 - - -From c2f5306b77da24f1cb1cf45b9dbda93b817bf6e6 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Tue, 26 Feb 2013 16:49:09 -0500 -Subject: [PATCH 108/149] Python 3 compatibility: print syntax (former tests) - -Signed-off-by: Kamil Dudka ---- - examples/tests/test_gtk.py | 2 +- - examples/tests/test_xmlrpc.py | 2 +- - 2 files changed, 2 insertions(+), 2 deletions(-) - -diff --git a/examples/tests/test_gtk.py b/examples/tests/test_gtk.py -index 7104439..da8c22a 100644 ---- a/examples/tests/test_gtk.py -+++ b/examples/tests/test_gtk.py -@@ -83,7 +83,7 @@ class Test(threading.Thread): - - # Check command line args - if len(sys.argv) < 3: -- print "Usage: %s " % sys.argv[0] -+ print("Usage: %s " % sys.argv[0]) - raise SystemExit - - # Make a progress bar window -diff --git a/examples/tests/test_xmlrpc.py b/examples/tests/test_xmlrpc.py -index bc5953e..3a5469a 100644 ---- a/examples/tests/test_xmlrpc.py -+++ b/examples/tests/test_xmlrpc.py -@@ -24,6 +24,6 @@ c.setopt(c.POST, 1) - c.setopt(c.HTTPHEADER, xmlrpc_header) - c.setopt(c.POSTFIELDS, xmlrpc_template % ("examples.getStateName", xmlrpclib.dumps((5,)))) - --print 'Response from http://betty.userland.com/' -+print('Response from http://betty.userland.com/') - c.perform() - c.close() --- -1.7.1 - - -From 0106dfe86ca2a5620f07c2687cb05502636298ce Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Wed, 27 Feb 2013 20:23:39 -0500 -Subject: [PATCH 109/149] Python 3 compatibility: print syntax (examples) - -Signed-off-by: Kamil Dudka ---- - examples/basicfirst.py | 4 +- - examples/file_upload.py | 6 +- - examples/linksys.py | 182 +++++++++++++++++++++--------------------- - examples/retriever-multi.py | 10 +- - examples/retriever.py | 6 +- - examples/sfquery.py | 2 +- - examples/xmlrpc_curl.py | 6 +- - 7 files changed, 108 insertions(+), 108 deletions(-) - -diff --git a/examples/basicfirst.py b/examples/basicfirst.py -index af59405..44060af 100644 ---- a/examples/basicfirst.py -+++ b/examples/basicfirst.py -@@ -13,7 +13,7 @@ class Test: - def body_callback(self, buf): - self.contents = self.contents + buf - --print >>sys.stderr, 'Testing', pycurl.version -+sys.stderr.write("Testing %s\n" % pycurl.version) - - t = Test() - c = pycurl.Curl() -@@ -22,4 +22,4 @@ c.setopt(c.WRITEFUNCTION, t.body_callback) - c.perform() - c.close() - --print t.contents -+print(t.contents) -diff --git a/examples/file_upload.py b/examples/file_upload.py -index a514c1c..7750865 100644 ---- a/examples/file_upload.py -+++ b/examples/file_upload.py -@@ -15,13 +15,13 @@ class FileReader: - - # Check commandline arguments - if len(sys.argv) < 3: -- print "Usage: %s " % sys.argv[0] -+ print("Usage: %s " % sys.argv[0]) - raise SystemExit - url = sys.argv[1] - filename = sys.argv[2] - - if not os.path.exists(filename): -- print "Error: the file '%s' does not exist" % filename -+ print("Error: the file '%s' does not exist" % filename) - raise SystemExit - - # Initialize pycurl -@@ -41,6 +41,6 @@ filesize = os.path.getsize(filename) - c.setopt(pycurl.INFILESIZE, filesize) - - # Start transfer --print 'Uploading file %s to url %s' % (filename, url) -+print('Uploading file %s to url %s' % (filename, url)) - c.perform() - c.close() -diff --git a/examples/linksys.py b/examples/linksys.py -index 5304886..24cb2a9 100755 ---- a/examples/linksys.py -+++ b/examples/linksys.py -@@ -224,11 +224,11 @@ if __name__ == "__main__": - self.session = LinksysSession() - if os.isatty(0): - import readline -- print "Type ? or `help' for help." -+ print("Type ? or `help' for help.") - self.prompt = self.session.host + ": " - else: - self.prompt = "" -- print "Bar1" -+ print("Bar1") - - def flag_command(self, func, line): - if line.strip() in ("on", "enable", "yes"): -@@ -246,96 +246,96 @@ if __name__ == "__main__": - self.session.cache_flush() - self.prompt = self.session.host + ": " - else: -- print self.session.host -+ print(self.session.host) - return 0 - def help_connect(self): -- print "Usage: connect []" -- print "Connect to a Linksys by name or IP address." -- print "If no argument is given, print the current host." -+ print("Usage: connect []") -+ print("Connect to a Linksys by name or IP address.") -+ print("If no argument is given, print the current host.") - - def do_status(self, line): - self.session.cache_load("") - if "" in self.session.pagecache: -- print "Firmware:", self.session.get_firmware_version() -- print "LAN MAC:", self.session.get_LAN_MAC() -- print "Wireless MAC:", self.session.get_Wireless_MAC() -- print "WAN MAC:", self.session.get_WAN_MAC() -- print "." -+ print("Firmware:", self.session.get_firmware_version()) -+ print("LAN MAC:", self.session.get_LAN_MAC()) -+ print("Wireless MAC:", self.session.get_Wireless_MAC()) -+ print("WAN MAC:", self.session.get_WAN_MAC()) -+ print(".") - return 0 - def help_status(self): -- print "Usage: status" -- print "The status command shows the status of the Linksys." -- print "It is mainly useful as a sanity check to make sure" -- print "the box is responding correctly." -+ print("Usage: status") -+ print("The status command shows the status of the Linksys.") -+ print("It is mainly useful as a sanity check to make sure") -+ print("the box is responding correctly.") - - def do_verbose(self, line): - self.flag_command(self.session.set_verbosity, line) - def help_verbose(self): -- print "Usage: verbose {on|off|enable|disable|yes|no}" -- print "Enables display of HTTP requests." -+ print("Usage: verbose {on|off|enable|disable|yes|no}") -+ print("Enables display of HTTP requests.") - - def do_host(self, line): - self.session.set_host_name(line) - return 0 - def help_host(self): -- print "Usage: host " -- print "Sets the Host field to be queried by the ISP." -+ print("Usage: host ") -+ print("Sets the Host field to be queried by the ISP.") - - def do_domain(self, line): -- print "Usage: host " -+ print("Usage: host ") - self.session.set_domain_name(line) - return 0 - def help_domain(self): -- print "Sets the Domain field to be queried by the ISP." -+ print("Sets the Domain field to be queried by the ISP.") - - def do_lan_address(self, line): - self.session.set_LAN_IP(line) - return 0 - def help_lan_address(self): -- print "Usage: lan_address " -- print "Sets the LAN IP address." -+ print("Usage: lan_address ") -+ print("Sets the LAN IP address.") - - def do_lan_netmask(self, line): - self.session.set_LAN_netmask(line) - return 0 - def help_lan_netmask(self): -- print "Usage: lan_netmask " -- print "Sets the LAN subnetwork mask." -+ print("Usage: lan_netmask ") -+ print("Sets the LAN subnetwork mask.") - - def do_wireless(self, line): - self.flag_command(self.session.set_wireless, line) - return 0 - def help_wireless(self): -- print "Usage: wireless {on|off|enable|disable|yes|no}" -- print "Switch to enable or disable wireless features." -+ print("Usage: wireless {on|off|enable|disable|yes|no}") -+ print("Switch to enable or disable wireless features.") - - def do_ssid(self, line): - self.session.set_SSID(line) - return 0 - def help_ssid(self): -- print "Usage: ssid " -- print "Sets the SSID used to control wireless access." -+ print("Usage: ssid ") -+ print("Sets the SSID used to control wireless access.") - - def do_ssid_broadcast(self, line): - self.flag_command(self.session.set_SSID_broadcast, line) - return 0 - def help_ssid_broadcast(self): -- print "Usage: ssid_broadcast {on|off|enable|disable|yes|no}" -- print "Switch to enable or disable SSID broadcast." -+ print("Usage: ssid_broadcast {on|off|enable|disable|yes|no}") -+ print("Switch to enable or disable SSID broadcast.") - - def do_channel(self, line): - self.session.set_channel(line) - return 0 - def help_channel(self): -- print "Usage: channel " -- print "Sets the wireless channel." -+ print("Usage: channel ") -+ print("Sets the wireless channel.") - - def do_wep(self, line): - self.flag_command(self.session.set_WEP, line) - return 0 - def help_wep(self): -- print "Usage: wep {on|off|enable|disable|yes|no}" -- print "Switch to enable or disable WEP security." -+ print("Usage: wep {on|off|enable|disable|yes|no}") -+ print("Switch to enable or disable WEP security.") - - def do_wan_type(self, line): - try: -@@ -345,29 +345,29 @@ if __name__ == "__main__": - print >>sys.stderr, "linksys: unknown connection type." - return 0 - def help_wan_type(self): -- print "Usage: wan_type {auto|static|ppoe|ras|pptp|heartbeat}" -- print "Set the WAN connection type." -+ print("Usage: wan_type {auto|static|ppoe|ras|pptp|heartbeat}") -+ print("Set the WAN connection type.") - - def do_wan_address(self, line): - self.session.set_WAN_IP(line) - return 0 - def help_wan_address(self): -- print "Usage: wan_address " -- print "Sets the WAN IP address." -+ print("Usage: wan_address ") -+ print("Sets the WAN IP address.") - - def do_wan_netmask(self, line): - self.session.set_WAN_netmask(line) - return 0 - def help_wan_netmask(self): -- print "Usage: wan_netmask " -- print "Sets the WAN subnetwork mask." -+ print("Usage: wan_netmask ") -+ print("Sets the WAN subnetwork mask.") - - def do_wan_gateway(self, line): - self.session.set_WAN_gateway(line) - return 0 - def help_wan_gateway(self): -- print "Usage: wan_gateway " -- print "Sets the LAN subnetwork mask." -+ print("Usage: wan_gateway ") -+ print("Sets the LAN subnetwork mask.") - - def do_dns(self, line): - (index, address) = line.split() -@@ -377,52 +377,52 @@ if __name__ == "__main__": - print >>sys.stderr, "linksys: server index out of bounds." - return 0 - def help_dns(self): -- print "Usage: dns {1|2|3} " -- print "Sets a primary, secondary, or tertiary DNS server address." -+ print("Usage: dns {1|2|3} ") -+ print("Sets a primary, secondary, or tertiary DNS server address.") - - def do_password(self, line): - self.session.set_password(line) - return 0 - def help_password(self): -- print "Usage: password " -- print "Sets the router password." -+ print("Usage: password ") -+ print("Sets the router password.") - - def do_upnp(self, line): - self.flag_command(self.session.set_UPnP, line) - return 0 - def help_upnp(self): -- print "Usage: upnp {on|off|enable|disable|yes|no}" -- print "Switch to enable or disable Universal Plug and Play." -+ print("Usage: upnp {on|off|enable|disable|yes|no}") -+ print("Switch to enable or disable Universal Plug and Play.") - - def do_reset(self, line): - self.session.reset() - def help_reset(self): -- print "Usage: reset" -- print "Reset Linksys settings to factory defaults." -+ print("Usage: reset") -+ print("Reset Linksys settings to factory defaults.") - - def do_dhcp(self, line): - self.flag_command(self.session.set_DHCP, line) - def help_dhcp(self): -- print "Usage: dhcp {on|off|enable|disable|yes|no}" -- print "Switch to enable or disable DHCP features." -+ print("Usage: dhcp {on|off|enable|disable|yes|no}") -+ print("Switch to enable or disable DHCP features.") - - def do_dhcp_start(self, line): - self.session.set_DHCP_starting_IP(line) - def help_dhcp_start(self): -- print "Usage: dhcp_start " -- print "Set the start address of the DHCP pool." -+ print("Usage: dhcp_start ") -+ print("Set the start address of the DHCP pool.") - - def do_dhcp_users(self, line): - self.session.set_DHCP_users(line) - def help_dhcp_users(self): -- print "Usage: dhcp_users " -- print "Set number of address slots to allocate in the DHCP pool." -+ print("Usage: dhcp_users ") -+ print("Set number of address slots to allocate in the DHCP pool.") - - def do_dhcp_lease(self, line): - self.session.set_DHCP_lease(line) - def help_dhcp_lease(self): -- print "Usage: dhcp_lease " -- print "Set number of address slots to allocate in the DHCP pool." -+ print("Usage: dhcp_lease ") -+ print("Set number of address slots to allocate in the DHCP pool.") - - def do_dhcp_dns(self, line): - (index, address) = line.split() -@@ -432,46 +432,46 @@ if __name__ == "__main__": - print >>sys.stderr, "linksys: server index out of bounds." - return 0 - def help_dhcp_dns(self): -- print "Usage: dhcp_dns {1|2|3} " -- print "Sets primary, secondary, or tertiary DNS server address." -+ print("Usage: dhcp_dns {1|2|3} ") -+ print("Sets primary, secondary, or tertiary DNS server address.") - - def do_logging(self, line): - self.flag_command(self.session.set_logging, line) - def help_logging(self): -- print "Usage: logging {on|off|enable|disable|yes|no}" -- print "Switch to enable or disable session logging." -+ print("Usage: logging {on|off|enable|disable|yes|no}") -+ print("Switch to enable or disable session logging.") - - def do_log_address(self, line): - self.session.set_Log_address(line) - def help_log_address(self): -- print "Usage: log_address " -- print "Set the last quad of the address to which to log." -+ print("Usage: log_address ") -+ print("Set the last quad of the address to which to log.") - - def do_configure(self, line): - self.session.configure() - return 0 - def help_configure(self): -- print "Usage: configure" -- print "Writes the configuration to the Linksys." -+ print("Usage: configure") -+ print("Writes the configuration to the Linksys.") - - def do_cache(self, line): -- print self.session.pagecache -+ print(self.session.pagecache) - def help_cache(self): -- print "Usage: cache" -- print "Display the page cache." -+ print("Usage: cache") -+ print("Display the page cache.") - - def do_quit(self, line): - return 1 - def help_quit(self, line): -- print "The quit command ends your linksys session without" -- print "writing configuration changes to the Linksys." -+ print("The quit command ends your linksys session without") -+ print("writing configuration changes to the Linksys.") - def do_EOF(self, line): -- print "" -+ print("") - self.session.configure() - return 1 - def help_EOF(self): -- print "The EOF command writes the configuration to the linksys" -- print "and ends your session." -+ print("The EOF command writes the configuration to the linksys") -+ print("and ends your session.") - - def default(self, line): - """Pass the command through to be executed by the shell.""" -@@ -479,11 +479,11 @@ if __name__ == "__main__": - return 0 - - def help_help(self): -- print "On-line help is available through this command." -- print "? is a convenience alias for help." -+ print("On-line help is available through this command.") -+ print("? is a convenience alias for help.") - - def help_introduction(self): -- print """\ -+ print("""\ - - This program supports changing the settings on Linksys blue-box routers. This - capability may come in handy when they freeze up and have to be reset. Though -@@ -506,16 +506,16 @@ will be shipped to the Linksys at the end of session (e.g. when the program - running in batch mode encounters end-of-file or you type a control-D). If you - end the session with `quit', pending changes will be discarded. - --For more help, read the topics 'wan', 'lan', and 'wireless'.""" -+For more help, read the topics 'wan', 'lan', and 'wireless'.""") - - def help_lan(self): -- print """\ -+ print("""\ - The `lan_address' and `lan_netmask' commands let you set the IP location of - the Linksys on your LAN, or inside. Normally you'll want to leave these --untouched.""" -+untouched.""") - - def help_wan(self): -- print """\ -+ print("""\ - The WAN commands become significant if you are using the BEFSR41 or any of - the other Linksys boxes designed as DSL or cable-modem gateways. You will - need to use `wan_type' to declare how you expect to get your address. -@@ -527,22 +527,22 @@ need to use the `dns' command to declare which remote servers your DNS - requests should be forwarded to. - - Some ISPs may require you to set host and domain for use with dynamic-address --allocation.""" -+allocation.""") - - def help_wireless(self): -- print """\ -+ print("""\ - The channel, ssid, ssid_broadcast, wep, and wireless commands control --wireless routing.""" -+wireless routing.""") - - def help_switches(self): -- print "Switches may be turned on with 'on', 'enable', or 'yes'." -- print "Switches may be turned off with 'off', 'disable', or 'no'." -- print "Switch commands include: wireless, ssid_broadcast." -+ print("Switches may be turned on with 'on', 'enable', or 'yes'.") -+ print("Switches may be turned off with 'off', 'disable', or 'no'.") -+ print("Switch commands include: wireless, ssid_broadcast.") - - def help_addresses(self): -- print "An address argument must be a valid IP address;" -- print "four decimal numbers separated by dots, each " -- print "between 0 and 255." -+ print("An address argument must be a valid IP address;") -+ print("four decimal numbers separated by dots, each ") -+ print("between 0 and 255.") - - def emptyline(self): - pass -diff --git a/examples/retriever-multi.py b/examples/retriever-multi.py -index 1a941be..ad4cebd 100644 ---- a/examples/retriever-multi.py -+++ b/examples/retriever-multi.py -@@ -31,7 +31,7 @@ try: - if len(sys.argv) >= 3: - num_conn = int(sys.argv[2]) - except: -- print "Usage: %s [<# of concurrent connections>]" % sys.argv[0] -+ print("Usage: %s [<# of concurrent connections>]" % sys.argv[0]) - raise SystemExit - - -@@ -50,8 +50,8 @@ assert queue, "no URLs given" - num_urls = len(queue) - num_conn = min(num_conn, num_urls) - assert 1 <= num_conn <= 10000, "invalid number of concurrent connections" --print "PycURL %s (compiled against 0x%x)" % (pycurl.version, pycurl.COMPILE_LIBCURL_VERSION_NUM) --print "----- Getting", num_urls, "URLs using", num_conn, "connections -----" -+print("PycURL %s (compiled against 0x%x)" % (pycurl.version, pycurl.COMPILE_LIBCURL_VERSION_NUM)) -+print("----- Getting", num_urls, "URLs using", num_conn, "connections -----") - - - # Pre-allocate a list of curl objects -@@ -95,13 +95,13 @@ while num_processed < num_urls: - c.fp.close() - c.fp = None - m.remove_handle(c) -- print "Success:", c.filename, c.url, c.getinfo(pycurl.EFFECTIVE_URL) -+ print("Success:", c.filename, c.url, c.getinfo(pycurl.EFFECTIVE_URL)) - freelist.append(c) - for c, errno, errmsg in err_list: - c.fp.close() - c.fp = None - m.remove_handle(c) -- print "Failed: ", c.filename, c.url, errno, errmsg -+ print("Failed: ", c.filename, c.url, errno, errmsg) - freelist.append(c) - num_processed = num_processed + len(ok_list) + len(err_list) - if num_q == 0: -diff --git a/examples/retriever.py b/examples/retriever.py -index 2c91d07..be1b6ea 100644 ---- a/examples/retriever.py -+++ b/examples/retriever.py -@@ -31,7 +31,7 @@ try: - if len(sys.argv) >= 3: - num_conn = int(sys.argv[2]) - except: -- print "Usage: %s [<# of concurrent connections>]" % sys.argv[0] -+ print("Usage: %s [<# of concurrent connections>]" % sys.argv[0]) - raise SystemExit - - -@@ -50,8 +50,8 @@ assert queue.queue, "no URLs given" - num_urls = len(queue.queue) - num_conn = min(num_conn, num_urls) - assert 1 <= num_conn <= 10000, "invalid number of concurrent connections" --print "PycURL %s (compiled against 0x%x)" % (pycurl.version, pycurl.COMPILE_LIBCURL_VERSION_NUM) --print "----- Getting", num_urls, "URLs using", num_conn, "connections -----" -+print("PycURL %s (compiled against 0x%x)" % (pycurl.version, pycurl.COMPILE_LIBCURL_VERSION_NUM)) -+print("----- Getting", num_urls, "URLs using", num_conn, "connections -----") - - - class WorkerThread(threading.Thread): -diff --git a/examples/sfquery.py b/examples/sfquery.py -index 741feb0..16aa9d4 100644 ---- a/examples/sfquery.py -+++ b/examples/sfquery.py -@@ -40,7 +40,7 @@ if __name__ == "__main__": - name, account, password = auth - except: - if len(sys.argv) < 4: -- print "Usage: %s " % sys.argv[0] -+ print("Usage: %s " % sys.argv[0]) - raise SystemExit - name = sys.argv[2] - password = sys.argv[3] -diff --git a/examples/xmlrpc_curl.py b/examples/xmlrpc_curl.py -index ec0df50..a9a7d9e 100644 ---- a/examples/xmlrpc_curl.py -+++ b/examples/xmlrpc_curl.py -@@ -55,8 +55,8 @@ if __name__ == "__main__": - ## Test - server = xmlrpclib.ServerProxy("http://betty.userland.com", - transport=CURLTransport()) -- print server -+ print(server) - try: -- print server.examples.getStateName(41) -+ print(server.examples.getStateName(41)) - except xmlrpclib.Error, v: -- print "ERROR", v -+ print("ERROR", v) --- -1.7.1 - - -From bb811ad5b2d2819d502c84c735b7efec84d4d557 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Thu, 28 Feb 2013 00:30:00 -0500 -Subject: [PATCH 110/149] Python 3 compatibility: printing to stderr (linksys example) - -Signed-off-by: Kamil Dudka ---- - examples/linksys.py | 14 +++++++++----- - 1 files changed, 9 insertions(+), 5 deletions(-) - -diff --git a/examples/linksys.py b/examples/linksys.py -index 24cb2a9..f66ff0d 100755 ---- a/examples/linksys.py -+++ b/examples/linksys.py -@@ -34,6 +34,10 @@ - - import sys, re, copy, curl, exceptions - -+def print_stderr(arg): -+ sys.stderr.write(arg) -+ sys.stderr.write("\n") -+ - class LinksysError(exceptions.Exception): - def __init__(self, *args): - self.args = args -@@ -202,7 +206,7 @@ class LinksysSession: - for (page, field, value) in self.actions: - self.cache_load(page) - if self.pagecache[page].find(field) == -1: -- print >>sys.stderr, "linksys: field %s not found where expected in page %s!" % (field, os.path.join(self.host, page)) -+ print_stderr("linksys: field %s not found where expected in page %s!" % (field, os.path.join(self.host, page))) - continue - else: - fields.append((field, value)) -@@ -236,7 +240,7 @@ if __name__ == "__main__": - elif line.strip() in ("off", "disable", "no"): - func(False) - else: -- print >>sys.stderr, "linksys: unknown switch value" -+ print_stderr("linksys: unknown switch value") - return 0 - - def do_connect(self, line): -@@ -342,7 +346,7 @@ if __name__ == "__main__": - type=eval("LinksysSession.WAN_CONNECT_"+line.strip().upper()) - self.session.set_connection_type(type) - except ValueError: -- print >>sys.stderr, "linksys: unknown connection type." -+ print_stderr("linksys: unknown connection type.") - return 0 - def help_wan_type(self): - print("Usage: wan_type {auto|static|ppoe|ras|pptp|heartbeat}") -@@ -374,7 +378,7 @@ if __name__ == "__main__": - if index in ("1", "2", "3"): - self.session.set_DNS_server(eval(index), address) - else: -- print >>sys.stderr, "linksys: server index out of bounds." -+ print_stderr("linksys: server index out of bounds.") - return 0 - def help_dns(self): - print("Usage: dns {1|2|3} ") -@@ -429,7 +433,7 @@ if __name__ == "__main__": - if index in ("1", "2", "3"): - self.session.set_DHCP_DNS_server(eval(index), address) - else: -- print >>sys.stderr, "linksys: server index out of bounds." -+ print_stderr("linksys: server index out of bounds.") - return 0 - def help_dhcp_dns(self): - print("Usage: dhcp_dns {1|2|3} ") --- -1.7.1 - - -From 2ea28565bcf16bb2e792467650012081e2268cf1 Mon Sep 17 00:00:00 2001 -From: Kamil Dudka -Date: Mon, 8 Apr 2013 14:25:26 +0200 -Subject: [PATCH 111/149] allow to unset a previously set RANGE value -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -... and add the unset_range_test.py unit test exercising it - -Reported by: Zdeněk Pavlas -Bug: https://bugzilla.redhat.com/928370 - -Signed-off-by: Kamil Dudka ---- - src/pycurl.c | 1 + - tests/unset_range_test.py | 39 +++++++++++++++++++++++++++++++++++++++ - 2 files changed, 40 insertions(+), 0 deletions(-) - create mode 100644 tests/unset_range_test.py - -diff --git a/src/pycurl.c b/src/pycurl.c -index a30c339..bd28b81 100644 ---- a/src/pycurl.c -+++ b/src/pycurl.c -@@ -1561,6 +1561,7 @@ util_curl_unsetopt(CurlObject *self, int option) - case CURLOPT_RANDOM_FILE: - case CURLOPT_SSL_CIPHER_LIST: - case CURLOPT_USERPWD: -+ case CURLOPT_RANGE: - SETOPT((char *) 0); - break; - -diff --git a/tests/unset_range_test.py b/tests/unset_range_test.py -new file mode 100644 -index 0000000..4400851 ---- /dev/null -+++ b/tests/unset_range_test.py -@@ -0,0 +1,39 @@ -+#! /usr/bin/env python -+# -*- coding: iso-8859-1 -*- -+# vi:ts=4:et -+ -+import os.path -+import pycurl -+import sys -+import unittest -+ -+class UnsetRangeTest(unittest.TestCase): -+ def setUp(self): -+ self.curl = pycurl.Curl() -+ -+ def tearDown(self): -+ self.curl.close() -+ -+ def test_unset_range(self): -+ def write_cb(data): -+ self.read += len(data) -+ return None -+ -+ # download bytes 0-9 of the script itself through the file:// protocol -+ self.read = 0 -+ self.curl.setopt(pycurl.URL, 'file://' + os.path.abspath(sys.argv[0])) -+ self.curl.setopt(pycurl.WRITEFUNCTION, write_cb) -+ self.curl.setopt(pycurl.RANGE, '0-9') -+ self.curl.perform() -+ assert 10 == self.read -+ -+ # the RANGE setting should be preserved from the previous transfer -+ self.read = 0 -+ self.curl.perform() -+ assert 10 == self.read -+ -+ # drop the RANGE setting using unsetopt() and download entire script -+ self.read = 0 -+ self.curl.unsetopt(pycurl.RANGE) -+ self.curl.perform() -+ assert 10 < self.read --- -1.7.1 - - -From 576e87580356497b601ec16a9a794db959d43f0e Mon Sep 17 00:00:00 2001 -From: Kamil Dudka -Date: Mon, 8 Apr 2013 14:31:30 +0200 -Subject: [PATCH 112/149] allow to use setopt(..., None) as unsetopt() -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -... and extend the unset_range_test.py unit test to exercise it - -Reported by: Zdeněk Pavlas -Bug: https://bugzilla.redhat.com/928370 - -Signed-off-by: Kamil Dudka ---- - src/pycurl.c | 4 +--- - tests/unset_range_test.py | 12 ++++++++++++ - 2 files changed, 13 insertions(+), 3 deletions(-) - -diff --git a/src/pycurl.c b/src/pycurl.c -index bd28b81..619ca20 100644 ---- a/src/pycurl.c -+++ b/src/pycurl.c -@@ -1631,12 +1631,10 @@ do_curl_setopt(CurlObject *self, PyObject *args) - if (option % 10000 >= OPTIONS_SIZE) - goto error; - --#if 0 /* XXX - should we ??? */ -- /* Handle the case of None */ -+ /* Handle the case of None as the call of unsetopt() */ - if (obj == Py_None) { - return util_curl_unsetopt(self, option); - } --#endif - - /* Handle the case of string arguments */ - if (PyString_Check(obj)) { -diff --git a/tests/unset_range_test.py b/tests/unset_range_test.py -index 4400851..10ee801 100644 ---- a/tests/unset_range_test.py -+++ b/tests/unset_range_test.py -@@ -37,3 +37,15 @@ class UnsetRangeTest(unittest.TestCase): - self.curl.unsetopt(pycurl.RANGE) - self.curl.perform() - assert 10 < self.read -+ -+ # now set the RANGE again and check that pycurl takes it into account -+ self.read = 0 -+ self.curl.setopt(pycurl.RANGE, '0-9') -+ self.curl.perform() -+ assert 10 == self.read -+ -+ # now drop the RANGE setting using setopt(..., None) -+ self.read = 0 -+ self.curl.setopt(pycurl.RANGE, None) -+ self.curl.perform() -+ assert 10 < self.read --- -1.7.1 - - -From 5c27a721fc3aaeaeddabb394ae6c9502391bdea6 Mon Sep 17 00:00:00 2001 -From: Zdenek Pavlas -Date: Wed, 13 Mar 2013 16:55:58 +0100 -Subject: [PATCH 113/149] add the GLOBAL_ACK_EINTR constant to the list of exported symbols - -... if built against a new enough version of libcurl - -Bug: https://bugzilla.redhat.com/920589 - -Signed-off-by: Kamil Dudka ---- - src/pycurl.c | 19 +++++++++++++++---- - 1 files changed, 15 insertions(+), 4 deletions(-) - -diff --git a/src/pycurl.c b/src/pycurl.c -index 619ca20..9950e00 100644 ---- a/src/pycurl.c -+++ b/src/pycurl.c -@@ -3210,6 +3210,16 @@ static PyTypeObject CurlMulti_Type = { - */ - }; - -+static int -+are_global_init_flags_valid(int flags) -+{ -+#ifdef CURL_GLOBAL_ACK_EINTR -+ /* CURL_GLOBAL_ACK_EINTR was introduced in libcurl-7.30.0 */ -+ return !(flags & ~(CURL_GLOBAL_ALL | CURL_GLOBAL_ACK_EINTR)); -+#else -+ return !(flags & ~(CURL_GLOBAL_ALL)); -+#endif -+} - - /************************************************************************* - // module level -@@ -3227,10 +3237,7 @@ do_global_init(PyObject *dummy, PyObject *args) - return NULL; - } - -- if (!(option == CURL_GLOBAL_SSL || -- option == CURL_GLOBAL_WIN32 || -- option == CURL_GLOBAL_ALL || -- option == CURL_GLOBAL_NOTHING)) { -+ if (!are_global_init_flags_valid(option)) { - PyErr_SetString(PyExc_ValueError, "invalid option to global_init"); - return NULL; - } -@@ -3866,6 +3873,10 @@ initpycurl(void) - insint(d, "GLOBAL_ALL", CURL_GLOBAL_ALL); - insint(d, "GLOBAL_NOTHING", CURL_GLOBAL_NOTHING); - insint(d, "GLOBAL_DEFAULT", CURL_GLOBAL_DEFAULT); -+#ifdef CURL_GLOBAL_ACK_EINTR -+ /* CURL_GLOBAL_ACK_EINTR was introduced in libcurl-7.30.0 */ -+ insint(d, "GLOBAL_ACK_EINTR", CURL_GLOBAL_ACK_EINTR); -+#endif - - - /* constants for curl_multi_socket interface */ --- -1.7.1 - - -From 5a165baaa2b275293c1e045d8c0023d55bd0cbfc Mon Sep 17 00:00:00 2001 -From: Kamil Dudka -Date: Wed, 3 Apr 2013 15:06:32 +0200 -Subject: [PATCH 114/149] tests/global_init_ack_eintr.py: test GLOBAL_ACK_EINTR - -... if we have a new enough version of libcurl - -Signed-off-by: Kamil Dudka ---- - tests/global_init_ack_eintr.py | 22 ++++++++++++++++++++++ - 1 files changed, 22 insertions(+), 0 deletions(-) - create mode 100644 tests/global_init_ack_eintr.py - -diff --git a/tests/global_init_ack_eintr.py b/tests/global_init_ack_eintr.py -new file mode 100644 -index 0000000..429fc3f ---- /dev/null -+++ b/tests/global_init_ack_eintr.py -@@ -0,0 +1,22 @@ -+#! /usr/bin/env python -+# -*- coding: iso-8859-1 -*- -+# vi:ts=4:et -+ -+import pycurl -+import unittest -+ -+from . import util -+ -+class GlobalInitAckEintrTest(unittest.TestCase): -+ def test_global_init_default(self): -+ # initialize libcurl with DEFAULT flags -+ pycurl.global_init(pycurl.GLOBAL_DEFAULT) -+ pycurl.global_cleanup() -+ -+ def test_global_init_ack_eintr(self): -+ # the GLOBAL_ACK_EINTR flag was introduced in libcurl-7.30, but can also -+ # be backported for older versions of libcurl at the distribution level -+ if not util.pycurl_version_less_than(7, 30) or hasattr(pycurl, 'GLOBAL_ACK_EINTR'): -+ # initialize libcurl with the GLOBAL_ACK_EINTR flag -+ pycurl.global_init(pycurl.GLOBAL_ACK_EINTR) -+ pycurl.global_cleanup() --- -1.7.1 - - -From 788e012e64250cfb473d201a836e1a92c7772154 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Tue, 9 Apr 2013 18:52:41 -0400 -Subject: [PATCH 115/149] Add a test for bogus init flags and fix test name - -Signed-off-by: Kamil Dudka ---- - tests/global_init_ack_eintr.py | 22 ---------------------- - tests/global_init_test.py | 28 ++++++++++++++++++++++++++++ - 2 files changed, 28 insertions(+), 22 deletions(-) - delete mode 100644 tests/global_init_ack_eintr.py - create mode 100644 tests/global_init_test.py - -diff --git a/tests/global_init_ack_eintr.py b/tests/global_init_ack_eintr.py -deleted file mode 100644 -index 429fc3f..0000000 ---- a/tests/global_init_ack_eintr.py -+++ /dev/null -@@ -1,22 +0,0 @@ --#! /usr/bin/env python --# -*- coding: iso-8859-1 -*- --# vi:ts=4:et -- --import pycurl --import unittest -- --from . import util -- --class GlobalInitAckEintrTest(unittest.TestCase): -- def test_global_init_default(self): -- # initialize libcurl with DEFAULT flags -- pycurl.global_init(pycurl.GLOBAL_DEFAULT) -- pycurl.global_cleanup() -- -- def test_global_init_ack_eintr(self): -- # the GLOBAL_ACK_EINTR flag was introduced in libcurl-7.30, but can also -- # be backported for older versions of libcurl at the distribution level -- if not util.pycurl_version_less_than(7, 30) or hasattr(pycurl, 'GLOBAL_ACK_EINTR'): -- # initialize libcurl with the GLOBAL_ACK_EINTR flag -- pycurl.global_init(pycurl.GLOBAL_ACK_EINTR) -- pycurl.global_cleanup() -diff --git a/tests/global_init_test.py b/tests/global_init_test.py -new file mode 100644 -index 0000000..b76254b ---- /dev/null -+++ b/tests/global_init_test.py -@@ -0,0 +1,28 @@ -+#! /usr/bin/env python -+# -*- coding: iso-8859-1 -*- -+# vi:ts=4:et -+ -+import pycurl -+import unittest -+import nose.tools -+ -+from . import util -+ -+class GlobalInitTest(unittest.TestCase): -+ def test_global_init_default(self): -+ # initialize libcurl with DEFAULT flags -+ pycurl.global_init(pycurl.GLOBAL_DEFAULT) -+ pycurl.global_cleanup() -+ -+ def test_global_init_ack_eintr(self): -+ # the GLOBAL_ACK_EINTR flag was introduced in libcurl-7.30, but can also -+ # be backported for older versions of libcurl at the distribution level -+ if not util.pycurl_version_less_than(7, 30) or hasattr(pycurl, 'GLOBAL_ACK_EINTR'): -+ # initialize libcurl with the GLOBAL_ACK_EINTR flag -+ pycurl.global_init(pycurl.GLOBAL_ACK_EINTR) -+ pycurl.global_cleanup() -+ -+ @nose.tools.raises(ValueError) -+ def test_global_init_bogus(self): -+ # initialize libcurl with bogus flags -+ pycurl.global_init(0xffff) --- -1.7.1 - - -From 91992e2ef3664d68c8e98269b78911b929ad991a Mon Sep 17 00:00:00 2001 -From: anonymous -Date: Tue, 26 Feb 2013 16:32:20 -0500 -Subject: [PATCH 116/149] Python 3 support. - -http://sourceforge.net/tracker/?func=detail&aid=2917775&group_id=28236&atid=392779 - -Signed-off-by: Kamil Dudka ---- - setup.py | 18 +++++++++--------- - 1 files changed, 9 insertions(+), 9 deletions(-) - -diff --git a/setup.py b/setup.py -index 725007c..e0d0379 100644 ---- a/setup.py -+++ b/setup.py -@@ -31,7 +31,7 @@ def scan_argv(s, default): - i = 1 - while i < len(sys.argv): - arg = sys.argv[i] -- if string.find(arg, s) == 0: -+ if str.find(arg, s) == 0: - p = arg[len(s):] - assert p, arg - del sys.argv[i] -@@ -46,8 +46,8 @@ def add_libdirs(envvar, sep, fatal=0): - v = os.environ.get(envvar) - if not v: - return -- for dir in string.split(v, sep): -- dir = string.strip(dir) -+ for dir in str.split(v, sep): -+ dir = str.strip(dir) - if not dir: - continue - dir = os.path.normpath(dir) -@@ -55,7 +55,7 @@ def add_libdirs(envvar, sep, fatal=0): - if not dir in library_dirs: - library_dirs.append(dir) - elif fatal: -- print "FATAL: bad directory %s in environment variable %s" % (dir, envvar) -+ print("FATAL: bad directory %s in environment variable %s" % (dir, envvar)) - sys.exit(1) - - -@@ -65,13 +65,13 @@ if sys.platform == "win32": - # and thus unlikely to match your installation. - CURL_DIR = r"c:\src\build\pycurl\curl-7.16.2.1" - CURL_DIR = scan_argv("--curl-dir=", CURL_DIR) -- print "Using curl directory:", CURL_DIR -+ print("Using curl directory:", CURL_DIR) - assert os.path.isdir(CURL_DIR), "please check CURL_DIR in setup.py" - include_dirs.append(os.path.join(CURL_DIR, "include")) - extra_objects.append(os.path.join(CURL_DIR, "lib", "libcurl.lib")) - extra_link_args.extend(["gdi32.lib", "wldap32.lib", "winmm.lib", "ws2_32.lib",]) - add_libdirs("LIB", ";") -- if string.find(sys.version, "MSC") >= 0: -+ if str.find(sys.version, "MSC") >= 0: - extra_compile_args.append("-O2") - extra_compile_args.append("-GF") # enable read-only string pooling - extra_compile_args.append("-WX") # treat warnings as errors -@@ -85,10 +85,10 @@ else: - CURL_CONFIG = scan_argv("--curl-config=", CURL_CONFIG) - d = os.popen("'%s' --version" % CURL_CONFIG).read() - if d: -- d = string.strip(d) -+ d = str.strip(d) - if not d: -- raise Exception, ("`%s' not found -- please install the libcurl development files" % CURL_CONFIG) -- print "Using %s (%s)" % (CURL_CONFIG, d) -+ raise Exception("`%s' not found -- please install the libcurl development files" % CURL_CONFIG) -+ print("Using %s (%s)" % (CURL_CONFIG, d)) - for e in split_quoted(os.popen("'%s' --cflags" % CURL_CONFIG).read()): - if e[:2] == "-I": - # do not add /usr/include --- -1.7.1 - - -From 6f6649b79ea66493e878eda3e3cfd87cba18a02f Mon Sep 17 00:00:00 2001 -From: decitre -Date: Tue, 26 Feb 2013 16:36:32 -0500 -Subject: [PATCH 117/149] Corrected Python 3 support. - -http://sourceforge.net/tracker/?func=detail&aid=3188495&group_id=28236&atid=392779 - -Signed-off-by: Kamil Dudka ---- - setup.py | 2 +- - 1 files changed, 1 insertions(+), 1 deletions(-) - -diff --git a/setup.py b/setup.py -index e0d0379..385facb 100644 ---- a/setup.py -+++ b/setup.py -@@ -106,7 +106,7 @@ else: - stdout=subprocess.PIPE) - (stdout, stderr) = p.communicate() - if p.wait() == 0: -- optbuf += stdout -+ optbuf += stdout.decode() - if optbuf == "": - raise Exception("Neither of curl-config --libs or --static-libs" + - "produced output") --- -1.7.1 - - -From d256095c74857a87fa7e3c9dbb0df58cdeded17f Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Thu, 28 Feb 2013 00:14:03 -0500 -Subject: [PATCH 118/149] Python 3 compatibility: StringIO (src) - -Signed-off-by: Kamil Dudka ---- - python/curl/__init__.py | 5 ++++- - 1 files changed, 4 insertions(+), 1 deletions(-) - -diff --git a/python/curl/__init__.py b/python/curl/__init__.py -index 5617262..3ab019f 100644 ---- a/python/curl/__init__.py -+++ b/python/curl/__init__.py -@@ -10,7 +10,10 @@ import os, sys, urllib, exceptions, mimetools, pycurl - try: - from cStringIO import StringIO - except ImportError: -- from StringIO import StringIO -+ try: -+ from StringIO import StringIO -+ except ImportError: -+ from io import StringIO - - try: - import signal --- -1.7.1 - - -From 04ebd364f9b027e0bbdaeecfca02493244efcfbd Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Thu, 28 Feb 2013 00:11:58 -0500 -Subject: [PATCH 119/149] Python 3 compatibility: StringIO (examples) - -Signed-off-by: Kamil Dudka ---- - examples/xmlrpc_curl.py | 5 ++++- - 1 files changed, 4 insertions(+), 1 deletions(-) - -diff --git a/examples/xmlrpc_curl.py b/examples/xmlrpc_curl.py -index a9a7d9e..bb6799e 100644 ---- a/examples/xmlrpc_curl.py -+++ b/examples/xmlrpc_curl.py -@@ -14,7 +14,10 @@ except ImportError: - try: - from cStringIO import StringIO - except ImportError: -- from StringIO import StringIO -+ try: -+ from StringIO import StringIO -+ except ImportError: -+ from io import StringIO - import xmlrpclib, pycurl - - --- -1.7.1 - - -From 936cceff39402efa5b91b2ff4d4d36c04c1675c8 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Thu, 28 Feb 2013 00:23:29 -0500 -Subject: [PATCH 120/149] Python 3 compatibility: xmlrpclib - -Signed-off-by: Kamil Dudka ---- - examples/tests/test_xmlrpc.py | 5 ++++- - examples/xmlrpc_curl.py | 6 +++++- - 2 files changed, 9 insertions(+), 2 deletions(-) - -diff --git a/examples/tests/test_xmlrpc.py b/examples/tests/test_xmlrpc.py -index 3a5469a..d64794e 100644 ---- a/examples/tests/test_xmlrpc.py -+++ b/examples/tests/test_xmlrpc.py -@@ -4,7 +4,10 @@ - # $Id$ - - ## XML-RPC lib included in python2.2 --import xmlrpclib -+try: -+ import xmlrpclib -+except ImportError: -+ import xmlrpc.client as xmlrpclib - import pycurl - - # Header fields passed in request -diff --git a/examples/xmlrpc_curl.py b/examples/xmlrpc_curl.py -index bb6799e..21418b5 100644 ---- a/examples/xmlrpc_curl.py -+++ b/examples/xmlrpc_curl.py -@@ -18,7 +18,11 @@ except ImportError: - from StringIO import StringIO - except ImportError: - from io import StringIO --import xmlrpclib, pycurl -+try: -+ import xmlrpclib -+except ImportError: -+ import xmlrpc.client as xmlrpclib -+import pycurl - - - class CURLTransport(xmlrpclib.Transport): --- -1.7.1 - - -From 00ce4e4176540bc899713bcd2c364ad8d3887a8b Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Thu, 28 Feb 2013 00:34:01 -0500 -Subject: [PATCH 121/149] Python 3 compatibility: urllib (src) - -Signed-off-by: Kamil Dudka ---- - python/curl/__init__.py | 10 +++++++--- - 1 files changed, 7 insertions(+), 3 deletions(-) - -diff --git a/python/curl/__init__.py b/python/curl/__init__.py -index 3ab019f..fd179fc 100644 ---- a/python/curl/__init__.py -+++ b/python/curl/__init__.py -@@ -6,7 +6,11 @@ - # - # By Eric S. Raymond, April 2003. - --import os, sys, urllib, exceptions, mimetools, pycurl -+import os, sys, exceptions, mimetools, pycurl -+try: -+ import urllib.parse as urllib_parse -+except ImportError: -+ import urllib as urllib_parse - try: - from cStringIO import StringIO - except ImportError: -@@ -86,14 +90,14 @@ class Curl: - def get(self, url="", params=None): - "Ship a GET request for a specified URL, capture the response." - if params: -- url += "?" + urllib.urlencode(params) -+ url += "?" + urllib_parse.urlencode(params) - self.set_option(pycurl.HTTPGET, 1) - return self.__request(url) - - def post(self, cgi, params): - "Ship a POST request to a specified CGI, capture the response." - self.set_option(pycurl.POST, 1) -- self.set_option(pycurl.POSTFIELDS, urllib.urlencode(params)) -+ self.set_option(pycurl.POSTFIELDS, urllib_parse.urlencode(params)) - return self.__request(cgi) - - def body(self): --- -1.7.1 - - -From 59cde00a2b6f4329d19579c3e8982d194c71eb66 Mon Sep 17 00:00:00 2001 -From: Yuri Ushakov -Date: Thu, 18 Apr 2013 12:29:07 -0400 -Subject: [PATCH 122/149] Correctly handle big timeout values - -http://curl.haxx.se/mail/curlpython-2013-04/0000.html - -Signed-off-by: Kamil Dudka ---- - src/pycurl.c | 2 +- - 1 files changed, 1 insertions(+), 1 deletions(-) - -diff --git a/src/pycurl.c b/src/pycurl.c -index 9950e00..388595c 100644 ---- a/src/pycurl.c -+++ b/src/pycurl.c -@@ -2567,7 +2567,7 @@ do_multi_timeout(CurlMultiObject *self) - } - - /* Return number of millisecs until timeout */ -- return Py_BuildValue("i", timeout); -+ return Py_BuildValue("l", timeout); - } - - --- -1.7.1 - - -From 575f40dd24cea343980b0c628327d9a1008fa021 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Thu, 30 May 2013 13:55:35 -0400 -Subject: [PATCH 123/149] Add support for CURLOPT_USERNAME / _PASSWORD - -Original patch by Wim Lewis (https://sourceforge.net/u/wiml/profile/). - -Original patch description: - -This is a really trivial patch to add support for the CURLOPT_USERNAME and -CURLOPT_PASSWORD options (and their _PROXY equivalents), which affect -the same parts of libcurl's state that CURLOPT_USERPWD does but which -don't require you to combine the username and password into one string first. -(Libcurl just immediately parses it apart again anyway.) - -I've tested against libcurl-7.21.0, and looked through the source for -libcurl 7.20.0 and 7.19.5 to verify that it looks like it should still do -the right things there (in particular that curl_easy_setopt(..., NULL) -does the reasonable thing with these options). - -https://sourceforge.net/p/pycurl/patches/10/ - -Signed-off-by: Kamil Dudka ---- - src/pycurl.c | 31 +++++++++++++++++++++++++++++++ - tests/curlopt_test.py | 18 ++++++++++++++++++ - 2 files changed, 49 insertions(+), 0 deletions(-) - create mode 100644 tests/curlopt_test.py - -diff --git a/src/pycurl.c b/src/pycurl.c -index 388595c..9c76609 100644 ---- a/src/pycurl.c -+++ b/src/pycurl.c -@@ -61,6 +61,13 @@ - # error "Need libcurl version 7.19.0 or greater to compile pycurl." - #endif - -+#if LIBCURL_VERSION_MAJOR >= 8 || \ -+ LIBCURL_VERSION_MAJOR == 7 && LIBCURL_VERSION_MINOR >= 20 || \ -+ LIBCURL_VERSION_MAJOR == 7 && LIBCURL_VERSION_MINOR == 19 && LIBCURL_VERSION_PATCH >= 1 -+#define HAVE_CURLOPT_USERNAME -+#define HAVE_CURLOPT_PROXYUSERNAME -+#endif -+ - /* Python < 2.5 compat for Py_ssize_t */ - #if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN) - typedef int Py_ssize_t; -@@ -1558,9 +1565,17 @@ util_curl_unsetopt(CurlObject *self, int option) - case CURLOPT_EGDSOCKET: - case CURLOPT_FTPPORT: - case CURLOPT_PROXYUSERPWD: -+#ifdef HAVE_CURLOPT_PROXYUSERNAME -+ case CURLOPT_PROXYUSERNAME: -+ case CURLOPT_PROXYPASSWORD: -+#endif - case CURLOPT_RANDOM_FILE: - case CURLOPT_SSL_CIPHER_LIST: - case CURLOPT_USERPWD: -+#ifdef HAVE_CURLOPT_USERNAME -+ case CURLOPT_USERNAME: -+ case CURLOPT_PASSWORD: -+#endif - case CURLOPT_RANGE: - SETOPT((char *) 0); - break; -@@ -1658,6 +1673,10 @@ do_curl_setopt(CurlObject *self, PyObject *args) - case CURLOPT_NETRC_FILE: - case CURLOPT_PROXY: - case CURLOPT_PROXYUSERPWD: -+#ifdef HAVE_CURLOPT_PROXYUSERNAME -+ case CURLOPT_PROXYUSERNAME: -+ case CURLOPT_PROXYPASSWORD: -+#endif - case CURLOPT_RANDOM_FILE: - case CURLOPT_RANGE: - case CURLOPT_REFERER: -@@ -1671,6 +1690,10 @@ do_curl_setopt(CurlObject *self, PyObject *args) - case CURLOPT_URL: - case CURLOPT_USERAGENT: - case CURLOPT_USERPWD: -+#ifdef HAVE_CURLOPT_USERNAME -+ case CURLOPT_USERNAME: -+ case CURLOPT_PASSWORD: -+#endif - case CURLOPT_FTP_ALTERNATIVE_TO_USER: - case CURLOPT_SSH_PUBLIC_KEYFILE: - case CURLOPT_SSH_PRIVATE_KEYFILE: -@@ -3654,7 +3677,15 @@ initpycurl(void) - insint_c(d, "PORT", CURLOPT_PORT); - insint_c(d, "PROXY", CURLOPT_PROXY); - insint_c(d, "USERPWD", CURLOPT_USERPWD); -+#ifdef HAVE_CURLOPT_USERNAME -+ insint_c(d, "USERNAME", CURLOPT_USERNAME); -+ insint_c(d, "PASSWORD", CURLOPT_PASSWORD); -+#endif - insint_c(d, "PROXYUSERPWD", CURLOPT_PROXYUSERPWD); -+#ifdef HAVE_CURLOPT_PROXYUSERNAME -+ insint_c(d, "PROXYUSERNAME", CURLOPT_PROXYUSERNAME); -+ insint_c(d, "PROXYPASSWORD", CURLOPT_PROXYPASSWORD); -+#endif - insint_c(d, "RANGE", CURLOPT_RANGE); - insint_c(d, "INFILE", CURLOPT_READDATA); - /* ERRORBUFFER is not supported */ -diff --git a/tests/curlopt_test.py b/tests/curlopt_test.py -new file mode 100644 -index 0000000..9392a39 ---- /dev/null -+++ b/tests/curlopt_test.py -@@ -0,0 +1,18 @@ -+#! /usr/bin/env python -+# -*- coding: iso-8859-1 -*- -+# vi:ts=4:et -+ -+import pycurl -+import unittest -+import nose.tools -+ -+from . import util -+ -+class CurloptTest(unittest.TestCase): -+ def test_username(self): -+ # CURLOPT_USERNAME was introduced in libcurl-7.19.1 -+ if not util.pycurl_version_less_than(7, 19, 1): -+ assert hasattr(pycurl, 'USERNAME') -+ assert hasattr(pycurl, 'PASSWORD') -+ assert hasattr(pycurl, 'PROXYUSERNAME') -+ assert hasattr(pycurl, 'PROXYPASSWORD') --- -1.7.1 - - -From db2f9a10ef399f0a69b2e4a215fe44752f780b96 Mon Sep 17 00:00:00 2001 -From: Adam Guthrie -Date: Thu, 30 May 2013 14:10:44 -0400 -Subject: [PATCH 124/149] Fixes invalid XHTML in documentation - -doc/callbacks.html and doc/curlshareobject.html were not valid xhtml. - -Thanks to Ivo Timmermans for the original report and patch to Ubuntu (LP: #364168) - -Signed-off-by: Kamil Dudka ---- - doc/callbacks.html | 2 +- - doc/curlshareobject.html | 3 ++- - 2 files changed, 3 insertions(+), 2 deletions(-) - -diff --git a/doc/callbacks.html b/doc/callbacks.html -index b98e0a9..a117acc 100644 ---- a/doc/callbacks.html -+++ b/doc/callbacks.html -@@ -120,7 +120,7 @@ VERBOSE option must be enabled for this callback to be invoked.

- - -

Other examples

--The pycurl distribution also contains a number of test scripts and -+

The pycurl distribution also contains a number of test scripts and - examples which show how to use the various callbacks in libcurl. - For instance, the file 'examples/file_upload.py' in the distribution contains - example code for using READFUNCTION, 'tests/test_cb.py' shows -diff --git a/doc/curlshareobject.html b/doc/curlshareobject.html -index c11cafb..2043e48 100644 ---- a/doc/curlshareobject.html -+++ b/doc/curlshareobject.html -@@ -14,6 +14,7 @@ - -

CurlShare objects have the following methods:

- -+
-
setopt(option, value) -> None
-
- -@@ -22,7 +23,7 @@ - href="http://curl.haxx.se/libcurl/c/curl_share_setopt.html">curl_share_setopt in libcurl, where - option is specified with the CURLSHOPT_* constants in libcurl, - except that the CURLSHOPT_ prefix has been changed to SH_. Currently, --value must be either LOCK_DATA_COOKIE or LOCK_DATA_DNS. -+value must be either LOCK_DATA_COOKIE or LOCK_DATA_DNS.

- -

Example usage:

- --- -1.7.1 - - -From 87aa7c722ddfefd38faef5a8a038c2e8e64db003 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Thu, 30 May 2013 15:27:02 -0400 -Subject: [PATCH 125/149] Use nose test skipping functionality - -Signed-off-by: Kamil Dudka ---- - tests/curlopt_test.py | 14 ++++++++------ - tests/global_init_test.py | 11 +++++++---- - 2 files changed, 15 insertions(+), 10 deletions(-) - -diff --git a/tests/curlopt_test.py b/tests/curlopt_test.py -index 9392a39..fd30394 100644 ---- a/tests/curlopt_test.py -+++ b/tests/curlopt_test.py -@@ -4,15 +4,17 @@ - - import pycurl - import unittest --import nose.tools -+import nose.plugins.skip - - from . import util - - class CurloptTest(unittest.TestCase): - def test_username(self): - # CURLOPT_USERNAME was introduced in libcurl-7.19.1 -- if not util.pycurl_version_less_than(7, 19, 1): -- assert hasattr(pycurl, 'USERNAME') -- assert hasattr(pycurl, 'PASSWORD') -- assert hasattr(pycurl, 'PROXYUSERNAME') -- assert hasattr(pycurl, 'PROXYPASSWORD') -+ if util.pycurl_version_less_than(7, 19, 1): -+ raise nose.plugins.skip.SkipTest('libcurl < 7.19.1') -+ -+ assert hasattr(pycurl, 'USERNAME') -+ assert hasattr(pycurl, 'PASSWORD') -+ assert hasattr(pycurl, 'PROXYUSERNAME') -+ assert hasattr(pycurl, 'PROXYPASSWORD') -diff --git a/tests/global_init_test.py b/tests/global_init_test.py -index b76254b..443ca49 100644 ---- a/tests/global_init_test.py -+++ b/tests/global_init_test.py -@@ -5,6 +5,7 @@ - import pycurl - import unittest - import nose.tools -+import nose.plugins.skip - - from . import util - -@@ -17,10 +18,12 @@ class GlobalInitTest(unittest.TestCase): - def test_global_init_ack_eintr(self): - # the GLOBAL_ACK_EINTR flag was introduced in libcurl-7.30, but can also - # be backported for older versions of libcurl at the distribution level -- if not util.pycurl_version_less_than(7, 30) or hasattr(pycurl, 'GLOBAL_ACK_EINTR'): -- # initialize libcurl with the GLOBAL_ACK_EINTR flag -- pycurl.global_init(pycurl.GLOBAL_ACK_EINTR) -- pycurl.global_cleanup() -+ if util.pycurl_version_less_than(7, 30) or hasattr(pycurl, 'GLOBAL_ACK_EINTR'): -+ raise nose.plugins.skip.SkipTest('libcurl < 7.30.0 or no GLOBAL_ACK_EINTR') -+ -+ # initialize libcurl with the GLOBAL_ACK_EINTR flag -+ pycurl.global_init(pycurl.GLOBAL_ACK_EINTR) -+ pycurl.global_cleanup() - - @nose.tools.raises(ValueError) - def test_global_init_bogus(self): --- -1.7.1 - - -From 7e8144d0d83062de394582207197e1081eafcc24 Mon Sep 17 00:00:00 2001 -From: Wim Lewis -Date: Thu, 21 Jul 2011 12:00:00 -0400 -Subject: [PATCH 126/149] Fix a tiny memory leak in util_curl_init() - -Each time a new Curl object is created util_curl_init() leaked -a small string buffer. The attached patch fixes the leak -- -curl_easy_setopt() strdup's its argument; the caller does not need -to keep the buffer around for it. - -https://sourceforge.net/p/pycurl/patches/13/ - -Signed-off-by: Kamil Dudka ---- - src/pycurl.c | 2 +- - 1 files changed, 1 insertions(+), 1 deletions(-) - -diff --git a/src/pycurl.c b/src/pycurl.c -index 9c76609..ef03747 100644 ---- a/src/pycurl.c -+++ b/src/pycurl.c -@@ -793,8 +793,8 @@ util_curl_init(CurlObject *self) - } - strcpy(s, "PycURL/"); strcpy(s+7, LIBCURL_VERSION); - res = curl_easy_setopt(self->handle, CURLOPT_USERAGENT, (char *) s); -+ free(s); - if (res != CURLE_OK) { -- free(s); - return (-1); - } - return (0); --- -1.7.1 - - -From 14e3edf2f77368af4f3838202527c8a48a359d6a Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Fri, 31 May 2013 13:37:39 -0400 -Subject: [PATCH 127/149] Use correct logic when checking for GLOBAL_ACK_EINTR (#34) - -Signed-off-by: Kamil Dudka ---- - tests/global_init_test.py | 2 +- - 1 files changed, 1 insertions(+), 1 deletions(-) - -diff --git a/tests/global_init_test.py b/tests/global_init_test.py -index 443ca49..b0d1986 100644 ---- a/tests/global_init_test.py -+++ b/tests/global_init_test.py -@@ -18,7 +18,7 @@ class GlobalInitTest(unittest.TestCase): - def test_global_init_ack_eintr(self): - # the GLOBAL_ACK_EINTR flag was introduced in libcurl-7.30, but can also - # be backported for older versions of libcurl at the distribution level -- if util.pycurl_version_less_than(7, 30) or hasattr(pycurl, 'GLOBAL_ACK_EINTR'): -+ if util.pycurl_version_less_than(7, 30) and not hasattr(pycurl, 'GLOBAL_ACK_EINTR'): - raise nose.plugins.skip.SkipTest('libcurl < 7.30.0 or no GLOBAL_ACK_EINTR') - - # initialize libcurl with the GLOBAL_ACK_EINTR flag --- -1.7.1 - - -From b2157864dc9b80222d04e6a681d923c1bdc6c64f Mon Sep 17 00:00:00 2001 -From: Roland Sommer -Date: Wed, 8 May 2013 15:13:14 +0300 -Subject: [PATCH 128/149] Use urlparse.urljoin instead of os.path.join - -Using a base_url like "http://www.google.de" and calling -get("/") on a curl.Curl-instance does not work. - -os.path.join("http://www.google.de", "/") does not yield -the expected "http://www.google.de/" but "/" which leads to: - -error: (3, ' malformed') - -Using urljoin fixes this. - -https://github.com/christophwarner/PyCurl/pull/2 - -Signed-off-by: Kamil Dudka ---- - python/curl/__init__.py | 6 ++++-- - 1 files changed, 4 insertions(+), 2 deletions(-) - -diff --git a/python/curl/__init__.py b/python/curl/__init__.py -index fd179fc..31b9d49 100644 ---- a/python/curl/__init__.py -+++ b/python/curl/__init__.py -@@ -6,11 +6,13 @@ - # - # By Eric S. Raymond, April 2003. - --import os, sys, exceptions, mimetools, pycurl -+import sys, exceptions, mimetools, pycurl - try: - import urllib.parse as urllib_parse -+ from urllib.parse import urljoin - except ImportError: - import urllib as urllib_parse -+ from urlparse import urljoin - try: - from cStringIO import StringIO - except ImportError: -@@ -81,7 +83,7 @@ class Curl: - if self.fakeheaders: - self.set_option(pycurl.HTTPHEADER, self.fakeheaders) - if relative_url: -- self.set_option(pycurl.URL,os.path.join(self.base_url,relative_url)) -+ self.set_option(pycurl.URL, urljoin(self.base_url, relative_url)) - self.payload = "" - self.hdr = "" - self.handle.perform() --- -1.7.1 - - -From b12be12fc45fc786e2d537a3bbd3044488f1e367 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Tue, 4 Jun 2013 13:30:39 -0400 -Subject: [PATCH 129/149] Test coverage for relative urls in curl module - -Signed-off-by: Kamil Dudka ---- - tests/relative_url_test.py | 24 ++++++++++++++++++++++++ - 1 files changed, 24 insertions(+), 0 deletions(-) - create mode 100644 tests/relative_url_test.py - -diff --git a/tests/relative_url_test.py b/tests/relative_url_test.py -new file mode 100644 -index 0000000..ddff2b6 ---- /dev/null -+++ b/tests/relative_url_test.py -@@ -0,0 +1,24 @@ -+#! /usr/bin/env python -+# -*- coding: iso-8859-1 -*- -+# vi:ts=4:et -+ -+# uses the high level interface -+import curl -+import unittest -+ -+from . import app -+from . import runwsgi -+from . import util -+ -+setup_module, teardown_module = runwsgi.app_runner_setup((app.app, 8380)) -+ -+class RelativeUrlTest(unittest.TestCase): -+ def setUp(self): -+ self.curl = curl.Curl('http://localhost:8380/') -+ -+ def tearDown(self): -+ self.curl.close() -+ -+ def test_get_relative(self): -+ self.curl.get('/success') -+ self.assertEqual('success', self.curl.body()) --- -1.7.1 - - -From d29d339d892c52aad6e0be0b22b75dc52e9d9450 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Sun, 2 Jun 2013 01:57:16 -0400 -Subject: [PATCH 130/149] Avoid dynamically allocating memory for default pycurl user agent. - -This implementation requires LIBCURL_VERSION to be a #define. - -Signed-off-by: Kamil Dudka ---- - src/pycurl.c | 11 ++++------- - 1 files changed, 4 insertions(+), 7 deletions(-) - -diff --git a/src/pycurl.c b/src/pycurl.c -index ef03747..b9d797e 100644 ---- a/src/pycurl.c -+++ b/src/pycurl.c -@@ -105,6 +105,9 @@ static void pycurl_ssl_cleanup(void); - #define OPTIONS_SIZE ((int)CURLOPT_LASTENTRY % 10000) - #define MOPTIONS_SIZE ((int)CURLMOPT_LASTENTRY % 10000) - -+/* Keep some default variables around */ -+static char *g_pycurl_useragent = "PycURL/" LIBCURL_VERSION; -+ - /* Type objects */ - static PyObject *ErrorObject = NULL; - static PyTypeObject *p_Curl_Type = NULL; -@@ -787,13 +790,7 @@ util_curl_init(CurlObject *self) - } - - /* Set default USERAGENT */ -- s = (char *) malloc(7 + strlen(LIBCURL_VERSION) + 1); -- if (s == NULL) { -- return (-1); -- } -- strcpy(s, "PycURL/"); strcpy(s+7, LIBCURL_VERSION); -- res = curl_easy_setopt(self->handle, CURLOPT_USERAGENT, (char *) s); -- free(s); -+ res = curl_easy_setopt(self->handle, CURLOPT_USERAGENT, (char *) g_pycurl_useragent); - if (res != CURLE_OK) { - return (-1); - } --- -1.7.1 - - -From f8d2f64764ee25572453317371ff58fe048a8a8a Mon Sep 17 00:00:00 2001 -From: Marien Zwart -Date: Wed, 13 Jun 2012 00:00:00 -0400 -Subject: [PATCH 131/149] Mark NSS as supported. - -Mark NSS as supported, as it does not require the application to -initialize threading. - -If curl is using nss we do not seem to need to initialize threading -explicitly: the only threading-related bit I see in the nss -headers/documentation has to do with simultaneous non-nss use of -pkcs11 modules, nss itself seems to be thread-safe by default. -So silence the build warning. - -http://sourceforge.net/p/pycurl/patches/15/ - -Signed-off-by: Kamil Dudka ---- - setup.py | 2 ++ - src/pycurl.c | 4 ++-- - 2 files changed, 4 insertions(+), 2 deletions(-) - -diff --git a/setup.py b/setup.py -index 385facb..40da07a 100644 ---- a/setup.py -+++ b/setup.py -@@ -119,6 +119,8 @@ else: - define_macros.append(('HAVE_CURL_OPENSSL', 1)) - if e[2:] == 'gnutls': - define_macros.append(('HAVE_CURL_GNUTLS', 1)) -+ if e[2:] == 'ssl3': -+ define_macros.append(('HAVE_CURL_NSS', 1)) - elif e[:2] == "-L": - library_dirs.append(e[2:]) - else: -diff --git a/src/pycurl.c b/src/pycurl.c -index b9d797e..a913c47 100644 ---- a/src/pycurl.c -+++ b/src/pycurl.c -@@ -88,12 +88,12 @@ typedef int Py_ssize_t; - # define PYCURL_NEED_SSL_TSL - # define PYCURL_NEED_GNUTLS_TSL - # include --# else -+# elif !defined(HAVE_CURL_NSS) - # warning \ - "libcurl was compiled with SSL support, but configure could not determine which " \ - "library was used; thus no SSL crypto locking callbacks will be set, which may " \ - "cause random crashes on SSL requests" --# endif /* HAVE_CURL_OPENSSL || HAVE_CURL_GNUTLS */ -+# endif /* HAVE_CURL_OPENSSL || HAVE_CURL_GNUTLS || HAVE_CURL_NSS */ - #endif /* HAVE_CURL_SSL */ - - #if defined(PYCURL_NEED_SSL_TSL) --- -1.7.1 - - -From 256f4bece80ee579daf18cbccc6007c0fa517903 Mon Sep 17 00:00:00 2001 -From: Marien Zwart -Date: Wed, 13 Jun 2012 00:00:00 -0400 -Subject: [PATCH 132/149] Only initialize gcrypt if we are using an older gnutls that needs this. - -This is necessary to support newer gnutls linked to libnettle instead -of libgcrypt. - -If curl is using a recent version of gnutls we do not need to initialize -libgcrypt threading: gnutls does that for us. And because recent versions -of gnutls may be using nettle instead of gcrypt it is important that we -don't: those functions are not necessarily defined (and linking to -libgcrypt explicitly to get them is stupid, as they're not used). The -modification is based on instructions from the gnutls NEWS file. - -http://sourceforge.net/p/pycurl/patches/15/ - -Signed-off-by: Kamil Dudka ---- - src/pycurl.c | 9 ++++++--- - 1 files changed, 6 insertions(+), 3 deletions(-) - -diff --git a/src/pycurl.c b/src/pycurl.c -index a913c47..5d4e5b0 100644 ---- a/src/pycurl.c -+++ b/src/pycurl.c -@@ -85,9 +85,12 @@ typedef int Py_ssize_t; - # define PYCURL_NEED_OPENSSL_TSL - # include - # elif defined(HAVE_CURL_GNUTLS) --# define PYCURL_NEED_SSL_TSL --# define PYCURL_NEED_GNUTLS_TSL --# include -+# include -+# if GNUTLS_VERSION_NUMBER <= 0x020b00 -+# define PYCURL_NEED_SSL_TSL -+# define PYCURL_NEED_GNUTLS_TSL -+# include -+# endif - # elif !defined(HAVE_CURL_NSS) - # warning \ - "libcurl was compiled with SSL support, but configure could not determine which " \ --- -1.7.1 - - -From f4ec6883c9d5ebfa961aa6063313903b2ca7ce1e Mon Sep 17 00:00:00 2001 -From: Romulo A. Ceccon -Date: Sun, 9 Jun 2013 17:34:41 +0000 -Subject: [PATCH 133/149] Allow pycURL to be used with Python binaries built without thread support - -Signed-off-by: Kamil Dudka ---- - src/pycurl.c | 215 ++++++++++++++++++++++++++++++++++++++------------------- - 1 files changed, 143 insertions(+), 72 deletions(-) - -diff --git a/src/pycurl.c b/src/pycurl.c -index 5d4e5b0..a9923ed 100644 ---- a/src/pycurl.c -+++ b/src/pycurl.c -@@ -104,6 +104,30 @@ static void pycurl_ssl_init(void); - static void pycurl_ssl_cleanup(void); - #endif - -+#ifdef WITH_THREAD -+# define PYCURL_DECLARE_THREAD_STATE PyThreadState *tmp_state -+# define PYCURL_ACQUIRE_THREAD() acquire_thread(self, &tmp_state) -+# define PYCURL_ACQUIRE_THREAD_MULTI() acquire_thread_multi(self, &tmp_state) -+# define PYCURL_RELEASE_THREAD() release_thread(tmp_state) -+/* Replacement for Py_BEGIN_ALLOW_THREADS/Py_END_ALLOW_THREADS when python -+ callbacks are expected during blocking i/o operations: self->state will hold -+ the handle to current thread to be used as context */ -+# define PYCURL_BEGIN_ALLOW_THREADS \ -+ self->state = PyThreadState_Get(); \ -+ assert(self->state != NULL); \ -+ Py_BEGIN_ALLOW_THREADS -+# define PYCURL_END_ALLOW_THREADS \ -+ Py_END_ALLOW_THREADS \ -+ self->state = NULL; -+#else -+# define PYCURL_DECLARE_THREAD_STATE -+# define PYCURL_ACQUIRE_THREAD() (1) -+# define PYCURL_ACQUIRE_THREAD_MULTI() (1) -+# define PYCURL_RELEASE_THREAD() -+# define PYCURL_BEGIN_ALLOW_THREADS -+# define PYCURL_END_ALLOW_THREADS -+#endif -+ - /* Calculate the number of OBJECTPOINT options we need to store */ - #define OPTIONS_SIZE ((int)CURLOPT_LASTENTRY % 10000) - #define MOPTIONS_SIZE ((int)CURLMOPT_LASTENTRY % 10000) -@@ -126,14 +150,18 @@ typedef struct { - PyObject_HEAD - PyObject *dict; /* Python attributes dictionary */ - CURLSH *share_handle; -+#ifdef WITH_THREAD - ShareLock *lock; /* lock object to implement CURLSHOPT_LOCKFUNC */ -+#endif - } CurlShareObject; - - typedef struct { - PyObject_HEAD - PyObject *dict; /* Python attributes dictionary */ - CURLM *multi_handle; -+#ifdef WITH_THREAD - PyThreadState *state; -+#endif - fd_set read_fd_set; - fd_set write_fd_set; - fd_set exc_fd_set; -@@ -146,7 +174,9 @@ typedef struct { - PyObject_HEAD - PyObject *dict; /* Python attributes dictionary */ - CURL *handle; -+#ifdef WITH_THREAD - PyThreadState *state; -+#endif - CurlMultiObject *multi_stack; - CurlShareObject *share; - struct curl_httppost *httppost; -@@ -261,6 +291,7 @@ error: - } - - -+#ifdef WITH_THREAD - /************************************************************************* - // static utility functions - **************************************************************************/ -@@ -317,13 +348,42 @@ get_thread_state_multi(const CurlMultiObject *self) - } - - -+static int acquire_thread(const CurlObject *self, PyThreadState **state) -+{ -+ *state = get_thread_state(self); -+ if (*state == NULL) -+ return 0; -+ PyEval_AcquireThread(*state); -+ return 1; -+} -+ -+ -+static int acquire_thread_multi(const CurlMultiObject *self, PyThreadState **state) -+{ -+ *state = get_thread_state_multi(self); -+ if (*state == NULL) -+ return 0; -+ PyEval_AcquireThread(*state); -+ return 1; -+} -+ -+ -+static void release_thread(PyThreadState *state) -+{ -+ PyEval_ReleaseThread(state); -+} -+#endif -+ -+ - /* assert some CurlShareObject invariants */ - static void - assert_share_state(const CurlShareObject *self) - { - assert(self != NULL); - assert(self->ob_type == p_CurlShare_Type); -+#ifdef WITH_THREAD - assert(self->lock != NULL); -+#endif - } - - -@@ -333,7 +393,9 @@ assert_curl_state(const CurlObject *self) - { - assert(self != NULL); - assert(self->ob_type == p_Curl_Type); -+#ifdef WITH_THREAD - (void) get_thread_state(self); -+#endif - } - - -@@ -343,9 +405,11 @@ assert_multi_state(const CurlMultiObject *self) - { - assert(self != NULL); - assert(self->ob_type == p_CurlMulti_Type); -+#ifdef WITH_THREAD - if (self->state != NULL) { - assert(self->multi_handle != NULL); - } -+#endif - } - - -@@ -358,10 +422,12 @@ check_curl_state(const CurlObject *self, int flags, const char *name) - PyErr_Format(ErrorObject, "cannot invoke %s() - no curl handle", name); - return -1; - } -+#ifdef WITH_THREAD - if ((flags & 2) && get_thread_state(self) != NULL) { - PyErr_Format(ErrorObject, "cannot invoke %s() - perform() is currently running", name); - return -1; - } -+#endif - return 0; - } - -@@ -373,10 +439,12 @@ check_multi_state(const CurlMultiObject *self, int flags, const char *name) - 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; - } - -@@ -391,6 +459,7 @@ check_share_state(const CurlShareObject *self, int flags, const char *name) - // SSL TSL - **************************************************************************/ - -+#ifdef WITH_THREAD - #ifdef PYCURL_NEED_OPENSSL_TSL - - static PyThread_type_lock *pycurl_openssl_tsl = NULL; -@@ -560,6 +629,19 @@ share_unlock_callback(CURL *handle, curl_lock_data data, void *userptr) - share_lock_unlock(share->lock, data); - } - -+#else /* WITH_THREAD */ -+ -+static void pycurl_ssl_init(void) -+{ -+ return; -+} -+ -+static void pycurl_ssl_cleanup(void) -+{ -+ return; -+} -+ -+#endif /* WITH_THREAD */ - - /* constructor - this is a module-level function returning a new instance */ - static CurlShareObject * -@@ -567,8 +649,10 @@ 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); - -@@ -583,8 +667,10 @@ do_share_new(PyObject *dummy) - - /* 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(); -@@ -594,6 +680,7 @@ do_share_new(PyObject *dummy) - 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); -@@ -601,6 +688,7 @@ do_share_new(PyObject *dummy) - assert(res == CURLE_OK); - res = curl_share_setopt(self->share_handle, CURLSHOPT_UNLOCKFUNC, unlock_cb); - assert(res == CURLE_OK); -+#endif - - return self; - } -@@ -632,7 +720,9 @@ do_share_clear(CurlShareObject *self) - static void - util_share_close(CurlShareObject *self){ - curl_share_cleanup(self->share_handle); -+#ifdef WITH_THREAD - share_lock_destroy(self->lock); -+#endif - } - - -@@ -723,7 +813,9 @@ util_curl_new(void) - /* Set python curl object initial values */ - self->dict = NULL; - self->handle = NULL; -+#ifdef WITH_THREAD - self->state = NULL; -+#endif - self->share = NULL; - self->multi_stack = NULL; - self->httppost = NULL; -@@ -898,12 +990,16 @@ util_curl_close(CurlObject *self) - if (handle == NULL) { - /* Some paranoia assertions just to make sure the object - * deallocation problem is finally really fixed... */ -+#ifdef WITH_THREAD - assert(self->state == NULL); -+#endif - assert(self->multi_stack == NULL); - assert(self->share == NULL); - return; /* already closed */ - } -+#ifdef WITH_THREAD - self->state = NULL; -+#endif - - /* Decref multi stuff which uses this handle */ - util_curl_xdecref(self, 2, handle); -@@ -977,7 +1073,9 @@ do_curl_errstr(CurlObject *self) - static int - do_curl_clear(CurlObject *self) - { -+#ifdef WITH_THREAD - assert(get_thread_state(self) == NULL); -+#endif - util_curl_xdecref(self, 1 | 2 | 4 | 8 | 16, self->handle); - return 0; - } -@@ -1020,17 +1118,9 @@ do_curl_perform(CurlObject *self) - return NULL; - } - -- /* Save handle to current thread (used as context for python callbacks) */ -- self->state = PyThreadState_Get(); -- assert(self->state != NULL); -- -- /* Release global lock and start */ -- Py_BEGIN_ALLOW_THREADS -+ PYCURL_BEGIN_ALLOW_THREADS - res = curl_easy_perform(self->handle); -- Py_END_ALLOW_THREADS -- -- /* Zero thread-state to disallow callbacks to be run from now on */ -- self->state = NULL; -+ PYCURL_END_ALLOW_THREADS - - if (res != CURLE_OK) { - CURLERROR_RETVAL(); -@@ -1050,7 +1140,7 @@ static size_t - util_write_callback(int flags, char *ptr, size_t size, size_t nmemb, void *stream) - { - CurlObject *self; -- PyThreadState *tmp_state; -+ PYCURL_DECLARE_THREAD_STATE; - PyObject *arglist; - PyObject *result = NULL; - size_t ret = 0; /* assume error */ -@@ -1059,10 +1149,8 @@ util_write_callback(int flags, char *ptr, size_t size, size_t nmemb, void *strea - - /* acquire thread */ - self = (CurlObject *)stream; -- tmp_state = get_thread_state(self); -- if (tmp_state == NULL) -+ if (!PYCURL_ACQUIRE_THREAD()) - return ret; -- PyEval_AcquireThread(tmp_state); - - /* check args */ - cb = flags ? self->h_cb : self->w_cb; -@@ -1101,7 +1189,7 @@ util_write_callback(int flags, char *ptr, size_t size, size_t nmemb, void *strea - done: - silent_error: - Py_XDECREF(result); -- PyEval_ReleaseThread(tmp_state); -+ PYCURL_RELEASE_THREAD(); - return ret; - verbose_error: - PyErr_Print(); -@@ -1131,13 +1219,12 @@ opensocket_callback(void *clientp, curlsocktype purpose, - PyObject *result = NULL; - PyObject *fileno_result = NULL; - CurlObject *self; -- PyThreadState *tmp_state; -+ PYCURL_DECLARE_THREAD_STATE; - int ret = CURL_SOCKET_BAD; - - self = (CurlObject *)clientp; -- tmp_state = get_thread_state(self); -+ PYCURL_ACQUIRE_THREAD(); - -- PyEval_AcquireThread(tmp_state); - arglist = Py_BuildValue("(iii)", address->family, address->socktype, address->protocol); - if (arglist == NULL) - goto verbose_error; -@@ -1171,7 +1258,7 @@ silent_error: - done: - Py_XDECREF(result); - Py_XDECREF(fileno_result); -- PyEval_ReleaseThread(tmp_state); -+ PYCURL_RELEASE_THREAD(); - return ret; - verbose_error: - PyErr_Print(); -@@ -1182,7 +1269,7 @@ static int - seek_callback(void *stream, curl_off_t offset, int origin) - { - CurlObject *self; -- PyThreadState *tmp_state; -+ PYCURL_DECLARE_THREAD_STATE; - PyObject *arglist; - PyObject *result = NULL; - int ret = 2; /* assume error 2 (can't seek, libcurl free to work around). */ -@@ -1191,10 +1278,8 @@ seek_callback(void *stream, curl_off_t offset, int origin) - - /* acquire thread */ - self = (CurlObject *)stream; -- tmp_state = get_thread_state(self); -- if (tmp_state == NULL) -+ if (!PYCURL_ACQUIRE_THREAD()) - return ret; -- PyEval_AcquireThread(tmp_state); - - /* check arguments */ - switch (origin) -@@ -1244,7 +1329,7 @@ seek_callback(void *stream, curl_off_t offset, int origin) - - silent_error: - Py_XDECREF(result); -- PyEval_ReleaseThread(tmp_state); -+ PYCURL_RELEASE_THREAD(); - return ret; - verbose_error: - PyErr_Print(); -@@ -1258,7 +1343,7 @@ static size_t - read_callback(char *ptr, size_t size, size_t nmemb, void *stream) - { - CurlObject *self; -- PyThreadState *tmp_state; -+ PYCURL_DECLARE_THREAD_STATE; - PyObject *arglist; - PyObject *result = NULL; - -@@ -1267,10 +1352,8 @@ read_callback(char *ptr, size_t size, size_t nmemb, void *stream) - - /* acquire thread */ - self = (CurlObject *)stream; -- tmp_state = get_thread_state(self); -- if (tmp_state == NULL) -+ if (!PYCURL_ACQUIRE_THREAD()) - return ret; -- PyEval_AcquireThread(tmp_state); - - /* check args */ - if (self->r_cb == NULL) -@@ -1328,7 +1411,7 @@ read_callback(char *ptr, size_t size, size_t nmemb, void *stream) - done: - silent_error: - Py_XDECREF(result); -- PyEval_ReleaseThread(tmp_state); -+ PYCURL_RELEASE_THREAD(); - return ret; - verbose_error: - PyErr_Print(); -@@ -1341,17 +1424,15 @@ progress_callback(void *stream, - double dltotal, double dlnow, double ultotal, double ulnow) - { - CurlObject *self; -- PyThreadState *tmp_state; -+ PYCURL_DECLARE_THREAD_STATE; - PyObject *arglist; - PyObject *result = NULL; - int ret = 1; /* assume error */ - - /* acquire thread */ - self = (CurlObject *)stream; -- tmp_state = get_thread_state(self); -- if (tmp_state == NULL) -+ if (!PYCURL_ACQUIRE_THREAD()) - return ret; -- PyEval_AcquireThread(tmp_state); - - /* check args */ - if (self->pro_cb == NULL) -@@ -1379,7 +1460,7 @@ progress_callback(void *stream, - - silent_error: - Py_XDECREF(result); -- PyEval_ReleaseThread(tmp_state); -+ PYCURL_RELEASE_THREAD(); - return ret; - verbose_error: - PyErr_Print(); -@@ -1392,7 +1473,7 @@ debug_callback(CURL *curlobj, curl_infotype type, - char *buffer, size_t total_size, void *stream) - { - CurlObject *self; -- PyThreadState *tmp_state; -+ PYCURL_DECLARE_THREAD_STATE; - PyObject *arglist; - PyObject *result = NULL; - int ret = 0; /* always success */ -@@ -1401,10 +1482,8 @@ debug_callback(CURL *curlobj, curl_infotype type, - - /* acquire thread */ - self = (CurlObject *)stream; -- tmp_state = get_thread_state(self); -- if (tmp_state == NULL) -+ if (!PYCURL_ACQUIRE_THREAD()) - return ret; -- PyEval_AcquireThread(tmp_state); - - /* check args */ - if (self->debug_cb == NULL) -@@ -1427,7 +1506,7 @@ debug_callback(CURL *curlobj, curl_infotype type, - - silent_error: - Py_XDECREF(result); -- PyEval_ReleaseThread(tmp_state); -+ PYCURL_RELEASE_THREAD(); - return ret; - verbose_error: - PyErr_Print(); -@@ -1439,7 +1518,7 @@ static curlioerr - ioctl_callback(CURL *curlobj, int cmd, void *stream) - { - CurlObject *self; -- PyThreadState *tmp_state; -+ PYCURL_DECLARE_THREAD_STATE; - PyObject *arglist; - PyObject *result = NULL; - int ret = CURLIOE_FAILRESTART; /* assume error */ -@@ -1448,10 +1527,8 @@ ioctl_callback(CURL *curlobj, int cmd, void *stream) - - /* acquire thread */ - self = (CurlObject *)stream; -- tmp_state = get_thread_state(self); -- if (tmp_state == NULL) -+ if (!PYCURL_ACQUIRE_THREAD()) - return (curlioerr) ret; -- PyEval_AcquireThread(tmp_state); - - /* check args */ - if (self->ioctl_cb == NULL) -@@ -1480,7 +1557,7 @@ ioctl_callback(CURL *curlobj, int cmd, void *stream) - - silent_error: - Py_XDECREF(result); -- PyEval_ReleaseThread(tmp_state); -+ PYCURL_RELEASE_THREAD(); - return (curlioerr) ret; - verbose_error: - PyErr_Print(); -@@ -2324,7 +2401,9 @@ do_multi_new(PyObject *dummy) - - /* Initialize object attributes */ - self->dict = NULL; -+#ifdef WITH_THREAD - self->state = NULL; -+#endif - self->t_cb = NULL; - self->s_cb = NULL; - -@@ -2342,7 +2421,9 @@ 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; -@@ -2411,7 +2492,7 @@ int multi_socket_callback(CURL *easy, - { - CurlMultiObject *self; - CurlObject *easy_self; -- PyThreadState *tmp_state; -+ PYCURL_DECLARE_THREAD_STATE; - PyObject *arglist; - PyObject *result = NULL; - int ret; -@@ -2419,10 +2500,8 @@ int multi_socket_callback(CURL *easy, - /* acquire thread */ - self = (CurlMultiObject *)userp; - ret = curl_easy_getinfo(easy, CURLINFO_PRIVATE, &easy_self); -- tmp_state = get_thread_state_multi(self); -- if (tmp_state == NULL) -+ if (!PYCURL_ACQUIRE_THREAD_MULTI()) - return 0; -- PyEval_AcquireThread(tmp_state); - - /* check args */ - if (self->s_cb == NULL) -@@ -2446,7 +2525,7 @@ int multi_socket_callback(CURL *easy, - - silent_error: - Py_XDECREF(result); -- PyEval_ReleaseThread(tmp_state); -+ PYCURL_RELEASE_THREAD(); - return 0; - verbose_error: - PyErr_Print(); -@@ -2460,7 +2539,7 @@ int multi_timer_callback(CURLM *multi, - void *userp) - { - CurlMultiObject *self; -- PyThreadState *tmp_state; -+ PYCURL_DECLARE_THREAD_STATE; - PyObject *arglist; - PyObject *result = NULL; - int ret = 0; /* always success */ -@@ -2469,10 +2548,8 @@ int multi_timer_callback(CURLM *multi, - - /* acquire thread */ - self = (CurlMultiObject *)userp; -- tmp_state = get_thread_state_multi(self); -- if (tmp_state == NULL) -+ if (!PYCURL_ACQUIRE_THREAD_MULTI()) - return ret; -- PyEval_AcquireThread(tmp_state); - - /* check args */ - if (self->t_cb == NULL) -@@ -2491,7 +2568,7 @@ int multi_timer_callback(CURLM *multi, - - silent_error: - Py_XDECREF(result); -- PyEval_ReleaseThread(tmp_state); -+ PYCURL_RELEASE_THREAD(); - return ret; - verbose_error: - PyErr_Print(); -@@ -2634,14 +2711,10 @@ do_multi_socket_action(CurlMultiObject *self, PyObject *args) - if (check_multi_state(self, 1 | 2, "socket_action") != 0) { - return NULL; - } -- /* Release global lock and start */ -- self->state = PyThreadState_Get(); -- assert(self->state != NULL); -- Py_BEGIN_ALLOW_THREADS - -+ PYCURL_BEGIN_ALLOW_THREADS - res = curl_multi_socket_action(self->multi_handle, socket, ev_bitmask, &running); -- Py_END_ALLOW_THREADS -- self->state = NULL; -+ PYCURL_END_ALLOW_THREADS - - if (res != CURLM_OK) { - CURLERROR_MSG("multi_socket_action failed"); -@@ -2662,13 +2735,9 @@ do_multi_socket_all(CurlMultiObject *self) - return NULL; - } - -- /* Release global lock and start */ -- self->state = PyThreadState_Get(); -- assert(self->state != NULL); -- Py_BEGIN_ALLOW_THREADS -+ PYCURL_BEGIN_ALLOW_THREADS - res = curl_multi_socket_all(self->multi_handle, &running); -- Py_END_ALLOW_THREADS -- self->state = NULL; -+ PYCURL_END_ALLOW_THREADS - - /* We assume these errors are ok, otherwise throw exception */ - if (res != CURLM_OK && res != CURLM_CALL_MULTI_PERFORM) { -@@ -2692,13 +2761,9 @@ do_multi_perform(CurlMultiObject *self) - return NULL; - } - -- /* Release global lock and start */ -- self->state = PyThreadState_Get(); -- assert(self->state != NULL); -- Py_BEGIN_ALLOW_THREADS -+ PYCURL_BEGIN_ALLOW_THREADS - res = curl_multi_perform(self->multi_handle, &running); -- Py_END_ALLOW_THREADS -- self->state = NULL; -+ PYCURL_END_ALLOW_THREADS - - /* We assume these errors are ok, otherwise throw exception */ - if (res != CURLM_OK && res != CURLM_CALL_MULTI_PERFORM) { -@@ -2722,16 +2787,20 @@ check_multi_add_remove(const CurlMultiObject *self, const CurlObject *obj) - 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; -@@ -3989,8 +4058,10 @@ initpycurl(void) - pycurl_ssl_init(); - #endif - -+#ifdef WITH_THREAD - /* Finally initialize global interpreter lock */ - PyEval_InitThreads(); -+#endif - - } - --- -1.7.1 - - -From 9c06c6f29f4fcb5b6be9200f96c19b54b71e4b3b Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Wed, 12 Jun 2013 00:48:14 -0400 -Subject: [PATCH 134/149] Test for seek function - -Original patch by Jonas . - -http://sourceforge.net/p/pycurl/patches/8/ - -Signed-off-by: Kamil Dudka ---- - .gitignore | 1 + - Makefile | 1 + - tests/seek_function_test.py | 72 +++++++++++++++++++++++++++++++++++++++++++ - tests/vsftpd.conf | 4 ++- - 4 files changed, 77 insertions(+), 1 deletions(-) - create mode 100644 tests/seek_function_test.py - -diff --git a/.gitignore b/.gitignore -index c873e32..abc741a 100644 ---- a/.gitignore -+++ b/.gitignore -@@ -3,6 +3,7 @@ - /MANIFEST - /build - /dist -+/tests/tmp - /www/htdocs/download/*.bz2 - /www/htdocs/download/*.exe - /www/htdocs/download/*.gz -diff --git a/Makefile b/Makefile -index 9475250..cb12aaf 100644 ---- a/Makefile -+++ b/Makefile -@@ -16,6 +16,7 @@ build-7.10.8: - $(PYTHON) setup.py build --curl-config=/home/hosts/localhost/packages/curl-7.10.8/bin/curl-config - - test: build -+ mkdir -p tests/tmp - PYTHONPATH=$$(ls -d build/lib.*):$$PYTHONPATH \ - $(NOSETESTS) - -diff --git a/tests/seek_function_test.py b/tests/seek_function_test.py -new file mode 100644 -index 0000000..2a28078 ---- /dev/null -+++ b/tests/seek_function_test.py -@@ -0,0 +1,72 @@ -+#! /usr/bin/env python -+# -*- coding: iso-8859-1 -*- -+# vi:ts=4:et -+ -+# Note: this test is meant to be run from pycurl project root. -+ -+import pycurl -+import unittest -+import os.path -+ -+from . import util -+from . import procmgr -+ -+setup_module, teardown_module = procmgr.vsftpd_setup() -+ -+class PartialFileSource: -+ def __init__(self): -+ self.__buf = '1234567890.1234567890' -+ self.__maxread = None -+ self.__bufptr = 0 -+ -+ def read(self, size): -+ p = self.__bufptr -+ end = p+size -+ if self.__maxread: -+ end = min(self.__maxread, end) -+ ret = self.__buf[p:end] -+ self.__bufptr+= len(ret) -+ #print 20*">>>", "read(%s) ==> %s" % (size, len(ret)) -+ return ret -+ -+ def seek(self, offset, origin): -+ #print 20*">>>", "seek(%s, %s)" % (offset, origin) -+ self.__bufptr = offset -+ -+ def set_maxread(self, maxread): -+ self.__maxread = maxread -+ -+class SeekFunctionTest(unittest.TestCase): -+ def test_seek_function(self): -+ c = pycurl.Curl() -+ c.setopt(pycurl.UPLOAD, 1) -+ c.setopt(pycurl.URL, "ftp://localhost:8321/tests/tmp/upload.txt") -+ c.setopt(pycurl.RESUME_FROM, 0) -+ #c.setopt(pycurl.VERBOSE, 1) -+ upload_file = PartialFileSource() -+ c.setopt(pycurl.READFUNCTION, upload_file.read) -+ upload_file.set_maxread(10) -+ c.perform() -+ -+ with open(os.path.join(os.path.dirname(__file__), 'tmp', 'upload.txt')) as f: -+ content = f.read() -+ self.assertEqual('1234567890', content) -+ -+ c.close() -+ del c -+ del upload_file -+ -+ c = pycurl.Curl() -+ c.setopt(pycurl.URL, "ftp://localhost:8321/tests/tmp/upload.txt") -+ c.setopt(pycurl.RESUME_FROM, -1) -+ c.setopt(pycurl.UPLOAD, 1) -+ #c.setopt(pycurl.VERBOSE, 1) -+ upload_file = PartialFileSource() -+ c.setopt(pycurl.READFUNCTION, upload_file.read) -+ c.setopt(pycurl.SEEKFUNCTION, upload_file.seek) -+ c.perform() -+ c.close() -+ -+ with open(os.path.join(os.path.dirname(__file__), 'tmp', 'upload.txt')) as f: -+ content = f.read() -+ self.assertEqual('1234567890.1234567890', content) -diff --git a/tests/vsftpd.conf b/tests/vsftpd.conf -index b4e4972..787da0e 100644 ---- a/tests/vsftpd.conf -+++ b/tests/vsftpd.conf -@@ -5,7 +5,9 @@ background=no - download_enable=no - listen=yes - run_as_launching_user=yes --write_enable=no -+write_enable=yes -+anon_upload_enable=yes -+anon_other_write_enable=yes - listen_port=8321 - # should be supplied on command line - anon_root=/var/empty --- -1.7.1 - - -From 7445cc96205e9c2b734ec07955519db224aa3ab7 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Wed, 12 Jun 2013 18:01:16 -0400 -Subject: [PATCH 135/149] Allow multiple ftp tests to work in the suite - -Signed-off-by: Kamil Dudka ---- - tests/procmgr.py | 15 ++++++++++----- - 1 files changed, 10 insertions(+), 5 deletions(-) - -diff --git a/tests/procmgr.py b/tests/procmgr.py -index ce08da9..18c3dd2 100644 ---- a/tests/procmgr.py -+++ b/tests/procmgr.py -@@ -12,6 +12,7 @@ class ProcessManager(object): - - def start(self): - self.process = subprocess.Popen(self.cmd) -+ self.running = True - - self.thread = threading.Thread(target=self.run) - self.thread.daemon = True -@@ -19,11 +20,18 @@ class ProcessManager(object): - - def run(self): - self.process.communicate() -+ -+ def stop(self): -+ try: -+ os.kill(self.process.pid, signal.SIGTERM) -+ except OSError: -+ pass -+ self.running = False - - managers = {} - - def start(cmd): -- if str(cmd) in managers: -+ if str(cmd) in managers and managers[str(cmd)].running: - # already started - return - -@@ -75,9 +83,6 @@ def vsftpd_setup(): - except KeyError: - pass - else: -- try: -- os.kill(manager.process.pid, signal.SIGTERM) -- except OSError: -- pass -+ manager.stop() - - return do_setup_module, teardown_module --- -1.7.1 - - -From 4f8e4def8eddac6d5e5239602ef00230a2be3690 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Wed, 12 Jun 2013 19:26:25 -0400 -Subject: [PATCH 136/149] Do not run vsftpd tests by default due to security implications - -Signed-off-by: Kamil Dudka ---- - README.rst | 18 ++++++++++++++++++ - tests/procmgr.py | 14 +++++++++++++- - 2 files changed, 31 insertions(+), 1 deletions(-) - -diff --git a/README.rst b/README.rst -index 3518d9d..c4427aa 100644 ---- a/README.rst -+++ b/README.rst -@@ -49,6 +49,24 @@ or `pip`_:: - .. _easy_install: http://peak.telecommunity.com/DevCenter/EasyInstall - .. _pip: http://pypi.python.org/pypi/pip - -+Automated Tests -+--------------- -+ -+PycURL comes with an automated test suite. To run the tests, execute:: -+ -+ make test -+ -+Some tests use vsftpd configured to accept anonymous uploads. These tests -+are not run by default. As configured, vsftpd will allow reads and writes to -+anything the user running the tests has read and write access. To run -+vsftpd tests you must explicitly set PYCURL_VSFTPD_PATH variable like so:: -+ -+ # use vsftpd in PATH -+ export PYCURL_VSFTPD_PATH=vsftpd -+ -+ # specify full path to vsftpd -+ export PYCURL_VSFTPD_PATH=/usr/local/libexec/vsftpd -+ - Contribute - ---------- - -diff --git a/tests/procmgr.py b/tests/procmgr.py -index 18c3dd2..9309aa8 100644 ---- a/tests/procmgr.py -+++ b/tests/procmgr.py -@@ -3,6 +3,7 @@ import subprocess - import os - import sys - import signal -+import nose.plugins.skip - - from . import runwsgi - -@@ -50,7 +51,16 @@ def start_setup(cmd): - if 'PYCURL_VSFTPD_PATH' in os.environ: - vsftpd_path = os.environ['PYCURL_VSFTPD_PATH'] - else: -- vsftpd_path = 'vsftpd' -+ vsftpd_path = None -+ -+try: -+ # python 2 -+ exception_base = StandardError -+except NameError: -+ # python 3 -+ exception_base = Exception -+class VsftpdNotConfigured(exception_base): -+ pass - - def vsftpd_setup(): - config_file_path = os.path.join(os.path.dirname(__file__), 'vsftpd.conf') -@@ -62,6 +72,8 @@ def vsftpd_setup(): - ] - setup_module = start_setup(cmd) - def do_setup_module(): -+ if vsftpd_path is None: -+ raise nose.plugins.skip.SkipTest('PYCURL_VSFTPD_PATH environment variable not set') - try: - setup_module() - except OSError: --- -1.7.1 - - -From 079b262507e77a61772255aea969471b2d81f16c Mon Sep 17 00:00:00 2001 -From: Romulo A. Ceccon -Date: Tue, 16 Jul 2013 11:05:54 -0300 -Subject: [PATCH 137/149] Adjusted declaration order for C89 compilers - -PYCURL_DECLARE_THREAD_STATE looks like an -executable statement for some non-C99 compilers -(e.g. armcc), so it must appear at the end of -the declaration list. - -Signed-off-by: Kamil Dudka ---- - src/pycurl.c | 19 ++++++++++--------- - 1 files changed, 10 insertions(+), 9 deletions(-) - -diff --git a/src/pycurl.c b/src/pycurl.c -index a9923ed..cdb934e 100644 ---- a/src/pycurl.c -+++ b/src/pycurl.c -@@ -1140,12 +1140,12 @@ static size_t - util_write_callback(int flags, char *ptr, size_t size, size_t nmemb, void *stream) - { - CurlObject *self; -- PYCURL_DECLARE_THREAD_STATE; - PyObject *arglist; - PyObject *result = NULL; - size_t ret = 0; /* assume error */ - PyObject *cb; - int total_size; -+ PYCURL_DECLARE_THREAD_STATE; - - /* acquire thread */ - self = (CurlObject *)stream; -@@ -1219,8 +1219,8 @@ opensocket_callback(void *clientp, curlsocktype purpose, - PyObject *result = NULL; - PyObject *fileno_result = NULL; - CurlObject *self; -- PYCURL_DECLARE_THREAD_STATE; - int ret = CURL_SOCKET_BAD; -+ PYCURL_DECLARE_THREAD_STATE; - - self = (CurlObject *)clientp; - PYCURL_ACQUIRE_THREAD(); -@@ -1269,12 +1269,12 @@ static int - seek_callback(void *stream, curl_off_t offset, int origin) - { - CurlObject *self; -- PYCURL_DECLARE_THREAD_STATE; - PyObject *arglist; - PyObject *result = NULL; - int ret = 2; /* assume error 2 (can't seek, libcurl free to work around). */ - PyObject *cb; - int source = 0; /* assume beginning */ -+ PYCURL_DECLARE_THREAD_STATE; - - /* acquire thread */ - self = (CurlObject *)stream; -@@ -1343,13 +1343,14 @@ static size_t - read_callback(char *ptr, size_t size, size_t nmemb, void *stream) - { - CurlObject *self; -- PYCURL_DECLARE_THREAD_STATE; - PyObject *arglist; - PyObject *result = NULL; - - size_t ret = CURL_READFUNC_ABORT; /* assume error, this actually works */ - int total_size; - -+ PYCURL_DECLARE_THREAD_STATE; -+ - /* acquire thread */ - self = (CurlObject *)stream; - if (!PYCURL_ACQUIRE_THREAD()) -@@ -1424,10 +1425,10 @@ progress_callback(void *stream, - double dltotal, double dlnow, double ultotal, double ulnow) - { - CurlObject *self; -- PYCURL_DECLARE_THREAD_STATE; - PyObject *arglist; - PyObject *result = NULL; - int ret = 1; /* assume error */ -+ PYCURL_DECLARE_THREAD_STATE; - - /* acquire thread */ - self = (CurlObject *)stream; -@@ -1473,10 +1474,10 @@ debug_callback(CURL *curlobj, curl_infotype type, - char *buffer, size_t total_size, void *stream) - { - CurlObject *self; -- PYCURL_DECLARE_THREAD_STATE; - PyObject *arglist; - PyObject *result = NULL; - int ret = 0; /* always success */ -+ PYCURL_DECLARE_THREAD_STATE; - - UNUSED(curlobj); - -@@ -1518,10 +1519,10 @@ static curlioerr - ioctl_callback(CURL *curlobj, int cmd, void *stream) - { - CurlObject *self; -- PYCURL_DECLARE_THREAD_STATE; - PyObject *arglist; - PyObject *result = NULL; - int ret = CURLIOE_FAILRESTART; /* assume error */ -+ PYCURL_DECLARE_THREAD_STATE; - - UNUSED(curlobj); - -@@ -2492,10 +2493,10 @@ int multi_socket_callback(CURL *easy, - { - CurlMultiObject *self; - CurlObject *easy_self; -- PYCURL_DECLARE_THREAD_STATE; - PyObject *arglist; - PyObject *result = NULL; - int ret; -+ PYCURL_DECLARE_THREAD_STATE; - - /* acquire thread */ - self = (CurlMultiObject *)userp; -@@ -2539,10 +2540,10 @@ int multi_timer_callback(CURLM *multi, - void *userp) - { - CurlMultiObject *self; -- PYCURL_DECLARE_THREAD_STATE; - PyObject *arglist; - PyObject *result = NULL; - int ret = 0; /* always success */ -+ PYCURL_DECLARE_THREAD_STATE; - - UNUSED(multi); - --- -1.7.1 - - -From 350016b90e38505eeee46358d5303de2a7faca5e Mon Sep 17 00:00:00 2001 -From: Romulo A. Ceccon -Date: Tue, 16 Jul 2013 11:13:52 -0300 -Subject: [PATCH 138/149] Silence compiler hints about unused variables - -Signed-off-by: Kamil Dudka ---- - src/pycurl.c | 4 +--- - 1 files changed, 1 insertions(+), 3 deletions(-) - -diff --git a/src/pycurl.c b/src/pycurl.c -index cdb934e..7b96b01 100644 ---- a/src/pycurl.c -+++ b/src/pycurl.c -@@ -851,7 +851,6 @@ static int - util_curl_init(CurlObject *self) - { - int res; -- char *s = NULL; - - /* Set curl error buffer and zero it */ - res = curl_easy_setopt(self->handle, CURLOPT_ERRORBUFFER, self->error); -@@ -2495,12 +2494,11 @@ int multi_socket_callback(CURL *easy, - CurlObject *easy_self; - PyObject *arglist; - PyObject *result = NULL; -- int ret; - PYCURL_DECLARE_THREAD_STATE; - - /* acquire thread */ - self = (CurlMultiObject *)userp; -- ret = curl_easy_getinfo(easy, CURLINFO_PRIVATE, &easy_self); -+ curl_easy_getinfo(easy, CURLINFO_PRIVATE, &easy_self); - if (!PYCURL_ACQUIRE_THREAD_MULTI()) - return 0; - --- -1.7.1 - - -From 19af5bbbc98dc2198cc7541b81ed313a75faeb9c Mon Sep 17 00:00:00 2001 -From: Romulo A. Ceccon -Date: Tue, 16 Jul 2013 11:44:36 -0300 -Subject: [PATCH 139/149] Added support for CURLOPT_RESOLVE - -http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTRESOLVE - -Signed-off-by: Kamil Dudka ---- - src/pycurl.c | 36 ++++++++++++++++++++++++++++++++---- - 1 files changed, 32 insertions(+), 4 deletions(-) - -diff --git a/src/pycurl.c b/src/pycurl.c -index 7b96b01..bb6056e 100644 ---- a/src/pycurl.c -+++ b/src/pycurl.c -@@ -68,6 +68,10 @@ - #define HAVE_CURLOPT_PROXYUSERNAME - #endif - -+#if LIBCURL_VERSION_NUM >= 0x071503 /* check for 7.21.3 or greater */ -+#define HAVE_CURLOPT_RESOLVE -+#endif -+ - /* Python < 2.5 compat for Py_ssize_t */ - #if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN) - typedef int Py_ssize_t; -@@ -185,6 +189,9 @@ typedef struct { - struct curl_slist *quote; - struct curl_slist *postquote; - struct curl_slist *prequote; -+#ifdef HAVE_CURLOPT_RESOLVE -+ struct curl_slist *resolve; -+#endif - /* callbacks */ - PyObject *w_cb; - PyObject *h_cb; -@@ -824,6 +831,9 @@ util_curl_new(void) - self->quote = NULL; - self->postquote = NULL; - self->prequote = NULL; -+#ifdef HAVE_CURLOPT_RESOLVE -+ self->resolve = NULL; -+#endif - - /* Set callback pointers to NULL by default */ - self->w_cb = NULL; -@@ -1025,6 +1035,9 @@ util_curl_close(CurlObject *self) - SFREE(self->quote); - SFREE(self->postquote); - SFREE(self->prequote); -+#ifdef HAVE_CURLOPT_RESOLVE -+ SFREE(self->resolve); -+#endif - #undef SFREE - } - -@@ -1588,6 +1601,9 @@ do_curl_reset(CurlObject *self) - SFREE(self->quote); - SFREE(self->postquote); - SFREE(self->prequote); -+#ifdef HAVE_CURLOPT_RESOLVE -+ SFREE(self->resolve); -+#endif - #undef SFREE - res = util_curl_init(self); - if (res < 0) { -@@ -1933,6 +1949,11 @@ do_curl_setopt(CurlObject *self, PyObject *args) - case CURLOPT_PREQUOTE: - old_slist = &self->prequote; - break; -+#ifdef HAVE_CURLOPT_RESOLVE -+ case CURLOPT_RESOLVE: -+ old_slist = &self->resolve; -+ break; -+#endif - case CURLOPT_HTTPPOST: - break; - default: -@@ -1944,10 +1965,14 @@ do_curl_setopt(CurlObject *self, PyObject *args) - len = PyList_Size(obj); - if (len == 0) { - /* Empty list - do nothing */ -- if (!(option == CURLOPT_HTTPHEADER || -- option == CURLOPT_QUOTE || -- option == CURLOPT_POSTQUOTE || -- option == CURLOPT_PREQUOTE)) { -+ if (!(option == CURLOPT_HTTPHEADER -+ || option == CURLOPT_QUOTE -+ || option == CURLOPT_POSTQUOTE -+ || option == CURLOPT_PREQUOTE -+#ifdef HAVE_CURLOPT_RESOLVE -+ || option == CURLOPT_RESOLVE -+#endif -+ )) { - /* Empty list - do nothing */ - Py_INCREF(Py_None); - return Py_None; -@@ -3888,6 +3913,9 @@ initpycurl(void) - insint_c(d, "CRLFILE", CURLOPT_CRLFILE); - insint_c(d, "ISSUERCERT", CURLOPT_ISSUERCERT); - insint_c(d, "ADDRESS_SCOPE", CURLOPT_ADDRESS_SCOPE); -+#ifdef HAVE_CURLOPT_RESOLVE -+ insint_c(d, "RESOLVE", CURLOPT_RESOLVE); -+#endif - - insint_c(d, "M_TIMERFUNCTION", CURLMOPT_TIMERFUNCTION); - insint_c(d, "M_SOCKETFUNCTION", CURLMOPT_SOCKETFUNCTION); --- -1.7.1 - - -From 576efa57797d878e23dd97b1f5ec2393167b30c6 Mon Sep 17 00:00:00 2001 -From: Romulo A. Ceccon -Date: Fri, 19 Jul 2013 11:36:43 -0300 -Subject: [PATCH 140/149] Added test-case for CURLOPT_RESOLVE - -Signed-off-by: Kamil Dudka ---- - tests/resolve_test.py | 22 ++++++++++++++++++++++ - 1 files changed, 22 insertions(+), 0 deletions(-) - create mode 100644 tests/resolve_test.py - -diff --git a/tests/resolve_test.py b/tests/resolve_test.py -new file mode 100644 -index 0000000..fcee830 ---- /dev/null -+++ b/tests/resolve_test.py -@@ -0,0 +1,22 @@ -+# -*- coding: iso-8859-1 -*- -+ -+import pycurl -+import unittest -+ -+from . import app -+from . import runwsgi -+ -+setup_module, teardown_module = runwsgi.app_runner_setup((app.app, 8380)) -+ -+class ResolveTest(unittest.TestCase): -+ def setUp(self): -+ self.curl = pycurl.Curl() -+ -+ def tearDown(self): -+ self.curl.close() -+ -+ def test_resolve(self): -+ self.curl.setopt(pycurl.URL, 'http://p.localhost:8380/success') -+ self.curl.setopt(pycurl.RESOLVE, ['p.localhost:8380:127.0.0.1']) -+ self.curl.perform() -+ self.assertEqual(200, self.curl.getinfo(pycurl.RESPONSE_CODE)) --- -1.7.1 - - -From 2ebc6b6097df0a83a6982e8941af3e880e18a74f Mon Sep 17 00:00:00 2001 -From: Romulo A. Ceccon -Date: Fri, 19 Jul 2013 11:24:40 -0300 -Subject: [PATCH 141/149] Test suite documentation - -Briefly clarified documentation about dependencies -of the test suite. - -Signed-off-by: Kamil Dudka ---- - README.rst | 5 +++++ - 1 files changed, 5 insertions(+), 0 deletions(-) - -diff --git a/README.rst b/README.rst -index c4427aa..b416b97 100644 ---- a/README.rst -+++ b/README.rst -@@ -56,6 +56,8 @@ PycURL comes with an automated test suite. To run the tests, execute:: - - make test - -+The suite depends on packages `nose`_ and `bottle`_. -+ - Some tests use vsftpd configured to accept anonymous uploads. These tests - are not run by default. As configured, vsftpd will allow reads and writes to - anything the user running the tests has read and write access. To run -@@ -67,6 +69,9 @@ vsftpd tests you must explicitly set PYCURL_VSFTPD_PATH variable like so:: - # specify full path to vsftpd - export PYCURL_VSFTPD_PATH=/usr/local/libexec/vsftpd - -+.. _nose: https://nose.readthedocs.org/ -+.. _bottle: http://bottlepy.org/ -+ - Contribute - ---------- - --- -1.7.1 - - -From 59fd64c38e4adf28242dca3fd3d3a0167016945a Mon Sep 17 00:00:00 2001 -From: Wim Lewis -Date: Thu, 21 Jul 2011 12:00:00 -0400 -Subject: [PATCH 142/149] Add suport for CURLINFO_CERTINFO and CURLOPT_CERTINFO. - -This patch adds support for CURLINFO_CERTINFO (and CURLOPT_CERTINFO), -as well as documentation for them and a couple of minor documentation fixes. -These options appeared in libcurl 7.19.1. - -The format of the returned information was chosen to be reasonably close -to the underlying libcurl data structure, while also allowing a -Python programmer to pass the cerificate info to dict() in order to get -a more convenient representation. - -https://sourceforge.net/p/pycurl/patches/14/ - -Signed-off-by: Kamil Dudka ---- - doc/curlobject.html | 19 ++++++++++- - src/pycurl.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++ - 2 files changed, 100 insertions(+), 2 deletions(-) - -diff --git a/doc/curlobject.html b/doc/curlobject.html -index a3d421f..394b950 100644 ---- a/doc/curlobject.html -+++ b/doc/curlobject.html -@@ -43,9 +43,11 @@ href="http://curl.haxx.se/libcurl/c/curl_easy_reset.html">curl_easy_reset< -

Corresponds to - curl_easy_setopt in libcurl, where - option is specified with the CURLOPT_* constants in libcurl, --except that the CURLOPT_ prefix has been removed. The type for -+except that the CURLOPT_ prefix has been removed. -+(See below for exceptions.) -+The type for - value depends on the option, and can be either a string, --integer, long integer, file objects, lists, or functions.

-+integer, long integer, file object, list, or function.

- -

Example usage:

- -@@ -72,6 +74,7 @@ print b.getvalue() - curl_easy_getinfo in libcurl, where - option is the same as the CURLINFO_* constants in libcurl, - except that the CURLINFO_ prefix has been removed. -+(See below for exceptions.) - Result contains an integer, float or string, depending on - which option is given. The getinfo method should - not be called unless perform has been called and -@@ -97,6 +100,18 @@ print c.getinfo(pycurl.HTTP_CODE), c.getinfo(pycurl.EFFECTIVE_URL) -
-
- -+

In order to distinguish between similarly-named CURLOPT and -+CURLINFO constants, some have OPT_ -+and INFO_ prefixes. These are -+INFO_FILETIME, OPT_FILETIME, -+INFO_COOKIELIST (but setopt uses COOKIELIST!), -+INFO_CERTINFO, and OPT_CERTINFO.

-+ -+

The value returned by getinfo(INFO_CERTINFO) is a list -+with one element per certificate in the chain, starting with the leaf; -+each element is a sequence -+of (key, value) -+tuples.

- -
-

-diff --git a/src/pycurl.c b/src/pycurl.c -index bb6056e..5da354d 100644 ---- a/src/pycurl.c -+++ b/src/pycurl.c -@@ -297,6 +297,71 @@ error: - return NULL; - } - -+/* Convert a struct curl_certinfo into a Python data structure. -+ * In case of error return NULL with an exception set. -+ */ -+static PyObject *convert_certinfo(struct curl_certinfo *cinfo) -+{ -+ PyObject *certs; -+ int cert_index; -+ -+ if (!cinfo) { -+ certs = Py_None; -+ Py_INCREF(certs); -+ return certs; -+ } -+ -+ certs = PyList_New((Py_ssize_t)(cinfo->num_of_certs)); -+ if (!certs) -+ return NULL; -+ -+ for (cert_index = 0; cert_index < cinfo->num_of_certs; cert_index ++) { -+ struct curl_slist *fields = cinfo->certinfo[cert_index]; -+ struct curl_slist *field_cursor; -+ int field_count, field_index; -+ PyObject *cert; -+ -+ field_count = 0; -+ field_cursor = fields; -+ while (field_cursor != NULL) { -+ field_cursor = field_cursor->next; -+ field_count ++; -+ } -+ -+ -+ cert = PyTuple_New((Py_ssize_t)field_count); -+ if (!cert) -+ goto error; -+ PyList_SetItem(certs, cert_index, cert); /* Eats the ref from New() */ -+ -+ for(field_index = 0, field_cursor = fields; -+ field_cursor != NULL; -+ field_index ++, field_cursor = field_cursor->next) { -+ const char *field = field_cursor->data; -+ PyObject *field_tuple; -+ -+ if (!field) { -+ field_tuple = Py_None; Py_INCREF(field_tuple); -+ } else { -+ const char *sep = strchr(field, ':'); -+ if (!sep) { -+ field_tuple = PyString_FromString(field); -+ } else { -+ field_tuple = Py_BuildValue("s#s", field, (int)(sep - field), sep+1); -+ } -+ if (!field_tuple) -+ goto error; -+ } -+ PyTuple_SET_ITEM(cert, field_index, field_tuple); /* Eats the ref */ -+ } -+ } -+ -+ return certs; -+ -+ error: -+ Py_XDECREF(certs); -+ return NULL; -+} - - #ifdef WITH_THREAD - /************************************************************************* -@@ -1673,6 +1738,10 @@ util_curl_unsetopt(CurlObject *self, int option) - SETOPT((char *) 0); - break; - -+ case CURLOPT_CERTINFO: -+ SETOPT((long) 0); -+ break; -+ - /* info: we explicitly list unsupported options here */ - case CURLOPT_COOKIEFILE: - default: -@@ -2394,6 +2463,18 @@ do_curl_getinfo(CurlObject *self, PyObject *args) - } - return convert_slist(slist, 1 | 2); - } -+ -+ case CURLINFO_CERTINFO: -+ { -+ /* Return a list of lists of 2-tuples */ -+ struct curl_certinfo *clist = NULL; -+ res = curl_easy_getinfo(self->handle, CURLINFO_CERTINFO, &clist); -+ if (res != CURLE_OK) { -+ CURLERROR_RETVAL(); -+ } else { -+ return convert_certinfo(clist); -+ } -+ } - } - - /* Got wrong option on the method call */ -@@ -3916,6 +3997,7 @@ initpycurl(void) - #ifdef HAVE_CURLOPT_RESOLVE - insint_c(d, "RESOLVE", CURLOPT_RESOLVE); - #endif -+ insint_c(d, "OPT_CERTINFO", CURLOPT_CERTINFO); - - insint_c(d, "M_TIMERFUNCTION", CURLMOPT_TIMERFUNCTION); - insint_c(d, "M_SOCKETFUNCTION", CURLMOPT_SOCKETFUNCTION); -@@ -3993,6 +4075,7 @@ initpycurl(void) - insint_c(d, "INFO_COOKIELIST", CURLINFO_COOKIELIST); - insint_c(d, "LASTSOCKET", CURLINFO_LASTSOCKET); - insint_c(d, "FTP_ENTRY_PATH", CURLINFO_FTP_ENTRY_PATH); -+ insint_c(d, "INFO_CERTINFO", CURLINFO_CERTINFO); - - /* options for global_init() */ - insint(d, "GLOBAL_SSL", CURL_GLOBAL_SSL); --- -1.7.1 - - -From aa82321393ebc813d326cb305a1e2f73bbe45abe Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Thu, 30 May 2013 14:42:38 -0400 -Subject: [PATCH 143/149] Conditional compilation for CERTINFO bits - -Signed-off-by: Kamil Dudka ---- - src/pycurl.c | 11 +++++++++++ - 1 files changed, 11 insertions(+), 0 deletions(-) - -diff --git a/src/pycurl.c b/src/pycurl.c -index 5da354d..c0fec4b 100644 ---- a/src/pycurl.c -+++ b/src/pycurl.c -@@ -66,6 +66,7 @@ - LIBCURL_VERSION_MAJOR == 7 && LIBCURL_VERSION_MINOR == 19 && LIBCURL_VERSION_PATCH >= 1 - #define HAVE_CURLOPT_USERNAME - #define HAVE_CURLOPT_PROXYUSERNAME -+#define HAVE_CURLOPT_CERTINFO - #endif - - #if LIBCURL_VERSION_NUM >= 0x071503 /* check for 7.21.3 or greater */ -@@ -297,6 +298,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. - */ -@@ -362,6 +364,7 @@ static PyObject *convert_certinfo(struct curl_certinfo *cinfo) - Py_XDECREF(certs); - return NULL; - } -+#endif - - #ifdef WITH_THREAD - /************************************************************************* -@@ -1738,9 +1741,11 @@ util_curl_unsetopt(CurlObject *self, int option) - SETOPT((char *) 0); - break; - -+#ifdef HAVE_CURLOPT_CERTINFO - case CURLOPT_CERTINFO: - SETOPT((long) 0); - break; -+#endif - - /* info: we explicitly list unsupported options here */ - case CURLOPT_COOKIEFILE: -@@ -2464,6 +2469,7 @@ do_curl_getinfo(CurlObject *self, PyObject *args) - return convert_slist(slist, 1 | 2); - } - -+#ifdef HAVE_CURLOPT_CERTINFO - case CURLINFO_CERTINFO: - { - /* Return a list of lists of 2-tuples */ -@@ -2476,6 +2482,7 @@ do_curl_getinfo(CurlObject *self, PyObject *args) - } - } - } -+#endif - - /* Got wrong option on the method call */ - PyErr_SetString(PyExc_ValueError, "invalid argument to getinfo"); -@@ -3997,7 +4004,9 @@ initpycurl(void) - #ifdef HAVE_CURLOPT_RESOLVE - insint_c(d, "RESOLVE", CURLOPT_RESOLVE); - #endif -+#ifdef HAVE_CURLOPT_CERTINFO - insint_c(d, "OPT_CERTINFO", CURLOPT_CERTINFO); -+#endif - - insint_c(d, "M_TIMERFUNCTION", CURLMOPT_TIMERFUNCTION); - insint_c(d, "M_SOCKETFUNCTION", CURLMOPT_SOCKETFUNCTION); -@@ -4075,7 +4084,9 @@ initpycurl(void) - insint_c(d, "INFO_COOKIELIST", CURLINFO_COOKIELIST); - insint_c(d, "LASTSOCKET", CURLINFO_LASTSOCKET); - insint_c(d, "FTP_ENTRY_PATH", CURLINFO_FTP_ENTRY_PATH); -+#ifdef HAVE_CURLOPT_CERTINFO - insint_c(d, "INFO_CERTINFO", CURLINFO_CERTINFO); -+#endif - - /* options for global_init() */ - insint(d, "GLOBAL_SSL", CURL_GLOBAL_SSL); --- -1.7.1 - - -From 68713450eef4fd4252babafefe24d42faebc6de9 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Thu, 30 May 2013 14:49:44 -0400 -Subject: [PATCH 144/149] Use an ordinary DECREF - -Signed-off-by: Kamil Dudka ---- - src/pycurl.c | 2 +- - 1 files changed, 1 insertions(+), 1 deletions(-) - -diff --git a/src/pycurl.c b/src/pycurl.c -index c0fec4b..c8b8402 100644 ---- a/src/pycurl.c -+++ b/src/pycurl.c -@@ -361,7 +361,7 @@ static PyObject *convert_certinfo(struct curl_certinfo *cinfo) - return certs; - - error: -- Py_XDECREF(certs); -+ Py_DECREF(certs); - return NULL; - } - #endif --- -1.7.1 - - -From 53999f0ca88f6a71ff0b9331ce9da89a5f07fb0f Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Thu, 30 May 2013 15:35:31 -0400 -Subject: [PATCH 145/149] SSL certinfo test using github - -Signed-off-by: Kamil Dudka ---- - tests/certinfo_test.py | 52 ++++++++++++++++++++++++++++++++++++++++++++++++ - 1 files changed, 52 insertions(+), 0 deletions(-) - create mode 100644 tests/certinfo_test.py - -diff --git a/tests/certinfo_test.py b/tests/certinfo_test.py -new file mode 100644 -index 0000000..25c05af ---- /dev/null -+++ b/tests/certinfo_test.py -@@ -0,0 +1,52 @@ -+#! /usr/bin/env python -+# -*- coding: iso-8859-1 -*- -+# vi:ts=4:et -+ -+import pycurl -+import unittest -+import nose.plugins.skip -+ -+from . import util -+ -+class CertinfoTest(unittest.TestCase): -+ def setUp(self): -+ self.curl = pycurl.Curl() -+ -+ def tearDown(self): -+ self.curl.close() -+ -+ def test_certinfo_option(self): -+ # CURLOPT_CERTINFO was introduced in libcurl-7.19.1 -+ if util.pycurl_version_less_than(7, 19, 1): -+ raise nose.plugins.skip.SkipTest('libcurl < 7.19.1') -+ -+ assert hasattr(pycurl, 'OPT_CERTINFO') -+ -+ def test_request_without_certinfo(self): -+ # CURLOPT_CERTINFO was introduced in libcurl-7.19.1 -+ if util.pycurl_version_less_than(7, 19, 1): -+ raise nose.plugins.skip.SkipTest('libcurl < 7.19.1') -+ -+ self.curl.setopt(pycurl.URL, 'https://github.com/') -+ sio = util.StringIO() -+ self.curl.setopt(pycurl.WRITEFUNCTION, sio.write) -+ self.curl.perform() -+ assert 'GitHub' in sio.getvalue() -+ -+ certinfo = self.curl.getinfo(pycurl.INFO_CERTINFO) -+ self.assertEqual([], certinfo) -+ -+ def test_request_with_certinfo(self): -+ # CURLOPT_CERTINFO was introduced in libcurl-7.19.1 -+ if util.pycurl_version_less_than(7, 19, 1): -+ raise nose.plugins.skip.SkipTest('libcurl < 7.19.1') -+ -+ self.curl.setopt(pycurl.URL, 'https://github.com/') -+ sio = util.StringIO() -+ self.curl.setopt(pycurl.WRITEFUNCTION, sio.write) -+ self.curl.setopt(pycurl.OPT_CERTINFO, 1) -+ self.curl.perform() -+ assert 'GitHub' in sio.getvalue() -+ -+ certinfo = self.curl.getinfo(pycurl.INFO_CERTINFO) -+ assert len(certinfo) > 0 --- -1.7.1 - - -From 6ca51826965392ed3ac2338b5da7dac5215bd840 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Tue, 23 Jul 2013 20:10:47 -0400 -Subject: [PATCH 146/149] Self signed certificate for ssl tests - -Signed-off-by: Kamil Dudka ---- - tests/certs/server.crt | 14 ++++++++++++++ - tests/certs/server.key | 15 +++++++++++++++ - 2 files changed, 29 insertions(+), 0 deletions(-) - create mode 100644 tests/certs/server.crt - create mode 100644 tests/certs/server.key - -diff --git a/tests/certs/server.crt b/tests/certs/server.crt -new file mode 100644 -index 0000000..4a8decc ---- /dev/null -+++ b/tests/certs/server.crt -@@ -0,0 +1,14 @@ -+-----BEGIN CERTIFICATE----- -+MIICJTCCAY4CCQDfQAHGuFkN2zANBgkqhkiG9w0BAQUFADBXMQswCQYDVQQGEwJB -+VTETMBEGA1UECBMKU29tZS1TdGF0ZTEaMBgGA1UEChMRcHljdXJsIHRlc3Qgc3Vp -+dGUxFzAVBgNVBAMTDmxvY2FsaG9zdDo4MzgzMB4XDTEzMDcyNDAwMDgxNVoXDTE0 -+MDcyNDAwMDgxNVowVzELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUx -+GjAYBgNVBAoTEXB5Y3VybCB0ZXN0IHN1aXRlMRcwFQYDVQQDEw5sb2NhbGhvc3Q6 -+ODM4MzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAxE0+59Kf2z9ccZyUAuKG -+QpkQaXtEJC13exY4SWIfr78FfCStdqpZdfmm66djFENhmaAZYGsPHGXrEIHQqja2 -+7KYkHo4cXLxksR4Db01yPMtMU9xHzg37OTIS2lGRmMxLduKc5XKsxA98PV/D1k4k -+sqLcLDH//YdLR0iYUYLOIgMCAwEAATANBgkqhkiG9w0BAQUFAAOBgQAppFdMNMHe -+68uQA1y2xAYW7faUH8/g+XAuH9WjLL2QfWGXgWey/pwofsrTO2Hl+D9y8Rey4eJ/ -+BDv3OV2cBWBYBOxZv/kqyDHQc38tho9gdaPQnD4ttFk2TSgaOs1W39pGY1On0Ejd -+O6CXEGV7p8C613AgEkbdudnn+ChvyH/Shw== -+-----END CERTIFICATE----- -diff --git a/tests/certs/server.key b/tests/certs/server.key -new file mode 100644 -index 0000000..5bdbbf9 ---- /dev/null -+++ b/tests/certs/server.key -@@ -0,0 +1,15 @@ -+-----BEGIN RSA PRIVATE KEY----- -+MIICXAIBAAKBgQDETT7n0p/bP1xxnJQC4oZCmRBpe0QkLXd7FjhJYh+vvwV8JK12 -+qll1+abrp2MUQ2GZoBlgaw8cZesQgdCqNrbspiQejhxcvGSxHgNvTXI8y0xT3EfO -+Dfs5MhLaUZGYzEt24pzlcqzED3w9X8PWTiSyotwsMf/9h0tHSJhRgs4iAwIDAQAB -+AoGAMLNFTvgnJpqikaEZ61lNm8ojkze8oQkSolvR3QrV96D5eGIVEuKSTT2Blucx -+In7RAO8CPLRyzEXQuoiqPwBSAxY2Xswd+zcAgN2Zi8uqWTmPNsW6451BJRemgLjK -+OxLxCdVTOTxHfttj/CnwYQ6zn55oyZJGGmaVGykbvH/AgukCQQD3HfhOPExsI/6X -+Bp7CeZOhmM+LBOQGQGDjRnBdRp0s3HiUfaDxU2mbEafGPI2OyuzpYAqxHVTJLai6 -+CQlJGuQXAkEAy1upObz2bcN2dUCHNufk2qdfRSCRkmKemuqznwCW3fSoRKB+qOu3 -+xyTLEkTvLBNnAFjoyd6B75QzL/7//qvo9QJAE0xV3dY7qZ5N/YFY2Jsh+layroqd -+PBe++UDA+afQEnbNO9trvCzlbGS+k26bJ3GVeswzSY2e128nZA/cl8bv1QJAfTEO -+uybjpqtAj+qL03drYljLw+jK9Y2VCtYWgnqAZmAp/yW3FBMZbpXuFm8ttrqzHHmf -+xjcfUvivkoqv2n7GyQJBAKxbBVx/LQiSVpOTnXTEA1NJF8NS2NCF+3sm3kGhFKql -+Hi/cCAFrhBl9MoPJF/6noukfIkq0SzjkWrYIcoBDoVg= -+-----END RSA PRIVATE KEY----- --- -1.7.1 - - -From 2bc1535519faf3e574bac514b634d29ed7e28c20 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Sat, 1 Jun 2013 05:23:41 -0400 -Subject: [PATCH 147/149] Support for testing against a local ssl server - -Signed-off-by: Kamil Dudka ---- - tests/runwsgi.py | 34 +++++++++++++++++++++++++++++----- - 1 files changed, 29 insertions(+), 5 deletions(-) - -diff --git a/tests/runwsgi.py b/tests/runwsgi.py -index 114ce42..c9bc236 100644 ---- a/tests/runwsgi.py -+++ b/tests/runwsgi.py -@@ -4,6 +4,7 @@ import sys - import bottle - import threading - import socket -+import os.path - import time as _time - - try: -@@ -37,6 +38,21 @@ class Server(bottle.WSGIRefServer): - else: - self.srv.serve_forever(poll_interval=0.1) - -+class SslServer(bottle.CherryPyServer): -+ def run(self, handler): -+ import cherrypy.wsgiserver, cherrypy.wsgiserver.ssl_builtin -+ server = cherrypy.wsgiserver.CherryPyWSGIServer((self.host, self.port), handler) -+ cert_dir = os.path.join(os.path.dirname(__file__), 'certs') -+ ssl_adapter = cherrypy.wsgiserver.ssl_builtin.BuiltinSSLAdapter( -+ os.path.join(cert_dir, 'server.crt'), -+ os.path.join(cert_dir, 'server.key'), -+ ) -+ server.ssl_adapter = ssl_adapter -+ try: -+ server.start() -+ finally: -+ server.stop() -+ - def wait_for_network_service(netloc, check_interval, num_attempts): - ok = False - for i in range(num_attempts): -@@ -51,8 +67,8 @@ def wait_for_network_service(netloc, check_interval, num_attempts): - break - return ok - --def start_bottle_server(app, port, **kwargs): -- server_thread = ServerThread(app, port, kwargs) -+def start_bottle_server(app, port, server, **kwargs): -+ server_thread = ServerThread(app, port, server, kwargs) - server_thread.daemon = True - server_thread.start() - -@@ -64,12 +80,12 @@ def start_bottle_server(app, port, **kwargs): - return server_thread.server - - class ServerThread(threading.Thread): -- def __init__(self, app, port, server_kwargs): -+ def __init__(self, app, port, server, server_kwargs): - threading.Thread.__init__(self) - self.app = app - self.port = port - self.server_kwargs = server_kwargs -- self.server = Server(host='localhost', port=self.port, **self.server_kwargs) -+ self.server = server(host='localhost', port=self.port, **self.server_kwargs) - - def run(self): - bottle.run(self.app, server=self.server, quiet=True) -@@ -104,7 +120,15 @@ def app_runner_setup(*specs): - if port in started_servers: - assert started_servers[port] == (app, kwargs) - else: -- self.servers.append(start_bottle_server(app, port, **kwargs)) -+ server = Server -+ if 'server' in kwargs: -+ server = kwargs['server'] -+ del kwargs['server'] -+ elif 'ssl' in kwargs: -+ if kwargs['ssl']: -+ server = SslServer -+ del kwargs['ssl'] -+ self.servers.append(start_bottle_server(app, port, server, **kwargs)) - started_servers[port] = (app, kwargs) - - def teardown(self): --- -1.7.1 - - -From 1bae541fbeb5d7554c29d76f7e57dc3bc6cb4985 Mon Sep 17 00:00:00 2001 -From: Oleg Pudeyev -Date: Tue, 23 Jul 2013 20:11:25 -0400 -Subject: [PATCH 148/149] Test against a local ssl server - -Signed-off-by: Kamil Dudka ---- - tests/certinfo_test.py | 26 +++++++++++++++++++++----- - 1 files changed, 21 insertions(+), 5 deletions(-) - -diff --git a/tests/certinfo_test.py b/tests/certinfo_test.py -index 25c05af..1473d39 100644 ---- a/tests/certinfo_test.py -+++ b/tests/certinfo_test.py -@@ -6,8 +6,12 @@ import pycurl - import unittest - import nose.plugins.skip - -+from . import app -+from . import runwsgi - from . import util - -+setup_module, teardown_module = runwsgi.app_runner_setup((app.app, 8383, dict(ssl=True))) -+ - class CertinfoTest(unittest.TestCase): - def setUp(self): - self.curl = pycurl.Curl() -@@ -27,11 +31,13 @@ class CertinfoTest(unittest.TestCase): - if util.pycurl_version_less_than(7, 19, 1): - raise nose.plugins.skip.SkipTest('libcurl < 7.19.1') - -- self.curl.setopt(pycurl.URL, 'https://github.com/') -+ self.curl.setopt(pycurl.URL, 'https://localhost:8383/success') - sio = util.StringIO() - self.curl.setopt(pycurl.WRITEFUNCTION, sio.write) -+ # self signed certificate -+ self.curl.setopt(pycurl.SSL_VERIFYPEER, 0) - self.curl.perform() -- assert 'GitHub' in sio.getvalue() -+ assert sio.getvalue() == 'success' - - certinfo = self.curl.getinfo(pycurl.INFO_CERTINFO) - self.assertEqual([], certinfo) -@@ -41,12 +47,22 @@ class CertinfoTest(unittest.TestCase): - if util.pycurl_version_less_than(7, 19, 1): - raise nose.plugins.skip.SkipTest('libcurl < 7.19.1') - -- self.curl.setopt(pycurl.URL, 'https://github.com/') -+ self.curl.setopt(pycurl.URL, 'https://localhost:8383/success') - sio = util.StringIO() - self.curl.setopt(pycurl.WRITEFUNCTION, sio.write) - self.curl.setopt(pycurl.OPT_CERTINFO, 1) -+ # self signed certificate -+ self.curl.setopt(pycurl.SSL_VERIFYPEER, 0) - self.curl.perform() -- assert 'GitHub' in sio.getvalue() -+ assert sio.getvalue() == 'success' - - certinfo = self.curl.getinfo(pycurl.INFO_CERTINFO) -- assert len(certinfo) > 0 -+ # self signed certificate, one certificate in chain -+ assert len(certinfo) == 1 -+ certinfo = certinfo[0] -+ # convert to a dictionary -+ certinfo_dict = {} -+ for entry in certinfo: -+ certinfo_dict[entry[0]] = entry[1] -+ assert 'Subject' in certinfo_dict -+ assert 'pycurl test suite' in certinfo_dict['Subject'] --- -1.7.1 - - -From bb2b2e6f5a7dd7a343e2c1207ca152dbeb4cf14b Mon Sep 17 00:00:00 2001 -From: Romulo A. Ceccon -Date: Fri, 26 Jul 2013 10:23:26 -0300 -Subject: [PATCH 149/149] Added documentation for dependency on CherryPy - -Signed-off-by: Kamil Dudka ---- - README.rst | 3 ++- - requirements-dev.txt | 1 + - 2 files changed, 3 insertions(+), 1 deletions(-) - -diff --git a/README.rst b/README.rst -index b416b97..e0641d0 100644 ---- a/README.rst -+++ b/README.rst -@@ -56,7 +56,7 @@ PycURL comes with an automated test suite. To run the tests, execute:: - - make test - --The suite depends on packages `nose`_ and `bottle`_. -+The suite depends on packages `nose`_, `bottle`_ and `cherrypy`_. - - Some tests use vsftpd configured to accept anonymous uploads. These tests - are not run by default. As configured, vsftpd will allow reads and writes to -@@ -71,6 +71,7 @@ vsftpd tests you must explicitly set PYCURL_VSFTPD_PATH variable like so:: - - .. _nose: https://nose.readthedocs.org/ - .. _bottle: http://bottlepy.org/ -+.. _cherrypy: http://www.cherrypy.org/ - - Contribute - ---------- -diff --git a/requirements-dev.txt b/requirements-dev.txt -index 36b0b24..ea11ac9 100644 ---- a/requirements-dev.txt -+++ b/requirements-dev.txt -@@ -1,2 +1,3 @@ - bottle - nose -+cherrypy --- -1.7.1 - diff --git a/0001-do_curl_getinfo-fix-misplaced-endif.patch b/0001-do_curl_getinfo-fix-misplaced-endif.patch deleted file mode 100644 index 0e8261b..0000000 --- a/0001-do_curl_getinfo-fix-misplaced-endif.patch +++ /dev/null @@ -1,26 +0,0 @@ -From dc431c729b9639485a45dbd91e020c5c95508c90 Mon Sep 17 00:00:00 2001 -From: Kamil Dudka -Date: Thu, 8 Aug 2013 12:51:48 +0200 -Subject: [PATCH 1/2] do_curl_getinfo: fix misplaced #endif - ---- - src/pycurl.c | 2 +- - 1 files changed, 1 insertions(+), 1 deletions(-) - -diff --git a/src/pycurl.c b/src/pycurl.c -index c8b8402..87dac7e 100644 ---- a/src/pycurl.c -+++ b/src/pycurl.c -@@ -2481,8 +2481,8 @@ do_curl_getinfo(CurlObject *self, PyObject *args) - return convert_certinfo(clist); - } - } -- } - #endif -+ } - - /* Got wrong option on the method call */ - PyErr_SetString(PyExc_ValueError, "invalid argument to getinfo"); --- -1.7.1 - diff --git a/0002-runwsgi.py-start-the-server-explicitly-at-127.0.0.1.patch b/0002-runwsgi.py-start-the-server-explicitly-at-127.0.0.1.patch deleted file mode 100644 index e8a7591..0000000 --- a/0002-runwsgi.py-start-the-server-explicitly-at-127.0.0.1.patch +++ /dev/null @@ -1,26 +0,0 @@ -From 9540e6097563ea754b8619fe85ba010071a67244 Mon Sep 17 00:00:00 2001 -From: Kamil Dudka -Date: Thu, 8 Aug 2013 12:54:53 +0200 -Subject: [PATCH 2/2] runwsgi.py: start the server explicitly at 127.0.0.1 - -Otherwise it may start on ::1, which would consequently break self-test. ---- - tests/runwsgi.py | 2 +- - 1 files changed, 1 insertions(+), 1 deletions(-) - -diff --git a/tests/runwsgi.py b/tests/runwsgi.py -index c9bc236..096038c 100644 ---- a/tests/runwsgi.py -+++ b/tests/runwsgi.py -@@ -85,7 +85,7 @@ class ServerThread(threading.Thread): - self.app = app - self.port = port - self.server_kwargs = server_kwargs -- self.server = server(host='localhost', port=self.port, **self.server_kwargs) -+ self.server = server(host='127.0.0.1', port=self.port, **self.server_kwargs) - - def run(self): - bottle.run(self.app, server=self.server, quiet=True) --- -1.7.1 - diff --git a/python-pycurl.spec b/python-pycurl.spec index 8f16961..92a37c5 100644 --- a/python-pycurl.spec +++ b/python-pycurl.spec @@ -1,8 +1,8 @@ %{!?python_sitearch: %global python_sitearch %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib(1)")} Name: python-pycurl -Version: 7.19.0 -Release: 18.20130315git8d654296%{?dist} +Version: 7.19.3 +Release: 1%{?dist} Summary: A Python interface to libcurl Group: Development/Languages @@ -10,13 +10,6 @@ License: LGPLv2+ or MIT URL: http://pycurl.sourceforge.net/ Source0: http://pycurl.sourceforge.net/download/pycurl-%{version}.tar.gz -# sync with upstream's 8d654296 -Patch0: 0000-pycurl-7.19.7-8d654296.patch - -# get the test-suite running -Patch1: 0001-do_curl_getinfo-fix-misplaced-endif.patch -Patch2: 0002-runwsgi.py-start-the-server-explicitly-at-127.0.0.1.patch - Requires: keyutils-libs BuildRequires: python-devel BuildRequires: curl-devel >= 7.19.0 @@ -47,24 +40,8 @@ of features. %prep %setup0 -q -n pycurl-%{version} -# drop CVS stuff that would prevent git patches from being applied -find -type f | xargs sed -i 's/\$Id: [^$]*\$/$Id$/' - -# upstream patches -%patch0 -p1 - -# patches not yet upstream -%patch1 -p1 -%patch2 -p1 - -# remove a test specific to OpenSSL-powered libcurl -rm -f tests/certinfo_test.py - -# temporarily disable intermittently failing test-case -rm -f tests/multi_socket_select_test.py - %build -CFLAGS="$RPM_OPT_FLAGS -DHAVE_CURL_OPENSSL" %{__python} setup.py build +CFLAGS="$RPM_OPT_FLAGS" %{__python} setup.py build --with-nss %check export PYTHONPATH=$RPM_BUILD_ROOT%{python_sitearch} @@ -75,10 +52,13 @@ make test PYTHON=%{__python} rm -rf %{buildroot}%{_datadir}/doc/pycurl %files -%doc COPYING COPYING2 ChangeLog README.rst TODO examples doc tests +%doc COPYING-LGPL COPYING-MIT ChangeLog README.rst examples doc tests %{python_sitearch}/* %changelog +* Tue Jan 21 2014 Kamil Dudka - 7.19.3-1 +- update to 7.19.3 + * Thu Aug 08 2013 Kamil Dudka - 7.19.0-18.20130315git8d654296 - sync with upstream 8d654296 diff --git a/sources b/sources index 7b4fa17..0a4fe5f 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -919d58fe37e69fe87ce4534d8b6a1c7b pycurl-7.19.0.tar.gz +b9091b7438d3de1552e28862d2971cd1 pycurl-7.19.3.tar.gz