diff -Nru pycurl-7.21.5/AUTHORS pycurl-7.43.0/AUTHORS --- pycurl-7.21.5/AUTHORS 2016-01-05 07:03:44.000000000 +0000 +++ pycurl-7.43.0/AUTHORS 2016-02-04 14:32:37.000000000 +0000 @@ -23,6 +23,7 @@ Dominique Eric S. Raymond Francisco Alves +Gabi Davar Gisle Vanem Gregory Petukhov Jakob Truelsen diff -Nru pycurl-7.21.5/ChangeLog pycurl-7.43.0/ChangeLog --- pycurl-7.21.5/ChangeLog 2016-01-05 07:03:44.000000000 +0000 +++ pycurl-7.43.0/ChangeLog 2016-02-04 04:09:31.000000000 +0000 @@ -1,3 +1,80 @@ +Version 7.43.0 [requires libcurl-7.19.0 or better] - 2016-02-02 +--------------------------------------------------------------- + + * Added CURLINFO_RTSP_* constants (libcurl 7.20.0+). + + * Added CURLOPT_XOAUTH2_BEARER (libcurl 7.33.0+). + + * Added CURLOPT_SASL_IR (libcurl 7.31.0+). + + * Added CURLOPT_LOGIN_OPTIONS (libcurl 7.34.0+). + + * Added CURLOPT_FTP_USE_PRET (libcurl 7.20.0+). + + * Added setopt_string method to Curl objects to set arbitrary + string options. + + * Switched to Bintray for hosting release distributions. + + * Added CURLOPT_DEFAULT_PROTOCOL (libcurl 7.45.0+). + + * Added CURLOPT_TLSAUTH_* options (libcurl 7.21.4+). + + * Added CURLPROTO_SMB and CURLPROTO_SMBS constants (libcurl 7.40.0+). + + * Added CURL_SOCKOPT_* constants (libcurl 7.21.5+). + + * Added CURL_HTTP_VERSION_2_0, CURL_HTTP_VERSION_2 and + CURL_HTTP_VERSION_2TLS constants for CURLOPT_HTTP_VERSION + (various libcurl versions required for these). + + * winbuild.py can now build binary wheels on Windows. + + * Added failed memory allocation handling during SSL lock initialization. + + * CURLOPT_IOCTLDATA option support has been removed. + This option is used internally by PycURL and is not settable by + applications. + + * HTTPHEADER and PROXYHEADER options can now be unset. + + * Added CURLPIPE_* constants (libcurl 7.43.0+). + + * Added CURLOPT_PIPEWAIT (libcurl 7.43.0+). + + * Added CURLOPT_PATH_AS_IS (libcurl 7.42.0+). + + * Added CURLOPT_PROXYHEADER and CURLOPT_HEADEROPT as well as + CURLHEADER_UNIFIED and CURLHEADER_SEPARATE (libcurl 7.37.0+). + + * Added CURLOPT_EXPECT_100_TIMEOUT_MS (libcurl 7.36.0+). + + * Added CURLOPT_XFERINFOFUNCTION (libcurl 7.32.0+). + + * Added CURLM_ADDED_ALREADY error constant (libcurl 7.32.1+). + + * Added remaining CURLE_* constants through libcurl 7.46.0. + + * Unbroken `curl' module import on Windows - apparently Windows now + has a `signal' Python module but no `SIGPIPE' (patch by Gabi Davar). + + * Added CURLMOPT_PIPELINING_SITE_BL and CURLMOPT_PIPELINING_SERVER_BL + options (libcurl 7.30.0+). + + * Added CURLOPT_TCP_KEEPALIVE, CURLOPT_TCP_KEEPIDLE and + CURLOPT_TCP_KEEPINTVL options (libcurl 7.25.0+). + + * Added CURLOPT_ACCEPTTIMEOUT_MS (libcurl 7.24.0+). + + * Added CURLOPT_ACCEPT_ENCODING and CURLOPT_TRANSFER_ENCODING + options (libcurl 7.21.6+). + + * OPENSOCKETFUNCTION callback for AF_UNIX sockets was mistakenly + invoked with the address as a `string' rather than `bytes' on + Python 3. The callback now receives a `bytes' instance as was + documented. + + Version 7.21.5 [requires libcurl-7.19.0 or better] - 2016-01-05 --------------------------------------------------------------- diff -Nru pycurl-7.21.5/COPYING-LGPL pycurl-7.43.0/COPYING-LGPL --- pycurl-7.21.5/COPYING-LGPL 2015-05-19 15:31:32.000000000 +0000 +++ pycurl-7.43.0/COPYING-LGPL 2016-01-24 01:01:42.000000000 +0000 @@ -2,7 +2,7 @@ Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -485,7 +485,7 @@ You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. @@ -500,5 +500,3 @@ Ty Coon, President of Vice That's all there is to it! - - diff -Nru pycurl-7.21.5/debian/changelog pycurl-7.43.0/debian/changelog --- pycurl-7.21.5/debian/changelog 2016-01-19 00:10:12.000000000 +0000 +++ pycurl-7.43.0/debian/changelog 2016-03-10 17:21:35.000000000 +0000 @@ -1,8 +1,24 @@ -pycurl (7.21.5-1ubuntu2) xenial; urgency=medium +pycurl (7.43.0-1ubuntu1) xenial; urgency=medium - * No-change rebuild to drop python3.4 support. + * Merge with Debian; remaining changes: + - Remove build dependency on python-bottle (universe). + - Drop libssh2-1-dev from Build-Depends, as it is not in main, and is + not needed since in Ubuntu, curl-config --static-libs doesn't return + -lssh2. - -- Matthias Klose Tue, 19 Jan 2016 00:10:12 +0000 + -- Barry Warsaw Thu, 10 Mar 2016 11:54:05 -0500 + +pycurl (7.43.0-1) unstable; urgency=medium + + * New upstream release. + * d/control: + - Add python{,3}-flaky to Build-Depends for test suite. + - Test suite runs at build time but still fails, so continue not to fail. + - Bump Standards-Version to 3.9.7 with no other changes necessary. + * d/tests: Add a live test which downloads a well published resource. + (Closes: #763770) + + -- Barry Warsaw Thu, 10 Mar 2016 11:19:37 -0500 pycurl (7.21.5-1ubuntu1) xenial; urgency=medium diff -Nru pycurl-7.21.5/debian/control pycurl-7.43.0/debian/control --- pycurl-7.21.5/debian/control 2016-01-12 21:19:01.000000000 +0000 +++ pycurl-7.43.0/debian/control 2016-03-10 17:21:35.000000000 +0000 @@ -11,11 +11,13 @@ python-all-dbg, python-all-dev (>= 2.6.6-3~), python-docutils, + python-flaky, python-nose, python-sphinx, python3-all-dbg, - python3-all-dev -Standards-Version: 3.9.6 + python3-all-dev, + python3-flaky, +Standards-Version: 3.9.7 X-Python-Version: all X-Python3-Version: >= 3.3 Homepage: http://pycurl.sourceforge.net @@ -24,8 +26,12 @@ Package: python-pycurl Architecture: any -Suggests: libcurl4-gnutls-dev, python-pycurl-dbg, python-pycurl-doc -Depends: ${misc:Depends}, ${python:Depends}, ${shlibs:Depends} +Suggests: libcurl4-gnutls-dev, + python-pycurl-dbg, + python-pycurl-doc, +Depends: ${misc:Depends}, + ${python:Depends}, + ${shlibs:Depends}, Description: Python bindings to libcurl This module provides the Python bindings to libcurl. Please refer to the libcurl documentation available in libcurl4-gnutls-dev Debian package. @@ -36,8 +42,12 @@ Package: python3-pycurl Architecture: any -Suggests: libcurl4-gnutls-dev, python-pycurl-doc, python3-pycurl-dbg -Depends: ${misc:Depends}, ${python3:Depends}, ${shlibs:Depends} +Suggests: libcurl4-gnutls-dev, + python-pycurl-doc, + python3-pycurl-dbg, +Depends: ${misc:Depends}, + ${python3:Depends}, + ${shlibs:Depends}, Description: Python bindings to libcurl (Python 3) This module provides the Python bindings to libcurl. Please refer to the libcurl documentation available in libcurl4-gnutls-dev Debian package. @@ -53,7 +63,7 @@ Depends: python-dbg, python-pycurl (= ${binary:Version}), ${misc:Depends}, - ${shlibs:Depends} + ${shlibs:Depends}, Description: Python bindings to libcurl (debug extension) This module provides the Python bindings to libcurl. Please refer to the libcurl documentation available in libcurl4-gnutls-dev Debian package. @@ -67,7 +77,7 @@ Depends: python3-dbg, python3-pycurl (= ${binary:Version}), ${misc:Depends}, - ${shlibs:Depends} + ${shlibs:Depends}, Description: Python bindings to libcurl (debug extension, Python 3) This module provides the Python bindings to libcurl. Please refer to the libcurl documentation available in libcurl4-gnutls-dev Debian package. @@ -77,7 +87,7 @@ Package: python-pycurl-doc Architecture: all Section: doc -Depends: ${misc:Depends} +Depends: ${misc:Depends}, Description: Python bindings to libcurl (documentation) This module provides the Python bindings to libcurl. Please refer to the libcurl documentation available in libcurl4-gnutls-dev Debian package. diff -Nru pycurl-7.21.5/debian/.git-dpm pycurl-7.43.0/debian/.git-dpm --- pycurl-7.21.5/debian/.git-dpm 2016-01-12 21:19:01.000000000 +0000 +++ pycurl-7.43.0/debian/.git-dpm 2016-03-10 17:21:35.000000000 +0000 @@ -1,11 +1,11 @@ # see git-dpm(1) from git-dpm package -16e907fbfc8b8905cadd96ef54f7379fe0718c52 -16e907fbfc8b8905cadd96ef54f7379fe0718c52 -27c9dcbe03ae89b24377b8c629f3ed3c393d6432 -27c9dcbe03ae89b24377b8c629f3ed3c393d6432 -pycurl_7.21.5.orig.tar.gz -60865d22fc715ca5197117ea3ad32413d3c7402e -170962 +391a9238b7d78ffc991328ce016bf6628ab006af +391a9238b7d78ffc991328ce016bf6628ab006af +81834bfdd6e021247c5ca0c6ec4abe55cc246ca5 +81834bfdd6e021247c5ca0c6ec4abe55cc246ca5 +pycurl_7.43.0.orig.tar.gz +e8e9c7e9ae91ae32096b8c86cfc7d49976a66d1b +182522 debianTag="debian/%e%v" patchedTag="patched/%e%v" upstreamTag="upstream/%e%u" diff -Nru pycurl-7.21.5/debian/patches/10_setup.py.patch pycurl-7.43.0/debian/patches/10_setup.py.patch --- pycurl-7.21.5/debian/patches/10_setup.py.patch 2016-01-12 21:19:01.000000000 +0000 +++ pycurl-7.43.0/debian/patches/10_setup.py.patch 2016-03-10 17:21:35.000000000 +0000 @@ -1,4 +1,4 @@ -From fdd8cd2d8b00f4fd95d58dfc16372aa6309acf35 Mon Sep 17 00:00:00 2001 +From fbccae0358619dda2ae5c493a107db58af9c344b Mon Sep 17 00:00:00 2001 From: Sandro Tosi Date: Tue, 30 Sep 2014 17:38:05 -0400 Subject: Adjust setup.py file for Debian architecture @@ -9,7 +9,7 @@ 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py -index 0f14571..01233eb 100644 +index a743ef9..0216ac5 100644 --- a/setup.py +++ b/setup.py @@ -60,7 +60,7 @@ class ExtensionConfiguration(object): diff -Nru pycurl-7.21.5/debian/patches/20_build_doc_for_debian.patch pycurl-7.43.0/debian/patches/20_build_doc_for_debian.patch --- pycurl-7.21.5/debian/patches/20_build_doc_for_debian.patch 2016-01-12 21:19:01.000000000 +0000 +++ pycurl-7.43.0/debian/patches/20_build_doc_for_debian.patch 2016-03-10 17:21:35.000000000 +0000 @@ -1,4 +1,4 @@ -From 16e907fbfc8b8905cadd96ef54f7379fe0718c52 Mon Sep 17 00:00:00 2001 +From bd7908f6018f67aa3a8d974ce0e6ebca900ec2c1 Mon Sep 17 00:00:00 2001 From: Sandro Tosi Date: Tue, 30 Sep 2014 17:38:05 -0400 Subject: Adapt documentation build process for Debian packaging purposes @@ -8,17 +8,18 @@ Last-Update: 2014-02-15 Patch-Name: 20_build_doc_for_debian.patch --- - Makefile | 1 + - 1 file changed, 1 insertion(+) + Makefile | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile -index 5d58628..ac4c50f 100644 +index bcb13e8..dfe62cf 100644 --- a/Makefile +++ b/Makefile -@@ -143,6 +143,7 @@ docs: build +@@ -143,7 +143,7 @@ docs: build PYTHONSUFFIX=$$(python -V 2>&1 |awk '{print $$2}' |awk -F. '{print $$1 "." $$2}') && \ PYTHONPATH=$$(ls -d build/lib.*$$PYTHONSUFFIX):$$PYTHONPATH \ sphinx-build doc build/doc +- cp ChangeLog build/doc + rst2html README.rst build/doc/README.html # Rebuild all documentation. diff -Nru pycurl-7.21.5/debian/patches/30_debianize_tests.patch pycurl-7.43.0/debian/patches/30_debianize_tests.patch --- pycurl-7.21.5/debian/patches/30_debianize_tests.patch 1970-01-01 00:00:00.000000000 +0000 +++ pycurl-7.43.0/debian/patches/30_debianize_tests.patch 2016-03-10 17:21:35.000000000 +0000 @@ -0,0 +1,22 @@ +From 391a9238b7d78ffc991328ce016bf6628ab006af Mon Sep 17 00:00:00 2001 +From: Barry Warsaw +Date: Thu, 10 Mar 2016 10:56:38 -0500 +Subject: nose-show-skipped isn't in Debian yet. + +Patch-Name: 30_debianize_tests.patch +--- + tests/run.sh | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/tests/run.sh b/tests/run.sh +index 683c545..e893263 100755 +--- a/tests/run.sh ++++ b/tests/run.sh +@@ -25,5 +25,5 @@ if test "$CI" = true; then + fi + + $PYTHON -c 'import pycurl; print(pycurl.version)' +-$NOSETESTS -a \!standalone"$extra_attrs" --with-flaky --show-skipped "$@" +-$NOSETESTS -a standalone --with-flaky --show-skipped "$@" ++$NOSETESTS -a \!standalone"$extra_attrs" --with-flaky "$@" ++$NOSETESTS -a standalone --with-flaky "$@" diff -Nru pycurl-7.21.5/debian/patches/series pycurl-7.43.0/debian/patches/series --- pycurl-7.21.5/debian/patches/series 2016-01-12 21:19:01.000000000 +0000 +++ pycurl-7.43.0/debian/patches/series 2016-03-10 17:21:35.000000000 +0000 @@ -1,2 +1,3 @@ 10_setup.py.patch 20_build_doc_for_debian.patch +30_debianize_tests.patch diff -Nru pycurl-7.21.5/debian/tests/control pycurl-7.43.0/debian/tests/control --- pycurl-7.21.5/debian/tests/control 2016-01-12 21:19:01.000000000 +0000 +++ pycurl-7.43.0/debian/tests/control 2016-03-10 17:21:35.000000000 +0000 @@ -4,3 +4,7 @@ Test-Command: python -c "import curl; print curl.__file__" Test-Command: python3 -c "import curl; print(curl.__file__)" + +Tests: livetest.sh +Depends: @, ca-certificates +Restrictions: allow-stderr diff -Nru pycurl-7.21.5/debian/tests/livetest.sh pycurl-7.43.0/debian/tests/livetest.sh --- pycurl-7.21.5/debian/tests/livetest.sh 1970-01-01 00:00:00.000000000 +0000 +++ pycurl-7.43.0/debian/tests/livetest.sh 2016-03-10 17:21:35.000000000 +0000 @@ -0,0 +1,32 @@ +#!/bin/sh + +cat > $ADTTMP/livetest.py < status + + Callback for progress meter. Corresponds to `CURLOPT_XFERINFOFUNCTION`_ + in libcurl. + + ``XFERINFOFUNCTION`` receives amounts as long integers. + + ``NOPROGRESS`` option must be set for False libcurl to invoke a + progress callback, as PycURL by default sets ``NOPROGRESS`` to True. + Example: Download/upload progress callback ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -197,8 +218,8 @@ c = pycurl.Curl() c.setopt(c.URL, "http://slashdot.org/") - c.setopt(c.NOPROGRESS, 0) - c.setopt(c.PROGRESSFUNCTION, progress) + c.setopt(c.NOPROGRESS, False) + c.setopt(c.XFERINFOFUNCTION, progress) c.perform() @@ -312,6 +333,7 @@ .. _CURLOPT_WRITEFUNCTION: http://curl.haxx.se/libcurl/c/CURLOPT_WRITEFUNCTION.html .. _CURLOPT_READFUNCTION: http://curl.haxx.se/libcurl/c/CURLOPT_READFUNCTION.html .. _CURLOPT_PROGRESSFUNCTION: http://curl.haxx.se/libcurl/c/CURLOPT_PROGRESSFUNCTION.html +.. _CURLOPT_XFERINFOFUNCTION: http://curl.haxx.se/libcurl/c/CURLOPT_XFERINFOFUNCTION.html .. _CURLOPT_DEBUGFUNCTION: http://curl.haxx.se/libcurl/c/CURLOPT_DEBUGFUNCTION.html .. _CURLOPT_SEEKFUNCTION: http://curl.haxx.se/libcurl/c/CURLOPT_SEEKFUNCTION.html .. _CURLOPT_IOCTLFUNCTION: http://curl.haxx.se/libcurl/c/CURLOPT_IOCTLFUNCTION.html diff -Nru pycurl-7.21.5/doc/conf.py pycurl-7.43.0/doc/conf.py --- pycurl-7.21.5/doc/conf.py 2016-01-05 07:03:44.000000000 +0000 +++ pycurl-7.43.0/doc/conf.py 2016-02-04 04:09:31.000000000 +0000 @@ -54,9 +54,9 @@ # built documents. # # The short X.Y version. -version = '7.21.5' +version = '7.43.0' # The full version, including alpha/beta/rc tags. -release = '7.21.5' +release = '7.43.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff -Nru pycurl-7.21.5/doc/curlobject.rst pycurl-7.43.0/doc/curlobject.rst --- pycurl-7.21.5/doc/curlobject.rst 2015-12-13 01:20:37.000000000 +0000 +++ pycurl-7.43.0/doc/curlobject.rst 2016-02-01 05:14:18.000000000 +0000 @@ -24,3 +24,5 @@ .. automethod:: pycurl.Curl.pause .. automethod:: pycurl.Curl.errstr + + .. automethod:: pycurl.Curl.setopt_string diff -Nru pycurl-7.21.5/doc/docstrings/curl_setopt.rst pycurl-7.43.0/doc/docstrings/curl_setopt.rst --- pycurl-7.21.5/doc/docstrings/curl_setopt.rst 2015-12-13 00:51:05.000000000 +0000 +++ pycurl-7.43.0/doc/docstrings/curl_setopt.rst 2016-02-01 05:14:18.000000000 +0000 @@ -42,7 +42,8 @@ # Python 3.x only: c.setopt(pycurl.URL, b"http://www.python.org/") -- ``HTTP200ALIASES``, ``HTTPHEADER``, ``POSTQUOTE``, ``PREQUOTE`` and +- ``HTTP200ALIASES``, ``HTTPHEADER``, ``POSTQUOTE``, ``PREQUOTE``, + ``PROXYHEADER`` and ``QUOTE`` accept a list or tuple of strings. The same rules apply to these strings as do to string option values. Example:: @@ -87,7 +88,7 @@ c.setopt(42, 1) -*setopt* can reset an option to its default value, performing the job of +*setopt* can reset some options to their default value, performing the job of :py:meth:`pycurl.Curl.unsetopt`, if ``None`` is passed for the option value. The following two calls are equivalent:: diff -Nru pycurl-7.21.5/doc/docstrings/curl_setopt_string.rst pycurl-7.43.0/doc/docstrings/curl_setopt_string.rst --- pycurl-7.21.5/doc/docstrings/curl_setopt_string.rst 1970-01-01 00:00:00.000000000 +0000 +++ pycurl-7.43.0/doc/docstrings/curl_setopt_string.rst 2016-02-01 05:14:18.000000000 +0000 @@ -0,0 +1,31 @@ +setopt_string(option, value) -> None + +Set curl session option to a string value. + +This method allows setting string options that are not officially supported +by PycURL, for example because they did not exist when the version of PycURL +being used was released. +:py:meth:`pycurl.Curl.setopt` should be used for setting options that +PycURL knows about. + +**Warning:** No checking is performed that *option* does, in fact, +expect a string value. Using this method incorrectly can crash the program +and may lead to a security vulnerability. +Furthermore, it is on the application to ensure that the *value* object +does not get garbage collected while libcurl is using it. +libcurl copies most string options but not all; one option whose value +is not copied by libcurl is `CURLOPT_POSTFIELDS`_. + +*option* would generally need to be given as an integer literal rather than +a symbolic constant. + +*value* can be a binary string or a Unicode string using ASCII code points, +same as with string options given to PycURL elsewhere. + +Example setting URL via ``setopt_string``:: + + import pycurl + c = pycurl.Curl() + c.setopt_string(10002, "http://www.python.org/") + +.. _CURLOPT_POSTFIELDS: http://curl.haxx.se/libcurl/c/CURLOPT_POSTFIELDS.html diff -Nru pycurl-7.21.5/doc/docstrings/curl_unsetopt.rst pycurl-7.43.0/doc/docstrings/curl_unsetopt.rst --- pycurl-7.21.5/doc/docstrings/curl_unsetopt.rst 2015-12-13 00:51:14.000000000 +0000 +++ pycurl-7.43.0/doc/docstrings/curl_unsetopt.rst 2016-01-24 01:01:42.000000000 +0000 @@ -4,7 +4,7 @@ Only some curl options may be reset via this method. -libcurl does not provide a way to reset a single option to its default value; +libcurl does not provide a general way to reset a single option to its default value; :py:meth:`pycurl.Curl.reset` resets all options to their default values, otherwise :py:meth:`pycurl.Curl.setopt` must be called with whatever value is the default. For convenience, PycURL provides this unsetopt method diff -Nru pycurl-7.21.5/doc/index.rst pycurl-7.43.0/doc/index.rst --- pycurl-7.21.5/doc/index.rst 2015-11-12 01:53:19.000000000 +0000 +++ pycurl-7.43.0/doc/index.rst 2016-01-24 01:01:42.000000000 +0000 @@ -1,29 +1,116 @@ -``PycURL`` -- A Python Interface To The cURL library -==================================================== +PycURL -- A Python Interface To The cURL library +================================================ -The pycurl package is a Python interface to `libcurl`_. -pycurl has been successfully built and -tested with Python versions 2.6, 2.7 and 3.1 to 3.5. - -libcurl is a client-side URL transfer library supporting FTP, FTPS, HTTP, -HTTPS, GOPHER, TELNET, DICT, FILE and LDAP. libcurl also supports HTTPS -certificates, HTTP POST, HTTP PUT, FTP uploads, proxies, cookies, basic -authentication, file transfer resume of FTP sessions, HTTP proxy tunneling -and more. - -All the functionality provided by libcurl can used through the pycurl -interface. The following subsections describe how to use the pycurl -interface, and assume familiarity with how libcurl works. For information on -how libcurl works, please consult the `curl library C API`_. +PycURL is a Python interface to `libcurl`_, the multiprotocol file +transfer library. Similarly to the urllib_ Python module, +PycURL can be used to fetch objects identified by a URL from a Python program. +Beyond simple fetches however PycURL exposes most of the functionality of +libcurl, including: -.. _libcurl: http://curl.haxx.se/libcurl/ -.. _curl library C API: http://curl.haxx.se/libcurl/c/ +- Speed - libcurl is very fast and PycURL, being a thin wrapper above + libcurl, is very fast as well. PycURL `was benchmarked`_ to be several + times faster than requests_. +- Features including multiple protocol support, SSL, authentication and + proxy options. PycURL supports most of libcurl's callbacks. +- Multi_ and share_ interfaces. +- Sockets used for network operations, permitting integration of PycURL + into the application's I/O loop (e.g., using Tornado_). + +.. _was benchmarked: http://stackoverflow.com/questions/15461995/python-requests-vs-pycurl-performance +.. _requests: http://python-requests.org/ +.. _Multi: http://curl.haxx.se/libcurl/c/libcurl-multi.html +.. _share: http://curl.haxx.se/libcurl/c/libcurl-share.html +.. _Tornado: http://www.tornadoweb.org/ + + +About libcurl +------------- + +- libcurl is a free and easy-to-use client-side URL transfer library, supporting + DICT, FILE, FTP, FTPS, Gopher, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, POP3, + POP3S, RTMP, RTSP, SCP, SFTP, SMTP, SMTPS, Telnet and TFTP. + 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 + + +Requirements +------------ + +- Python 2.6, 2.7 or 3.1 through 3.5. +- libcurl 7.19.0 or better. + + +Installation +------------ + +On Unix, PycURL is easiest to install using your operating system's package +manager. This will also install libcurl and other dependencies as needed. + +Installation via easy_install and pip is also supported:: + + easy_install pycurl + pip install pycurl -Contents: +If this does not work, please see :ref:`install`. + +On Windows, use pip to install a binary wheel for Python 2.6, 2.7 or +3.2 through 3.5:: + + pip install pycurl + +If not using pip, binary distributions in other formats are available +`on Bintray`_. + +.. _on Bintray: https://dl.bintray.com/pycurl/pycurl/ + + +Support +------- + +For support questions, please use `curl-and-python mailing list`_. +`Mailing list archives`_ are available for your perusal as well. + +Although not an official support venue, `Stack Overflow`_ has been +popular with PycURL users as well. + +Bugs can be reported `via GitHub`_. Please only use GitHub issues when you are +certain you have found a bug in PycURL. If you do not have a patch to fix +the bug, or at least a specific code fragment in PycURL that you believe is +the cause, you should instead post your inquiry to the mailing list. + +.. _curl-and-python mailing list: http://cool.haxx.se/mailman/listinfo/curl-and-python +.. _Stack Overflow: http://stackoverflow.com/questions/tagged/pycurl +.. _Mailing list archives: http://curl.haxx.se/mail/list.cgi?list=curl-and-python +.. _via GitHub: https://github.com/pycurl/pycurl/issues + + +Documentation Contents +---------------------- .. toctree:: :maxdepth: 2 - + release-notes install quickstart @@ -35,6 +122,7 @@ curl unicode files + unimplemented Indices and tables @@ -43,3 +131,6 @@ * :ref:`genindex` * :ref:`modindex` * :ref:`search` + +.. _libcurl: http://curl.haxx.se/libcurl/ +.. _urllib: http://docs.python.org/library/urllib.html diff -Nru pycurl-7.21.5/doc/quickstart.rst pycurl-7.43.0/doc/quickstart.rst --- pycurl-7.21.5/doc/quickstart.rst 2015-11-08 15:09:43.000000000 +0000 +++ pycurl-7.43.0/doc/quickstart.rst 2016-02-01 05:14:18.000000000 +0000 @@ -19,7 +19,7 @@ buffer = StringIO() c = pycurl.Curl() - c.setopt(c.URL, 'http://pycurl.sourceforge.net/') + c.setopt(c.URL, 'http://pycurl.io/') c.setopt(c.WRITEDATA, buffer) c.perform() c.close() @@ -51,7 +51,7 @@ buffer = BytesIO() c = pycurl.Curl() - c.setopt(c.URL, 'http://pycurl.sourceforge.net/') + c.setopt(c.URL, 'http://pycurl.io/') c.setopt(c.WRITEDATA, buffer) c.perform() c.close() @@ -73,6 +73,22 @@ the response body as in Python 3 version. The code for the combined example can be found in ``examples/quickstart/get.py``. + +Troubleshooting +--------------- + +When things don't work as expected, use libcurl's ``VERBOSE`` option to +receive lots of debugging output pertaining to the request:: + + c.setopt(c.VERBOSE, True) + +It is often helpful to compare verbose output from the program using PycURL +with that of ``curl`` command line tool when the latter is invoked with +``-v`` option:: + + curl -v http://pycurl.io/ + + Examining Response Headers -------------------------- @@ -93,32 +109,32 @@ # On Python 2, decoding step can be skipped. # On Python 3, decoding step is required. header_line = header_line.decode('iso-8859-1') - + # Header lines include the first status line (HTTP/1.x ...). # We are going to ignore all lines that don't have a colon in them. # This will botch headers that are split on multiple lines... if ':' not in header_line: return - + # Break the header line into header name and value. name, value = header_line.split(':', 1) - + # Remove whitespace that may be present. # Header lines include the trailing newline, and there may be whitespace # around the colon. name = name.strip() value = value.strip() - + # Header names are case insensitive. # Lowercase name here. name = name.lower() - + # Now we can actually record the header name and value. headers[name] = value buffer = BytesIO() c = pycurl.Curl() - c.setopt(c.URL, 'http://pycurl.sourceforge.net') + c.setopt(c.URL, 'http://pycurl.io') c.setopt(c.WRITEFUNCTION, buffer.write) # Set our header function. c.setopt(c.HEADERFUNCTION, header_function) @@ -151,6 +167,7 @@ as libcurl refrains from allocating memory for response data, it is on our application to perform this grunt work. + Writing To A File ----------------- @@ -163,7 +180,7 @@ # can write response body to it without decoding. with open('out.html', 'wb') as f: c = pycurl.Curl() - c.setopt(c.URL, 'http://pycurl.sourceforge.net/') + c.setopt(c.URL, 'http://pycurl.io/') c.setopt(c.WRITEDATA, f) c.perform() c.close() @@ -173,6 +190,7 @@ The important part is opening the file in binary mode - then response body can be written bytewise without decoding or encoding steps. + Following Redirects ------------------- @@ -194,6 +212,7 @@ As we did not set a write callback, the default libcurl and PycURL behavior to write response body to standard output takes effect. + Setting Options --------------- @@ -205,6 +224,7 @@ .. _curl_easy_setopt: http://curl.haxx.se/libcurl/c/curl_easy_setopt.html + Examining Response ------------------ @@ -219,7 +239,7 @@ buffer = BytesIO() c = pycurl.Curl() - c.setopt(c.URL, 'http://pycurl.sourceforge.net/') + c.setopt(c.URL, 'http://pycurl.io/') c.setopt(c.WRITEDATA, buffer) c.perform() @@ -243,6 +263,7 @@ .. _curl_easy_getinfo: http://curl.haxx.se/libcurl/c/curl_easy_getinfo.html + Sending Form Data ----------------- @@ -258,7 +279,7 @@ from urllib import urlencode c = pycurl.Curl() - c.setopt(c.URL, 'http://pycurl.sourceforge.net/tests/testpostvars.php') + c.setopt(c.URL, 'http://pycurl.io/tests/testpostvars.php') post_data = {'field': 'value'} # Form data must be provided already urlencoded. @@ -278,6 +299,7 @@ c.setopt(c.CUSTOMREQUEST, 'PATCH') + File Upload ----------- @@ -287,7 +309,7 @@ import pycurl c = pycurl.Curl() - c.setopt(c.URL, 'http://pycurl.sourceforge.net/tests/testfileupload.php') + c.setopt(c.URL, 'http://pycurl.io/tests/testfileupload.php') c.setopt(c.HTTPPOST, [ ('fileupload', ( @@ -308,7 +330,7 @@ import pycurl c = pycurl.Curl() - c.setopt(c.URL, 'http://pycurl.sourceforge.net/tests/testfileupload.php') + c.setopt(c.URL, 'http://pycurl.io/tests/testfileupload.php') c.setopt(c.HTTPPOST, [ ('fileupload', ( @@ -331,7 +353,7 @@ import pycurl c = pycurl.Curl() - c.setopt(c.URL, 'http://pycurl.sourceforge.net/tests/testfileupload.php') + c.setopt(c.URL, 'http://pycurl.io/tests/testfileupload.php') c.setopt(c.HTTPPOST, [ ('fileupload', ( diff -Nru pycurl-7.21.5/doc/release-process.rst pycurl-7.43.0/doc/release-process.rst --- pycurl-7.21.5/doc/release-process.rst 2016-01-05 07:03:44.000000000 +0000 +++ pycurl-7.43.0/doc/release-process.rst 2016-01-24 01:01:42.000000000 +0000 @@ -20,14 +20,13 @@ 9. ``python setup.py sdist``. 10. Manually test install the built package. 11. Build windows packages using winbuild.py. -12. Add windows packages to downloads repo on github. +12. Add sdist and windows packages to downloads repo on github. 13. Tag the new version. 14. Register new version with pypi - ``python setup.py register``. -15. Upload source distribution to pypi - ``python setup.py sdist upload``. - This recreates the source distribution. -16. Add the source distribution to downloads repo on github. -17. Rsync downloads repo to sourceforge. -18. Rsync build/www/htdocs to sourceforge. +15. Upload source distribution to pypi using twine. +16. Upload windows wheels to pypi using twine. +17. Upload windows exe installers to pypi using twine. +18. Upload release files to bintray. 19. Push tag to github pycurl repo. 20. Announce release on mailing list. 21. Link to announcement from website. diff -Nru pycurl-7.21.5/doc/unicode.rst pycurl-7.43.0/doc/unicode.rst --- pycurl-7.21.5/doc/unicode.rst 2015-11-08 15:09:43.000000000 +0000 +++ pycurl-7.43.0/doc/unicode.rst 2016-02-01 05:14:18.000000000 +0000 @@ -99,7 +99,7 @@ import pycurl c = pycurl.Curl() - c.setopt(c.URL,'http://pycurl.sourceforge.net') + c.setopt(c.URL,'http://pycurl.io') # File opened in binary mode. with open('/dev/null','wb') as f: c.setopt(c.WRITEDATA, f) @@ -131,7 +131,7 @@ import pycurl from StringIO import StringIO c = pycurl.Curl() - c.setopt(c.URL,'http://pycurl.sourceforge.net') + c.setopt(c.URL,'http://pycurl.io') buffer = StringIO() c.setopt(c.WRITEDATA, buffer) # Same result if using WRITEFUNCTION instead: @@ -145,7 +145,7 @@ import pycurl from io import BytesIO c = pycurl.Curl() - c.setopt(c.URL,'http://pycurl.sourceforge.net') + c.setopt(c.URL,'http://pycurl.io') buffer = BytesIO() c.setopt(c.WRITEDATA, buffer) # Same result if using WRITEFUNCTION instead: @@ -158,7 +158,7 @@ import pycurl from io import StringIO c = pycurl.Curl() - c.setopt(c.URL,'http://pycurl.sourceforge.net') + c.setopt(c.URL,'http://pycurl.io') buffer = StringIO() c.setopt(c.WRITEDATA, buffer) c.perform() @@ -180,7 +180,7 @@ # Python 2 from StringIO import StringIO as BytesIO c = pycurl.Curl() - c.setopt(c.URL,'http://pycurl.sourceforge.net') + c.setopt(c.URL,'http://pycurl.io') buffer = BytesIO() c.setopt(c.WRITEDATA, buffer) c.perform() diff -Nru pycurl-7.21.5/doc/unimplemented.rst pycurl-7.43.0/doc/unimplemented.rst --- pycurl-7.21.5/doc/unimplemented.rst 1970-01-01 00:00:00.000000000 +0000 +++ pycurl-7.43.0/doc/unimplemented.rst 2016-01-24 01:01:42.000000000 +0000 @@ -0,0 +1,65 @@ +Unimplemented Options And Constants +=================================== + +PycURL intentionally does not expose some of the libcurl options and constants. +This document explains libcurl symbols that were omitted from PycURL. + + +``*DATA`` options +----------------- + +In libcurl, the ``*aDATA`` options set *client data* for various callbacks. +Each callback has a corresponding ``*DATA`` option. + +In Python - a language with closures - such options are unnecessary. +For example, the following code invokes an instance's ``write`` method +which has full access to its class instance:: + + class Writer(object): + def __init__(self): + self.foo = True + + def write(chunk): + # can use self.foo + + writer = Writer() + curl = pycurl.Curl() + curl.setopt(curl.WRITEFUNCTION, writer.write) + +As of version 7.19.3, PycURL does implement three ``*DATA`` options for +convenience: +``WRITEDATA``, ``HEADERDATA`` and ``READDATA``. These are equivalent to +setting the respective callback option with either a ``write`` or ``read`` +method, as appropriate:: + + # equivalent pairs: + curl.setopt(curl.WRITEDATA, writer) + curl.setopt(curl.WRITEFUNCTION, writer.write) + + curl.setopt(curl.HEADERDATA, writer) + curl.setopt(curl.HEADERFUNCTION, writer.write) + + curl.setopt(curl.READDATA, reader) + curl.setopt(curl.READFUNCTION, reader.read) + + +``CURLINFO_TLS_SESSION`` +------------------------ + +It is unclear how the SSL context should be exposed to Python code. +This option can be implemented if it finds a use case. + + + +Undocumented symbols +-------------------- + +Some symbols are present in libcurl's `symbols in versions`_ document but +are not documented by libcurl. These symbols are not impemented by PycURL. + +As of this writing, the following symbols are thusly omitted: + +- ``CURLPAUSE_RECV_CONT`` +- ``CURLPAUSE_SEND_CONT`` + +.. _symbols in versions: http://curl.haxx.se/libcurl/c/symbols-in-versions.html diff -Nru pycurl-7.21.5/examples/quickstart/get.py pycurl-7.43.0/examples/quickstart/get.py --- pycurl-7.21.5/examples/quickstart/get.py 2015-11-08 15:09:43.000000000 +0000 +++ pycurl-7.43.0/examples/quickstart/get.py 2016-02-01 05:14:18.000000000 +0000 @@ -10,7 +10,7 @@ buffer = BytesIO() c = pycurl.Curl() -c.setopt(c.URL, 'http://pycurl.sourceforge.net/') +c.setopt(c.URL, 'http://pycurl.io/') c.setopt(c.WRITEDATA, buffer) # For older PycURL versions: #c.setopt(c.WRITEFUNCTION, buffer.write) diff -Nru pycurl-7.21.5/examples/quickstart/get_python2.py pycurl-7.43.0/examples/quickstart/get_python2.py --- pycurl-7.21.5/examples/quickstart/get_python2.py 2015-11-08 15:09:43.000000000 +0000 +++ pycurl-7.43.0/examples/quickstart/get_python2.py 2016-02-01 05:14:18.000000000 +0000 @@ -7,7 +7,7 @@ buffer = StringIO() c = pycurl.Curl() -c.setopt(c.URL, 'http://pycurl.sourceforge.net/') +c.setopt(c.URL, 'http://pycurl.io/') c.setopt(c.WRITEDATA, buffer) # For older PycURL versions: #c.setopt(c.WRITEFUNCTION, buffer.write) diff -Nru pycurl-7.21.5/examples/quickstart/get_python3.py pycurl-7.43.0/examples/quickstart/get_python3.py --- pycurl-7.21.5/examples/quickstart/get_python3.py 2015-11-08 15:09:43.000000000 +0000 +++ pycurl-7.43.0/examples/quickstart/get_python3.py 2016-02-01 05:14:18.000000000 +0000 @@ -7,7 +7,7 @@ buffer = BytesIO() c = pycurl.Curl() -c.setopt(c.URL, 'http://pycurl.sourceforge.net/') +c.setopt(c.URL, 'http://pycurl.io/') c.setopt(c.WRITEDATA, buffer) c.perform() c.close() diff -Nru pycurl-7.21.5/examples/quickstart/response_headers.py pycurl-7.43.0/examples/quickstart/response_headers.py --- pycurl-7.21.5/examples/quickstart/response_headers.py 2015-11-08 15:09:43.000000000 +0000 +++ pycurl-7.43.0/examples/quickstart/response_headers.py 2016-02-01 05:14:18.000000000 +0000 @@ -15,32 +15,32 @@ # On Python 2, decoding step can be skipped. # On Python 3, decoding step is required. header_line = header_line.decode('iso-8859-1') - + # Header lines include the first status line (HTTP/1.x ...). # We are going to ignore all lines that don't have a colon in them. # This will botch headers that are split on multiple lines... if ':' not in header_line: return - + # Break the header line into header name and value. name, value = header_line.split(':', 1) - + # Remove whitespace that may be present. # Header lines include the trailing newline, and there may be whitespace # around the colon. name = name.strip() value = value.strip() - + # Header names are case insensitive. # Lowercase name here. name = name.lower() - + # Now we can actually record the header name and value. headers[name] = value buffer = BytesIO() c = pycurl.Curl() -c.setopt(c.URL, 'http://pycurl.sourceforge.net') +c.setopt(c.URL, 'http://pycurl.io') c.setopt(c.WRITEFUNCTION, buffer.write) # Set our header function. c.setopt(c.HEADERFUNCTION, header_function) diff -Nru pycurl-7.21.5/examples/quickstart/response_info.py pycurl-7.43.0/examples/quickstart/response_info.py --- pycurl-7.21.5/examples/quickstart/response_info.py 2015-11-08 15:09:43.000000000 +0000 +++ pycurl-7.43.0/examples/quickstart/response_info.py 2016-02-01 05:14:18.000000000 +0000 @@ -10,7 +10,7 @@ buffer = BytesIO() c = pycurl.Curl() -c.setopt(c.URL, 'http://pycurl.sourceforge.net/') +c.setopt(c.URL, 'http://pycurl.io/') c.setopt(c.WRITEDATA, buffer) c.perform() diff -Nru pycurl-7.21.5/examples/tests/test_build_config.py pycurl-7.43.0/examples/tests/test_build_config.py --- pycurl-7.21.5/examples/tests/test_build_config.py 2015-11-08 15:09:43.000000000 +0000 +++ pycurl-7.43.0/examples/tests/test_build_config.py 2016-02-01 05:14:18.000000000 +0000 @@ -13,7 +13,7 @@ from StringIO import StringIO as BytesIO c = pycurl.Curl() -c.setopt(c.URL, 'http://pycurl.sourceforge.net') +c.setopt(c.URL, 'http://pycurl.io') #c.setopt(c.ENCODING, 'deflate') c.setopt(c.HTTPHEADER, ['Accept-Encoding: deflate']) body = BytesIO() @@ -33,7 +33,7 @@ print('Server served deflated body') c.reset() -c.setopt(c.URL, 'http://pycurl.sourceforge.net') +c.setopt(c.URL, 'http://pycurl.io') c.setopt(c.ENCODING, 'deflate') body = BytesIO() c.setopt(c.WRITEFUNCTION, body.write) diff -Nru pycurl-7.21.5/INSTALL.rst pycurl-7.43.0/INSTALL.rst --- pycurl-7.21.5/INSTALL.rst 2016-01-05 07:03:44.000000000 +0000 +++ pycurl-7.43.0/INSTALL.rst 2016-01-24 01:01:42.000000000 +0000 @@ -1,3 +1,5 @@ +.. _install: + PycURL Installation =================== @@ -115,35 +117,53 @@ Windows ------- -Binary Packages -^^^^^^^^^^^^^^^ +Official Packages +^^^^^^^^^^^^^^^^^ + +As of version 7.43.0, PycURL provides binary wheels for Windows. If you are +using an official distribution of Python (i.e., one downloaded from +https://www.python.org/), and you are using pip, you should be able to +install PycURL by running: + + pip install pycurl + +If you are not using pip, EXE and MSI installers are available in the +`download area`_. + +Both 32-bit and 64-bit builds of PycURL are available for Windows. + -Binary packages are available in the `download area`_ -for some Windows and Python version combinations. -Currently, 32-bit packages are available for Python 2.6, 2.7, 3.2 and 3.3. -64-bit packages are not presently available. - -In order to use the official binary packages, your installation of Python must -have been compiled against the same MS Visual C++ runtime that the packages -have been compiled against. Importantly, which version of MSVC is used -has changed in minor releases of Python, for example between 2.7.3 and 2.7.6. -As such, you may need to upgrade or downgrade your version of Python to use -official PycURL packages. +Using PycURL With Custom Python Builds +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +As of version 7.21.5, PycURL is linked statically against all of its +dependencies except MSVCRT. This means that as long as your custom Python +build uses the same version of MSVC as the corresponding official Python build +as well as the same MSVCRT linking setting (/MD et. al.) you should be +able to use an official PycURL package. + +If your Python build uses different MSVCRT settings or a different MSVC +version from the official Python builds, you will need to compile PycURL +from source. Currently official PycURL packages are built against the following Python versions: - 2.6.6 -- 2.7.6 +- 2.7.10 - 3.2.5 - 3.3.5 +- 3.4.3 +- 3.5.0 -If CRTs used by PycURL and Python do not match, you will receive a message -like following when trying to import pycurl module:: +If the C runtime library (MSVCRT.DLL) versions used by PycURL and Python +do not match, you will receive a message +like the following one when trying to import ``pycurl`` module:: ImportError: DLL load failed: The specified procedure could not be found. -To troubleshoot this situation use the `application profiling feature`_ of +To identify which MSVCRT version your Python uses use the +`application profiling feature`_ of `Dependency Walker`_ and look for `msvcrt.dll variants`_ being loaded. You may find `the entire thread starting here`_ helpful. @@ -153,11 +173,25 @@ .. _the entire thread starting here: http://curl.haxx.se/mail/curlpython-2014-05/0000.html -Installing From Source -^^^^^^^^^^^^^^^^^^^^^^ +Building From Source +^^^^^^^^^^^^^^^^^^^^ -First, you will need to obtain dependencies. These can be precompiled binaries -or source packages that you are going to compile yourself. +Building PycURL from source is not for the faint of heart due to the multitude +of possible dependencies. Additionally different dependencies have different +settings for MSVCRT usage, and an application must have all of its parts +agreeing on a single setting. If you decide to build PycURL from source +you should familiarize yourself with the ``winbuild.py`` +script - it is used to build the official binaries and tweaking it for +your environment is likely to be less work than starting from scratch. + +If you are compiling PycURL from source it is recommended to compile all of its +dependencies from source as well. Using precompiled libraries may lead to +multiple MSVCRT versions mixed in the resulting PycURL binary, which will +not be good. + +If PycURL is to be linked statically against its dependencies, OpenSSL must +be patched to link to the DLL version of MSVCRT. There is a patch for this in +``winbuild`` directory of PycURL source. For a minimum build you will just need libcurl source. Follow its Windows build instructions to build either a static or a DLL version of the library, @@ -165,7 +199,7 @@ python setup.py --curl-dir=c:\dev\curl-7.33.0\builds\libcurl-vc-x86-release-dll-ipv6-sspi-spnego-winssl --use-libcurl-dll -Note that ``--curl-dir`` does not point to libcurl source but rather to headers +Note that ``--curl-dir`` must point not to libcurl source but rather to headers and compiled libraries. If libcurl and Python are not linked against the same exact C runtime @@ -184,7 +218,7 @@ OpenSSL. - ``--with-ssl``: legacy alias for ``--with-openssl``. - ``--avoid-stdio``: on Windows, a process and each library it is using - may be linked to its own version of the C runtime (msvcrt). + may be linked to its own version of the C runtime (MSVCRT). FILE pointers from one C runtime may not be passed to another C runtime. This option prevents direct passing of FILE pointers from Python to libcurl, thus permitting Python and libcurl to be linked against different C runtimes. @@ -216,19 +250,26 @@ Prerequisites: -- msysgit_. +- `Git for Windows`_. - Appropriate `Python versions`_ installed. - MS Visual C++ 9/2008 for Python <= 3.2, MS Visual C++ 10/2010 for - Python >= 3.3. Express versions of Visual Studio work fine for this. + Python 3.3 or 3.4, MS Visual C++ 14/2015 for Python 3.5. + Express versions of Visual Studio work fine for this, + although getting 64 bit compilers to wok in some Express versions involves + jumping through several hoops. +- NASM if building libcurl against OpenSSL. +- ActivePerl if building libcurl against OpenSSL. The perl shipping with + Git for Windows handles forward and backslashes in paths in a way that is + incompatible with OpenSSL's build scripts. -.. _msysgit: http://msysgit.github.io/ +.. _Git for Windows: https://git-for-windows.github.io/ .. _Python versions: http://python.org/download/ ``winbuild.py`` assumes all programs are installed in their default locations, if this is not the case edit it as needed. ``winbuild.py`` itself can be run -with any Python it supports - 2.6, 2.7, 3.2, 3.3 or 3.4. +with any Python it supports - 2.6, 2.7 or 3.2 through 3.5. -.. _`download area`: http://pycurl.sourceforge.net/download/ +.. _`download area`: https://dl.bintray.com/pycurl/pycurl/ Git Checkout @@ -272,3 +313,19 @@ .. _reported: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=515200 .. _problems: https://bugs.launchpad.net/ubuntu/+source/pycurl/+bug/1111673 + + +SSL Certificate Bundle +---------------------- + +libcurl, and PycURL, by default verify validity of HTTPS servers' SSL +certificates. Doing so requires a CA certificate bundle, which libcurl +and most SSL libraries do not provide. + +Here_ is a good resource on how to build your own certificate bundle. +certifie.com also has a `prebuilt certificate bundle`_. +To use the certificate bundle, use ``CAINFO`` or ``CAPPATH`` PycURL +options. + +.. _Here: http://certifie.com/ca-bundle/ +.. _prebuilt certificate bundle: http://certifie.com/ca-bundle/ca-bundle.crt.txt diff -Nru pycurl-7.21.5/Makefile pycurl-7.43.0/Makefile --- pycurl-7.21.5/Makefile 2015-12-12 13:52:09.000000000 +0000 +++ pycurl-7.43.0/Makefile 2016-02-01 05:14:18.000000000 +0000 @@ -143,6 +143,7 @@ PYTHONSUFFIX=$$(python -V 2>&1 |awk '{print $$2}' |awk -F. '{print $$1 "." $$2}') && \ PYTHONPATH=$$(ls -d build/lib.*$$PYTHONSUFFIX):$$PYTHONPATH \ sphinx-build doc build/doc + cp ChangeLog build/doc # Rebuild all documentation. # As sphinx extracts documentation from pycurl modules, docs targets @@ -154,6 +155,7 @@ PYTHONSUFFIX=$$(python -V 2>&1 |awk '{print $$2}' |awk -F. '{print $$1 "." $$2}') && \ PYTHONPATH=$$(ls -d build/lib.*$$PYTHONSUFFIX):$$PYTHONPATH \ sphinx-build doc build/doc + cp ChangeLog build/doc www: docs mkdir -p build diff -Nru pycurl-7.21.5/PKG-INFO pycurl-7.43.0/PKG-INFO --- pycurl-7.21.5/PKG-INFO 2016-01-05 07:05:18.000000000 +0000 +++ pycurl-7.43.0/PKG-INFO 2016-02-04 14:40:38.000000000 +0000 @@ -1,14 +1,93 @@ Metadata-Version: 1.1 Name: pycurl -Version: 7.21.5 -Summary: PycURL -- cURL library module for Python -Home-page: http://pycurl.sourceforge.net/ +Version: 7.43.0 +Summary: PycURL -- A Python Interface To The cURL library +Home-page: http://pycurl.io/ Author: Oleg Pudeyev Author-email: oleg@bsdpower.com License: LGPL/MIT -Download-URL: http://pycurl.sourceforge.net/download/ -Description: - This module provides Python bindings for the cURL library. +Description: PycURL -- A Python Interface To The cURL library + ================================================ + + PycURL is a Python interface to `libcurl`_, the multiprotocol file + transfer library. Similarly to the urllib_ Python module, + PycURL can be used to fetch objects identified by a URL from a Python program. + Beyond simple fetches however PycURL exposes most of the functionality of + libcurl, including: + + - Speed - libcurl is very fast and PycURL, being a thin wrapper above + libcurl, is very fast as well. PycURL `was benchmarked`_ to be several + times faster than requests_. + - Features including multiple protocol support, SSL, authentication and + proxy options. PycURL supports most of libcurl's callbacks. + - Multi_ and share_ interfaces. + - Sockets used for network operations, permitting integration of PycURL + into the application's I/O loop (e.g., using Tornado_). + + .. _was benchmarked: http://stackoverflow.com/questions/15461995/python-requests-vs-pycurl-performance + .. _requests: http://python-requests.org/ + .. _Multi: http://curl.haxx.se/libcurl/c/libcurl-multi.html + .. _share: http://curl.haxx.se/libcurl/c/libcurl-share.html + .. _Tornado: http://www.tornadoweb.org/ + + + Requirements + ------------ + + - Python 2.6, 2.7 or 3.1 through 3.5. + - libcurl 7.19.0 or better. + + + Installation + ------------ + + Download source and binary distributions from `PyPI`_ or `Bintray`_. + Binary wheels are now available for 32 and 64 bit Windows versions. + + Please see `the installation documentation`_ for installation instructions. + + .. _PyPI: https://pypi.python.org/pypi/pycurl + .. _Bintray: https://dl.bintray.com/pycurl/pycurl/ + .. _the installation documentation: http://pycurl.io/docs/latest/install.html + + + Documentation + ------------- + + Documentation for the most recent PycURL release is available on + `PycURL website `_. + + + Support + ------- + + For support questions please use `curl-and-python mailing list`_. + `Mailing list archives`_ are available for your perusal as well. + + Although not an official support venue, `Stack Overflow`_ has been + popular with some PycURL users. + + Bugs can be reported `via GitHub`_. Please use GitHub only for bug + reports and direct questions to our mailing list instead. + + .. _curl-and-python mailing list: http://cool.haxx.se/mailman/listinfo/curl-and-python + .. _Stack Overflow: http://stackoverflow.com/questions/tagged/pycurl + .. _Mailing list archives: http://curl.haxx.se/mail/list.cgi?list=curl-and-python + .. _via GitHub: https://github.com/pycurl/pycurl/issues + + + License + ------- + + PycURL is dual licensed under the LGPL and an MIT/X derivative license + based on the libcurl license. The complete text of the licenses is available + in COPYING-LGPL_ and COPYING-MIT_ files in the source distribution. + + .. _libcurl: http://curl.haxx.se/libcurl/ + .. _urllib: http://docs.python.org/library/urllib.html + .. _COPYING-LGPL: https://raw.githubusercontent.com/pycurl/pycurl/master/COPYING-LGPL + .. _COPYING-MIT: https://raw.githubusercontent.com/pycurl/pycurl/master/COPYING-MIT + Keywords: curl,libcurl,urllib,wget,download,file transfer,http,www Platform: All Classifier: Development Status :: 5 - Production/Stable diff -Nru pycurl-7.21.5/python/curl/__init__.py pycurl-7.43.0/python/curl/__init__.py --- pycurl-7.21.5/python/curl/__init__.py 2015-11-08 15:09:43.000000000 +0000 +++ pycurl-7.43.0/python/curl/__init__.py 2016-01-24 01:01:42.000000000 +0000 @@ -23,7 +23,8 @@ try: import signal - signal.signal(signal.SIGPIPE, signal.SIG_IGN) + from signal import SIGPIPE, SIG_IGN + signal.signal(SIGPIPE, SIG_IGN) except ImportError: pass diff -Nru pycurl-7.21.5/README.rst pycurl-7.43.0/README.rst --- pycurl-7.21.5/README.rst 2016-01-05 07:03:44.000000000 +0000 +++ pycurl-7.43.0/README.rst 2016-02-04 04:39:06.000000000 +0000 @@ -1,41 +1,31 @@ -PycURL: Python interface to libcurl -==================================== +PycURL -- A Python Interface To The cURL library +================================================ .. image:: https://api.travis-ci.org/pycurl/pycurl.png :target: https://travis-ci.org/pycurl/pycurl -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 + +PycURL is a Python interface to `libcurl`_, the multiprotocol file +transfer library. Similarly to the urllib_ Python module, +PycURL can be used to fetch objects identified by a URL from a Python program. +Beyond simple fetches however PycURL exposes most of the functionality of +libcurl, including: + +- Speed - libcurl is very fast and PycURL, being a thin wrapper above + libcurl, is very fast as well. PycURL `was benchmarked`_ to be several + times faster than requests_. +- Features including multiple protocol support, SSL, authentication and + proxy options. PycURL supports most of libcurl's callbacks. +- Multi_ and share_ interfaces. +- Sockets used for network operations, permitting integration of PycURL + into the application's I/O loop (e.g., using Tornado_). + +.. _was benchmarked: http://stackoverflow.com/questions/15461995/python-requests-vs-pycurl-performance +.. _requests: http://python-requests.org/ +.. _Multi: http://curl.haxx.se/libcurl/c/libcurl-multi.html +.. _share: http://curl.haxx.se/libcurl/c/libcurl-share.html +.. _Tornado: http://www.tornadoweb.org/ + Requirements ------------ @@ -43,18 +33,31 @@ - Python 2.6, 2.7 or 3.1 through 3.5. - libcurl 7.19.0 or better. + Installation ------------ -Please see INSTALL.rst for installation instructions. If installing from -a Git checkout, please follow instruction in the "Git Checkout" section -in INSTALL.rst. +Download source and binary distributions from `PyPI`_ or `Bintray`_. +Binary wheels are now available for 32 and 64 bit Windows versions. + +Please see `INSTALL.rst`_ for installation instructions. If installing from +a Git checkout, please follow instruction in the `Git Checkout`_ section +of INSTALL.rst. + +.. _PyPI: https://pypi.python.org/pypi/pycurl +.. _Bintray: https://dl.bintray.com/pycurl/pycurl/ +.. _INSTALL.rst: http://pycurl.io/docs/latest/install.html +.. _Git Checkout: http://pycurl.io/docs/latest/install.html#git-checkout + Documentation ------------- Documentation for the most recent PycURL release is available on -`PycURL website `_. +`PycURL website `_. + +Documentation for the development version of PycURL +is available `here `. To build documentation from source, run ``make docs``. Building documentation requires `Sphinx `_ to @@ -62,25 +65,25 @@ extracted from it. Built documentation is stored in ``build/doc`` subdirectory. + Support ------- -For support questions, please use `curl-and-python mailing list`_. +For support questions please use `curl-and-python mailing list`_. `Mailing list archives`_ are available for your perusal as well. -Although not an official support venue, `Stack Overflow`_ has been quite -popular with PycURL users as well. +Although not an official support venue, `Stack Overflow`_ has been +popular with some PycURL users. -Bugs can be reported `via GitHub`_. Please only use GitHub issues when you are -certain you have found a bug in PycURL. If you do not have a patch to fix -the bug, or at least a specific code fragment in PycURL that you believe is -the cause, you should instead post your inquiry to the mailing list. +Bugs can be reported `via GitHub`_. Please use GitHub only for bug +reports and direct questions to our mailing list instead. .. _curl-and-python mailing list: http://cool.haxx.se/mailman/listinfo/curl-and-python .. _Stack Overflow: http://stackoverflow.com/questions/tagged/pycurl .. _Mailing list archives: http://curl.haxx.se/mail/list.cgi?list=curl-and-python .. _via GitHub: https://github.com/pycurl/pycurl/issues + Automated Tests --------------- @@ -105,6 +108,7 @@ .. _bottle: http://bottlepy.org/ .. _vsftpd: http://vsftpd.beasts.org/ + Test Matrix ----------- @@ -137,6 +141,7 @@ To see what the combinations are, look in `tests/matrix.py `_. + Contribute ---------- @@ -161,6 +166,7 @@ Please contribute binary distributions for your system to the `downloads repository`_. + License ------- @@ -178,7 +184,7 @@ included in the file COPYING-MIT. You can redistribute and/or modify PycURL according to the terms of either license. -.. _PycURL: http://pycurl.sourceforge.net/ +.. _PycURL: http://pycurl.io/ .. _libcurl: http://curl.haxx.se/libcurl/ .. _urllib: http://docs.python.org/library/urllib.html .. _`the repository`: https://github.com/pycurl/pycurl diff -Nru pycurl-7.21.5/RELEASE-NOTES.rst pycurl-7.43.0/RELEASE-NOTES.rst --- pycurl-7.21.5/RELEASE-NOTES.rst 2016-01-05 07:03:44.000000000 +0000 +++ pycurl-7.43.0/RELEASE-NOTES.rst 2016-02-04 04:09:31.000000000 +0000 @@ -1,6 +1,24 @@ Release Notes ============= +PycURL 7.43.0 - 2016-02-02 +-------------------------- + +Highlights of this release: + +1. Binary wheels are now built for Windows systems. + +2. setopt_string method added to Curl objects to permit setting string libcurl + options that PycURL does not know about. + +3. curl module can now be imported on Windows again. + +4. OPENSOCKETFUNCTION callback is now invoked with the address as bytes on + Python 3 as was documented. + +5. Support for many libcurl options and constants was added. + + PycURL 7.21.5 - 2016-01-05 -------------------------- diff -Nru pycurl-7.21.5/requirements-dev-3.1.txt pycurl-7.43.0/requirements-dev-3.1.txt --- pycurl-7.21.5/requirements-dev-3.1.txt 2015-11-08 15:09:43.000000000 +0000 +++ pycurl-7.43.0/requirements-dev-3.1.txt 2016-01-24 01:01:42.000000000 +0000 @@ -5,3 +5,4 @@ # flaky 2.2.0 is not installable on python 3.1.5, # install it manually pyflakes +nose-show-skipped diff -Nru pycurl-7.21.5/requirements-dev.txt pycurl-7.43.0/requirements-dev.txt --- pycurl-7.21.5/requirements-dev.txt 2015-11-08 15:09:43.000000000 +0000 +++ pycurl-7.43.0/requirements-dev.txt 2016-01-24 01:01:42.000000000 +0000 @@ -4,3 +4,4 @@ nose>=1.3.2 flaky pyflakes +nose-show-skipped diff -Nru pycurl-7.21.5/setup.py pycurl-7.43.0/setup.py --- pycurl-7.21.5/setup.py 2016-01-05 07:03:44.000000000 +0000 +++ pycurl-7.43.0/setup.py 2016-02-04 04:39:06.000000000 +0000 @@ -6,7 +6,7 @@ PACKAGE = "pycurl" PY_PACKAGE = "curl" -VERSION = "7.21.5" +VERSION = "7.43.0" import glob, os, re, sys, subprocess import distutils @@ -655,13 +655,95 @@ setup_args = dict( name=PACKAGE, version=VERSION, - description="PycURL -- cURL library module for Python", + description='PycURL -- A Python Interface To The cURL library', + long_description='''\ +PycURL -- A Python Interface To The cURL library +================================================ + +PycURL is a Python interface to `libcurl`_, the multiprotocol file +transfer library. Similarly to the urllib_ Python module, +PycURL can be used to fetch objects identified by a URL from a Python program. +Beyond simple fetches however PycURL exposes most of the functionality of +libcurl, including: + +- Speed - libcurl is very fast and PycURL, being a thin wrapper above + libcurl, is very fast as well. PycURL `was benchmarked`_ to be several + times faster than requests_. +- Features including multiple protocol support, SSL, authentication and + proxy options. PycURL supports most of libcurl's callbacks. +- Multi_ and share_ interfaces. +- Sockets used for network operations, permitting integration of PycURL + into the application's I/O loop (e.g., using Tornado_). + +.. _was benchmarked: http://stackoverflow.com/questions/15461995/python-requests-vs-pycurl-performance +.. _requests: http://python-requests.org/ +.. _Multi: http://curl.haxx.se/libcurl/c/libcurl-multi.html +.. _share: http://curl.haxx.se/libcurl/c/libcurl-share.html +.. _Tornado: http://www.tornadoweb.org/ + + +Requirements +------------ + +- Python 2.6, 2.7 or 3.1 through 3.5. +- libcurl 7.19.0 or better. + + +Installation +------------ + +Download source and binary distributions from `PyPI`_ or `Bintray`_. +Binary wheels are now available for 32 and 64 bit Windows versions. + +Please see `the installation documentation`_ for installation instructions. + +.. _PyPI: https://pypi.python.org/pypi/pycurl +.. _Bintray: https://dl.bintray.com/pycurl/pycurl/ +.. _the installation documentation: http://pycurl.io/docs/latest/install.html + + +Documentation +------------- + +Documentation for the most recent PycURL release is available on +`PycURL website `_. + + +Support +------- + +For support questions please use `curl-and-python mailing list`_. +`Mailing list archives`_ are available for your perusal as well. + +Although not an official support venue, `Stack Overflow`_ has been +popular with some PycURL users. + +Bugs can be reported `via GitHub`_. Please use GitHub only for bug +reports and direct questions to our mailing list instead. + +.. _curl-and-python mailing list: http://cool.haxx.se/mailman/listinfo/curl-and-python +.. _Stack Overflow: http://stackoverflow.com/questions/tagged/pycurl +.. _Mailing list archives: http://curl.haxx.se/mail/list.cgi?list=curl-and-python +.. _via GitHub: https://github.com/pycurl/pycurl/issues + + +License +------- + +PycURL is dual licensed under the LGPL and an MIT/X derivative license +based on the libcurl license. The complete text of the licenses is available +in COPYING-LGPL_ and COPYING-MIT_ files in the source distribution. + +.. _libcurl: http://curl.haxx.se/libcurl/ +.. _urllib: http://docs.python.org/library/urllib.html +.. _COPYING-LGPL: https://raw.githubusercontent.com/pycurl/pycurl/master/COPYING-LGPL +.. _COPYING-MIT: https://raw.githubusercontent.com/pycurl/pycurl/master/COPYING-MIT +''', author="Kjetil Jacobsen, Markus F.X.J. Oberhumer, Oleg Pudeyev", author_email="kjetilja at gmail.com, markus at oberhumer.com, oleg at bsdpower.com", maintainer="Oleg Pudeyev", maintainer_email="oleg@bsdpower.com", - url="http://pycurl.sourceforge.net/", - download_url="http://pycurl.sourceforge.net/download/", + url="http://pycurl.io/", license="LGPL/MIT", keywords=['curl', 'libcurl', 'urllib', 'wget', 'download', 'file transfer', 'http', 'www'], @@ -680,8 +762,6 @@ ], packages=[PY_PACKAGE], package_dir={ PY_PACKAGE: os.path.join('python', 'curl') }, - long_description=""" -This module provides Python bindings for the cURL library.""", ) if sys.platform == "win32": diff -Nru pycurl-7.21.5/src/docstrings.c pycurl-7.43.0/src/docstrings.c --- pycurl-7.21.5/src/docstrings.c 2015-12-13 00:51:21.000000000 +0000 +++ pycurl-7.43.0/src/docstrings.c 2016-02-01 05:58:19.000000000 +0000 @@ -144,7 +144,8 @@ # Python 3.x only:\n\ c.setopt(pycurl.URL, b\"http://www.python.org/\")\n\ \n\ -- ``HTTP200ALIASES``, ``HTTPHEADER``, ``POSTQUOTE``, ``PREQUOTE`` and\n\ +- ``HTTP200ALIASES``, ``HTTPHEADER``, ``POSTQUOTE``, ``PREQUOTE``,\n\ + ``PROXYHEADER`` and\n\ ``QUOTE`` accept a list or tuple of strings. The same rules apply to these\n\ strings as do to string option values. Example::\n\ \n\ @@ -189,7 +190,7 @@ \n\ c.setopt(42, 1)\n\ \n\ -*setopt* can reset an option to its default value, performing the job of\n\ +*setopt* can reset some options to their default value, performing the job of\n\ :py:meth:`pycurl.Curl.unsetopt`, if ``None`` is passed\n\ for the option value. The following two calls are equivalent::\n\ \n\ @@ -202,13 +203,45 @@ \n\ .. _curl_easy_setopt: http://curl.haxx.se/libcurl/c/curl_easy_setopt.html"; +PYCURL_INTERNAL const char curl_setopt_string_doc[] = "setopt_string(option, value) -> None\n\ +\n\ +Set curl session option to a string value.\n\ +\n\ +This method allows setting string options that are not officially supported\n\ +by PycURL, for example because they did not exist when the version of PycURL\n\ +being used was released.\n\ +:py:meth:`pycurl.Curl.setopt` should be used for setting options that\n\ +PycURL knows about.\n\ +\n\ +**Warning:** No checking is performed that *option* does, in fact,\n\ +expect a string value. Using this method incorrectly can crash the program\n\ +and may lead to a security vulnerability.\n\ +Furthermore, it is on the application to ensure that the *value* object\n\ +does not get garbage collected while libcurl is using it.\n\ +libcurl copies most string options but not all; one option whose value\n\ +is not copied by libcurl is `CURLOPT_POSTFIELDS`_.\n\ +\n\ +*option* would generally need to be given as an integer literal rather than\n\ +a symbolic constant.\n\ +\n\ +*value* can be a binary string or a Unicode string using ASCII code points,\n\ +same as with string options given to PycURL elsewhere.\n\ +\n\ +Example setting URL via ``setopt_string``::\n\ +\n\ + import pycurl\n\ + c = pycurl.Curl()\n\ + c.setopt_string(10002, \"http://www.python.org/\")\n\ +\n\ +.. _CURLOPT_POSTFIELDS: http://curl.haxx.se/libcurl/c/CURLOPT_POSTFIELDS.html"; + PYCURL_INTERNAL const char curl_unsetopt_doc[] = "unsetopt(option) -> None\n\ \n\ Reset curl session option to its default value.\n\ \n\ Only some curl options may be reset via this method.\n\ \n\ -libcurl does not provide a way to reset a single option to its default value;\n\ +libcurl does not provide a general way to reset a single option to its default value;\n\ :py:meth:`pycurl.Curl.reset` resets all options to their default values,\n\ otherwise :py:meth:`pycurl.Curl.setopt` must be called with whatever value\n\ is the default. For convenience, PycURL provides this unsetopt method\n\ diff -Nru pycurl-7.21.5/src/docstrings.h pycurl-7.43.0/src/docstrings.h --- pycurl-7.21.5/src/docstrings.h 2015-12-13 00:51:21.000000000 +0000 +++ pycurl-7.43.0/src/docstrings.h 2016-02-01 05:58:19.000000000 +0000 @@ -9,6 +9,7 @@ extern const char curl_perform_doc[]; extern const char curl_reset_doc[]; extern const char curl_setopt_doc[]; +extern const char curl_setopt_string_doc[]; extern const char curl_unsetopt_doc[]; extern const char multi_doc[]; extern const char multi_add_handle_doc[]; diff -Nru pycurl-7.21.5/src/easy.c pycurl-7.43.0/src/easy.c --- pycurl-7.21.5/src/easy.c 2016-01-02 23:57:41.000000000 +0000 +++ pycurl-7.43.0/src/easy.c 2016-02-04 04:09:31.000000000 +0000 @@ -44,6 +44,43 @@ } +static struct curl_slist * +pycurl_list_or_tuple_to_slist(int which, PyObject *obj, Py_ssize_t len) +{ + struct curl_slist *slist = NULL; + Py_ssize_t i; + + for (i = 0; i < len; i++) { + PyObject *listitem = PyListOrTuple_GetItem(obj, i, which); + struct curl_slist *nlist; + char *str; + PyObject *sencoded_obj; + + if (!PyText_Check(listitem)) { + curl_slist_free_all(slist); + PyErr_SetString(PyExc_TypeError, "list items must be byte strings or Unicode strings with ASCII code points only"); + return NULL; + } + /* INFO: curl_slist_append() internally does strdup() the data, so + * no embedded NUL characters allowed here. */ + str = PyText_AsString_NoNUL(listitem, &sencoded_obj); + if (str == NULL) { + curl_slist_free_all(slist); + return NULL; + } + nlist = curl_slist_append(slist, str); + PyText_EncodedDecref(sencoded_obj); + if (nlist == NULL || nlist->data == NULL) { + curl_slist_free_all(slist); + PyErr_NoMemory(); + return NULL; + } + slist = nlist; + } + return slist; +} + + #ifdef HAVE_CURLOPT_CERTINFO /* Convert a struct curl_certinfo into a Python data structure. * In case of error return NULL with an exception set. @@ -261,6 +298,9 @@ Py_CLEAR(self->h_cb); Py_CLEAR(self->r_cb); Py_CLEAR(self->pro_cb); +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 32, 0) + Py_CLEAR(self->xferinfo_cb); +#endif Py_CLEAR(self->debug_cb); Py_CLEAR(self->ioctl_cb); Py_CLEAR(self->seek_cb); @@ -349,6 +389,9 @@ #undef SFREE #define SFREE(v) if ((v) != NULL) (curl_slist_free_all(v), (v) = NULL) SFREE(self->httpheader); +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 37, 0) + SFREE(self->proxyheader); +#endif SFREE(self->http200aliases); SFREE(self->quote); SFREE(self->postquote); @@ -430,6 +473,9 @@ VISIT(self->h_cb); VISIT(self->r_cb); VISIT(self->pro_cb); +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 32, 0) + VISIT(self->xferinfo_cb); +#endif VISIT(self->debug_cb); VISIT(self->ioctl_cb); VISIT(self->seek_cb); @@ -568,7 +614,7 @@ case AF_INET: { struct sockaddr_in* sin = (struct sockaddr_in*)saddr; - char *addr_str = (char *)PyMem_Malloc(INET_ADDRSTRLEN); + char *addr_str = PyMem_New(char, INET_ADDRSTRLEN); if (addr_str == NULL) { PyErr_NoMemory(); @@ -587,7 +633,7 @@ case AF_INET6: { struct sockaddr_in6* sin6 = (struct sockaddr_in6*)saddr; - char *addr_str = (char *)PyMem_Malloc(INET6_ADDRSTRLEN); + char *addr_str = PyMem_New(char, INET6_ADDRSTRLEN); if (addr_str == NULL) { PyErr_NoMemory(); @@ -609,7 +655,11 @@ { struct sockaddr_un* sun = (struct sockaddr_un*)saddr; +#if PY_MAJOR_VERSION >= 3 + res_obj = Py_BuildValue("y", sun->sun_path); +#else res_obj = Py_BuildValue("s", sun->sun_path); +#endif } break; #endif @@ -1152,6 +1202,59 @@ } +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 32, 0) +static int +xferinfo_callback(void *stream, + curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) +{ + CurlObject *self; + PyObject *arglist; + PyObject *result = NULL; + int ret = 1; /* assume error */ + PYCURL_DECLARE_THREAD_STATE; + + /* acquire thread */ + self = (CurlObject *)stream; + if (!PYCURL_ACQUIRE_THREAD()) + return ret; + + /* check args */ + if (self->xferinfo_cb == NULL) + goto silent_error; + + /* run callback */ + arglist = Py_BuildValue("(LLLL)", + (PY_LONG_LONG) dltotal, (PY_LONG_LONG) dlnow, + (PY_LONG_LONG) ultotal, (PY_LONG_LONG) ulnow); + if (arglist == NULL) + goto verbose_error; + result = PyEval_CallObject(self->xferinfo_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)) { + ret = (int) PyInt_AsLong(result); + } + else { + ret = PyObject_IsTrue(result); /* FIXME ??? */ + } + +silent_error: + Py_XDECREF(result); + PYCURL_RELEASE_THREAD(); + return ret; +verbose_error: + PyErr_Print(); + goto silent_error; +} +#endif + + static int debug_callback(CURL *curlobj, curl_infotype type, char *buffer, size_t total_size, void *stream) @@ -1272,6 +1375,9 @@ #undef SFREE #define SFREE(v) if ((v) != NULL) (curl_slist_free_all(v), (v) = NULL) SFREE(self->httpheader); +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 37, 0) + SFREE(self->proxyheader); +#endif SFREE(self->http200aliases); SFREE(self->quote); SFREE(self->postquote); @@ -1361,7 +1467,11 @@ case CURLOPT_SERVICE_NAME: case CURLOPT_PROXY_SERVICE_NAME: #endif - SETOPT((char *) 0); + case CURLOPT_HTTPHEADER: +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 37, 0) + case CURLOPT_PROXYHEADER: +#endif + SETOPT((char *) NULL); break; #ifdef HAVE_CURLOPT_CERTINFO @@ -1426,99 +1536,76 @@ static PyObject * -do_curl_setopt(CurlObject *self, PyObject *args) +do_curl_setopt_string_impl(CurlObject *self, int option, PyObject *obj) { - int option; - PyObject *obj; - int res; + char *str = NULL; + Py_ssize_t len = -1; PyObject *encoded_obj; - int which; - - if (!PyArg_ParseTuple(args, "iO:setopt", &option, &obj)) - return NULL; - if (check_curl_state(self, 1 | 2, "setopt") != 0) - return NULL; - - /* early checks of option value */ - if (option <= 0) - goto error; - if (option >= (int)CURLOPTTYPE_OFF_T + OPTIONS_SIZE) - goto error; - if (option % 10000 >= OPTIONS_SIZE) - goto error; - - /* Handle the case of None as the call of unsetopt() */ - if (obj == Py_None) { - return util_curl_unsetopt(self, option); - } - - /* Handle the case of string arguments */ - - if (PyText_Check(obj)) { - char *str = NULL; - Py_ssize_t len = -1; + int res; - /* Check that the option specified a string as well as the input */ - switch (option) { - case CURLOPT_CAINFO: - case CURLOPT_CAPATH: - case CURLOPT_COOKIE: - case CURLOPT_COOKIEFILE: - case CURLOPT_COOKIELIST: - case CURLOPT_COOKIEJAR: - case CURLOPT_CUSTOMREQUEST: - case CURLOPT_EGDSOCKET: - case CURLOPT_ENCODING: - case CURLOPT_FTPPORT: - case CURLOPT_INTERFACE: - case CURLOPT_KEYPASSWD: - case CURLOPT_NETRC_FILE: - case CURLOPT_PROXY: - case CURLOPT_PROXYUSERPWD: + /* Check that the option specified a string as well as the input */ + switch (option) { + case CURLOPT_CAINFO: + case CURLOPT_CAPATH: + case CURLOPT_COOKIE: + case CURLOPT_COOKIEFILE: + case CURLOPT_COOKIELIST: + case CURLOPT_COOKIEJAR: + case CURLOPT_CUSTOMREQUEST: + case CURLOPT_EGDSOCKET: + /* use CURLOPT_ENCODING instead of CURLOPT_ACCEPT_ENCODING + for compatibility with older libcurls */ + case CURLOPT_ENCODING: + case CURLOPT_FTPPORT: + case CURLOPT_INTERFACE: + case CURLOPT_KEYPASSWD: + case CURLOPT_NETRC_FILE: + case CURLOPT_PROXY: + case CURLOPT_PROXYUSERPWD: #ifdef HAVE_CURLOPT_PROXYUSERNAME - case CURLOPT_PROXYUSERNAME: - case CURLOPT_PROXYPASSWORD: + case CURLOPT_PROXYUSERNAME: + case CURLOPT_PROXYPASSWORD: #endif - case CURLOPT_RANDOM_FILE: - case CURLOPT_RANGE: - case CURLOPT_REFERER: - case CURLOPT_SSLCERT: - case CURLOPT_SSLCERTTYPE: - case CURLOPT_SSLENGINE: - case CURLOPT_SSLKEY: - case CURLOPT_SSLKEYTYPE: - case CURLOPT_SSL_CIPHER_LIST: - case CURLOPT_URL: - case CURLOPT_USERAGENT: - case CURLOPT_USERPWD: + case CURLOPT_RANDOM_FILE: + case CURLOPT_RANGE: + case CURLOPT_REFERER: + case CURLOPT_SSLCERT: + case CURLOPT_SSLCERTTYPE: + case CURLOPT_SSLENGINE: + case CURLOPT_SSLKEY: + case CURLOPT_SSLKEYTYPE: + case CURLOPT_SSL_CIPHER_LIST: + case CURLOPT_URL: + case CURLOPT_USERAGENT: + case CURLOPT_USERPWD: #ifdef HAVE_CURLOPT_USERNAME - case CURLOPT_USERNAME: - case CURLOPT_PASSWORD: + case CURLOPT_USERNAME: + case CURLOPT_PASSWORD: #endif - case CURLOPT_FTP_ALTERNATIVE_TO_USER: - case CURLOPT_SSH_PUBLIC_KEYFILE: - case CURLOPT_SSH_PRIVATE_KEYFILE: - case CURLOPT_COPYPOSTFIELDS: - case CURLOPT_SSH_HOST_PUBLIC_KEY_MD5: - case CURLOPT_CRLFILE: - case CURLOPT_ISSUERCERT: + case CURLOPT_FTP_ALTERNATIVE_TO_USER: + case CURLOPT_SSH_PUBLIC_KEYFILE: + case CURLOPT_SSH_PRIVATE_KEYFILE: + case CURLOPT_COPYPOSTFIELDS: + case CURLOPT_SSH_HOST_PUBLIC_KEY_MD5: + case CURLOPT_CRLFILE: + case CURLOPT_ISSUERCERT: #ifdef HAVE_CURLOPT_DNS_SERVERS - case CURLOPT_DNS_SERVERS: + case CURLOPT_DNS_SERVERS: #endif #ifdef HAVE_CURLOPT_NOPROXY - case CURLOPT_NOPROXY: + case CURLOPT_NOPROXY: #endif #ifdef HAVE_CURL_7_19_4_OPTS - case CURLOPT_SOCKS5_GSSAPI_SERVICE: + case CURLOPT_SOCKS5_GSSAPI_SERVICE: #endif #ifdef HAVE_CURL_7_19_6_OPTS - case CURLOPT_SSH_KNOWNHOSTS: + case CURLOPT_SSH_KNOWNHOSTS: #endif #ifdef HAVE_CURL_7_20_0_OPTS - case CURLOPT_MAIL_FROM: + case CURLOPT_MAIL_FROM: #endif #ifdef HAVE_CURL_7_25_0_OPTS - case CURLOPT_MAIL_AUTH: + case CURLOPT_MAIL_AUTH: #endif #if LIBCURL_VERSION_NUM >= 0x072700 /* check for 7.39.0 or greater */ case CURLOPT_PINNEDPUBLICKEY: @@ -1533,634 +1620,775 @@ #if LIBCURL_VERSION_NUM >= 0x072800 /* check for 7.40.0 or greater */ case CURLOPT_UNIX_SOCKET_PATH: #endif +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 4) + case CURLOPT_TLSAUTH_TYPE: + case CURLOPT_TLSAUTH_USERNAME: + case CURLOPT_TLSAUTH_PASSWORD: +#endif +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 45, 0) + case CURLOPT_DEFAULT_PROTOCOL: +#endif +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) + case CURLOPT_LOGIN_OPTIONS: +#endif +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 33, 0) + case CURLOPT_XOAUTH2_BEARER: +#endif case CURLOPT_KRBLEVEL: -/* FIXME: check if more of these options allow binary data */ - str = PyText_AsString_NoNUL(obj, &encoded_obj); - if (str == NULL) - return NULL; - break; - case CURLOPT_POSTFIELDS: - if (PyText_AsStringAndSize(obj, &str, &len, &encoded_obj) != 0) - return NULL; - /* automatically set POSTFIELDSIZE */ - if (len <= INT_MAX) { - res = curl_easy_setopt(self->handle, CURLOPT_POSTFIELDSIZE, (long)len); - } else { - res = curl_easy_setopt(self->handle, CURLOPT_POSTFIELDSIZE_LARGE, (curl_off_t)len); - } - if (res != CURLE_OK) { - PyText_EncodedDecref(encoded_obj); - CURLERROR_RETVAL(); - } - break; - default: - PyErr_SetString(PyExc_TypeError, "strings are not supported for this option"); + str = PyText_AsString_NoNUL(obj, &encoded_obj); + if (str == NULL) + return NULL; + break; + case CURLOPT_POSTFIELDS: + if (PyText_AsStringAndSize(obj, &str, &len, &encoded_obj) != 0) return NULL; + /* automatically set POSTFIELDSIZE */ + if (len <= INT_MAX) { + res = curl_easy_setopt(self->handle, CURLOPT_POSTFIELDSIZE, (long)len); + } else { + res = curl_easy_setopt(self->handle, CURLOPT_POSTFIELDSIZE_LARGE, (curl_off_t)len); } - assert(str != NULL); - /* Call setopt */ - res = curl_easy_setopt(self->handle, (CURLoption)option, str); - /* Check for errors */ if (res != CURLE_OK) { PyText_EncodedDecref(encoded_obj); CURLERROR_RETVAL(); } - /* libcurl does not copy the value of CURLOPT_POSTFIELDS */ - if (option == CURLOPT_POSTFIELDS) { - PyObject *store_obj; - - /* if obj was bytes, it was not encoded, and we need to incref obj. - * if obj was unicode, it was encoded, and we need to incref - * encoded_obj - except we can simply transfer ownership. - */ - if (encoded_obj) { - store_obj = encoded_obj; - } else { - /* no encoding is performed, incref the original object. */ - store_obj = obj; - Py_INCREF(store_obj); - } - - util_curl_xdecref(self, PYCURL_MEMGROUP_POSTFIELDS, self->handle); - self->postfields_obj = store_obj; + break; + default: + PyErr_SetString(PyExc_TypeError, "strings are not supported for this option"); + return NULL; + } + assert(str != NULL); + /* Call setopt */ + res = curl_easy_setopt(self->handle, (CURLoption)option, str); + /* Check for errors */ + if (res != CURLE_OK) { + PyText_EncodedDecref(encoded_obj); + CURLERROR_RETVAL(); + } + /* libcurl does not copy the value of CURLOPT_POSTFIELDS */ + if (option == CURLOPT_POSTFIELDS) { + PyObject *store_obj; + + /* if obj was bytes, it was not encoded, and we need to incref obj. + * if obj was unicode, it was encoded, and we need to incref + * encoded_obj - except we can simply transfer ownership. + */ + if (encoded_obj) { + store_obj = encoded_obj; } else { - PyText_EncodedDecref(encoded_obj); + /* no encoding is performed, incref the original object. */ + store_obj = obj; + Py_INCREF(store_obj); } - Py_RETURN_NONE; + + util_curl_xdecref(self, PYCURL_MEMGROUP_POSTFIELDS, self->handle); + self->postfields_obj = store_obj; + } else { + PyText_EncodedDecref(encoded_obj); } + Py_RETURN_NONE; +} + #define IS_LONG_OPTION(o) (o < CURLOPTTYPE_OBJECTPOINT) #define IS_OFF_T_OPTION(o) (o >= CURLOPTTYPE_OFF_T) - /* Handle the case of integer arguments */ - if (PyInt_Check(obj)) { - long d = PyInt_AsLong(obj); - if (IS_LONG_OPTION(option)) - res = curl_easy_setopt(self->handle, (CURLoption)option, (long)d); - else if (IS_OFF_T_OPTION(option)) - res = curl_easy_setopt(self->handle, (CURLoption)option, (curl_off_t)d); - else { - PyErr_SetString(PyExc_TypeError, "integers are not supported for this option"); - return NULL; - } - if (res != CURLE_OK) { - CURLERROR_RETVAL(); - } - Py_RETURN_NONE; +static PyObject * +do_curl_setopt_int(CurlObject *self, int option, PyObject *obj) +{ + long d = PyInt_AsLong(obj); + int res; + + if (IS_LONG_OPTION(option)) + res = curl_easy_setopt(self->handle, (CURLoption)option, (long)d); + else if (IS_OFF_T_OPTION(option)) + res = curl_easy_setopt(self->handle, (CURLoption)option, (curl_off_t)d); + else { + PyErr_SetString(PyExc_TypeError, "integers are not supported for this option"); + return NULL; + } + if (res != CURLE_OK) { + CURLERROR_RETVAL(); } + Py_RETURN_NONE; +} - /* Handle the case of long arguments (used by *_LARGE options) */ - if (PyLong_Check(obj)) { - PY_LONG_LONG d = PyLong_AsLongLong(obj); - if (d == -1 && PyErr_Occurred()) - return NULL; - if (IS_LONG_OPTION(option) && (long)d == d) - res = curl_easy_setopt(self->handle, (CURLoption)option, (long)d); - else if (IS_OFF_T_OPTION(option) && (curl_off_t)d == d) - res = curl_easy_setopt(self->handle, (CURLoption)option, (curl_off_t)d); - else { - PyErr_SetString(PyExc_TypeError, "longs are not supported for this option"); - return NULL; - } - if (res != CURLE_OK) { - CURLERROR_RETVAL(); - } - Py_RETURN_NONE; +static PyObject * +do_curl_setopt_long(CurlObject *self, int option, PyObject *obj) +{ + int res; + PY_LONG_LONG d = PyLong_AsLongLong(obj); + if (d == -1 && PyErr_Occurred()) + return NULL; + + if (IS_LONG_OPTION(option) && (long)d == d) + res = curl_easy_setopt(self->handle, (CURLoption)option, (long)d); + else if (IS_OFF_T_OPTION(option) && (curl_off_t)d == d) + res = curl_easy_setopt(self->handle, (CURLoption)option, (curl_off_t)d); + else { + PyErr_SetString(PyExc_TypeError, "longs are not supported for this option"); + return NULL; + } + if (res != CURLE_OK) { + CURLERROR_RETVAL(); } + Py_RETURN_NONE; +} + #undef IS_LONG_OPTION #undef IS_OFF_T_OPTION + #if PY_MAJOR_VERSION < 3 && !defined(PYCURL_AVOID_STDIO) - /* Handle the case of file objects */ - if (PyFile_Check(obj)) { - FILE *fp; +static PyObject * +do_curl_setopt_file_passthrough(CurlObject *self, int option, PyObject *obj) +{ + FILE *fp; + int res; - /* Ensure the option specified a file as well as the input */ - switch (option) { - case CURLOPT_READDATA: - case CURLOPT_WRITEDATA: - break; - case CURLOPT_WRITEHEADER: - if (self->w_cb != NULL) { - PyErr_SetString(ErrorObject, "cannot combine WRITEHEADER with WRITEFUNCTION."); - return NULL; - } - break; - default: - PyErr_SetString(PyExc_TypeError, "files are not supported for this option"); + /* Ensure the option specified a file as well as the input */ + switch (option) { + case CURLOPT_READDATA: + case CURLOPT_WRITEDATA: + break; + case CURLOPT_WRITEHEADER: + if (self->w_cb != NULL) { + PyErr_SetString(ErrorObject, "cannot combine WRITEHEADER with WRITEFUNCTION."); return NULL; } + break; + default: + PyErr_SetString(PyExc_TypeError, "files are not supported for this option"); + return NULL; + } - fp = PyFile_AsFile(obj); - if (fp == NULL) { - PyErr_SetString(PyExc_TypeError, "second argument must be open file"); - return NULL; - } - res = curl_easy_setopt(self->handle, (CURLoption)option, fp); - if (res != CURLE_OK) { - CURLERROR_RETVAL(); - } - Py_INCREF(obj); + fp = PyFile_AsFile(obj); + if (fp == NULL) { + PyErr_SetString(PyExc_TypeError, "second argument must be open file"); + return NULL; + } + res = curl_easy_setopt(self->handle, (CURLoption)option, fp); + if (res != CURLE_OK) { + CURLERROR_RETVAL(); + } + Py_INCREF(obj); - switch (option) { - case CURLOPT_READDATA: - Py_CLEAR(self->readdata_fp); - self->readdata_fp = obj; - break; - case CURLOPT_WRITEDATA: - Py_CLEAR(self->writedata_fp); - self->writedata_fp = obj; - break; - case CURLOPT_WRITEHEADER: - Py_CLEAR(self->writeheader_fp); - self->writeheader_fp = obj; - break; - default: - assert(0); - break; - } - /* Return success */ - Py_RETURN_NONE; + switch (option) { + case CURLOPT_READDATA: + Py_CLEAR(self->readdata_fp); + self->readdata_fp = obj; + break; + case CURLOPT_WRITEDATA: + Py_CLEAR(self->writedata_fp); + self->writedata_fp = obj; + break; + case CURLOPT_WRITEHEADER: + Py_CLEAR(self->writeheader_fp); + self->writeheader_fp = obj; + break; + default: + assert(0); + break; } + /* Return success */ + Py_RETURN_NONE; +} #endif - /* Handle the case of list or tuple objects */ - which = PyListOrTuple_Check(obj); - if (which) { - struct curl_slist **old_slist = NULL; - struct curl_slist *slist = NULL; - Py_ssize_t i, len; - switch (option) { - case CURLOPT_HTTP200ALIASES: - old_slist = &self->http200aliases; - break; - case CURLOPT_HTTPHEADER: - old_slist = &self->httpheader; - break; - case CURLOPT_POSTQUOTE: - old_slist = &self->postquote; - break; - case CURLOPT_PREQUOTE: - old_slist = &self->prequote; - break; - case CURLOPT_QUOTE: - old_slist = &self->quote; - break; - case CURLOPT_TELNETOPTIONS: - old_slist = &self->telnetoptions; - break; -#ifdef HAVE_CURLOPT_RESOLVE - case CURLOPT_RESOLVE: - old_slist = &self->resolve; - break; -#endif -#ifdef HAVE_CURL_7_20_0_OPTS - case CURLOPT_MAIL_RCPT: - old_slist = &self->mail_rcpt; - break; -#endif - case CURLOPT_HTTPPOST: - break; - default: - /* None of the list options were recognized, raise exception */ - PyErr_SetString(PyExc_TypeError, "lists are not supported for this option"); - return NULL; - } +static PyObject * +do_curl_setopt_httppost(CurlObject *self, int option, int which, PyObject *obj) +{ + struct curl_httppost *post = NULL; + struct curl_httppost *last = NULL; + /* List of all references that have been INCed as a result of + * this operation */ + PyObject *ref_params = NULL; + PyObject *nencoded_obj, *cencoded_obj, *oencoded_obj; + int which_httppost_item, which_httppost_option; + PyObject *httppost_option; + Py_ssize_t i, len; + int res; - len = PyListOrTuple_Size(obj, which); - if (len == 0) - Py_RETURN_NONE; + len = PyListOrTuple_Size(obj, which); + if (len == 0) + Py_RETURN_NONE; - /* Handle HTTPPOST different since we construct a HttpPost form struct */ - if (option == CURLOPT_HTTPPOST) { - struct curl_httppost *post = NULL; - struct curl_httppost *last = NULL; - /* List of all references that have been INCed as a result of - * this operation */ - PyObject *ref_params = NULL; - PyObject *nencoded_obj, *cencoded_obj, *oencoded_obj; - int which_httppost_item, which_httppost_option; - PyObject *httppost_option; - - for (i = 0; i < len; i++) { - char *nstr = NULL, *cstr = NULL; - Py_ssize_t nlen = -1, clen = -1; - PyObject *listitem = PyListOrTuple_GetItem(obj, i, which); - - which_httppost_item = PyListOrTuple_Check(listitem); - if (!which_httppost_item) { - curl_formfree(post); - Py_XDECREF(ref_params); - PyErr_SetString(PyExc_TypeError, "list items must be list or tuple objects"); - return NULL; - } - if (PyListOrTuple_Size(listitem, which_httppost_item) != 2) { - curl_formfree(post); - Py_XDECREF(ref_params); - PyErr_SetString(PyExc_TypeError, "list or tuple must contain two elements (name, value)"); - return NULL; - } - if (PyText_AsStringAndSize(PyListOrTuple_GetItem(listitem, 0, which_httppost_item), - &nstr, &nlen, &nencoded_obj) != 0) { - curl_formfree(post); - Py_XDECREF(ref_params); - PyErr_SetString(PyExc_TypeError, "list or tuple must contain a byte string or Unicode string with ASCII code points only as first element"); - return NULL; - } - httppost_option = PyListOrTuple_GetItem(listitem, 1, which_httppost_item); - if (PyText_Check(httppost_option)) { - /* Handle strings as second argument for backwards compatibility */ - - if (PyText_AsStringAndSize(httppost_option, &cstr, &clen, &cencoded_obj)) { - curl_formfree(post); - Py_XDECREF(ref_params); - PyText_EncodedDecref(nencoded_obj); - CURLERROR_RETVAL(); - } - /* INFO: curl_formadd() internally does memdup() the data, so - * embedded NUL characters _are_ allowed here. */ - res = curl_formadd(&post, &last, - CURLFORM_COPYNAME, nstr, - CURLFORM_NAMELENGTH, (long) nlen, - CURLFORM_COPYCONTENTS, cstr, - CURLFORM_CONTENTSLENGTH, (long) clen, - CURLFORM_END); - PyText_EncodedDecref(cencoded_obj); - if (res != CURLE_OK) { - curl_formfree(post); - Py_XDECREF(ref_params); - PyText_EncodedDecref(nencoded_obj); - CURLERROR_RETVAL(); - } - } - /* assignment is intended */ - else if ((which_httppost_option = PyListOrTuple_Check(httppost_option))) { - /* Supports content, file and content-type */ - Py_ssize_t tlen = PyListOrTuple_Size(httppost_option, which_httppost_option); - int j, k, l; - struct curl_forms *forms = NULL; - - /* Sanity check that there are at least two tuple items */ - if (tlen < 2) { - curl_formfree(post); - Py_XDECREF(ref_params); - PyText_EncodedDecref(nencoded_obj); - PyErr_SetString(PyExc_TypeError, "list or tuple must contain at least one option and one value"); - return NULL; - } + for (i = 0; i < len; i++) { + char *nstr = NULL, *cstr = NULL; + Py_ssize_t nlen = -1, clen = -1; + PyObject *listitem = PyListOrTuple_GetItem(obj, i, which); + + which_httppost_item = PyListOrTuple_Check(listitem); + if (!which_httppost_item) { + PyErr_SetString(PyExc_TypeError, "list items must be list or tuple objects"); + goto error; + } + if (PyListOrTuple_Size(listitem, which_httppost_item) != 2) { + PyErr_SetString(PyExc_TypeError, "list or tuple must contain two elements (name, value)"); + goto error; + } + if (PyText_AsStringAndSize(PyListOrTuple_GetItem(listitem, 0, which_httppost_item), + &nstr, &nlen, &nencoded_obj) != 0) { + PyErr_SetString(PyExc_TypeError, "list or tuple must contain a byte string or Unicode string with ASCII code points only as first element"); + goto error; + } + httppost_option = PyListOrTuple_GetItem(listitem, 1, which_httppost_item); + if (PyText_Check(httppost_option)) { + /* Handle strings as second argument for backwards compatibility */ - /* Allocate enough space to accommodate length options for content or buffers, plus a terminator. */ - forms = PyMem_Malloc(sizeof(struct curl_forms) * ((tlen*2) + 1)); - if (forms == NULL) { - curl_formfree(post); - Py_XDECREF(ref_params); - PyText_EncodedDecref(nencoded_obj); - PyErr_NoMemory(); - return NULL; - } + if (PyText_AsStringAndSize(httppost_option, &cstr, &clen, &cencoded_obj)) { + PyText_EncodedDecref(nencoded_obj); + CURLERROR_SET_RETVAL(); + goto error; + } + /* INFO: curl_formadd() internally does memdup() the data, so + * embedded NUL characters _are_ allowed here. */ + res = curl_formadd(&post, &last, + CURLFORM_COPYNAME, nstr, + CURLFORM_NAMELENGTH, (long) nlen, + CURLFORM_COPYCONTENTS, cstr, + CURLFORM_CONTENTSLENGTH, (long) clen, + CURLFORM_END); + PyText_EncodedDecref(cencoded_obj); + if (res != CURLE_OK) { + PyText_EncodedDecref(nencoded_obj); + CURLERROR_SET_RETVAL(); + goto error; + } + } + /* assignment is intended */ + else if ((which_httppost_option = PyListOrTuple_Check(httppost_option))) { + /* Supports content, file and content-type */ + Py_ssize_t tlen = PyListOrTuple_Size(httppost_option, which_httppost_option); + int j, k, l; + struct curl_forms *forms = NULL; - /* Iterate all the tuple members pairwise */ - for (j = 0, k = 0, l = 0; j < tlen; j += 2, l++) { - char *ostr; - Py_ssize_t olen; - int val; + /* Sanity check that there are at least two tuple items */ + if (tlen < 2) { + PyText_EncodedDecref(nencoded_obj); + PyErr_SetString(PyExc_TypeError, "list or tuple must contain at least one option and one value"); + goto error; + } - if (j == (tlen-1)) { - PyErr_SetString(PyExc_TypeError, "expected value"); - PyMem_Free(forms); - curl_formfree(post); - Py_XDECREF(ref_params); - PyText_EncodedDecref(nencoded_obj); - return NULL; - } - if (!PyInt_Check(PyListOrTuple_GetItem(httppost_option, j, which_httppost_option))) { - PyErr_SetString(PyExc_TypeError, "option must be long"); - PyMem_Free(forms); - curl_formfree(post); - Py_XDECREF(ref_params); - PyText_EncodedDecref(nencoded_obj); - return NULL; - } - if (!PyText_Check(PyListOrTuple_GetItem(httppost_option, j+1, which_httppost_option))) { - PyErr_SetString(PyExc_TypeError, "value must be a byte string or a Unicode string with ASCII code points only"); - PyMem_Free(forms); - curl_formfree(post); - Py_XDECREF(ref_params); - PyText_EncodedDecref(nencoded_obj); - return NULL; - } + if (tlen % 2 == 1) { + PyText_EncodedDecref(nencoded_obj); + PyErr_SetString(PyExc_TypeError, "list or tuple must contain an even number of items"); + goto error; + } - val = PyLong_AsLong(PyListOrTuple_GetItem(httppost_option, j, which_httppost_option)); - if (val != CURLFORM_COPYCONTENTS && - val != CURLFORM_FILE && - val != CURLFORM_FILENAME && - val != CURLFORM_CONTENTTYPE && - val != CURLFORM_BUFFER && - val != CURLFORM_BUFFERPTR) - { - PyErr_SetString(PyExc_TypeError, "unsupported option"); - PyMem_Free(forms); - curl_formfree(post); - Py_XDECREF(ref_params); - PyText_EncodedDecref(nencoded_obj); - return NULL; - } - if (PyText_AsStringAndSize(PyListOrTuple_GetItem(httppost_option, j+1, which_httppost_option), &ostr, &olen, &oencoded_obj)) { - /* exception should be already set */ + /* Allocate enough space to accommodate length options for content or buffers, plus a terminator. */ + forms = PyMem_New(struct curl_forms, (tlen*2) + 1); + if (forms == NULL) { + PyText_EncodedDecref(nencoded_obj); + PyErr_NoMemory(); + goto error; + } + + /* Iterate all the tuple members pairwise */ + for (j = 0, k = 0, l = 0; j < tlen; j += 2, l++) { + char *ostr; + Py_ssize_t olen; + int val; + + if (j == (tlen-1)) { + PyErr_SetString(PyExc_TypeError, "expected value"); + PyMem_Free(forms); + PyText_EncodedDecref(nencoded_obj); + goto error; + } + if (!PyInt_Check(PyListOrTuple_GetItem(httppost_option, j, which_httppost_option))) { + PyErr_SetString(PyExc_TypeError, "option must be an integer"); + PyMem_Free(forms); + PyText_EncodedDecref(nencoded_obj); + goto error; + } + if (!PyText_Check(PyListOrTuple_GetItem(httppost_option, j+1, which_httppost_option))) { + PyErr_SetString(PyExc_TypeError, "value must be a byte string or a Unicode string with ASCII code points only"); + PyMem_Free(forms); + PyText_EncodedDecref(nencoded_obj); + goto error; + } + + val = PyLong_AsLong(PyListOrTuple_GetItem(httppost_option, j, which_httppost_option)); + if (val != CURLFORM_COPYCONTENTS && + val != CURLFORM_FILE && + val != CURLFORM_FILENAME && + val != CURLFORM_CONTENTTYPE && + val != CURLFORM_BUFFER && + val != CURLFORM_BUFFERPTR) + { + PyErr_SetString(PyExc_TypeError, "unsupported option"); + PyMem_Free(forms); + PyText_EncodedDecref(nencoded_obj); + goto error; + } + + if (PyText_AsStringAndSize(PyListOrTuple_GetItem(httppost_option, j+1, which_httppost_option), &ostr, &olen, &oencoded_obj)) { + /* exception should be already set */ + PyMem_Free(forms); + PyText_EncodedDecref(nencoded_obj); + goto error; + } + forms[k].option = val; + forms[k].value = ostr; + ++k; + + if (val == CURLFORM_COPYCONTENTS) { + /* Contents can contain \0 bytes so we specify the length */ + forms[k].option = CURLFORM_CONTENTSLENGTH; + forms[k].value = (const char *)olen; + ++k; + } else if (val == CURLFORM_BUFFERPTR) { + PyObject *obj = NULL; + + if (ref_params == NULL) { + ref_params = PyList_New((Py_ssize_t)0); + if (ref_params == NULL) { + PyText_EncodedDecref(oencoded_obj); PyMem_Free(forms); - curl_formfree(post); - Py_XDECREF(ref_params); PyText_EncodedDecref(nencoded_obj); - return NULL; + goto error; } - forms[k].option = val; - forms[k].value = ostr; - ++k; - if (val == CURLFORM_COPYCONTENTS) { - /* Contents can contain \0 bytes so we specify the length */ - forms[k].option = CURLFORM_CONTENTSLENGTH; - forms[k].value = (const char *)olen; - ++k; - } - else if (val == CURLFORM_BUFFERPTR) { - PyObject *obj = NULL; + } - if (ref_params == NULL) { - ref_params = PyList_New((Py_ssize_t)0); - if (ref_params == NULL) { - PyText_EncodedDecref(oencoded_obj); - PyMem_Free(forms); - curl_formfree(post); - PyText_EncodedDecref(nencoded_obj); - return NULL; - } - } - - /* Keep a reference to the object that holds the ostr buffer. */ - if (oencoded_obj == NULL) { - obj = PyListOrTuple_GetItem(httppost_option, j+1, which_httppost_option); - } - else { - obj = oencoded_obj; - } - - /* Ensure that the buffer remains alive until curl_easy_cleanup() */ - if (PyList_Append(ref_params, obj) != 0) { - PyText_EncodedDecref(oencoded_obj); - PyMem_Free(forms); - curl_formfree(post); - Py_DECREF(ref_params); - PyText_EncodedDecref(nencoded_obj); - return NULL; - } - - /* As with CURLFORM_COPYCONTENTS, specify the length. */ - forms[k].option = CURLFORM_BUFFERLENGTH; - forms[k].value = (const char *)olen; - ++k; - } + /* Keep a reference to the object that holds the ostr buffer. */ + if (oencoded_obj == NULL) { + obj = PyListOrTuple_GetItem(httppost_option, j+1, which_httppost_option); } - forms[k].option = CURLFORM_END; - res = curl_formadd(&post, &last, - CURLFORM_COPYNAME, nstr, - CURLFORM_NAMELENGTH, (long) nlen, - CURLFORM_ARRAY, forms, - CURLFORM_END); - PyText_EncodedDecref(oencoded_obj); - PyMem_Free(forms); - if (res != CURLE_OK) { - curl_formfree(post); - Py_XDECREF(ref_params); + else { + obj = oencoded_obj; + } + + /* Ensure that the buffer remains alive until curl_easy_cleanup() */ + if (PyList_Append(ref_params, obj) != 0) { + PyText_EncodedDecref(oencoded_obj); + PyMem_Free(forms); PyText_EncodedDecref(nencoded_obj); - CURLERROR_RETVAL(); + goto error; } - } else { - /* Some other type was given, ignore */ - curl_formfree(post); - Py_XDECREF(ref_params); - PyText_EncodedDecref(nencoded_obj); - PyErr_SetString(PyExc_TypeError, "unsupported second type in tuple"); - return NULL; + + /* As with CURLFORM_COPYCONTENTS, specify the length. */ + forms[k].option = CURLFORM_BUFFERLENGTH; + forms[k].value = (const char *)olen; + ++k; } - PyText_EncodedDecref(nencoded_obj); } - res = curl_easy_setopt(self->handle, CURLOPT_HTTPPOST, post); - /* Check for errors */ + forms[k].option = CURLFORM_END; + res = curl_formadd(&post, &last, + CURLFORM_COPYNAME, nstr, + CURLFORM_NAMELENGTH, (long) nlen, + CURLFORM_ARRAY, forms, + CURLFORM_END); + PyText_EncodedDecref(oencoded_obj); + PyMem_Free(forms); if (res != CURLE_OK) { - curl_formfree(post); - Py_XDECREF(ref_params); - CURLERROR_RETVAL(); + PyText_EncodedDecref(nencoded_obj); + CURLERROR_SET_RETVAL(); + goto error; } - /* Finally, free previously allocated httppost, ZAP any - * buffer references, and update */ - curl_formfree(self->httppost); - util_curl_xdecref(self, PYCURL_MEMGROUP_HTTPPOST, self->handle); - self->httppost = post; - - /* The previous list of INCed references was ZAPed above; save - * the new one so that we can clean it up on the next - * self->httppost free. */ - self->httppost_ref_list = ref_params; - - Py_RETURN_NONE; + } else { + /* Some other type was given, ignore */ + PyText_EncodedDecref(nencoded_obj); + PyErr_SetString(PyExc_TypeError, "unsupported second type in tuple"); + goto error; } + PyText_EncodedDecref(nencoded_obj); + } + res = curl_easy_setopt(self->handle, CURLOPT_HTTPPOST, post); + /* Check for errors */ + if (res != CURLE_OK) { + CURLERROR_SET_RETVAL(); + goto error; + } + /* Finally, free previously allocated httppost, ZAP any + * buffer references, and update */ + curl_formfree(self->httppost); + util_curl_xdecref(self, PYCURL_MEMGROUP_HTTPPOST, self->handle); + self->httppost = post; + + /* The previous list of INCed references was ZAPed above; save + * the new one so that we can clean it up on the next + * self->httppost free. */ + self->httppost_ref_list = ref_params; - /* Just to be sure we do not bug off here */ - assert(old_slist != NULL && slist == NULL); + Py_RETURN_NONE; - /* Handle regular list operations on the other options */ - for (i = 0; i < len; i++) { - PyObject *listitem = PyListOrTuple_GetItem(obj, i, which); - struct curl_slist *nlist; - char *str; - PyObject *sencoded_obj; - - if (!PyText_Check(listitem)) { - curl_slist_free_all(slist); - PyErr_SetString(PyExc_TypeError, "list items must be byte strings or Unicode strings with ASCII code points only"); - return NULL; - } - /* INFO: curl_slist_append() internally does strdup() the data, so - * no embedded NUL characters allowed here. */ - str = PyText_AsString_NoNUL(listitem, &sencoded_obj); - if (str == NULL) { - curl_slist_free_all(slist); - return NULL; - } - nlist = curl_slist_append(slist, str); - PyText_EncodedDecref(sencoded_obj); - if (nlist == NULL || nlist->data == NULL) { - curl_slist_free_all(slist); - return PyErr_NoMemory(); - } - slist = nlist; - } - res = curl_easy_setopt(self->handle, (CURLoption)option, slist); - /* Check for errors */ - if (res != CURLE_OK) { - curl_slist_free_all(slist); - CURLERROR_RETVAL(); - } - /* Finally, free previously allocated list and update */ - curl_slist_free_all(*old_slist); - *old_slist = slist; +error: + curl_formfree(post); + Py_XDECREF(ref_params); + return NULL; +} + + +static PyObject * +do_curl_setopt_list(CurlObject *self, int option, int which, PyObject *obj) +{ + struct curl_slist **old_slist = NULL; + struct curl_slist *slist = NULL; + Py_ssize_t len; + int res; + + switch (option) { + case CURLOPT_HTTP200ALIASES: + old_slist = &self->http200aliases; + break; + case CURLOPT_HTTPHEADER: + old_slist = &self->httpheader; + break; +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 37, 0) + case CURLOPT_PROXYHEADER: + old_slist = &self->proxyheader; + break; +#endif + case CURLOPT_POSTQUOTE: + old_slist = &self->postquote; + break; + case CURLOPT_PREQUOTE: + old_slist = &self->prequote; + break; + case CURLOPT_QUOTE: + old_slist = &self->quote; + break; + case CURLOPT_TELNETOPTIONS: + old_slist = &self->telnetoptions; + break; +#ifdef HAVE_CURLOPT_RESOLVE + case CURLOPT_RESOLVE: + old_slist = &self->resolve; + break; +#endif +#ifdef HAVE_CURL_7_20_0_OPTS + case CURLOPT_MAIL_RCPT: + old_slist = &self->mail_rcpt; + break; +#endif + default: + /* None of the list options were recognized, raise exception */ + PyErr_SetString(PyExc_TypeError, "lists are not supported for this option"); + return NULL; + } + len = PyListOrTuple_Size(obj, which); + if (len == 0) Py_RETURN_NONE; + + /* Just to be sure we do not bug off here */ + assert(old_slist != NULL && slist == NULL); + + /* Handle regular list operations on the other options */ + slist = pycurl_list_or_tuple_to_slist(which, obj, len); + if (slist == NULL) { + return NULL; + } + res = curl_easy_setopt(self->handle, (CURLoption)option, slist); + /* Check for errors */ + if (res != CURLE_OK) { + curl_slist_free_all(slist); + CURLERROR_RETVAL(); } + /* Finally, free previously allocated list and update */ + curl_slist_free_all(*old_slist); + *old_slist = slist; - /* Handle the case of function objects for callbacks */ - if (PyFunction_Check(obj) || PyCFunction_Check(obj) || - PyCallable_Check(obj) || PyMethod_Check(obj)) { - /* We use function types here to make sure that our callback - * definitions exactly match the interface. - */ - const curl_write_callback w_cb = write_callback; - const curl_write_callback h_cb = header_callback; - const curl_read_callback r_cb = read_callback; - 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; + Py_RETURN_NONE; +} + + +static PyObject * +do_curl_setopt_callable(CurlObject *self, int option, PyObject *obj) +{ + /* We use function types here to make sure that our callback + * definitions exactly match the interface. + */ + const curl_write_callback w_cb = write_callback; + const curl_write_callback h_cb = header_callback; + const curl_read_callback r_cb = read_callback; + const curl_progress_callback pro_cb = progress_callback; +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 32, 0) + const curl_xferinfo_callback xferinfo_cb = xferinfo_callback; +#endif + const curl_debug_callback debug_cb = debug_callback; + const curl_ioctl_callback ioctl_cb = ioctl_callback; + const curl_opensocket_callback opensocket_cb = opensocket_callback; #if LIBCURL_VERSION_NUM >= 0x071507 /* check for 7.21.7 or greater */ - const curl_closesocket_callback closesocket_cb = closesocket_callback; + const curl_closesocket_callback closesocket_cb = closesocket_callback; #endif - const curl_seek_callback seek_cb = seek_callback; + const curl_seek_callback seek_cb = seek_callback; - switch(option) { - case CURLOPT_WRITEFUNCTION: - if (self->writeheader_fp != NULL) { - PyErr_SetString(ErrorObject, "cannot combine WRITEFUNCTION with WRITEHEADER option."); - return NULL; - } - Py_INCREF(obj); - Py_CLEAR(self->writedata_fp); - Py_CLEAR(self->w_cb); - self->w_cb = obj; - curl_easy_setopt(self->handle, CURLOPT_WRITEFUNCTION, w_cb); - curl_easy_setopt(self->handle, CURLOPT_WRITEDATA, self); - break; - case CURLOPT_HEADERFUNCTION: - Py_INCREF(obj); - Py_CLEAR(self->h_cb); - self->h_cb = obj; - curl_easy_setopt(self->handle, CURLOPT_HEADERFUNCTION, h_cb); - curl_easy_setopt(self->handle, CURLOPT_WRITEHEADER, self); - break; - case CURLOPT_READFUNCTION: - Py_INCREF(obj); - Py_CLEAR(self->readdata_fp); - Py_CLEAR(self->r_cb); - self->r_cb = obj; - curl_easy_setopt(self->handle, CURLOPT_READFUNCTION, r_cb); - curl_easy_setopt(self->handle, CURLOPT_READDATA, self); - break; - case CURLOPT_PROGRESSFUNCTION: - Py_INCREF(obj); - Py_CLEAR(self->pro_cb); - self->pro_cb = obj; - curl_easy_setopt(self->handle, CURLOPT_PROGRESSFUNCTION, pro_cb); - curl_easy_setopt(self->handle, CURLOPT_PROGRESSDATA, self); - break; - case CURLOPT_DEBUGFUNCTION: - Py_INCREF(obj); - Py_CLEAR(self->debug_cb); - self->debug_cb = obj; - curl_easy_setopt(self->handle, CURLOPT_DEBUGFUNCTION, debug_cb); - curl_easy_setopt(self->handle, CURLOPT_DEBUGDATA, self); - break; - case CURLOPT_IOCTLFUNCTION: - Py_INCREF(obj); - Py_CLEAR(self->ioctl_cb); - self->ioctl_cb = obj; - curl_easy_setopt(self->handle, CURLOPT_IOCTLFUNCTION, ioctl_cb); - curl_easy_setopt(self->handle, CURLOPT_IOCTLDATA, self); - break; - case CURLOPT_OPENSOCKETFUNCTION: - Py_INCREF(obj); - Py_CLEAR(self->opensocket_cb); - self->opensocket_cb = obj; - curl_easy_setopt(self->handle, CURLOPT_OPENSOCKETFUNCTION, opensocket_cb); - curl_easy_setopt(self->handle, CURLOPT_OPENSOCKETDATA, self); - break; + switch(option) { + case CURLOPT_WRITEFUNCTION: + if (self->writeheader_fp != NULL) { + PyErr_SetString(ErrorObject, "cannot combine WRITEFUNCTION with WRITEHEADER option."); + return NULL; + } + Py_INCREF(obj); + Py_CLEAR(self->writedata_fp); + Py_CLEAR(self->w_cb); + self->w_cb = obj; + curl_easy_setopt(self->handle, CURLOPT_WRITEFUNCTION, w_cb); + curl_easy_setopt(self->handle, CURLOPT_WRITEDATA, self); + break; + case CURLOPT_HEADERFUNCTION: + Py_INCREF(obj); + Py_CLEAR(self->h_cb); + self->h_cb = obj; + curl_easy_setopt(self->handle, CURLOPT_HEADERFUNCTION, h_cb); + curl_easy_setopt(self->handle, CURLOPT_WRITEHEADER, self); + break; + case CURLOPT_READFUNCTION: + Py_INCREF(obj); + Py_CLEAR(self->readdata_fp); + Py_CLEAR(self->r_cb); + self->r_cb = obj; + curl_easy_setopt(self->handle, CURLOPT_READFUNCTION, r_cb); + curl_easy_setopt(self->handle, CURLOPT_READDATA, self); + break; + case CURLOPT_PROGRESSFUNCTION: + Py_INCREF(obj); + Py_CLEAR(self->pro_cb); + self->pro_cb = obj; + curl_easy_setopt(self->handle, CURLOPT_PROGRESSFUNCTION, pro_cb); + curl_easy_setopt(self->handle, CURLOPT_PROGRESSDATA, self); + break; +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 32, 0) + case CURLOPT_XFERINFOFUNCTION: + Py_INCREF(obj); + Py_CLEAR(self->xferinfo_cb); + self->xferinfo_cb = obj; + curl_easy_setopt(self->handle, CURLOPT_XFERINFOFUNCTION, xferinfo_cb); + curl_easy_setopt(self->handle, CURLOPT_XFERINFODATA, self); + break; +#endif + case CURLOPT_DEBUGFUNCTION: + Py_INCREF(obj); + Py_CLEAR(self->debug_cb); + self->debug_cb = obj; + curl_easy_setopt(self->handle, CURLOPT_DEBUGFUNCTION, debug_cb); + curl_easy_setopt(self->handle, CURLOPT_DEBUGDATA, self); + break; + case CURLOPT_IOCTLFUNCTION: + Py_INCREF(obj); + Py_CLEAR(self->ioctl_cb); + self->ioctl_cb = obj; + curl_easy_setopt(self->handle, CURLOPT_IOCTLFUNCTION, ioctl_cb); + curl_easy_setopt(self->handle, CURLOPT_IOCTLDATA, self); + break; + case CURLOPT_OPENSOCKETFUNCTION: + Py_INCREF(obj); + Py_CLEAR(self->opensocket_cb); + self->opensocket_cb = obj; + curl_easy_setopt(self->handle, CURLOPT_OPENSOCKETFUNCTION, opensocket_cb); + curl_easy_setopt(self->handle, CURLOPT_OPENSOCKETDATA, self); + break; #if LIBCURL_VERSION_NUM >= 0x071507 /* check for 7.21.7 or greater */ - case CURLOPT_CLOSESOCKETFUNCTION: - Py_INCREF(obj); - Py_CLEAR(self->closesocket_cb); - self->closesocket_cb = obj; - curl_easy_setopt(self->handle, CURLOPT_CLOSESOCKETFUNCTION, closesocket_cb); - curl_easy_setopt(self->handle, CURLOPT_CLOSESOCKETDATA, self); - break; -#endif - case CURLOPT_SOCKOPTFUNCTION: - Py_INCREF(obj); - Py_CLEAR(self->sockopt_cb); - self->sockopt_cb = obj; - curl_easy_setopt(self->handle, CURLOPT_SOCKOPTFUNCTION, sockopt_cb); - curl_easy_setopt(self->handle, CURLOPT_SOCKOPTDATA, self); - break; + case CURLOPT_CLOSESOCKETFUNCTION: + Py_INCREF(obj); + Py_CLEAR(self->closesocket_cb); + self->closesocket_cb = obj; + curl_easy_setopt(self->handle, CURLOPT_CLOSESOCKETFUNCTION, closesocket_cb); + curl_easy_setopt(self->handle, CURLOPT_CLOSESOCKETDATA, self); + break; +#endif + case CURLOPT_SOCKOPTFUNCTION: + Py_INCREF(obj); + Py_CLEAR(self->sockopt_cb); + self->sockopt_cb = obj; + curl_easy_setopt(self->handle, CURLOPT_SOCKOPTFUNCTION, sockopt_cb); + curl_easy_setopt(self->handle, CURLOPT_SOCKOPTDATA, self); + break; #ifdef HAVE_CURL_7_19_6_OPTS - case CURLOPT_SSH_KEYFUNCTION: - Py_INCREF(obj); - Py_CLEAR(self->ssh_key_cb); - self->ssh_key_cb = obj; - curl_easy_setopt(self->handle, CURLOPT_SSH_KEYFUNCTION, ssh_key_cb); - curl_easy_setopt(self->handle, CURLOPT_SSH_KEYDATA, self); - break; -#endif - case CURLOPT_SEEKFUNCTION: - Py_INCREF(obj); - Py_CLEAR(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, raise exception */ - PyErr_SetString(PyExc_TypeError, "functions are not supported for this option"); + case CURLOPT_SSH_KEYFUNCTION: + Py_INCREF(obj); + Py_CLEAR(self->ssh_key_cb); + self->ssh_key_cb = obj; + curl_easy_setopt(self->handle, CURLOPT_SSH_KEYFUNCTION, ssh_key_cb); + curl_easy_setopt(self->handle, CURLOPT_SSH_KEYDATA, self); + break; +#endif + case CURLOPT_SEEKFUNCTION: + Py_INCREF(obj); + Py_CLEAR(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, raise exception */ + PyErr_SetString(PyExc_TypeError, "functions are not supported for this option"); + return NULL; + } + Py_RETURN_NONE; +} + + +static PyObject * +do_curl_setopt_share(CurlObject *self, PyObject *obj) +{ + CurlShareObject *share; + int res; + + if (self->share == NULL && (obj == NULL || obj == Py_None)) + Py_RETURN_NONE; + + if (self->share) { + if (obj != Py_None) { + PyErr_SetString(ErrorObject, "Curl object already sharing. Unshare first."); return NULL; } - Py_RETURN_NONE; + else { + share = self->share; + res = curl_easy_setopt(self->handle, CURLOPT_SHARE, NULL); + if (res != CURLE_OK) { + CURLERROR_RETVAL(); + } + self->share = NULL; + Py_DECREF(share); + Py_RETURN_NONE; + } } - /* handle the SHARE case */ - if (option == CURLOPT_SHARE) { - CurlShareObject *share; + if (Py_TYPE(obj) != p_CurlShare_Type) { + PyErr_SetString(PyExc_TypeError, "invalid arguments to setopt"); + return NULL; + } + share = (CurlShareObject*)obj; + res = curl_easy_setopt(self->handle, CURLOPT_SHARE, share->share_handle); + if (res != CURLE_OK) { + CURLERROR_RETVAL(); + } + self->share = share; + Py_INCREF(share); + Py_RETURN_NONE; +} - if (self->share == NULL && (obj == NULL || obj == Py_None)) - Py_RETURN_NONE; - if (self->share) { - if (obj != Py_None) { - PyErr_SetString(ErrorObject, "Curl object already sharing. Unshare first."); - return NULL; - } - else { - share = self->share; - res = curl_easy_setopt(self->handle, CURLOPT_SHARE, NULL); - if (res != CURLE_OK) { - CURLERROR_RETVAL(); +/* prototype for do_curl_setopt_filelike */ +static PyObject * +do_curl_setopt(CurlObject *self, PyObject *args); + + +static PyObject * +do_curl_setopt_filelike(CurlObject *self, int option, PyObject *obj) +{ + const char *method_name; + PyObject *method; + + if (option == CURLOPT_READDATA) { + method_name = "read"; + } else { + method_name = "write"; + } + method = PyObject_GetAttrString(obj, method_name); + if (method) { + PyObject *arglist; + PyObject *rv; + + switch (option) { + case CURLOPT_READDATA: + option = CURLOPT_READFUNCTION; + break; + case CURLOPT_WRITEDATA: + option = CURLOPT_WRITEFUNCTION; + break; + case CURLOPT_WRITEHEADER: + if (self->w_cb != NULL) { + PyErr_SetString(ErrorObject, "cannot combine WRITEHEADER with WRITEFUNCTION."); + Py_DECREF(method); + return NULL; } - self->share = NULL; - Py_DECREF(share); - Py_RETURN_NONE; - } + option = CURLOPT_HEADERFUNCTION; + break; + default: + PyErr_SetString(PyExc_TypeError, "objects are not supported for this option"); + Py_DECREF(method); + return NULL; } - if (Py_TYPE(obj) != p_CurlShare_Type) { - PyErr_SetString(PyExc_TypeError, "invalid arguments to setopt"); + + arglist = Py_BuildValue("(iO)", option, method); + /* reference is now in arglist */ + Py_DECREF(method); + if (arglist == NULL) { return NULL; } - share = (CurlShareObject*)obj; - res = curl_easy_setopt(self->handle, CURLOPT_SHARE, share->share_handle); - if (res != CURLE_OK) { - CURLERROR_RETVAL(); + rv = do_curl_setopt(self, arglist); + Py_DECREF(arglist); + return rv; + } else { + PyErr_SetString(ErrorObject, "object given without a write method"); + return NULL; + } +} + + +static PyObject * +do_curl_setopt(CurlObject *self, PyObject *args) +{ + int option; + PyObject *obj; + int which; + + if (!PyArg_ParseTuple(args, "iO:setopt", &option, &obj)) + return NULL; + if (check_curl_state(self, 1 | 2, "setopt") != 0) + return NULL; + + /* early checks of option value */ + if (option <= 0) + goto error; + if (option >= (int)CURLOPTTYPE_OFF_T + OPTIONS_SIZE) + goto error; + if (option % 10000 >= OPTIONS_SIZE) + goto error; + + /* Handle the case of None as the call of unsetopt() */ + if (obj == Py_None) { + return util_curl_unsetopt(self, option); + } + + /* Handle the case of string arguments */ + if (PyText_Check(obj)) { + return do_curl_setopt_string_impl(self, option, obj); + } + + /* Handle the case of integer arguments */ + if (PyInt_Check(obj)) { + return do_curl_setopt_int(self, option, obj); + } + + /* Handle the case of long arguments (used by *_LARGE options) */ + if (PyLong_Check(obj)) { + return do_curl_setopt_long(self, option, obj); + } + +#if PY_MAJOR_VERSION < 3 && !defined(PYCURL_AVOID_STDIO) + /* Handle the case of file objects */ + if (PyFile_Check(obj)) { + return do_curl_setopt_file_passthrough(self, option, obj); + } +#endif + + /* Handle the case of list or tuple objects */ + which = PyListOrTuple_Check(obj); + if (which) { + if (option == CURLOPT_HTTPPOST) { + return do_curl_setopt_httppost(self, option, which, obj); + } else { + return do_curl_setopt_list(self, option, which, obj); } - self->share = share; - Py_INCREF(share); - Py_RETURN_NONE; + } + + /* Handle the case of function objects for callbacks */ + if (PyFunction_Check(obj) || PyCFunction_Check(obj) || + PyCallable_Check(obj) || PyMethod_Check(obj)) { + return do_curl_setopt_callable(self, option, obj); + } + /* handle the SHARE case */ + if (option == CURLOPT_SHARE) { + return do_curl_setopt_share(self, obj); } /* @@ -2179,53 +2407,7 @@ option == CURLOPT_WRITEDATA || option == CURLOPT_WRITEHEADER) { - const char *method_name; - PyObject *method; - - if (option == CURLOPT_READDATA) { - method_name = "read"; - } else { - method_name = "write"; - } - method = PyObject_GetAttrString(obj, method_name); - if (method) { - PyObject *arglist; - PyObject *rv; - - switch (option) { - case CURLOPT_READDATA: - option = CURLOPT_READFUNCTION; - break; - case CURLOPT_WRITEDATA: - option = CURLOPT_WRITEFUNCTION; - break; - case CURLOPT_WRITEHEADER: - if (self->w_cb != NULL) { - PyErr_SetString(ErrorObject, "cannot combine WRITEHEADER with WRITEFUNCTION."); - Py_DECREF(method); - return NULL; - } - option = CURLOPT_HEADERFUNCTION; - break; - default: - PyErr_SetString(PyExc_TypeError, "objects are not supported for this option"); - Py_DECREF(method); - return NULL; - } - - arglist = Py_BuildValue("(iO)", option, method); - /* reference is now in arglist */ - Py_DECREF(method); - if (arglist == NULL) { - return NULL; - } - rv = do_curl_setopt(self, arglist); - Py_DECREF(arglist); - return rv; - } else { - PyErr_SetString(ErrorObject, "object given without a write method"); - return NULL; - } + return do_curl_setopt_filelike(self, option, obj); } /* Failed to match any of the function signatures -- return error */ @@ -2236,6 +2418,28 @@ static PyObject * +do_curl_setopt_string(CurlObject *self, PyObject *args) +{ + int option; + PyObject *obj; + + if (!PyArg_ParseTuple(args, "iO:setopt", &option, &obj)) + return NULL; + if (check_curl_state(self, 1 | 2, "setopt") != 0) + return NULL; + + /* Handle the case of string arguments */ + if (PyText_Check(obj)) { + return do_curl_setopt_string_impl(self, option, obj); + } + + /* Failed to match any of the function signatures -- return error */ + PyErr_SetString(PyExc_TypeError, "invalid arguments to setopt_string"); + return NULL; +} + + +static PyObject * do_curl_getinfo(CurlObject *self, PyObject *args) { int option; @@ -2251,7 +2455,7 @@ switch (option) { case CURLINFO_FILETIME: case CURLINFO_HEADER_SIZE: - case CURLINFO_HTTP_CODE: + case CURLINFO_RESPONSE_CODE: case CURLINFO_REDIRECT_COUNT: case CURLINFO_REQUEST_SIZE: case CURLINFO_SSL_VERIFYRESULT: @@ -2267,6 +2471,11 @@ #ifdef HAVE_CURLINFO_PRIMARY_PORT case CURLINFO_PRIMARY_PORT: #endif +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 20, 0) + case CURLINFO_RTSP_CLIENT_CSEQ: + case CURLINFO_RTSP_SERVER_CSEQ: + case CURLINFO_RTSP_CSEQ_RECV: +#endif { /* Return PyInt as result */ @@ -2288,6 +2497,9 @@ #ifdef HAVE_CURLINFO_LOCAL_IP case CURLINFO_LOCAL_IP: #endif +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 20, 0) + case CURLINFO_RTSP_SESSION_ID: +#endif { /* Return PyString as result */ char *s_res = NULL; @@ -2433,6 +2645,7 @@ {"pause", (PyCFunction)do_curl_pause, METH_VARARGS, curl_pause_doc}, {"perform", (PyCFunction)do_curl_perform, METH_NOARGS, curl_perform_doc}, {"setopt", (PyCFunction)do_curl_setopt, METH_VARARGS, curl_setopt_doc}, + {"setopt_string", (PyCFunction)do_curl_setopt_string, METH_VARARGS, curl_setopt_string_doc}, {"unsetopt", (PyCFunction)do_curl_unsetopt, METH_VARARGS, curl_unsetopt_doc}, {"reset", (PyCFunction)do_curl_reset, METH_NOARGS, curl_reset_doc}, {"__getstate__", (PyCFunction)do_curl_getstate, METH_NOARGS, NULL}, diff -Nru pycurl-7.21.5/src/module.c pycurl-7.43.0/src/module.c --- pycurl-7.21.5/src/module.c 2016-01-02 01:49:30.000000000 +0000 +++ pycurl-7.43.0/src/module.c 2016-02-02 02:54:05.000000000 +0000 @@ -187,7 +187,12 @@ PyString_InternInPlace(&key); /* XXX Should we really? */ #endif if (dict1 != NULL) { - assert(PyDict_GetItem(dict1, key) == NULL); +#if !defined(NDEBUG) + if (PyDict_GetItem(dict1, key) != NULL) { + fprintf(stderr, "Symbol already defined: %s\n", name); + assert(0); + } +#endif if (PyDict_SetItem(dict1, key, value) != 0) goto error; } @@ -405,7 +410,7 @@ * replaced with the space; libcurl_version_len does not include * terminating null. */ pycurl_version_len = PYCURL_VERSION_PREFIX_SIZE + libcurl_version_len + 1; - g_pycurl_useragent = PyMem_Malloc(pycurl_version_len); + g_pycurl_useragent = PyMem_New(char, pycurl_version_len); if (g_pycurl_useragent == NULL) goto error; memcpy(g_pycurl_useragent, PYCURL_VERSION_PREFIX, PYCURL_VERSION_PREFIX_SIZE); @@ -570,6 +575,24 @@ insint_c(d, "E_CONV_REQD", CURLE_CONV_REQD); insint_c(d, "E_REMOTE_FILE_NOT_FOUND", CURLE_REMOTE_FILE_NOT_FOUND); insint_c(d, "E_SSH", CURLE_SSH); +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 20, 0) + insint_c(d, "E_FTP_PRET_FAILED", CURLE_FTP_PRET_FAILED); + insint_c(d, "E_RTSP_CSEQ_ERROR", CURLE_RTSP_CSEQ_ERROR); + insint_c(d, "E_RTSP_SESSION_ERROR", CURLE_RTSP_SESSION_ERROR); +#endif +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 0) + insint_c(d, "E_CHUNK_FAILED", CURLE_CHUNK_FAILED); + insint_c(d, "E_FTP_BAD_FILE_LIST", CURLE_FTP_BAD_FILE_LIST); +#endif +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 24, 0) + insint_c(d, "E_FTP_ACCEPT_TIMEOUT", CURLE_FTP_ACCEPT_TIMEOUT); +#endif +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 30, 0) + insint_c(d, "E_NO_CONNECTION_AVAILABLE", CURLE_NO_CONNECTION_AVAILABLE); +#endif +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 38, 0) + insint_c(d, "E_HTTP2", CURLE_HTTP2); +#endif /* curl_proxytype: constants for setopt(PROXYTYPE, x) */ insint_c(d, "PROXYTYPE_HTTP", CURLPROXY_HTTP); @@ -690,6 +713,16 @@ insint_c(d, "POSTFIELDSIZE", CURLOPT_POSTFIELDSIZE_LARGE); /* _LARGE ! */ insint_c(d, "COOKIE", CURLOPT_COOKIE); insint_c(d, "HTTPHEADER", CURLOPT_HTTPHEADER); +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 37, 0) + insint_c(d, "PROXYHEADER", CURLOPT_PROXYHEADER); + insint_c(d, "HEADEROPT", CURLOPT_HEADEROPT); +#endif +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) + insint_c(d, "PATH_AS_IS", CURLOPT_PATH_AS_IS); +#endif +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 43, 0) + insint_c(d, "PIPEWAIT", CURLOPT_PIPEWAIT); +#endif insint_c(d, "HTTPPOST", CURLOPT_HTTPPOST); insint_c(d, "SSLCERT", CURLOPT_SSLCERT); insint_c(d, "SSLCERTPASSWD", CURLOPT_SSLCERTPASSWD); @@ -710,6 +743,21 @@ insint_c(d, "KRB4LEVEL", CURLOPT_KRB4LEVEL); insint_c(d, "KRBLEVEL", CURLOPT_KRBLEVEL); insint_c(d, "PROGRESSFUNCTION", CURLOPT_PROGRESSFUNCTION); +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 32, 0) + insint_c(d, "XFERINFOFUNCTION", CURLOPT_XFERINFOFUNCTION); +#endif +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 20, 0) + insint_c(d, "FTP_USE_PRET", CURLOPT_FTP_USE_PRET); +#endif +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) + insint_c(d, "LOGIN_OPTIONS", CURLOPT_LOGIN_OPTIONS); +#endif +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 31, 0) + insint_c(d, "SASL_IR", CURLOPT_SASL_IR); +#endif +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 33, 0) + insint_c(d, "XOAUTH2_BEARER", CURLOPT_XOAUTH2_BEARER); +#endif insint_c(d, "SSL_VERIFYPEER", CURLOPT_SSL_VERIFYPEER); insint_c(d, "CAPATH", CURLOPT_CAPATH); insint_c(d, "CAINFO", CURLOPT_CAINFO); @@ -741,7 +789,12 @@ insint_c(d, "NOSIGNAL", CURLOPT_NOSIGNAL); insint_c(d, "SHARE", CURLOPT_SHARE); insint_c(d, "PROXYTYPE", CURLOPT_PROXYTYPE); + /* superseded by ACCEPT_ENCODING */ insint_c(d, "ENCODING", CURLOPT_ENCODING); +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6) + insint_c(d, "ACCEPT_ENCODING", CURLOPT_ACCEPT_ENCODING); + insint_c(d, "TRANSFER_ENCODING", CURLOPT_TRANSFER_ENCODING); +#endif insint_c(d, "HTTP200ALIASES", CURLOPT_HTTP200ALIASES); insint_c(d, "UNRESTRICTED_AUTH", CURLOPT_UNRESTRICTED_AUTH); insint_c(d, "FTP_USE_EPRT", CURLOPT_FTP_USE_EPRT); @@ -760,7 +813,6 @@ insint_c(d, "TCP_NODELAY", CURLOPT_TCP_NODELAY); insint_c(d, "FTPSSLAUTH", CURLOPT_FTPSSLAUTH); insint_c(d, "IOCTLFUNCTION", CURLOPT_IOCTLFUNCTION); - insint_c(d, "IOCTLDATA", CURLOPT_IOCTLDATA); insint_c(d, "OPENSOCKETFUNCTION", CURLOPT_OPENSOCKETFUNCTION); #if LIBCURL_VERSION_NUM >= 0x071507 /* check for 7.21.7 or greater */ insint_c(d, "CLOSESOCKETFUNCTION", CURLOPT_CLOSESOCKETFUNCTION); @@ -791,6 +843,17 @@ insint_c(d, "FTP_SSL_CCC", CURLOPT_FTP_SSL_CCC); insint_c(d, "TIMEOUT_MS", CURLOPT_TIMEOUT_MS); insint_c(d, "CONNECTTIMEOUT_MS", CURLOPT_CONNECTTIMEOUT_MS); +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 24, 0) + insint_c(d, "ACCEPTTIMEOUT_MS", CURLOPT_ACCEPTTIMEOUT_MS); +#endif +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 36, 0) + insint_c(d, "EXPECT_100_TIMEOUT_MS", CURLOPT_EXPECT_100_TIMEOUT_MS); +#endif +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 25, 0) + insint_c(d, "TCP_KEEPALIVE", CURLOPT_TCP_KEEPALIVE); + insint_c(d, "TCP_KEEPIDLE", CURLOPT_TCP_KEEPIDLE); + insint_c(d, "TCP_KEEPINTVL", CURLOPT_TCP_KEEPINTVL); +#endif insint_c(d, "HTTP_TRANSFER_DECODING", CURLOPT_HTTP_TRANSFER_DECODING); insint_c(d, "HTTP_CONTENT_DECODING", CURLOPT_HTTP_CONTENT_DECODING); insint_c(d, "NEW_FILE_PERMS", CURLOPT_NEW_FILE_PERMS); @@ -854,6 +917,10 @@ #ifdef HAVE_CURL_7_21_2_OPTS insint_c(d, "PROTO_GOPHER", CURLPROTO_GOPHER); #endif +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 40, 0) + insint_c(d, "PROTO_SMB", CURLPROTO_SMB); + insint_c(d, "PROTO_SMBS", CURLPROTO_SMBS); +#endif insint_c(d, "PROTO_ALL", CURLPROTO_ALL); #endif #ifdef HAVE_CURL_7_19_4_OPTS @@ -891,6 +958,14 @@ insint_c(d, "SSLOPT_NO_REVOKE", CURLSSLOPT_NO_REVOKE); # endif #endif +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 4) + insint_c(d, "TLSAUTH_TYPE", CURLOPT_TLSAUTH_TYPE); + insint_c(d, "TLSAUTH_USERNAME", CURLOPT_TLSAUTH_USERNAME); + insint_c(d, "TLSAUTH_PASSWORD", CURLOPT_TLSAUTH_PASSWORD); +#endif +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 45, 0) + insint_c(d, "DEFAULT_PROTOCOL", CURLOPT_DEFAULT_PROTOCOL); +#endif insint_m(d, "M_TIMERFUNCTION", CURLMOPT_TIMERFUNCTION); insint_m(d, "M_SOCKETFUNCTION", CURLMOPT_SOCKETFUNCTION); @@ -902,6 +977,14 @@ insint_m(d, "M_MAX_PIPELINE_LENGTH", CURLMOPT_MAX_PIPELINE_LENGTH); insint_m(d, "M_CONTENT_LENGTH_PENALTY_SIZE", CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE); insint_m(d, "M_CHUNK_LENGTH_PENALTY_SIZE", CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE); + insint_m(d, "M_PIPELINING_SITE_BL", CURLMOPT_PIPELINING_SITE_BL); + insint_m(d, "M_PIPELINING_SERVER_BL", CURLMOPT_PIPELINING_SERVER_BL); +#endif + +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 43, 0) + insint_m(d, "PIPE_NOTHING", CURLPIPE_NOTHING); + insint_m(d, "PIPE_HTTP1", CURLPIPE_HTTP1); + insint_m(d, "PIPE_MULTIPLEX", CURLPIPE_MULTIPLEX); #endif /* constants for setopt(IPRESOLVE, x) */ @@ -913,6 +996,15 @@ insint_c(d, "CURL_HTTP_VERSION_NONE", CURL_HTTP_VERSION_NONE); insint_c(d, "CURL_HTTP_VERSION_1_0", CURL_HTTP_VERSION_1_0); insint_c(d, "CURL_HTTP_VERSION_1_1", CURL_HTTP_VERSION_1_1); +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 33, 0) + insint_c(d, "CURL_HTTP_VERSION_2_0", CURL_HTTP_VERSION_2_0); +#endif +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 43, 0) + insint_c(d, "CURL_HTTP_VERSION_2", CURL_HTTP_VERSION_2); +#endif +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 47, 0) + insint_c(d, "CURL_HTTP_VERSION_2TLS", CURL_HTTP_VERSION_2TLS); +#endif insint_c(d, "CURL_HTTP_VERSION_LAST", CURL_HTTP_VERSION_LAST); /* CURL_NETRC_OPTION: constants for setopt(NETRC, x) */ @@ -946,6 +1038,17 @@ insint_c(d, "SSH_AUTH_KEYBOARD", CURLSSH_AUTH_KEYBOARD); insint_c(d, "SSH_AUTH_DEFAULT", CURLSSH_AUTH_DEFAULT); +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 37, 0) + insint_c(d, "HEADER_UNIFIED", CURLHEADER_UNIFIED); + insint_c(d, "HEADER_SEPARATE", CURLHEADER_SEPARATE); +#endif + +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 5) + insint_c(d, "SOCKOPT_ALREADY_CONNECTED", CURL_SOCKOPT_ALREADY_CONNECTED); + insint_c(d, "SOCKOPT_ERROR", CURL_SOCKOPT_ERROR); + insint_c(d, "SOCKOPT_OK", CURL_SOCKOPT_OK); +#endif + #ifdef HAVE_CURL_7_19_6_OPTS /* curl_khtype constants */ insint_c(d, "KHTYPE_UNKNOWN", CURLKHTYPE_UNKNOWN); @@ -977,8 +1080,9 @@ /* CURLINFO: symbolic constants for getinfo(x) */ insint_c(d, "EFFECTIVE_URL", CURLINFO_EFFECTIVE_URL); + /* same as CURLINFO_RESPONSE_CODE */ insint_c(d, "HTTP_CODE", CURLINFO_HTTP_CODE); - insint_c(d, "RESPONSE_CODE", CURLINFO_HTTP_CODE); + insint_c(d, "RESPONSE_CODE", CURLINFO_RESPONSE_CODE); insint_c(d, "TOTAL_TIME", CURLINFO_TOTAL_TIME); insint_c(d, "NAMELOOKUP_TIME", CURLINFO_NAMELOOKUP_TIME); insint_c(d, "CONNECT_TIME", CURLINFO_CONNECT_TIME); @@ -1024,6 +1128,12 @@ #ifdef HAVE_CURL_7_19_4_OPTS insint_c(d, "CONDITION_UNMET", CURLINFO_CONDITION_UNMET); #endif +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 20, 0) + insint_c(d, "INFO_RTSP_CLIENT_CSEQ", CURLINFO_RTSP_CLIENT_CSEQ); + insint_c(d, "INFO_RTSP_CSEQ_RECV", CURLINFO_RTSP_CSEQ_RECV); + insint_c(d, "INFO_RTSP_SERVER_CSEQ", CURLINFO_RTSP_SERVER_CSEQ); + insint_c(d, "INFO_RTSP_SESSION_ID", CURLINFO_RTSP_SESSION_ID); +#endif /* CURLPAUSE: symbolic constants for pause(bitmask) */ insint_c(d, "PAUSE_RECV", CURLPAUSE_RECV); @@ -1144,7 +1254,9 @@ insint_m(d, "E_MULTI_OUT_OF_MEMORY", CURLM_OUT_OF_MEMORY); insint_m(d, "E_MULTI_INTERNAL_ERROR", CURLM_INTERNAL_ERROR); insint_m(d, "E_MULTI_UNKNOWN_OPTION", CURLM_UNKNOWN_OPTION); - +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 32, 1) + insint_m(d, "E_MULTI_ADDED_ALREADY", CURLM_ADDED_ALREADY); +#endif /* curl shared constants */ insint_s(d, "SH_SHARE", CURLSHOPT_SHARE); insint_s(d, "SH_UNSHARE", CURLSHOPT_UNSHARE); @@ -1155,7 +1267,9 @@ /* Initialize callback locks if ssl is enabled */ #if defined(PYCURL_NEED_SSL_TSL) - pycurl_ssl_init(); + if (pycurl_ssl_init() != 0) { + goto error; + } #endif collections_module = PyImport_ImportModule("collections"); diff -Nru pycurl-7.21.5/src/multi.c pycurl-7.43.0/src/multi.c --- pycurl-7.21.5/src/multi.c 2015-11-08 15:09:43.000000000 +0000 +++ pycurl-7.43.0/src/multi.c 2016-02-04 04:09:31.000000000 +0000 @@ -246,9 +246,150 @@ static PyObject * +do_multi_setopt_int(CurlMultiObject *self, int option, PyObject *obj) +{ + long d = PyInt_AsLong(obj); + switch(option) { + case CURLMOPT_MAXCONNECTS: + case CURLMOPT_PIPELINING: +#ifdef HAVE_CURL_7_30_0_PIPELINE_OPTS + case CURLMOPT_MAX_HOST_CONNECTIONS: + case CURLMOPT_MAX_TOTAL_CONNECTIONS: + case CURLMOPT_MAX_PIPELINE_LENGTH: + case CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE: + case CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE: +#endif + curl_multi_setopt(self->multi_handle, option, d); + break; + default: + PyErr_SetString(PyExc_TypeError, "integers are not supported for this option"); + return NULL; + } + Py_RETURN_NONE; +} + + +static PyObject * +do_multi_setopt_charpp(CurlMultiObject *self, int option, int which, PyObject *obj) +{ + Py_ssize_t len, i; + int res; + static const char *empty_list[] = { NULL }; + char **list = NULL; + PyObject **encoded_objs = NULL; + PyObject *encoded_obj = NULL; + char *encoded_str; + PyObject *rv = NULL; + + len = PyListOrTuple_Size(obj, which); + if (len == 0) { + res = curl_multi_setopt(self->multi_handle, option, empty_list); + if (res != CURLE_OK) { + CURLERROR_RETVAL_MULTI_DONE(); + } + Py_RETURN_NONE; + } + + /* add NULL terminator as the last list item */ + list = PyMem_New(char *, len+1); + if (list == NULL) { + PyErr_NoMemory(); + return NULL; + } + /* no need for the NULL terminator here */ + encoded_objs = PyMem_New(PyObject *, len); + if (encoded_objs == NULL) { + PyErr_NoMemory(); + goto done; + } + memset(encoded_objs, 0, sizeof(PyObject *) * len); + + for (i = 0; i < len; i++) { + PyObject *listitem = PyListOrTuple_GetItem(obj, i, which); + if (!PyText_Check(listitem)) { + PyErr_SetString(ErrorObject, "list/tuple items must be strings"); + goto done; + } + encoded_str = PyText_AsString_NoNUL(listitem, &encoded_obj); + if (encoded_str == NULL) { + goto done; + } + list[i] = encoded_str; + encoded_objs[i] = encoded_obj; + } + list[len] = NULL; + + res = curl_multi_setopt(self->multi_handle, option, list); + if (res != CURLE_OK) { + rv = NULL; + CURLERROR_RETVAL_MULTI_DONE(); + } + + rv = Py_None; +done: + if (encoded_objs) { + for (i = 0; i < len; i++) { + Py_XDECREF(encoded_objs[i]); + } + PyMem_Free(encoded_objs); + } + PyMem_Free(list); + return rv; +} + + +static PyObject * +do_multi_setopt_list(CurlMultiObject *self, int option, int which, PyObject *obj) +{ + switch(option) { +#ifdef HAVE_CURL_7_30_0_PIPELINE_OPTS + case CURLMOPT_PIPELINING_SITE_BL: + case CURLMOPT_PIPELINING_SERVER_BL: +#endif + return do_multi_setopt_charpp(self, option, which, obj); + break; + default: + PyErr_SetString(PyExc_TypeError, "lists/tuples are not supported for this option"); + return NULL; + } + Py_RETURN_NONE; +} + + +static PyObject * +do_multi_setopt_callable(CurlMultiObject *self, int option, PyObject *obj) +{ + /* We use function types here to make sure that our callback + * definitions exactly match the interface. + */ + const curl_multi_timer_callback t_cb = multi_timer_callback; + const curl_socket_callback s_cb = multi_socket_callback; + + switch(option) { + case CURLMOPT_SOCKETFUNCTION: + curl_multi_setopt(self->multi_handle, CURLMOPT_SOCKETFUNCTION, s_cb); + curl_multi_setopt(self->multi_handle, CURLMOPT_SOCKETDATA, self); + Py_INCREF(obj); + self->s_cb = obj; + break; + case CURLMOPT_TIMERFUNCTION: + curl_multi_setopt(self->multi_handle, CURLMOPT_TIMERFUNCTION, t_cb); + curl_multi_setopt(self->multi_handle, CURLMOPT_TIMERDATA, self); + Py_INCREF(obj); + self->t_cb = obj; + break; + default: + PyErr_SetString(PyExc_TypeError, "callables are not supported for this option"); + return NULL; + } + Py_RETURN_NONE; +} + + +static PyObject * do_multi_setopt(CurlMultiObject *self, PyObject *args) { - int option; + int option, which; PyObject *obj; if (!PyArg_ParseTuple(args, "iO:setopt", &option, &obj)) @@ -266,52 +407,20 @@ /* Handle the case of integer arguments */ if (PyInt_Check(obj)) { - long d = PyInt_AsLong(obj); - switch(option) { - case CURLMOPT_MAXCONNECTS: - case CURLMOPT_PIPELINING: -#ifdef HAVE_CURL_7_30_0_PIPELINE_OPTS - case CURLMOPT_MAX_HOST_CONNECTIONS: - case CURLMOPT_MAX_TOTAL_CONNECTIONS: - case CURLMOPT_MAX_PIPELINE_LENGTH: - case CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE: - case CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE: -#endif - curl_multi_setopt(self->multi_handle, option, d); - break; - default: - PyErr_SetString(PyExc_TypeError, "integers are not supported for this option"); - return NULL; - } - Py_RETURN_NONE; + return do_multi_setopt_int(self, option, obj); + } + + /* Handle the case of list or tuple objects */ + which = PyListOrTuple_Check(obj); + if (which) { + return do_multi_setopt_list(self, option, which, obj); } + if (PyFunction_Check(obj) || PyCFunction_Check(obj) || PyCallable_Check(obj) || PyMethod_Check(obj)) { - /* We use function types here to make sure that our callback - * definitions exactly match the interface. - */ - const curl_multi_timer_callback t_cb = multi_timer_callback; - const curl_socket_callback s_cb = multi_socket_callback; - - switch(option) { - case CURLMOPT_SOCKETFUNCTION: - curl_multi_setopt(self->multi_handle, CURLMOPT_SOCKETFUNCTION, s_cb); - curl_multi_setopt(self->multi_handle, CURLMOPT_SOCKETDATA, self); - Py_INCREF(obj); - self->s_cb = obj; - break; - case CURLMOPT_TIMERFUNCTION: - curl_multi_setopt(self->multi_handle, CURLMOPT_TIMERFUNCTION, t_cb); - curl_multi_setopt(self->multi_handle, CURLMOPT_TIMERDATA, self); - Py_INCREF(obj); - self->t_cb = obj; - break; - default: - PyErr_SetString(PyExc_TypeError, "callables are not supported for this option"); - return NULL; - } - Py_RETURN_NONE; + return do_multi_setopt_callable(self, option, obj); } + /* Failed to match any of the function signatures -- return error */ error: PyErr_SetString(PyExc_TypeError, "invalid arguments to setopt"); @@ -374,7 +483,7 @@ curl_socket_t socket; int ev_bitmask; int running = -1; - + if (!PyArg_ParseTuple(args, "ii:socket_action", &socket, &ev_bitmask)) return NULL; if (check_multi_state(self, 1 | 2, "socket_action") != 0) { @@ -642,7 +751,7 @@ } /* Fetch the curl object that corresponds to the curl handle in the message */ - res = curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &co); + res = curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, (char **) &co); if (res != CURLE_OK || co == NULL) { Py_DECREF(err_list); Py_DECREF(ok_list); diff -Nru pycurl-7.21.5/src/pycurl.h pycurl-7.43.0/src/pycurl.h --- pycurl-7.21.5/src/pycurl.h 2016-01-02 01:49:30.000000000 +0000 +++ pycurl-7.43.0/src/pycurl.h 2016-02-02 03:06:02.000000000 +0000 @@ -35,6 +35,14 @@ #undef NDEBUG #include +#define MAKE_LIBCURL_VERSION(major, minor, patch) \ + ((major) * 0x10000 + (minor) * 0x100 + (patch)) + +/* spot check */ +#if MAKE_LIBCURL_VERSION(7, 21, 16) != 0x071510 +# error MAKE_LIBCURL_VERSION is not working correctly +#endif + #if defined(PYCURL_SINGLE_FILE) # define PYCURL_INTERNAL static #else @@ -193,7 +201,7 @@ #endif /* HAVE_CURL_SSL */ #if defined(PYCURL_NEED_SSL_TSL) -PYCURL_INTERNAL void pycurl_ssl_init(void); +PYCURL_INTERNAL int pycurl_ssl_init(void); PYCURL_INTERNAL void pycurl_ssl_cleanup(void); #endif @@ -273,6 +281,20 @@ return NULL; \ } while (0) +#define CURLERROR_SET_RETVAL() do {\ + PyObject *v; \ + self->error[sizeof(self->error) - 1] = 0; \ + v = Py_BuildValue("(is)", (int) (res), self->error); \ + if (v != NULL) { PyErr_SetObject(ErrorObject, v); Py_DECREF(v); } \ +} while (0) + +#define CURLERROR_RETVAL_MULTI_DONE() do {\ + PyObject *v; \ + v = Py_BuildValue("(i)", (int) (res)); \ + if (v != NULL) { PyErr_SetObject(ErrorObject, v); Py_DECREF(v); } \ + goto done; \ +} while (0) + /* Raise exception based on return value `res' and custom message */ #define CURLERROR_MSG(msg) do {\ PyObject *v; const char *m = (msg); \ @@ -323,6 +345,9 @@ /* List of INC'ed references associated with httppost. */ PyObject *httppost_ref_list; struct curl_slist *httpheader; +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 37, 0) + struct curl_slist *proxyheader; +#endif struct curl_slist *http200aliases; struct curl_slist *quote; struct curl_slist *postquote; @@ -339,6 +364,9 @@ PyObject *h_cb; PyObject *r_cb; PyObject *pro_cb; +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 32, 0) + PyObject *xferinfo_cb; +#endif PyObject *debug_cb; PyObject *ioctl_cb; PyObject *opensocket_cb; @@ -414,13 +442,6 @@ #endif /* WITH_THREAD */ -#if defined(PYCURL_NEED_SSL_TSL) -PYCURL_INTERNAL void -pycurl_ssl_init(void); -PYCURL_INTERNAL void -pycurl_ssl_cleanup(void); -#endif - #if PY_MAJOR_VERSION >= 3 PYCURL_INTERNAL PyObject * my_getattro(PyObject *co, PyObject *name, PyObject *dict1, PyObject *dict2, PyMethodDef *m); diff -Nru pycurl-7.21.5/src/threadsupport.c pycurl-7.43.0/src/threadsupport.c --- pycurl-7.21.5/src/threadsupport.c 2015-11-08 15:09:43.000000000 +0000 +++ pycurl-7.43.0/src/threadsupport.c 2016-01-24 01:01:42.000000000 +0000 @@ -106,19 +106,33 @@ return (unsigned long) PyThread_get_thread_ident(); } -PYCURL_INTERNAL void +PYCURL_INTERNAL int pycurl_ssl_init(void) { int i, c = CRYPTO_num_locks(); - pycurl_openssl_tsl = PyMem_Malloc(c * sizeof(PyThread_type_lock)); + pycurl_openssl_tsl = PyMem_New(PyThread_type_lock, c); + if (pycurl_openssl_tsl == NULL) { + PyErr_NoMemory(); + return -1; + } + memset(pycurl_openssl_tsl, 0, sizeof(PyThread_type_lock) * c); for (i = 0; i < c; ++i) { pycurl_openssl_tsl[i] = PyThread_allocate_lock(); + if (pycurl_openssl_tsl[i] == NULL) { + for (--i; i >= 0; --i) { + PyThread_free_lock(pycurl_openssl_tsl[i]); + } + PyMem_Free(pycurl_openssl_tsl); + PyErr_NoMemory(); + return -1; + } } CRYPTO_set_id_callback(pycurl_ssl_id); CRYPTO_set_locking_callback(pycurl_ssl_lock); + return 0; } PYCURL_INTERNAL void @@ -180,10 +194,11 @@ pycurl_ssl_mutex_unlock }; -PYCURL_INTERNAL void +PYCURL_INTERNAL int pycurl_ssl_init(void) { gcry_control(GCRYCTL_SET_THREAD_CBS, &pycurl_gnutls_tsl); + return 0; } PYCURL_INTERNAL void @@ -213,17 +228,22 @@ share_lock_new(void) { int i; - ShareLock *lock = (ShareLock*)PyMem_Malloc(sizeof(ShareLock)); + ShareLock *lock = PyMem_New(ShareLock, 1); + if (lock == NULL) { + PyErr_NoMemory(); + return NULL; + } - assert(lock); for (i = 0; i < CURL_LOCK_DATA_LAST; ++i) { lock->locks[i] = PyThread_allocate_lock(); if (lock->locks[i] == NULL) { + PyErr_NoMemory(); goto error; } } return lock; - error: + +error: for (--i; i >= 0; --i) { PyThread_free_lock(lock->locks[i]); lock->locks[i] = NULL; @@ -266,7 +286,7 @@ PYCURL_INTERNAL void pycurl_ssl_init(void) { - return; + return 0; } PYCURL_INTERNAL void diff -Nru pycurl-7.21.5/tests/app.py pycurl-7.43.0/tests/app.py --- pycurl-7.21.5/tests/app.py 2015-11-08 15:09:43.000000000 +0000 +++ pycurl-7.43.0/tests/app.py 2016-01-24 01:01:42.000000000 +0000 @@ -78,7 +78,7 @@ @app.route('/header') def header(): - return bottle.request.headers[bottle.request.query['h']] + return bottle.request.headers.get(bottle.request.query['h'], '') # This is a hacky endpoint to test non-ascii text being given to libcurl # via headers. @@ -106,14 +106,18 @@ param = param.encode('latin').decode('utf8') return param -def pause_writer(): +def pause_writer(interval): yield 'part1' - _time.sleep(0.5) + _time.sleep(interval) yield 'part2' @app.route('/pause') def pause(): - return pause_writer() + return pause_writer(0.5) + +@app.route('/long_pause') +def long_pause(): + return pause_writer(1) @app.route('/utf8_body') def utf8_body(): diff -Nru pycurl-7.21.5/tests/getinfo_test.py pycurl-7.43.0/tests/getinfo_test.py --- pycurl-7.21.5/tests/getinfo_test.py 2015-11-08 15:09:43.000000000 +0000 +++ pycurl-7.43.0/tests/getinfo_test.py 2016-02-01 05:14:18.000000000 +0000 @@ -14,15 +14,16 @@ class GetinfoTest(unittest.TestCase): def setUp(self): self.curl = pycurl.Curl() - + def tearDown(self): self.curl.close() - + @flaky.flaky(max_runs=3) def test_getinfo(self): self.make_request() - + self.assertEqual(200, self.curl.getinfo(pycurl.HTTP_CODE)) + self.assertEqual(200, self.curl.getinfo(pycurl.RESPONSE_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 @@ -38,14 +39,14 @@ self.assertEqual(0, self.curl.getinfo(pycurl.REDIRECT_COUNT)) # time not requested self.assertEqual(-1, self.curl.getinfo(pycurl.INFO_FILETIME)) - + @util.min_libcurl(7, 21, 0) def test_primary_port_etc(self): self.make_request() assert type(self.curl.getinfo(pycurl.PRIMARY_PORT)) is int assert type(self.curl.getinfo(pycurl.LOCAL_IP)) is str assert type(self.curl.getinfo(pycurl.LOCAL_PORT)) is int - + def make_request(self): self.curl.setopt(pycurl.URL, 'http://localhost:8380/success') sio = util.BytesIO() diff -Nru pycurl-7.21.5/tests/memory_mgmt_test.py pycurl-7.43.0/tests/memory_mgmt_test.py --- pycurl-7.21.5/tests/memory_mgmt_test.py 2015-11-08 15:09:43.000000000 +0000 +++ pycurl-7.43.0/tests/memory_mgmt_test.py 2016-01-24 01:01:42.000000000 +0000 @@ -23,14 +23,14 @@ gc.collect() print("Tracked objects:", len(gc.get_objects())) - + def maybe_print_objects(self): if debug: print("Tracked objects:", len(gc.get_objects())) - + def tearDown(self): gc.set_debug(0) - + def test_multi_collection(self): gc.collect() self.maybe_enable_debug() @@ -42,7 +42,7 @@ curl = pycurl.Curl() multi.add_handle(curl) t.append(curl) - + c_id = id(curl) searches.append(c_id) m_id = id(multi) @@ -63,12 +63,12 @@ self.maybe_print_objects() gc.collect() self.maybe_print_objects() - + objects = gc.get_objects() for search in searches: for object in objects: assert search != id(object) - + def test_multi_cycle(self): gc.collect() self.maybe_enable_debug() @@ -80,7 +80,7 @@ curl = pycurl.Curl() multi.add_handle(curl) t.append(curl) - + c_id = id(curl) searches.append(c_id) m_id = id(multi) @@ -95,12 +95,12 @@ self.maybe_print_objects() gc.collect() self.maybe_print_objects() - + objects = gc.get_objects() for search in searches: for object in objects: assert search != id(object) - + def test_share_collection(self): gc.collect() self.maybe_enable_debug() @@ -112,7 +112,7 @@ curl = pycurl.Curl() curl.setopt(curl.SHARE, share) t.append(curl) - + c_id = id(curl) searches.append(c_id) m_id = id(share) @@ -133,12 +133,12 @@ self.maybe_print_objects() gc.collect() self.maybe_print_objects() - + objects = gc.get_objects() for search in searches: for object in objects: assert search != id(object) - + def test_share_cycle(self): gc.collect() self.maybe_enable_debug() @@ -150,7 +150,7 @@ curl = pycurl.Curl() curl.setopt(curl.SHARE, share) t.append(curl) - + c_id = id(curl) searches.append(c_id) m_id = id(share) @@ -165,7 +165,7 @@ self.maybe_print_objects() gc.collect() self.maybe_print_objects() - + objects = gc.get_objects() for search in searches: for object in objects: @@ -180,7 +180,7 @@ m = pycurl.CurlMulti() c.close() del m, c - + def test_cyclic_gc(self): gc.collect() c = pycurl.Curl() @@ -212,7 +212,7 @@ assert id(object) != c_id #if opts.verbose >= 1: #print("Tracked objects:", len(gc.get_objects())) - + def test_refcounting_bug_in_reset(self): try: range_generator = xrange @@ -222,31 +222,35 @@ for i in range_generator(100000): c = pycurl.Curl() c.reset() - + def test_writefunction_collection(self): self.check_callback(pycurl.WRITEFUNCTION) - + def test_headerfunction_collection(self): self.check_callback(pycurl.HEADERFUNCTION) - + def test_readfunction_collection(self): self.check_callback(pycurl.READFUNCTION) - + def test_progressfunction_collection(self): self.check_callback(pycurl.PROGRESSFUNCTION) - + + @util.min_libcurl(7, 32, 0) + def test_xferinfofunction_collection(self): + self.check_callback(pycurl.XFERINFOFUNCTION) + def test_debugfunction_collection(self): self.check_callback(pycurl.DEBUGFUNCTION) - + def test_ioctlfunction_collection(self): self.check_callback(pycurl.IOCTLFUNCTION) - + def test_opensocketfunction_collection(self): self.check_callback(pycurl.OPENSOCKETFUNCTION) - + def test_seekfunction_collection(self): self.check_callback(pycurl.SEEKFUNCTION) - + def check_callback(self, callback): # Note: extracting a context manager seems to result in # everything being garbage collected even if the C code @@ -254,11 +258,11 @@ object_count = 0 gc.collect() object_count = len(gc.get_objects()) - + c = pycurl.Curl() c.setopt(callback, lambda x: True) del c - + gc.collect() new_object_count = len(gc.get_objects()) # it seems that GC sometimes collects something that existed @@ -268,18 +272,18 @@ def test_postfields_unicode_memory_leak_gh252(self): # this test passed even before the memory leak was fixed, # not sure why. - + c = pycurl.Curl() gc.collect() before_object_count = len(gc.get_objects()) for i in range(100000): c.setopt(pycurl.POSTFIELDS, util.u('hello world')) - + gc.collect() after_object_count = len(gc.get_objects()) self.assert_(after_object_count <= before_object_count + 1000, 'Grew from %d to %d objects' % (before_object_count, after_object_count)) - + def test_form_bufferptr_memory_leak_gh267(self): c = pycurl.Curl() gc.collect() @@ -294,7 +298,7 @@ ("post1", (pycurl.FORM_BUFFER, 'foo.txt', pycurl.FORM_BUFFERPTR, "data1")), ("post2", (pycurl.FORM_BUFFER, 'bar.txt', pycurl.FORM_BUFFERPTR, "data2")), ]) - + gc.collect() after_object_count = len(gc.get_objects()) self.assert_(after_object_count <= before_object_count + 1000, 'Grew from %d to %d objects' % (before_object_count, after_object_count)) diff -Nru pycurl-7.21.5/tests/multi_option_constants_test.py pycurl-7.43.0/tests/multi_option_constants_test.py --- pycurl-7.21.5/tests/multi_option_constants_test.py 2015-11-08 15:09:43.000000000 +0000 +++ pycurl-7.43.0/tests/multi_option_constants_test.py 2016-01-24 01:01:42.000000000 +0000 @@ -2,18 +2,34 @@ # -*- coding: utf-8 -*- # vi:ts=4:et +import sys import pycurl import unittest from . import util class MultiOptionConstantsTest(unittest.TestCase): + def setUp(self): + super(MultiOptionConstantsTest, self).setUp() + + self.m = pycurl.CurlMulti() + + def tearDown(self): + super(MultiOptionConstantsTest, self).tearDown() + + self.m.close() + def test_option_constant_on_pycurl(self): assert hasattr(pycurl, 'M_PIPELINING') def test_option_constant_on_curlmulti(self): - m = pycurl.CurlMulti() - assert hasattr(m, 'M_PIPELINING') + assert hasattr(self.m, 'M_PIPELINING') + + @util.min_libcurl(7, 43, 0) + def test_pipe_constants(self): + self.m.setopt(self.m.M_PIPELINING, self.m.PIPE_NOTHING) + self.m.setopt(self.m.M_PIPELINING, self.m.PIPE_HTTP1) + self.m.setopt(self.m.M_PIPELINING, self.m.PIPE_MULTIPLEX) @util.min_libcurl(7, 30, 0) def test_multi_pipeline_opts(self): @@ -22,10 +38,35 @@ assert hasattr(pycurl, 'M_CONTENT_LENGTH_PENALTY_SIZE') assert hasattr(pycurl, 'M_CHUNK_LENGTH_PENALTY_SIZE') assert hasattr(pycurl, 'M_MAX_TOTAL_CONNECTIONS') - m = pycurl.CurlMulti() - m.setopt(pycurl.M_MAX_HOST_CONNECTIONS, 2) - m.setopt(pycurl.M_MAX_PIPELINE_LENGTH, 2) - m.setopt(pycurl.M_CONTENT_LENGTH_PENALTY_SIZE, 2) - m.setopt(pycurl.M_CHUNK_LENGTH_PENALTY_SIZE, 2) - m.setopt(pycurl.M_MAX_TOTAL_CONNECTIONS, 2) - m.close() + self.m.setopt(pycurl.M_MAX_HOST_CONNECTIONS, 2) + self.m.setopt(pycurl.M_MAX_PIPELINE_LENGTH, 2) + self.m.setopt(pycurl.M_CONTENT_LENGTH_PENALTY_SIZE, 2) + self.m.setopt(pycurl.M_CHUNK_LENGTH_PENALTY_SIZE, 2) + self.m.setopt(pycurl.M_MAX_TOTAL_CONNECTIONS, 2) + + @util.min_libcurl(7, 30, 0) + def test_multi_pipelining_site_bl(self): + self.check_multi_charpp_option(self.m.M_PIPELINING_SITE_BL) + + @util.min_libcurl(7, 30, 0) + def test_multi_pipelining_server_bl(self): + self.check_multi_charpp_option(self.m.M_PIPELINING_SERVER_BL) + + def check_multi_charpp_option(self, option): + input = [util.b('test1'), util.b('test2')] + self.m.setopt(option, input) + input = [util.u('test1'), util.u('test2')] + self.m.setopt(option, input) + self.m.setopt(option, []) + input = (util.b('test1'), util.b('test2')) + self.m.setopt(option, input) + input = (util.u('test1'), util.u('test2')) + self.m.setopt(option, input) + self.m.setopt(option, ()) + + try: + self.m.setopt(option, 1) + self.fail('expected to raise') + except TypeError: + exc = sys.exc_info()[1] + assert 'integers are not supported for this option' in str(exc) diff -Nru pycurl-7.21.5/tests/open_socket_cb_test.py pycurl-7.43.0/tests/open_socket_cb_test.py --- pycurl-7.21.5/tests/open_socket_cb_test.py 2016-01-01 05:07:53.000000000 +0000 +++ pycurl-7.43.0/tests/open_socket_cb_test.py 2016-01-24 01:01:42.000000000 +0000 @@ -104,9 +104,10 @@ assert socket_open_called_unix if util.py3: assert isinstance(socket_open_address, bytes) + self.assertEqual(b'/tmp/pycurl-test-path.sock', socket_open_address) else: assert isinstance(socket_open_address, str) - self.assertEqual('/tmp/pycurl-test-path.sock', socket_open_address) + self.assertEqual('/tmp/pycurl-test-path.sock', socket_open_address) def test_socket_open_none(self): self.curl.setopt(pycurl.OPENSOCKETFUNCTION, None) diff -Nru pycurl-7.21.5/tests/option_constants_test.py pycurl-7.43.0/tests/option_constants_test.py --- pycurl-7.21.5/tests/option_constants_test.py 2016-01-02 01:49:30.000000000 +0000 +++ pycurl-7.43.0/tests/option_constants_test.py 2016-02-02 02:54:05.000000000 +0000 @@ -341,3 +341,108 @@ self.curl.setopt(self.curl.USE_SSL, self.curl.USESSL_TRY) self.curl.setopt(self.curl.USE_SSL, self.curl.USESSL_CONTROL) self.curl.setopt(self.curl.USE_SSL, self.curl.USESSL_ALL) + + def test_encoding(self): + # old name for ACCEPT_ENCODING + self.curl.setopt(self.curl.ENCODING, "") + self.curl.setopt(self.curl.ENCODING, "application/json") + + @util.min_libcurl(7, 21, 6) + def test_accept_encoding(self): + self.curl.setopt(self.curl.ACCEPT_ENCODING, "") + self.curl.setopt(self.curl.ACCEPT_ENCODING, "application/json") + + @util.min_libcurl(7, 21, 6) + def test_transfer_encoding(self): + self.curl.setopt(self.curl.TRANSFER_ENCODING, True) + + @util.min_libcurl(7, 24, 0) + def test_accepttimeout_ms(self): + self.curl.setopt(self.curl.ACCEPTTIMEOUT_MS, 1000) + + @util.min_libcurl(7, 25, 0) + def test_tcp_keepalive(self): + self.curl.setopt(self.curl.TCP_KEEPALIVE, True) + + @util.min_libcurl(7, 25, 0) + def test_tcp_keepidle(self): + self.curl.setopt(self.curl.TCP_KEEPIDLE, 100) + + @util.min_libcurl(7, 25, 0) + def test_tcp_keepintvl(self): + self.curl.setopt(self.curl.TCP_KEEPINTVL, 100) + + @util.min_libcurl(7, 36, 0) + def test_expect_100_timeout_ms(self): + self.curl.setopt(self.curl.EXPECT_100_TIMEOUT_MS, 100) + + @util.min_libcurl(7, 37, 0) + def test_headeropt(self): + self.curl.setopt(self.curl.HEADEROPT, self.curl.HEADER_UNIFIED) + self.curl.setopt(self.curl.HEADEROPT, self.curl.HEADER_SEPARATE) + + @util.min_libcurl(7, 42, 0) + def test_path_as_is(self): + self.curl.setopt(self.curl.PATH_AS_IS, True) + + @util.min_libcurl(7, 43, 0) + def test_pipewait(self): + self.curl.setopt(self.curl.PIPEWAIT, True) + + def test_http_version(self): + self.curl.setopt(self.curl.HTTP_VERSION, self.curl.CURL_HTTP_VERSION_NONE) + self.curl.setopt(self.curl.HTTP_VERSION, self.curl.CURL_HTTP_VERSION_1_0) + self.curl.setopt(self.curl.HTTP_VERSION, self.curl.CURL_HTTP_VERSION_1_1) + + @nose.plugins.attrib.attr('http2') + @util.min_libcurl(7, 33, 0) + def test_http_version_2_0(self): + self.curl.setopt(self.curl.HTTP_VERSION, self.curl.CURL_HTTP_VERSION_2_0) + + @nose.plugins.attrib.attr('http2') + @util.min_libcurl(7, 43, 0) + def test_http_version_2(self): + self.curl.setopt(self.curl.HTTP_VERSION, self.curl.CURL_HTTP_VERSION_2) + + @nose.plugins.attrib.attr('http2') + @util.min_libcurl(7, 47, 0) + def test_http_version_2tls(self): + self.curl.setopt(self.curl.HTTP_VERSION, self.curl.CURL_HTTP_VERSION_2TLS) + + @util.min_libcurl(7, 21, 5) + def test_sockopt_constants(self): + assert self.curl.SOCKOPT_OK is not None + assert self.curl.SOCKOPT_ERROR is not None + assert self.curl.SOCKOPT_ALREADY_CONNECTED is not None + + @util.min_libcurl(7, 40, 0) + def test_proto_smb(self): + assert self.curl.PROTO_SMB is not None + assert self.curl.PROTO_SMBS is not None + + @util.min_libcurl(7, 21, 4) + @util.only_ssl_backends('openssl', 'gnutls') + def test_tlsauth(self): + self.curl.setopt(self.curl.TLSAUTH_TYPE, "SRP") + self.curl.setopt(self.curl.TLSAUTH_USERNAME, "test") + self.curl.setopt(self.curl.TLSAUTH_PASSWORD, "test") + + @util.min_libcurl(7, 45, 0) + def test_default_protocol(self): + self.curl.setopt(self.curl.DEFAULT_PROTOCOL, "http") + + @util.min_libcurl(7, 20, 0) + def test_ftp_use_pret(self): + self.curl.setopt(self.curl.FTP_USE_PRET, True) + + @util.min_libcurl(7, 34, 0) + def test_login_options(self): + self.curl.setopt(self.curl.LOGIN_OPTIONS, 'AUTH=NTLM') + + @util.min_libcurl(7, 31, 0) + def test_sasl_ir(self): + self.curl.setopt(self.curl.SASL_IR, True) + + @util.min_libcurl(7, 33, 0) + def test_xauth_bearer(self): + self.curl.setopt(self.curl.XOAUTH2_BEARER, 'test') diff -Nru pycurl-7.21.5/tests/post_test.py pycurl-7.43.0/tests/post_test.py --- pycurl-7.21.5/tests/post_test.py 2015-11-08 15:09:43.000000000 +0000 +++ pycurl-7.43.0/tests/post_test.py 2016-02-02 02:52:05.000000000 +0000 @@ -24,28 +24,28 @@ 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.urlencode_and_check(pf) - + def test_post_multiple_fields(self): pf = {'field1':'value1', 'field2':'value2 with blanks', 'field3':'value3'} self.urlencode_and_check(pf) - + def test_post_fields_with_ampersand(self): pf = {'field1':'value1', 'field2':'value2 with blanks and & chars', 'field3':'value3'} self.urlencode_and_check(pf) - + def urlencode_and_check(self, pf): self.curl.setopt(pycurl.URL, 'http://localhost:8380/postfields') 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: @@ -64,7 +64,7 @@ # 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.BytesIO() self.curl.setopt(pycurl.WRITEFUNCTION, sio.write) @@ -73,7 +73,7 @@ body = sio.getvalue().decode() 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')) @@ -100,7 +100,7 @@ 'data': contents, }] self.check_post(send, expect, 'http://localhost:8380/files') - + def test_post_byte_buffer(self): contents = util.b('hello, world!') send = [ @@ -112,7 +112,7 @@ 'data': 'hello, world!', }] self.check_post(send, expect, 'http://localhost:8380/files') - + def test_post_unicode_buffer(self): contents = util.u('hello, world!') send = [ @@ -124,7 +124,7 @@ 'data': 'hello, world!', }] self.check_post(send, expect, 'http://localhost:8380/files') - + def test_post_tuple_of_tuples_of_tuples(self): contents = util.u('hello, world!') send = ( @@ -136,7 +136,7 @@ 'data': 'hello, world!', }] self.check_post(send, expect, 'http://localhost:8380/files') - + def test_post_tuple_of_lists_of_tuples(self): contents = util.u('hello, world!') send = ( @@ -148,7 +148,7 @@ 'data': 'hello, world!', }] self.check_post(send, expect, 'http://localhost:8380/files') - + def test_post_tuple_of_tuple_of_lists(self): contents = util.u('hello, world!') send = ( @@ -160,7 +160,7 @@ 'data': 'hello, world!', }] self.check_post(send, expect, 'http://localhost:8380/files') - + def test_post_list_of_tuple_of_tuples(self): contents = util.u('hello, world!') send = [ @@ -172,7 +172,7 @@ 'data': 'hello, world!', }] self.check_post(send, expect, 'http://localhost:8380/files') - + def test_post_list_of_list_of_lists(self): contents = util.u('hello, world!') send = [ @@ -184,7 +184,7 @@ 'data': 'hello, world!', }] 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) diff -Nru pycurl-7.21.5/tests/run.sh pycurl-7.43.0/tests/run.sh --- pycurl-7.21.5/tests/run.sh 2016-01-02 01:52:43.000000000 +0000 +++ pycurl-7.43.0/tests/run.sh 2016-01-24 01:01:42.000000000 +0000 @@ -25,5 +25,5 @@ fi $PYTHON -c 'import pycurl; print(pycurl.version)' -$NOSETESTS -a \!standalone"$extra_attrs" --with-flaky "$@" -$NOSETESTS -a standalone --with-flaky "$@" +$NOSETESTS -a \!standalone"$extra_attrs" --with-flaky --show-skipped "$@" +$NOSETESTS -a standalone --with-flaky --show-skipped "$@" diff -Nru pycurl-7.21.5/tests/setopt_string_test.py pycurl-7.43.0/tests/setopt_string_test.py --- pycurl-7.21.5/tests/setopt_string_test.py 1970-01-01 00:00:00.000000000 +0000 +++ pycurl-7.43.0/tests/setopt_string_test.py 2016-02-01 05:14:18.000000000 +0000 @@ -0,0 +1,30 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- +# vi:ts=4:et + +import pycurl +import unittest +import nose.tools + +from . import appmanager +from . import util + +setup_module, teardown_module = appmanager.setup(('app', 8380)) + +class SetoptTest(unittest.TestCase): + def setUp(self): + self.curl = pycurl.Curl() + + def tearDown(self): + self.curl.close() + + def test_setopt_string(self): + self.curl.setopt_string(pycurl.URL, 'http://localhost:8380/success') + sio = util.BytesIO() + self.curl.setopt(pycurl.WRITEFUNCTION, sio.write) + self.curl.perform() + self.assertEqual('success', sio.getvalue().decode()) + + @nose.tools.raises(TypeError) + def test_setopt_string_integer(self): + self.curl.setopt_string(pycurl.VERBOSE, True) diff -Nru pycurl-7.21.5/tests/setopt_test.py pycurl-7.43.0/tests/setopt_test.py --- pycurl-7.21.5/tests/setopt_test.py 2015-11-08 15:09:43.000000000 +0000 +++ pycurl-7.43.0/tests/setopt_test.py 2016-01-24 01:01:42.000000000 +0000 @@ -6,37 +6,95 @@ import unittest import nose.tools +from . import appmanager +from . import util + +setup_module, teardown_module = appmanager.setup(('app', 8380)) + class SetoptTest(unittest.TestCase): def setUp(self): self.curl = pycurl.Curl() - + def tearDown(self): self.curl.close() - + def test_boolean_value(self): # expect no exceptions raised self.curl.setopt(pycurl.VERBOSE, True) - + def test_integer_value(self): # expect no exceptions raised self.curl.setopt(pycurl.VERBOSE, 1) - + @nose.tools.raises(TypeError) def test_string_value_for_integer_option(self): self.curl.setopt(pycurl.VERBOSE, "Hello, world!") - + def test_string_value(self): # expect no exceptions raised self.curl.setopt(pycurl.URL, 'http://hello.world') - + @nose.tools.raises(TypeError) def test_integer_value_for_string_option(self): self.curl.setopt(pycurl.URL, 1) - + @nose.tools.raises(TypeError) def test_float_value_for_integer_option(self): self.curl.setopt(pycurl.VERBOSE, 1.0) - - def test_list_value(self): - # expect no exceptions raised - self.curl.setopt(pycurl.HTTPHEADER, ['Accept:']) + + def test_httpheader_list(self): + self.curl.setopt(self.curl.HTTPHEADER, ['Accept:']) + + def test_httpheader_tuple(self): + self.curl.setopt(self.curl.HTTPHEADER, ('Accept:',)) + + def test_httpheader_unicode(self): + self.curl.setopt(self.curl.HTTPHEADER, (util.u('Accept:'),)) + + def test_unset_httpheader(self): + self.curl.setopt(self.curl.HTTPHEADER, ('x-test: foo',)) + self.curl.setopt(self.curl.URL, 'http://localhost:8380/header?h=x-test') + io = util.BytesIO() + self.curl.setopt(self.curl.WRITEDATA, io) + self.curl.perform() + self.assertEquals(util.b('foo'), io.getvalue()) + + self.curl.unsetopt(self.curl.HTTPHEADER) + io = util.BytesIO() + self.curl.setopt(self.curl.WRITEDATA, io) + self.curl.perform() + self.assertEquals(util.b(''), io.getvalue()) + + def test_set_httpheader_none(self): + self.curl.setopt(self.curl.HTTPHEADER, ('x-test: foo',)) + self.curl.setopt(self.curl.URL, 'http://localhost:8380/header?h=x-test') + io = util.BytesIO() + self.curl.setopt(self.curl.WRITEDATA, io) + self.curl.perform() + self.assertEquals(util.b('foo'), io.getvalue()) + + self.curl.setopt(self.curl.HTTPHEADER, None) + io = util.BytesIO() + self.curl.setopt(self.curl.WRITEDATA, io) + self.curl.perform() + self.assertEquals(util.b(''), io.getvalue()) + + @util.min_libcurl(7, 37, 0) + def test_proxyheader_list(self): + self.curl.setopt(self.curl.PROXYHEADER, ['Accept:']) + + @util.min_libcurl(7, 37, 0) + def test_proxyheader_tuple(self): + self.curl.setopt(self.curl.PROXYHEADER, ('Accept:',)) + + @util.min_libcurl(7, 37, 0) + def test_proxyheader_unicode(self): + self.curl.setopt(self.curl.PROXYHEADER, (util.u('Accept:'),)) + + @util.min_libcurl(7, 37, 0) + def test_unset_proxyheader(self): + self.curl.unsetopt(self.curl.PROXYHEADER) + + @util.min_libcurl(7, 37, 0) + def test_set_proxyheader_none(self): + self.curl.setopt(self.curl.PROXYHEADER, None) diff -Nru pycurl-7.21.5/tests/util.py pycurl-7.43.0/tests/util.py --- pycurl-7.21.5/tests/util.py 2016-01-01 05:07:53.000000000 +0000 +++ pycurl-7.43.0/tests/util.py 2016-02-04 04:09:31.000000000 +0000 @@ -23,6 +23,8 @@ return s text_type = str binary_type = bytes + + long_int = int else: try: from cStringIO import StringIO @@ -47,6 +49,11 @@ text_type = unicode binary_type = str + if False: + # pacify pyflakes + long = int + long_int = long + 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) @@ -60,7 +67,7 @@ def pycurl_version_less_than(*spec): import pycurl - version = [int(part) for part in pycurl.version_info()[1].split('.')] + version = [int(part) for part in pycurl.version_info()[1].split('-')[0].split('.')] return version_less_than_spec(version, spec) def only_python3(fn): diff -Nru pycurl-7.21.5/tests/xferinfo_cb_test.py pycurl-7.43.0/tests/xferinfo_cb_test.py --- pycurl-7.21.5/tests/xferinfo_cb_test.py 1970-01-01 00:00:00.000000000 +0000 +++ pycurl-7.43.0/tests/xferinfo_cb_test.py 2016-01-24 01:01:42.000000000 +0000 @@ -0,0 +1,74 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- +# vi:ts=4:et + +import unittest +import pycurl + +from . import util +from . import appmanager + +setup_module, teardown_module = appmanager.setup(('app', 8380)) + +class XferinfoCbTest(unittest.TestCase): + def setUp(self): + self.curl = pycurl.Curl() + self.curl.setopt(self.curl.URL, 'http://localhost:8380/long_pause') + + def tearDown(self): + self.curl.close() + + @util.min_libcurl(7, 32, 0) + def test_xferinfo_cb(self): + all_args = [] + + def xferinfofunction(*args): + all_args.append(args) + + self.curl.setopt(pycurl.XFERINFOFUNCTION, xferinfofunction) + self.curl.setopt(pycurl.NOPROGRESS, False) + + self.curl.perform() + assert len(all_args) > 0 + for args in all_args: + assert len(args) == 4 + for arg in args: + assert isinstance(arg, util.long_int) + + @util.min_libcurl(7, 32, 0) + def test_sockoptfunction_fail(self): + called = {} + + def xferinfofunction(*args): + called['called'] = True + return -1 + + self.curl.setopt(pycurl.XFERINFOFUNCTION, xferinfofunction) + self.curl.setopt(pycurl.NOPROGRESS, False) + + try: + self.curl.perform() + self.fail('should have raised') + except pycurl.error as e: + assert e.args[0] in [pycurl.E_ABORTED_BY_CALLBACK], \ + 'Unexpected pycurl error code %s' % e.args[0] + assert called['called'] + + @util.min_libcurl(7, 32, 0) + def test_sockoptfunction_exception(self): + called = {} + + def xferinfofunction(*args): + called['called'] = True + raise ValueError + + self.curl.setopt(pycurl.XFERINFOFUNCTION, xferinfofunction) + self.curl.setopt(pycurl.NOPROGRESS, False) + + try: + self.curl.perform() + self.fail('should have raised') + except pycurl.error as e: + assert e.args[0] in [pycurl.E_ABORTED_BY_CALLBACK], \ + 'Unexpected pycurl error code %s' % e.args[0] + assert called['called'] diff -Nru pycurl-7.21.5/winbuild.py pycurl-7.43.0/winbuild.py --- pycurl-7.21.5/winbuild.py 2016-01-05 07:03:44.000000000 +0000 +++ pycurl-7.43.0/winbuild.py 2016-02-04 04:09:31.000000000 +0000 @@ -37,7 +37,7 @@ # which versions of python to build against python_versions = ['2.6.6', '2.7.10', '3.2.5', '3.3.5', '3.4.3', '3.5.0'] # where pythons are installed -python_path_template = 'c:/dev/%(bitness)s/python%(python_version)s/python' +python_path_template = 'c:/dev/%(bitness)s/python%(python_release)s/python' vc_paths = { # where msvc 9 is installed, for python 2.6 through 3.2 'vc9': None, @@ -61,9 +61,13 @@ use_libssh2 = True libssh2_version = '1.6.0' # which version of libcurl to use, will be downloaded from internet -libcurl_version = '7.46.0' +libcurl_version = '7.47.0' +# virtualenv version +virtualenv_version = '13.1.2' +# whether to build binary wheels +build_wheels = True # pycurl version to build, we should know this ourselves -pycurl_version = '7.21.5' +pycurl_version = '7.43.0' default_vc_paths = { # where msvc 9 is installed, for python 2.6 through 3.2 @@ -158,6 +162,26 @@ os.rename(basename, suffixed_dir) return suffixed_dir +class PythonRelease(str): + @property + def dotless(self): + return self.replace('.', '') + +def python_releases(): + return [PythonRelease('.'.join(version.split('.')[:2])) + for version in python_versions] + +class PythonBinary(object): + def __init__(self, python_release, bitness): + self.python_release = python_release + self.bitness = bitness + + @property + def executable_path(self): + return python_path_template % dict( + python_release=self.python_release.dotless, + bitness=self.bitness) + class Builder(object): def __init__(self, **kwargs): bitness = kwargs.pop('bitness') @@ -522,8 +546,8 @@ class PycurlBuilder(Builder): def __init__(self, **kwargs): - self.python_version = kwargs.pop('python_version') - kwargs['vc_version'] = python_vc_versions[self.python_version] + self.python_release = kwargs.pop('python_release') + kwargs['vc_version'] = python_vc_versions[self.python_release] super(PycurlBuilder, self).__init__(**kwargs) self.pycurl_version = kwargs.pop('pycurl_version') self.libcurl_version = kwargs.pop('libcurl_version') @@ -538,9 +562,11 @@ @property def python_path(self): - return python_path_template % dict( - python_version=self.python_version.replace('.', ''), - bitness=self.bitness) + if build_wheels: + python_path = os.path.join(archives_path, 'venv-%s-%s' % (self.python_release, self.bitness), 'scripts', 'python') + else: + python_path = PythonBinary(self.python_release, self.bitness).executable_path + return python_path @property def platform_indicator(self): @@ -570,7 +596,7 @@ dll_paths = [os.path.abspath(dll_path) for dll_path in dll_paths] with in_dir(os.path.join('pycurl-%s' % self.pycurl_version)): dest_lib_path = 'build/lib.%s-%s' % (self.platform_indicator, - self.python_version) + self.python_release) if not os.path.exists(dest_lib_path): # exists for building additional targets for the same python version os.makedirs(dest_lib_path) @@ -588,19 +614,25 @@ openssl_builder = OpensslBuilder(bitness=self.bitness, vc_version=self.vc_version, openssl_version=self.openssl_version) f.write("set include=%%include%%;%s\n" % openssl_builder.include_path) f.write("set lib=%%lib%%;%s\n" % openssl_builder.lib_path) - f.write("%s setup.py %s --curl-dir=%s %s\n" % ( + #if build_wheels: + #f.write("call %s\n" % os.path.join('..', 'venv-%s-%s' % (self.python_release, self.bitness), 'Scripts', 'activate')) + if build_wheels: + targets = targets + ['bdist_wheel'] + f.write("%s setup.py %s --curl-dir=%s %s\n" % ( self.python_path, ' '.join(targets), libcurl_dir, libcurl_arg)) if 'bdist' in targets: zip_basename_orig = 'pycurl-%s.%s.zip' % ( self.pycurl_version, self.platform_indicator) zip_basename_new = 'pycurl-%s.%s-py%s.zip' % ( - self.pycurl_version, self.platform_indicator, self.python_version) + self.pycurl_version, self.platform_indicator, self.python_release) with zipfile.ZipFile('dist/%s' % zip_basename_orig, 'r') as src_zip: with zipfile.ZipFile('dist/%s' % zip_basename_new, 'w') as dest_zip: for name in src_zip.namelist(): parts = name.split('/') - while parts[0] != 'python%s' % self.python_version.replace('.', ''): - parts.pop(0) + while True: + popped = parts.pop(0) + if popped == 'python%s' % self.python_release.dotless or popped.startswith('venv-'): + break assert len(parts) > 0 new_name = '/'.join(parts) print('Recompressing %s -> %s' % (name, new_name)) @@ -648,22 +680,26 @@ build_dependencies() with in_dir(archives_path): def prepare_pycurl(): - #fetch('http://pycurl.sourceforge.net/download/pycurl-%s.tar.gz' % pycurl_version) + #fetch('https://dl.bintray.com/pycurl/pycurl/pycurl-%s.tar.gz' % pycurl_version) if os.path.exists('pycurl-%s' % pycurl_version): #shutil.rmtree('pycurl-%s' % pycurl_version) subprocess.check_call([rm_path, '-rf', 'pycurl-%s' % pycurl_version]) #subprocess.check_call([tar_path, 'xf', 'pycurl-%s.tar.gz' % pycurl_version]) shutil.copytree('c:/dev/pycurl', 'pycurl-%s' % pycurl_version) + if build_wheels: + with in_dir('pycurl-%s' % pycurl_version): + subprocess.check_call(['sed', '-i', + 's/from distutils.core import setup/from setuptools import setup/', + 'setup.py']) prepare_pycurl() for bitness in (32, 64): - python_releases = ['.'.join(version.split('.')[:2]) for version in python_versions] - for python_version in python_releases: + for python_release in python_releases(): targets = ['bdist', 'bdist_wininst', 'bdist_msi'] - vc_version = python_vc_versions[python_version] + vc_version = python_vc_versions[python_release] builder = PycurlBuilder(bitness=bitness, vc_version=vc_version, - python_version=python_version, pycurl_version=pycurl_version, + python_release=python_release, pycurl_version=pycurl_version, use_zlib=use_zlib, zlib_version=zlib_version, use_openssl=use_openssl, openssl_version=openssl_version, use_cares=use_cares, cares_version=cares_version, @@ -690,6 +726,30 @@ url = 'https://www.python.org/ftp/python/%s/python-%s.msi' % (version, version) fetch(url) +def install_virtualenv(): + with in_dir(archives_path): + fetch('https://pypi.python.org/packages/source/v/virtualenv/virtualenv-%s.tar.gz' % virtualenv_version) + for bitness in (32, 64): + for python_release in python_releases(): + print('Installing virtualenv %s for Python %s (%s bit)' % (virtualenv_version, python_release, bitness)) + sys.stdout.flush() + untar('virtualenv-%s' % virtualenv_version) + with in_dir('virtualenv-%s' % virtualenv_version): + python_binary = PythonBinary(python_release, bitness) + cmd = [python_binary.executable_path, 'setup.py', 'install'] + subprocess.check_call(cmd) + +def create_virtualenvs(): + for bitness in (32, 64): + for python_release in python_releases(): + print('Creating a virtualenv for Python %s (%s bit)' % (python_release, bitness)) + sys.stdout.flush() + with in_dir(archives_path): + python_binary = PythonBinary(python_release, bitness) + venv_basename = 'venv-%s-%s' % (python_release, bitness) + cmd = [python_binary.executable_path, '-m', 'virtualenv', venv_basename] + subprocess.check_call(cmd) + if len(sys.argv) > 1: if sys.argv[1] == 'download': download_pythons() @@ -697,6 +757,10 @@ download_bootstrap_python() elif sys.argv[1] == 'builddeps': build_dependencies() + elif sys.argv[1] == 'installvirtualenv': + install_virtualenv() + elif sys.argv[1] == 'createvirtualenvs': + create_virtualenvs() else: print('Unknown command: %s' % sys.argv[1]) exit(2)