pycurl-7.19.3/ 0000755 0004705 0004705 00000000000 12263734634 012525 5 ustar pie pie 0000000 0000000 pycurl-7.19.3/doc/ 0000755 0004705 0004705 00000000000 12263734634 013272 5 ustar pie pie 0000000 0000000 pycurl-7.19.3/doc/callbacks.html 0000644 0004705 0004705 00000013035 12256232314 016067 0 ustar pie pie 0000000 0000000
PyCurl: Callbacks
Callbacks
For more fine-grained control, libcurl allows a
number of callbacks to be associated with each connection. In
pycurl, callbacks are defined using the setopt()
method for
Curl objects with options WRITEFUNCTION, READFUNCTION, HEADERFUNCTION,
PROGRESSFUNCTION, IOCTLFUNCTION, or DEBUGFUNCTION. These options
correspond to the libcurl options with CURLOPT_* prefix removed. A
callback in pycurl must be either a regular Python function, a class
method or an extension type function.
There are some limitations to some of the options which can be used
concurrently with the pycurl callbacks compared to the libcurl callbacks.
This is to allow different callback functions to be associated with
different Curl objects. More specifically, WRITEDATA cannot
be used with WRITEFUNCTION, READDATA cannot be used with READFUNCTION,
WRITEHEADER cannot be used with HEADERFUNCTION, PROGRESSDATA cannot be
used with PROGRESSFUNCTION, IOCTLDATA cannot be used with IOCTLFUNCTION,
and DEBUGDATA cannot be used with DEBUGFUNCTION.
In practice, these limitations can be overcome by having a callback
function be a class instance method and rather use the class instance
attributes to store per object data such as files used in the callbacks.
The signature of each callback used in pycurl is as follows:
WRITEFUNCTION(
string)
-> number of characters written
READFUNCTION(
number of characters to read)
->
string
HEADERFUNCTION(
string)
-> number of characters written
PROGRESSFUNCTION(
download total, downloaded, upload total, uploaded)
-> status
DEBUGFUNCTION(
debug message type, debug message string)
-> None
IOCTLFUNCTION(
ioctl cmd)
-> status
In addition, READFUNCTION
may return
READFUNC_ABORT
or READFUNC_PAUSE
. See the libcurl
documentation for an explanation of these values.
The WRITEFUNCTION
and HEADERFUNCTION
callbacks
may return None
, which is an alternate way of indicating that
the callback has consumed all of the string passed to it.
Example: Callbacks for document header and body
This example prints the header data to stderr and the body data to
stdout. Also note that neither callback returns the number of bytes
written. For WRITEFUNCTION and HEADERFUNCTION callbacks, returning
None implies that all bytes where written.
## Callback function invoked when body data is ready
def body(buf):
# Print body data to stdout
import sys
sys.stdout.write(buf)
# Returning None implies that all bytes were written
## Callback function invoked when header data is ready
def header(buf):
# Print header data to stderr
import sys
sys.stderr.write(buf)
# Returning None implies that all bytes were written
c = pycurl.Curl()
c.setopt(pycurl.URL, "http://www.python.org/")
c.setopt(pycurl.WRITEFUNCTION, body)
c.setopt(pycurl.HEADERFUNCTION, header)
c.perform()
Example: Download/upload progress callback
This example shows how to use the progress callback. When downloading
a document, the arguments related to uploads are zero, and vice versa.
## Callback function invoked when download/upload has progress
def progress(download_t, download_d, upload_t, upload_d):
print "Total to download", download_t
print "Total downloaded", download_d
print "Total to upload", upload_t
print "Total uploaded", upload_d
c.setopt(c.URL, "http://slashdot.org/")
c.setopt(c.NOPROGRESS, 0)
c.setopt(c.PROGRESSFUNCTION, progress)
c.perform()
Example: Debug callbacks
This example shows how to use the debug callback. The debug message
type is an integer indicating the type of debug message. The
VERBOSE option must be enabled for this callback to be invoked.
def test(debug_type, debug_msg):
print "debug(%d): %s" % (debug_type, debug_msg)
c = pycurl.Curl()
c.setopt(pycurl.URL, "http://curl.haxx.se/")
c.setopt(pycurl.VERBOSE, 1)
c.setopt(pycurl.DEBUGFUNCTION, test)
c.perform()
Other examples
The pycurl distribution also contains a number of test scripts and
examples which show how to use the various callbacks in libcurl.
For instance, the file 'examples/file_upload.py' in the distribution contains
example code for using READFUNCTION, 'tests/test_cb.py' shows
WRITEFUNCTION and HEADERFUNCTION, 'tests/test_debug.py' shows DEBUGFUNCTION,
and 'tests/test_getinfo.py' shows PROGRESSFUNCTION.
pycurl-7.19.3/doc/curlmultiobject.html 0000644 0004705 0004705 00000010772 12256232314 017364 0 ustar pie pie 0000000 0000000
PycURL: CurlMulti Objects
CurlMulti Object
CurlMulti objects have the following methods:
close()
-> None
-
Corresponds to
curl_multi_cleanup()
in libcurl.
This method is automatically called by pycurl when a CurlMulti object no
longer has any references to it, but can also be called
explicitly.
perform()
-> tuple of status and the number of active Curl objects
-
Corresponds to
curl_multi_perform()
in libcurl.
add_handle(
Curl object)
-> None
-
Corresponds to
curl_multi_add_handle()
in libcurl.
This method adds an existing and valid Curl object to the CurlMulti
object.
IMPORTANT NOTE: add_handle does not implicitly add a Python reference
to the Curl object (and thus does not increase the reference count on the Curl
object).
remove_handle(
Curl object)
-> None
-
Corresponds to
curl_multi_remove_handle()
in libcurl.
This method removes an existing and valid Curl object from the CurlMulti
object.
IMPORTANT NOTE: remove_handle does not implicitly remove a Python reference
from the Curl object (and thus does not decrease the reference count on the Curl
object).
fdset()
->
triple of lists with active file descriptors,
readable, writeable, exceptions.
-
Corresponds to
curl_multi_fdset()
in libcurl.
This method extracts the file descriptor information from a CurlMulti object.
The returned lists can be used with the select
module to
poll for events.
Example usage:
import pycurl
c = pycurl.Curl()
c.setopt(pycurl.URL, "http://curl.haxx.se")
m = pycurl.CurlMulti()
m.add_handle(c)
while 1:
ret, num_handles = m.perform()
if ret != pycurl.E_CALL_MULTI_PERFORM: break
while num_handles:
apply(select.select, m.fdset() + (1,))
while 1:
ret, num_handles = m.perform()
if ret != pycurl.E_CALL_MULTI_PERFORM: break
select(
timeout)
->
number of ready file descriptors or -1 on timeout
-
This is a convenience function which simplifies the combined
use of fdset()
and the select
module.
Example usage:
import pycurl
c = pycurl.Curl()
c.setopt(pycurl.URL, "http://curl.haxx.se")
m = pycurl.CurlMulti()
m.add_handle(c)
while 1:
ret, num_handles = m.perform()
if ret != pycurl.E_CALL_MULTI_PERFORM: break
while num_handles:
ret = m.select(1.0)
if ret == -1: continue
while 1:
ret, num_handles = m.perform()
if ret != pycurl.E_CALL_MULTI_PERFORM: break
info_read(
[max])
->
numberof queued messages, a list of successful objects, a list of
failed objects
-
Corresponds to the
curl_multi_info_read()
function in libcurl.
This method extracts at most max messages
from the multi stack and returns them in two lists. The first
list contains the handles which completed successfully and the second
list contains a tuple <curl object, curl error number, curl
error message> for each failed curl object. The number
of queued messages after this method has been called is also
returned.
pycurl-7.19.3/doc/curlobject.html 0000644 0004705 0004705 00000010313 12256232314 016300 0 ustar pie pie 0000000 0000000
PycURL: Curl Objects
Curl Object
Curl objects have the following methods:
close()
-> None
-
Corresponds to
curl_easy_cleanup
in libcurl.
This method is automatically called by pycurl when a Curl object no longer has
any references to it, but can also be called explicitly.
perform()
-> None
-
Corresponds to
curl_easy_perform
in libcurl.
reset()
-> None
-
Corresponds to
curl_easy_reset
in libcurl.
setopt(
option, value)
-> None
-
Corresponds to
curl_easy_setopt
in libcurl, where
option is specified with the CURLOPT_* constants in libcurl,
except that the CURLOPT_ prefix has been removed.
(See below for exceptions.)
The type for
value depends on the option, and can be either a string,
integer, long integer, file object, list, or function.
Example usage:
import pycurl
c = pycurl.Curl()
c.setopt(pycurl.URL, "http://www.python.org/")
c.setopt(pycurl.HTTPHEADER, ["Accept:"])
import StringIO
b = StringIO.StringIO()
c.setopt(pycurl.WRITEFUNCTION, b.write)
c.setopt(pycurl.FOLLOWLOCATION, 1)
c.setopt(pycurl.MAXREDIRS, 5)
c.perform()
print b.getvalue()
...
getinfo(
option)
-> Result
-
Corresponds to
curl_easy_getinfo
in libcurl, where
option is the same as the CURLINFO_* constants in libcurl,
except that the CURLINFO_ prefix has been removed.
(See below for exceptions.)
Result contains an integer, float or string, depending on
which option is given. The getinfo
method should
not be called unless perform
has been called and
finished.
Example usage:
import pycurl
c = pycurl.Curl()
c.setopt(pycurl.URL, "http://sf.net")
c.setopt(pycurl.FOLLOWLOCATION, 1)
c.perform()
print c.getinfo(pycurl.HTTP_CODE), c.getinfo(pycurl.EFFECTIVE_URL)
...
--> 200 "http://sourceforge.net/"
pause(
bitmask)
-> None
-
Corresponds to
curl_easy_pause
in libcurl. The argument should be derived from
the PAUSE_RECV
, PAUSE_SEND
, PAUSE_ALL
and PAUSE_CONT
constants.
errstr()
-> String
-
Returns the internal libcurl error buffer of this handle as a string.
In order to distinguish between similarly-named CURLOPT and
CURLINFO constants, some have OPT_
and INFO_
prefixes. These are
INFO_FILETIME
, OPT_FILETIME
,
INFO_COOKIELIST
(but setopt
uses COOKIELIST
!),
INFO_CERTINFO
, and OPT_CERTINFO
.
The value returned by getinfo(INFO_CERTINFO)
is a list
with one element per certificate in the chain, starting with the leaf;
each element is a sequence
of (
key,
value)
tuples.
pycurl-7.19.3/doc/curlshareobject.html 0000644 0004705 0004705 00000002763 12256232314 017335 0 ustar pie pie 0000000 0000000
PycURL: CurlShare Objects
CurlShare Object
CurlShare objects have the following methods:
setopt(
option, value)
-> None
-
Corresponds to
curl_share_setopt
in libcurl, where
option is specified with the CURLSHOPT_* constants in libcurl,
except that the CURLSHOPT_ prefix has been changed to SH_. Currently,
value must be either LOCK_DATA_COOKIE or LOCK_DATA_DNS.
Example usage:
import pycurl
curl = pycurl.Curl()
s = pycurl.CurlShare()
s.setopt(pycurl.SH_SHARE, pycurl.LOCK_DATA_COOKIE)
s.setopt(pycurl.SH_SHARE, pycurl.LOCK_DATA_DNS)
curl.setopt(pycurl.URL, 'http://curl.haxx.se')
curl.setopt(pycurl.SHARE, s)
curl.perform()
curl.close()
pycurl-7.19.3/doc/files.rst 0000644 0004705 0004705 00000003041 12262776427 015131 0 ustar pie pie 0000000 0000000 Files
=====
In PycURL 7.19.0.3 and below, CURLOPT_READDATA, CURLOPT_WRITEDATA and
CURLOPT_WRITEHEADER options accepted file objects and directly passed
the underlying C library FILE pointers to libcurl.
Python 3 no longer implements files as C library FILE objects.
In PycURL 7.19.3 and above, when running on Python 3, these options
are implemented as calls to CURLOPT_READFUNCTION, CURLOPT_WRITEFUNCTION
and CURLOPT_HEADERFUNCTION, respectively, with the write method of the
Python file object as the parameter. As a result, any Python file-like
object implementing a write method can be passed to CURLOPT_READDATA,
CURLOPT_WRITEDATA or CURLOPT_WRITEHEADER options.
When running PycURL 7.19.3 and above on Python 2, the old behavior of
passing FILE pointers to libcurl remains when a true file object is given
to CURLOPT_READDATA, CURLOPT_WRITEDATA and CURLOPT_WRITEHEADER options.
For consistency with Python 3 behavior these options also accept file-like
objects implementing a write method as arguments, in which case the
Python 3 code path is used converting these options to CURLOPT_*FUNCTION
option calls.
Files given to PycURL as arguments to CURLOPT_READDATA, CURLOPT_WRITEDATA or
CURLOPT_WRITEHEADER must be opened for writing in binary mode. Files opened
in text mode (without "b" flag to open()) expect string objects and writing
to them from PycURL will fail. Similarly when passing f.write method of
an open file to CURLOPT_WRITEFUNCTION or CURLOPT_HEADERFUNCTION, or f.read
to CURLOPT_READFUNCTION, the file must have been be opened in binary mode.
pycurl-7.19.3/doc/internals.rst 0000644 0004705 0004705 00000000504 12256232314 016010 0 ustar pie pie 0000000 0000000 Internals
=========
Cleanup sequence:
x=curl/multi/share
x.close() -> do_x_close -> util_x_close
del x -> do_x_dealloc -> util_x_close
do_* functions are directly invoked by user code.
They check pycurl object state.
util_* functions are only invoked by other pycurl C functions.
They do not check pycurl object state.
pycurl-7.19.3/doc/pycurl.html 0000644 0004705 0004705 00000011267 12262776427 015512 0 ustar pie pie 0000000 0000000
PycURL Documentation
PycURL — A Python Interface To The cURL library
The pycurl package is a Python interface to libcurl (http://curl.haxx.se/libcurl/). pycurl
has been successfully built and tested with Python versions from
2.4 to 2.7.
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 web pages
(http://curl.haxx.se/libcurl/c/).
Module Functionality
pycurl.global_init(
option)
->None
option is one of the constants
pycurl.GLOBAL_SSL, pycurl.GLOBAL_WIN32, pycurl.GLOBAL_ALL,
pycurl.GLOBAL_NOTHING, pycurl.GLOBAL_DEFAULT. Corresponds to
curl_global_init()
in libcurl.
pycurl.global_cleanup()
-> None
-
Corresponds to
curl_global_cleanup()
in libcurl.
pycurl.version
This is a string with version information on libcurl,
corresponding to
curl_version()
in libcurl.
Example usage:
>>> import pycurl
>>> pycurl.version
'libcurl/7.12.3 OpenSSL/0.9.7e zlib/1.2.2.1 libidn/0.5.12'
pycurl.version_info()
-> Tuple
-
Corresponds to
curl_version_info()
in libcurl.
Returns a tuple of information which is similar to the
curl_version_info_data
struct returned by
curl_version_info()
in libcurl.
Example usage:
>>> import pycurl
>>> pycurl.version_info()
(2, '7.12.3', 461827, 'i586-pc-linux-gnu', 1565, 'OpenSSL/0.9.7e', 9465951,
'1.2.2.1', ('ftp', 'gopher', 'telnet', 'dict', 'ldap', 'http', 'file',
'https', 'ftps'), None, 0, '0.5.12')
pycurl.Curl()
-> Curl object
-
This function creates a new
Curl object which corresponds to a
CURL
handle in libcurl. Curl objects automatically
set CURLOPT_VERBOSE to 0, CURLOPT_NOPROGRESS to 1,
provide a default CURLOPT_USERAGENT and setup
CURLOPT_ERRORBUFFER to point to a private error buffer.
pycurl.CurlMulti()
-> CurlMulti object
-
This function creates a new
CurlMulti object which corresponds to
a CURLM
handle in libcurl.
pycurl.CurlShare()
-> CurlShare object
-
This function creates a new
CurlShare object which corresponds to
a CURLSH
handle in libcurl. CurlShare objects is what you
pass as an argument to the SHARE option on Curl objects.
Subsections
Documentation For Developers
pycurl-7.19.3/doc/release-process.rst 0000644 0004705 0004705 00000002016 12263710745 017114 0 ustar pie pie 0000000 0000000 Release Process
===============
1. Ensure changelog is up to date with commits in master.
2. Run `python setup.py manifest`, check that none of the listed files
should be in MANIFEST.in.
3. Make sure travis is green for master.
4. Update version numbers in:
- Changelog
- setup.py
- winbuild.py
- www/htdocs/index.php
5. Copy Changelog to www/htdocs.
6. Draft release notes.
7. `make docs`.
8. `python setup.py sdist`.
9. Manually test install the built package.
10. Build windows packages using winbuild.py.
11. Add windows packages to downloads repo on github.
12. Tag the new version.
13. Register new version with pypi - `python setup.py register`.
14. Upload source distribution to pypi - `python setup.py sdist upload`.
This recreates the source distribution.
15. Add the source distribution to downloads repo on github.
16. Rsync downloads repo to sourceforge.
17. Rsync www/htdocs to sourceforge.
18. Push tag to github pycurl repo.
19. Announce release on mailing list.
20. Link to announcement from website.
pycurl-7.19.3/doc/unicode.rst 0000644 0004705 0004705 00000011633 12262015545 015446 0 ustar pie pie 0000000 0000000 Unicode
=======
Python 2.x
----------
Under Python 2, the string type can hold arbitrary encoded byte strings.
PycURL will pass whatever byte strings it is given verbatim to libcurl.
If your application works with encoded byte strings, you should be able to
pass them to PycURL. If your application works with Unicode data, you need to
encode the data to byte strings yourself. Which encoding to use depends on
the protocol you are working with - HTTP headers should be encoded in latin1,
HTTP request bodies are commonly encoded in utf-8 and their encoding is
specified in the Content-Type header value.
Prior to PycURL 7.19.3, PycURL did not accept Unicode data under Python 2.
Even Unicode strings containing only ASCII code points had to be encoded to
byte strings.
As of PycURL 7.19.3, for compatibility with Python 3, PycURL will accept
Unicode strings under Python 2 provided they contain ASCII code points only.
In other words, PycURL will encode Unicode into ASCII for you. If you supply
a Unicode string containing characters that are outside of ASCII, the call will
fail with a UnicodeEncodeError.
PycURL will return data from libcurl, like request bodies and header values,
as byte strings. If the data is ASCII, you can treat it as string data.
Otherwise you will need to decode the byte strings usisng the correct encoding.
What encoding is correct depends on the protocol and potentially returned
data itself - HTTP response headers are supposed to be latin1 encoded but
encoding of response body is specified in the Content-Type header.
Python 3.x (from PycURL 7.19.3 onward)
--------------------------------------
Under Python 3, the rules are as follows:
PycURL will accept bytes for any string data passed to libcurl (e.g.
arguments to curl_easy_setopt).
PycURL will accept (Unicode) strings for string arguments to curl_easy_setopt.
libcurl generally expects the options to be already appropriately encoded
and escaped (e.g. for CURLOPT_URL). Also, Python 2 code dealing with
Unicode values for string options will typically manually encode such values.
Therefore PycURL will attempt to encode Unicode strings with the ascii codec
only, allowing the application to pass ASCII data in a straightforward manner
but requiring Unicode data to be appropriately encoded.
It may be helpful to remember that libcurl operates on byte arrays.
It is a C library and does not do any Unicode encoding or decoding, offloading
that task on the application using it. PycURL, being a thin wrapper around
libcurl, passes the Unicode encoding and decoding responsibilities to you
except for the trivial case of encoding Unicode data containing only ASCII
characters into ASCII.
Caution: when using CURLOPT_READFUNCTION in tandem with CURLOPT_POSTFIELDSIZE,
as would be done for HTTP for example, take care to pass the length of
encoded data to CURLOPT_POSTFIELDSIZE if you are doing the encoding from
Unicode strings. If you pass the number of Unicode characters rather than
encoded bytes to libcurl, the server will receive wrong Content-Length.
Alternatively you can return Unicode strings from a CURLOPT_READFUNCTION
function, if you are certain they will only contain ASCII code points.
If encoding to ASCII fails, PycURL will return an error to libcurl, and
libcurl in turn will fail the request with an exception like
"read function error/data error". You may examine sys.last_value for
information on exception that occurred during encoding in this case.
PycURL will return all data read from the network as bytes. In particular,
this means that BytesIO should be used rather than StringIO for writing the
response to memory. Header function will also receive bytes.
Because PycURL does not perform encoding or decoding, other than to ASCII,
any file objects that PycURL is meant to interact with via CURLOPT_READDATA,
CURLOPT_WRITEDATA, CURLOPT_WRITEHEADER, CURLOPT_READFUNCTION,
CURLOPT_WRITEFUNCTION or CURLOPT_HEADERFUNCTION must be opened in binary
mode ("b" flag to open() call).
Python 3.x before PycURL 7.19.3
-------------------------------
PycURL did not have official Python 3 support prior to PycURL 7.19.3.
There were two patches on SourceForge (original_, revised_)
adding Python 3 support, but they did not handle Unicode strings correctly.
Instead of using Python encoding functionality, these patches used
C standard library unicode to multibyte conversion functions, and thus
they can have the same behavior as Python encoding code or behave
entirely differently.
Python 3 support as implemented in PycURL 7.19.3 and documented here
does not, as mentioned, actually perform any encoding other than to convert
from Unicode strings containing ASCII-only bytes to ASCII byte strings.
Linux distributions that offered Python 3 packages of PycURL prior to 7.19.3
used SourceForge patches and may behave in ways contradictory to what is
described in this document.
.. _original: http://sourceforge.net/p/pycurl/patches/5/
.. _revised: http://sourceforge.net/p/pycurl/patches/12/
pycurl-7.19.3/examples/ 0000755 0004705 0004705 00000000000 12263734634 014343 5 ustar pie pie 0000000 0000000 pycurl-7.19.3/examples/tests/ 0000755 0004705 0004705 00000000000 12263734634 015505 5 ustar pie pie 0000000 0000000 pycurl-7.19.3/examples/tests/test_gtk.py 0000644 0004705 0004705 00000005154 12262015545 017700 0 ustar pie pie 0000000 0000000 #! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et
import sys, threading
import pycurl
import pygtk
pygtk.require('2.0')
import gtk
# We should ignore SIGPIPE when using pycurl.NOSIGNAL - see
# the libcurl tutorial for more info.
try:
import signal
from signal import SIGPIPE, SIG_IGN
signal.signal(signal.SIGPIPE, signal.SIG_IGN)
except ImportError:
pass
class ProgressBar:
def __init__(self, uri):
self.round = 0.0
win = gtk.Window(gtk.WINDOW_TOPLEVEL)
win.set_title("PycURL progress")
win.show()
vbox = gtk.VBox(spacing=5)
vbox.set_border_width(10)
win.add(vbox)
win.set_default_size(200, 20)
vbox.show()
label = gtk.Label("Downloading %s" % uri)
label.set_alignment(0, 0.5)
vbox.pack_start(label)
label.show()
pbar = gtk.ProgressBar()
pbar.show()
self.pbar = pbar
vbox.pack_start(pbar)
win.connect("destroy", self.close_app)
def progress(self, download_t, download_d, upload_t, upload_d):
if download_t == 0:
self.round = self.round + 0.1
if self.round >= 1.0: self.round = 0.0
else:
self.round = float(download_d) / float(download_t)
gtk.threads_enter()
self.pbar.set_fraction(self.round)
gtk.threads_leave()
def mainloop(self):
gtk.threads_enter()
gtk.main()
gtk.threads_leave()
def close_app(self, *args):
args[0].destroy()
gtk.main_quit()
class Test(threading.Thread):
def __init__(self, url, target_file, progress):
threading.Thread.__init__(self)
self.target_file = target_file
self.progress = progress
self.curl = pycurl.Curl()
self.curl.setopt(pycurl.URL, url)
self.curl.setopt(pycurl.WRITEDATA, self.target_file)
self.curl.setopt(pycurl.FOLLOWLOCATION, 1)
self.curl.setopt(pycurl.NOPROGRESS, 0)
self.curl.setopt(pycurl.PROGRESSFUNCTION, self.progress)
self.curl.setopt(pycurl.MAXREDIRS, 5)
self.curl.setopt(pycurl.NOSIGNAL, 1)
def run(self):
self.curl.perform()
self.curl.close()
self.target_file.close()
self.progress(1.0, 1.0, 0, 0)
# Check command line args
if len(sys.argv) < 3:
print("Usage: %s " % sys.argv[0])
raise SystemExit
# Make a progress bar window
p = ProgressBar(sys.argv[1])
# Start thread for fetching url
Test(sys.argv[1], open(sys.argv[2], 'wb'), p.progress).start()
# Enter the GTK mainloop
gtk.threads_init()
try:
p.mainloop()
except KeyboardInterrupt:
pass
pycurl-7.19.3/examples/tests/test_xmlrpc.py 0000644 0004705 0004705 00000001355 12262015545 020417 0 ustar pie pie 0000000 0000000 #! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et
## XML-RPC lib included in python2.2
try:
import xmlrpclib
except ImportError:
import xmlrpc.client as xmlrpclib
import pycurl
# Header fields passed in request
xmlrpc_header = [
"User-Agent: PycURL XML-RPC Test", "Content-Type: text/xml"
]
# XML-RPC request template
xmlrpc_template = """
%s%s
"""
# Engage
c = pycurl.Curl()
c.setopt(c.URL, 'http://betty.userland.com/RPC2')
c.setopt(c.POST, 1)
c.setopt(c.HTTPHEADER, xmlrpc_header)
c.setopt(c.POSTFIELDS, xmlrpc_template % ("examples.getStateName", xmlrpclib.dumps((5,))))
print('Response from http://betty.userland.com/')
c.perform()
c.close()
pycurl-7.19.3/examples/basicfirst.py 0000644 0004705 0004705 00000000666 12262015545 017046 0 ustar pie pie 0000000 0000000 #! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et
import sys
import pycurl
class Test:
def __init__(self):
self.contents = ''
def body_callback(self, buf):
self.contents = self.contents + buf
sys.stderr.write("Testing %s\n" % pycurl.version)
t = Test()
c = pycurl.Curl()
c.setopt(c.URL, 'http://curl.haxx.se/dev/')
c.setopt(c.WRITEFUNCTION, t.body_callback)
c.perform()
c.close()
print(t.contents)
pycurl-7.19.3/examples/file_upload.py 0000644 0004705 0004705 00000002233 12262015545 017170 0 ustar pie pie 0000000 0000000 #! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et
import os, sys
import pycurl
# Class which holds a file reference and the read callback
class FileReader:
def __init__(self, fp):
self.fp = fp
def read_callback(self, size):
return self.fp.read(size)
# Check commandline arguments
if len(sys.argv) < 3:
print("Usage: %s " % sys.argv[0])
raise SystemExit
url = sys.argv[1]
filename = sys.argv[2]
if not os.path.exists(filename):
print("Error: the file '%s' does not exist" % filename)
raise SystemExit
# Initialize pycurl
c = pycurl.Curl()
c.setopt(pycurl.URL, url)
c.setopt(pycurl.UPLOAD, 1)
# Two versions with the same semantics here, but the filereader version
# is useful when you have to process the data which is read before returning
if 1:
c.setopt(pycurl.READFUNCTION, FileReader(open(filename, 'rb')).read_callback)
else:
c.setopt(pycurl.READFUNCTION, open(filename, 'rb').read)
# Set size of file to be uploaded.
filesize = os.path.getsize(filename)
c.setopt(pycurl.INFILESIZE, filesize)
# Start transfer
print('Uploading file %s to url %s' % (filename, url))
c.perform()
c.close()
pycurl-7.19.3/examples/linksys.py 0000755 0004705 0004705 00000052375 12262015545 016420 0 ustar pie pie 0000000 0000000 #! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et
#
# linksys.py -- program settings on a Linkys router
#
# This tool is designed to help you recover from the occasional episodes
# of catatonia that afflict Linksys boxes. It allows you to batch-program
# them rather than manually entering values to the Web interface. Commands
# are taken from the command line first, then standard input.
#
# The somewhat spotty coverage of status queries is because I only did the
# ones that were either (a) easy, or (b) necessary. If you want to know the
# status of the box, look at the web interface.
#
# This code has been tested against the following hardware:
#
# Hardware Firmware
# ---------- ---------------------
# BEFW11S4v2 1.44.2.1, Dec 20 2002
#
# The code is, of course, sensitive to changes in the names of CGI pages
# and field names.
#
# Note: to make the no-arguments form work, you'll need to have the following
# entry in your ~/.netrc file. If you have changed the router IP address or
# name/password, modify accordingly.
#
# machine 192.168.1.1
# login ""
# password admin
#
# By Eric S. Raymond, August April 2003. All rites reversed.
import sys, re, copy, curl, exceptions
def print_stderr(arg):
sys.stderr.write(arg)
sys.stderr.write("\n")
class LinksysError(exceptions.Exception):
def __init__(self, *args):
self.args = args
class LinksysSession:
months = 'Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec'
WAN_CONNECT_AUTO = '1'
WAN_CONNECT_STATIC = '2'
WAN_CONNECT_PPOE = '3'
WAN_CONNECT_RAS = '4'
WAN_CONNECT_PPTP = '5'
WAN_CONNECT_HEARTBEAT = '6'
# Substrings to check for on each page load.
# This may enable us to detect when a firmware change has hosed us.
check_strings = {
"": "basic setup functions",
"Passwd.htm": "For security reasons,",
"DHCP.html": "You can configure the router to act as a DHCP",
"Log.html": "There are some log settings and lists in this page.",
"Forward.htm":"Port forwarding can be used to set up public services",
}
def __init__(self):
self.actions = []
self.host = "http://192.168.1.1"
self.verbosity = False
self.pagecache = {}
def set_verbosity(self, flag):
self.verbosity = flag
# This is not a performance hack -- we need the page cache to do
# sanity checks at configure time.
def cache_load(self, page):
if page not in self.pagecache:
fetch = curl.Curl(self.host)
fetch.set_verbosity(self.verbosity)
fetch.get(page)
self.pagecache[page] = fetch.body()
if fetch.answered("401"):
raise LinksysError("authorization failure.", True)
elif not fetch.answered(LinksysSession.check_strings[page]):
del self.pagecache[page]
raise LinksysError("check string for page %s missing!" % os.path.join(self.host, page), False)
fetch.close()
def cache_flush(self):
self.pagecache = {}
# Primitives
def screen_scrape(self, page, template):
self.cache_load(page)
match = re.compile(template).search(self.pagecache[page])
if match:
result = match.group(1)
else:
result = None
return result
def get_MAC_address(self, page, prefix):
return self.screen_scrape("", prefix+r":[^M]*\(MAC Address: *([^)]*)")
def set_flag(page, flag, value):
if value:
self.actions.append(page, flag, "1")
else:
self.actions.append(page, flag, "0")
def set_IP_address(self, page, cgi, role, ip):
ind = 0
for octet in ip.split("."):
self.actions.append(("", "F1", role + `ind+1`, octet))
ind += 1
# Scrape configuration data off the main page
def get_firmware_version(self):
# This is fragile. There is no distinguishing tag before the firmware
# version, so we have to key off the pattern of the version number.
# Our model is ">1.44.2.1, Dec 20 2002<"
return self.screen_scrape("", ">([0-9.v]*, (" + \
LinksysSession.months + ")[^<]*)<", )
def get_LAN_MAC(self):
return self.get_MAC_address("", r"LAN IP Address")
def get_Wireless_MAC(self):
return self.get_MAC_address("", r"Wireless")
def get_WAN_MAC(self):
return self.get_MAC_address("", r"WAN Connection Type")
# Set configuration data on the main page
def set_host_name(self, name):
self.actions.append(("", "hostName", name))
def set_domain_name(self, name):
self.actions.append(("", "DomainName", name))
def set_LAN_IP(self, ip):
self.set_IP_address("", "ipAddr", ip)
def set_LAN_netmask(self, ip):
if not ip.startswith("255.255.255."):
raise ValueError
lastquad = ip.split(".")[-1]
if lastquad not in ("0", "128", "192", "240", "252"):
raise ValueError
self.actions.append("", "netMask", lastquad)
def set_wireless(self, flag):
self.set_flag("", "wirelessStatus")
def set_SSID(self, ssid):
self.actions.append(("", "wirelessESSID", ssid))
def set_SSID_broadcast(self, flag):
self.set_flag("", "broadcastSSID")
def set_channel(self, channel):
self.actions.append(("", "wirelessChannel", channel))
def set_WEP(self, flag):
self.set_flag("", "WepType")
# FIXME: Add support for setting WEP keys
def set_connection_type(self, type):
self.actions.append(("", "WANConnectionType", type))
def set_WAN_IP(self, ip):
self.set_IP_address("", "aliasIP", ip)
def set_WAN_netmask(self, ip):
self.set_IP_address("", "aliasMaskIP", ip)
def set_WAN_gateway_address(self, ip):
self.set_IP_address("", "routerIP", ip)
def set_DNS_server(self, index, ip):
self.set_IP_address("", "dns" + "ABC"[index], ip)
# Set configuration data on the password page
def set_password(self, str):
self.actions.append("Passwd.htm","sysPasswd", str)
self.actions.append("Passwd.htm","sysPasswdConfirm", str)
def set_UPnP(self, flag):
self.set_flag("Passwd.htm", "UPnP_Work")
def reset(self):
self.actions.append("Passwd.htm", "FactoryDefaults")
# DHCP features
def set_DHCP(self, flag):
if flag:
self.actions.append("DHCP.htm","dhcpStatus","Enable")
else:
self.actions.append("DHCP.htm","dhcpStatus","Disable")
def set_DHCP_starting_IP(self, val):
self.actions.append("DHCP.htm","dhcpS4", str(val))
def set_DHCP_users(self, val):
self.actions.append("DHCP.htm","dhcpLen", str(val))
def set_DHCP_lease_time(self, val):
self.actions.append("DHCP.htm","leaseTime", str(val))
def set_DHCP_DNS_server(self, index, ip):
self.set_IP_address("DHCP.htm", "dns" + "ABC"[index], ip)
# FIXME: add support for setting WINS key
# Logging features
def set_logging(self, flag):
if flag:
self.actions.append("Log.htm", "rLog", "Enable")
else:
self.actions.append("Log.htm", "rLog", "Disable")
def set_log_address(self, val):
self.actions.append("DHCP.htm","trapAddr3", str(val))
# The AOL parental control flag is not supported by design.
# FIXME: add Filters and other advanced features
def configure(self):
"Write configuration changes to the Linksys."
if self.actions:
fields = []
self.cache_flush()
for (page, field, value) in self.actions:
self.cache_load(page)
if self.pagecache[page].find(field) == -1:
print_stderr("linksys: field %s not found where expected in page %s!" % (field, os.path.join(self.host, page)))
continue
else:
fields.append((field, value))
# Clearing the action list before fieldsping is deliberate.
# Otherwise we could get permanently wedged by a 401.
self.actions = []
transaction = curl.Curl(self.host)
transaction.set_verbosity(self.verbosity)
transaction.get("Gozila.cgi", tuple(fields))
transaction.close()
if __name__ == "__main__":
import os, cmd
class LinksysInterpreter(cmd.Cmd):
"""Interpret commands to perform LinkSys programming actions."""
def __init__(self):
cmd.Cmd.__init__(self)
self.session = LinksysSession()
if os.isatty(0):
import readline
print("Type ? or `help' for help.")
self.prompt = self.session.host + ": "
else:
self.prompt = ""
print("Bar1")
def flag_command(self, func, line):
if line.strip() in ("on", "enable", "yes"):
func(True)
elif line.strip() in ("off", "disable", "no"):
func(False)
else:
print_stderr("linksys: unknown switch value")
return 0
def do_connect(self, line):
newhost = line.strip()
if newhost:
self.session.host = newhost
self.session.cache_flush()
self.prompt = self.session.host + ": "
else:
print(self.session.host)
return 0
def help_connect(self):
print("Usage: connect []")
print("Connect to a Linksys by name or IP address.")
print("If no argument is given, print the current host.")
def do_status(self, line):
self.session.cache_load("")
if "" in self.session.pagecache:
print("Firmware:", self.session.get_firmware_version())
print("LAN MAC:", self.session.get_LAN_MAC())
print("Wireless MAC:", self.session.get_Wireless_MAC())
print("WAN MAC:", self.session.get_WAN_MAC())
print(".")
return 0
def help_status(self):
print("Usage: status")
print("The status command shows the status of the Linksys.")
print("It is mainly useful as a sanity check to make sure")
print("the box is responding correctly.")
def do_verbose(self, line):
self.flag_command(self.session.set_verbosity, line)
def help_verbose(self):
print("Usage: verbose {on|off|enable|disable|yes|no}")
print("Enables display of HTTP requests.")
def do_host(self, line):
self.session.set_host_name(line)
return 0
def help_host(self):
print("Usage: host ")
print("Sets the Host field to be queried by the ISP.")
def do_domain(self, line):
print("Usage: host ")
self.session.set_domain_name(line)
return 0
def help_domain(self):
print("Sets the Domain field to be queried by the ISP.")
def do_lan_address(self, line):
self.session.set_LAN_IP(line)
return 0
def help_lan_address(self):
print("Usage: lan_address ")
print("Sets the LAN IP address.")
def do_lan_netmask(self, line):
self.session.set_LAN_netmask(line)
return 0
def help_lan_netmask(self):
print("Usage: lan_netmask ")
print("Sets the LAN subnetwork mask.")
def do_wireless(self, line):
self.flag_command(self.session.set_wireless, line)
return 0
def help_wireless(self):
print("Usage: wireless {on|off|enable|disable|yes|no}")
print("Switch to enable or disable wireless features.")
def do_ssid(self, line):
self.session.set_SSID(line)
return 0
def help_ssid(self):
print("Usage: ssid ")
print("Sets the SSID used to control wireless access.")
def do_ssid_broadcast(self, line):
self.flag_command(self.session.set_SSID_broadcast, line)
return 0
def help_ssid_broadcast(self):
print("Usage: ssid_broadcast {on|off|enable|disable|yes|no}")
print("Switch to enable or disable SSID broadcast.")
def do_channel(self, line):
self.session.set_channel(line)
return 0
def help_channel(self):
print("Usage: channel ")
print("Sets the wireless channel.")
def do_wep(self, line):
self.flag_command(self.session.set_WEP, line)
return 0
def help_wep(self):
print("Usage: wep {on|off|enable|disable|yes|no}")
print("Switch to enable or disable WEP security.")
def do_wan_type(self, line):
try:
type=eval("LinksysSession.WAN_CONNECT_"+line.strip().upper())
self.session.set_connection_type(type)
except ValueError:
print_stderr("linksys: unknown connection type.")
return 0
def help_wan_type(self):
print("Usage: wan_type {auto|static|ppoe|ras|pptp|heartbeat}")
print("Set the WAN connection type.")
def do_wan_address(self, line):
self.session.set_WAN_IP(line)
return 0
def help_wan_address(self):
print("Usage: wan_address ")
print("Sets the WAN IP address.")
def do_wan_netmask(self, line):
self.session.set_WAN_netmask(line)
return 0
def help_wan_netmask(self):
print("Usage: wan_netmask ")
print("Sets the WAN subnetwork mask.")
def do_wan_gateway(self, line):
self.session.set_WAN_gateway(line)
return 0
def help_wan_gateway(self):
print("Usage: wan_gateway ")
print("Sets the LAN subnetwork mask.")
def do_dns(self, line):
(index, address) = line.split()
if index in ("1", "2", "3"):
self.session.set_DNS_server(eval(index), address)
else:
print_stderr("linksys: server index out of bounds.")
return 0
def help_dns(self):
print("Usage: dns {1|2|3} ")
print("Sets a primary, secondary, or tertiary DNS server address.")
def do_password(self, line):
self.session.set_password(line)
return 0
def help_password(self):
print("Usage: password ")
print("Sets the router password.")
def do_upnp(self, line):
self.flag_command(self.session.set_UPnP, line)
return 0
def help_upnp(self):
print("Usage: upnp {on|off|enable|disable|yes|no}")
print("Switch to enable or disable Universal Plug and Play.")
def do_reset(self, line):
self.session.reset()
def help_reset(self):
print("Usage: reset")
print("Reset Linksys settings to factory defaults.")
def do_dhcp(self, line):
self.flag_command(self.session.set_DHCP, line)
def help_dhcp(self):
print("Usage: dhcp {on|off|enable|disable|yes|no}")
print("Switch to enable or disable DHCP features.")
def do_dhcp_start(self, line):
self.session.set_DHCP_starting_IP(line)
def help_dhcp_start(self):
print("Usage: dhcp_start ")
print("Set the start address of the DHCP pool.")
def do_dhcp_users(self, line):
self.session.set_DHCP_users(line)
def help_dhcp_users(self):
print("Usage: dhcp_users ")
print("Set number of address slots to allocate in the DHCP pool.")
def do_dhcp_lease(self, line):
self.session.set_DHCP_lease(line)
def help_dhcp_lease(self):
print("Usage: dhcp_lease ")
print("Set number of address slots to allocate in the DHCP pool.")
def do_dhcp_dns(self, line):
(index, address) = line.split()
if index in ("1", "2", "3"):
self.session.set_DHCP_DNS_server(eval(index), address)
else:
print_stderr("linksys: server index out of bounds.")
return 0
def help_dhcp_dns(self):
print("Usage: dhcp_dns {1|2|3} ")
print("Sets primary, secondary, or tertiary DNS server address.")
def do_logging(self, line):
self.flag_command(self.session.set_logging, line)
def help_logging(self):
print("Usage: logging {on|off|enable|disable|yes|no}")
print("Switch to enable or disable session logging.")
def do_log_address(self, line):
self.session.set_Log_address(line)
def help_log_address(self):
print("Usage: log_address ")
print("Set the last quad of the address to which to log.")
def do_configure(self, line):
self.session.configure()
return 0
def help_configure(self):
print("Usage: configure")
print("Writes the configuration to the Linksys.")
def do_cache(self, line):
print(self.session.pagecache)
def help_cache(self):
print("Usage: cache")
print("Display the page cache.")
def do_quit(self, line):
return 1
def help_quit(self, line):
print("The quit command ends your linksys session without")
print("writing configuration changes to the Linksys.")
def do_EOF(self, line):
print("")
self.session.configure()
return 1
def help_EOF(self):
print("The EOF command writes the configuration to the linksys")
print("and ends your session.")
def default(self, line):
"""Pass the command through to be executed by the shell."""
os.system(line)
return 0
def help_help(self):
print("On-line help is available through this command.")
print("? is a convenience alias for help.")
def help_introduction(self):
print("""\
This program supports changing the settings on Linksys blue-box routers. This
capability may come in handy when they freeze up and have to be reset. Though
it can be used interactively (and will command-prompt when standard input is a
terminal) it is really designed to be used in batch mode. Commands are taken
from the command line first, then standard input.
By default, it is assumed that the Linksys is at http://192.168.1.1, the
default LAN address. You can connect to a different address or IP with the
'connect' command. Note that your .netrc must contain correct user/password
credentials for the router. The entry corresponding to the defaults is:
machine 192.168.1.1
login ""
password admin
Most commands queue up changes but don't actually send them to the Linksys.
You can force pending changes to be written with 'configure'. Otherwise, they
will be shipped to the Linksys at the end of session (e.g. when the program
running in batch mode encounters end-of-file or you type a control-D). If you
end the session with `quit', pending changes will be discarded.
For more help, read the topics 'wan', 'lan', and 'wireless'.""")
def help_lan(self):
print("""\
The `lan_address' and `lan_netmask' commands let you set the IP location of
the Linksys on your LAN, or inside. Normally you'll want to leave these
untouched.""")
def help_wan(self):
print("""\
The WAN commands become significant if you are using the BEFSR41 or any of
the other Linksys boxes designed as DSL or cable-modem gateways. You will
need to use `wan_type' to declare how you expect to get your address.
If your ISP has issued you a static address, you'll need to use the
`wan_address', `wan_netmask', and `wan_gateway' commands to set the address
of the router as seen from the WAN, the outside. In this case you will also
need to use the `dns' command to declare which remote servers your DNS
requests should be forwarded to.
Some ISPs may require you to set host and domain for use with dynamic-address
allocation.""")
def help_wireless(self):
print("""\
The channel, ssid, ssid_broadcast, wep, and wireless commands control
wireless routing.""")
def help_switches(self):
print("Switches may be turned on with 'on', 'enable', or 'yes'.")
print("Switches may be turned off with 'off', 'disable', or 'no'.")
print("Switch commands include: wireless, ssid_broadcast.")
def help_addresses(self):
print("An address argument must be a valid IP address;")
print("four decimal numbers separated by dots, each ")
print("between 0 and 255.")
def emptyline(self):
pass
interpreter = LinksysInterpreter()
for arg in sys.argv[1:]:
interpreter.onecmd(arg)
fatal = False
while not fatal:
try:
interpreter.cmdloop()
fatal = True
except LinksysError, (message, fatal):
print "linksys:", message
# The following sets edit modes for GNU EMACS
# Local Variables:
# mode:python
# End:
pycurl-7.19.3/examples/retriever-multi.py 0000644 0004705 0004705 00000006446 12262015545 020056 0 ustar pie pie 0000000 0000000 #! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et
#
# Usage: python retriever-multi.py [<# of
# concurrent connections>]
#
import sys
import pycurl
# We should ignore SIGPIPE when using pycurl.NOSIGNAL - see
# the libcurl tutorial for more info.
try:
import signal
from signal import SIGPIPE, SIG_IGN
signal.signal(signal.SIGPIPE, signal.SIG_IGN)
except ImportError:
pass
# Get args
num_conn = 10
try:
if sys.argv[1] == "-":
urls = sys.stdin.readlines()
else:
urls = open(sys.argv[1]).readlines()
if len(sys.argv) >= 3:
num_conn = int(sys.argv[2])
except:
print("Usage: %s [<# of concurrent connections>]" % sys.argv[0])
raise SystemExit
# Make a queue with (url, filename) tuples
queue = []
for url in urls:
url = url.strip()
if not url or url[0] == "#":
continue
filename = "doc_%03d.dat" % (len(queue) + 1)
queue.append((url, filename))
# Check args
assert queue, "no URLs given"
num_urls = len(queue)
num_conn = min(num_conn, num_urls)
assert 1 <= num_conn <= 10000, "invalid number of concurrent connections"
print("PycURL %s (compiled against 0x%x)" % (pycurl.version, pycurl.COMPILE_LIBCURL_VERSION_NUM))
print("----- Getting", num_urls, "URLs using", num_conn, "connections -----")
# Pre-allocate a list of curl objects
m = pycurl.CurlMulti()
m.handles = []
for i in range(num_conn):
c = pycurl.Curl()
c.fp = None
c.setopt(pycurl.FOLLOWLOCATION, 1)
c.setopt(pycurl.MAXREDIRS, 5)
c.setopt(pycurl.CONNECTTIMEOUT, 30)
c.setopt(pycurl.TIMEOUT, 300)
c.setopt(pycurl.NOSIGNAL, 1)
m.handles.append(c)
# Main loop
freelist = m.handles[:]
num_processed = 0
while num_processed < num_urls:
# If there is an url to process and a free curl object, add to multi stack
while queue and freelist:
url, filename = queue.pop(0)
c = freelist.pop()
c.fp = open(filename, "wb")
c.setopt(pycurl.URL, url)
c.setopt(pycurl.WRITEDATA, c.fp)
m.add_handle(c)
# store some info
c.filename = filename
c.url = url
# Run the internal curl state machine for the multi stack
while 1:
ret, num_handles = m.perform()
if ret != pycurl.E_CALL_MULTI_PERFORM:
break
# Check for curl objects which have terminated, and add them to the freelist
while 1:
num_q, ok_list, err_list = m.info_read()
for c in ok_list:
c.fp.close()
c.fp = None
m.remove_handle(c)
print("Success:", c.filename, c.url, c.getinfo(pycurl.EFFECTIVE_URL))
freelist.append(c)
for c, errno, errmsg in err_list:
c.fp.close()
c.fp = None
m.remove_handle(c)
print("Failed: ", c.filename, c.url, errno, errmsg)
freelist.append(c)
num_processed = num_processed + len(ok_list) + len(err_list)
if num_q == 0:
break
# Currently no more I/O is pending, could do something in the meantime
# (display a progress bar, etc.).
# We just call select() to sleep until some more data is available.
m.select(1.0)
# Cleanup
for c in m.handles:
if c.fp is not None:
c.fp.close()
c.fp = None
c.close()
m.close()
pycurl-7.19.3/examples/retriever.py 0000644 0004705 0004705 00000005064 12262015545 016721 0 ustar pie pie 0000000 0000000 #! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et
#
# Usage: python retriever.py [<# of
# concurrent connections>]
#
import sys, threading, Queue
import pycurl
# We should ignore SIGPIPE when using pycurl.NOSIGNAL - see
# the libcurl tutorial for more info.
try:
import signal
from signal import SIGPIPE, SIG_IGN
signal.signal(signal.SIGPIPE, signal.SIG_IGN)
except ImportError:
pass
# Get args
num_conn = 10
try:
if sys.argv[1] == "-":
urls = sys.stdin.readlines()
else:
urls = open(sys.argv[1]).readlines()
if len(sys.argv) >= 3:
num_conn = int(sys.argv[2])
except:
print("Usage: %s [<# of concurrent connections>]" % sys.argv[0])
raise SystemExit
# Make a queue with (url, filename) tuples
queue = Queue.Queue()
for url in urls:
url = url.strip()
if not url or url[0] == "#":
continue
filename = "doc_%03d.dat" % (len(queue.queue) + 1)
queue.put((url, filename))
# Check args
assert queue.queue, "no URLs given"
num_urls = len(queue.queue)
num_conn = min(num_conn, num_urls)
assert 1 <= num_conn <= 10000, "invalid number of concurrent connections"
print("PycURL %s (compiled against 0x%x)" % (pycurl.version, pycurl.COMPILE_LIBCURL_VERSION_NUM))
print("----- Getting", num_urls, "URLs using", num_conn, "connections -----")
class WorkerThread(threading.Thread):
def __init__(self, queue):
threading.Thread.__init__(self)
self.queue = queue
def run(self):
while 1:
try:
url, filename = self.queue.get_nowait()
except Queue.Empty:
raise SystemExit
fp = open(filename, "wb")
curl = pycurl.Curl()
curl.setopt(pycurl.URL, url)
curl.setopt(pycurl.FOLLOWLOCATION, 1)
curl.setopt(pycurl.MAXREDIRS, 5)
curl.setopt(pycurl.CONNECTTIMEOUT, 30)
curl.setopt(pycurl.TIMEOUT, 300)
curl.setopt(pycurl.NOSIGNAL, 1)
curl.setopt(pycurl.WRITEDATA, fp)
try:
curl.perform()
except:
import traceback
traceback.print_exc(file=sys.stderr)
sys.stderr.flush()
curl.close()
fp.close()
sys.stdout.write(".")
sys.stdout.flush()
# Start a bunch of threads
threads = []
for dummy in range(num_conn):
t = WorkerThread(queue)
t.start()
threads.append(t)
# Wait for all threads to finish
for thread in threads:
thread.join()
pycurl-7.19.3/examples/sfquery.py 0000644 0004705 0004705 00000004635 12262015545 016413 0 ustar pie pie 0000000 0000000 #! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et
#
# sfquery -- Source Forge query script using the ClientCGI high-level interface
#
# Retrieves a SourceForge XML export object for a given project.
# Specify the *numeric* project ID. the user name, and the password,
# as arguments. If you have a valid ~/.netrc entry for sourceforge.net,
# you can just give the project ID.
#
# By Eric S. Raymond, August 2002. All rites reversed.
import os, sys, netrc
import curl
class SourceForgeUserSession(curl.Curl):
# SourceForge-specific methods. Sensitive to changes in site design.
def login(self, name, password):
"Establish a login session."
self.post("account/login.php", (("form_loginname", name),
("form_pw", password),
("return_to", ""),
("stay_in_ssl", "1"),
("login", "Login With SSL")))
def logout(self):
"Log out of SourceForge."
self.get("account/logout.php")
def fetch_xml(self, numid):
self.get("export/xml_export.php?group_id=%s" % numid)
if __name__ == "__main__":
if len(sys.argv) == 1:
project_id = '28236' # PyCurl project ID
else:
project_id = sys.argv[1]
# Try to grab authenticators out of your .netrc
try:
auth = netrc.netrc().authenticators("sourceforge.net")
name, account, password = auth
except:
if len(sys.argv) < 4:
print("Usage: %s " % sys.argv[0])
raise SystemExit
name = sys.argv[2]
password = sys.argv[3]
session = SourceForgeUserSession("https://sourceforge.net/")
session.set_verbosity(0)
session.login(name, password)
# Login could fail.
if session.answered("Invalid Password or User Name"):
sys.stderr.write("Login/password not accepted (%d bytes)\n" % len(session.body()))
sys.exit(1)
# We'll see this if we get the right thing.
elif session.answered("Personal Page For: " + name):
session.fetch_xml(project_id)
sys.stdout.write(session.body())
session.logout()
sys.exit(0)
# Or maybe SourceForge has changed its site design so our check strings
# are no longer valid.
else:
sys.stderr.write("Unexpected page (%d bytes)\n"%len(session.body()))
sys.exit(1)
pycurl-7.19.3/examples/xmlrpc_curl.py 0000644 0004705 0004705 00000003767 12262015545 017254 0 ustar pie pie 0000000 0000000 #! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et
# We should ignore SIGPIPE when using pycurl.NOSIGNAL - see
# the libcurl tutorial for more info.
try:
import signal
from signal import SIGPIPE, SIG_IGN
signal.signal(signal.SIGPIPE, signal.SIG_IGN)
except ImportError:
pass
try:
from cStringIO import StringIO
except ImportError:
try:
from StringIO import StringIO
except ImportError:
from io import StringIO
try:
import xmlrpclib
except ImportError:
import xmlrpc.client as xmlrpclib
import pycurl
class CURLTransport(xmlrpclib.Transport):
"""Handles a cURL HTTP transaction to an XML-RPC server."""
xmlrpc_h = [ "Content-Type: text/xml" ]
def __init__(self, username=None, password=None):
self.c = pycurl.Curl()
self.c.setopt(pycurl.POST, 1)
self.c.setopt(pycurl.NOSIGNAL, 1)
self.c.setopt(pycurl.CONNECTTIMEOUT, 30)
self.c.setopt(pycurl.HTTPHEADER, self.xmlrpc_h)
if username != None and password != None:
self.c.setopt(pycurl.USERPWD, '%s:%s' % (username, password))
self._use_datetime = False
def request(self, host, handler, request_body, verbose=0):
b = StringIO()
self.c.setopt(pycurl.URL, 'http://%s%s' % (host, handler))
self.c.setopt(pycurl.POSTFIELDS, request_body)
self.c.setopt(pycurl.WRITEFUNCTION, b.write)
self.c.setopt(pycurl.VERBOSE, verbose)
self.verbose = verbose
try:
self.c.perform()
except pycurl.error, v:
raise xmlrpclib.ProtocolError(
host + handler,
v[0], v[1], None
)
b.seek(0)
return self.parse_response(b)
if __name__ == "__main__":
## Test
server = xmlrpclib.ServerProxy("http://betty.userland.com",
transport=CURLTransport())
print(server)
try:
print(server.examples.getStateName(41))
except xmlrpclib.Error, v:
print("ERROR", v)
pycurl-7.19.3/python/ 0000755 0004705 0004705 00000000000 12263734634 014046 5 ustar pie pie 0000000 0000000 pycurl-7.19.3/python/curl/ 0000755 0004705 0004705 00000000000 12263734634 015013 5 ustar pie pie 0000000 0000000 pycurl-7.19.3/python/curl/__init__.py 0000644 0004705 0004705 00000015636 12256232314 017125 0 ustar pie pie 0000000 0000000 # A high-level interface to the pycurl extension
#
# ** mfx NOTE: the CGI class uses "black magic" using COOKIEFILE in
# combination with a non-existant file name. See the libcurl docs
# for more info.
#
# By Eric S. Raymond, April 2003.
import sys, pycurl
py3 = sys.version_info[0] == 3
# python 2/3 compatibility
if py3:
import urllib.parse as urllib_parse
from urllib.parse import urljoin
from io import BytesIO
else:
import urllib as urllib_parse
from urlparse import urljoin
try:
from cStringIO import StringIO as BytesIO
except ImportError:
from StringIO import StringIO as BytesIO
try:
import signal
from signal import SIGPIPE, SIG_IGN
signal.signal(signal.SIGPIPE, signal.SIG_IGN)
except ImportError:
pass
class Curl:
"High-level interface to pycurl functions."
def __init__(self, base_url="", fakeheaders=[]):
self.handle = pycurl.Curl()
# These members might be set.
self.set_url(base_url)
self.verbosity = 0
self.fakeheaders = fakeheaders
# Nothing past here should be modified by the caller.
self.payload = None
self.payload_io = BytesIO()
self.hrd = ""
# Verify that we've got the right site; harmless on a non-SSL connect.
self.set_option(pycurl.SSL_VERIFYHOST, 2)
# Follow redirects in case it wants to take us to a CGI...
self.set_option(pycurl.FOLLOWLOCATION, 1)
self.set_option(pycurl.MAXREDIRS, 5)
self.set_option(pycurl.NOSIGNAL, 1)
# Setting this option with even a nonexistent file makes libcurl
# handle cookie capture and playback automatically.
self.set_option(pycurl.COOKIEFILE, "/dev/null")
# Set timeouts to avoid hanging too long
self.set_timeout(30)
# Use password identification from .netrc automatically
self.set_option(pycurl.NETRC, 1)
self.set_option(pycurl.WRITEFUNCTION, self.payload_io.write)
def header_callback(x):
self.hdr += x.decode('ascii')
self.set_option(pycurl.HEADERFUNCTION, header_callback)
def set_timeout(self, timeout):
"Set timeout for a retrieving an object"
self.set_option(pycurl.TIMEOUT, timeout)
def set_url(self, url):
"Set the base URL to be retrieved."
self.base_url = url
self.set_option(pycurl.URL, self.base_url)
def set_option(self, *args):
"Set an option on the retrieval."
self.handle.setopt(*args)
def set_verbosity(self, level):
"Set verbosity to 1 to see transactions."
self.set_option(pycurl.VERBOSE, level)
def __request(self, relative_url=None):
"Perform the pending request."
if self.fakeheaders:
self.set_option(pycurl.HTTPHEADER, self.fakeheaders)
if relative_url:
self.set_option(pycurl.URL, urljoin(self.base_url, relative_url))
self.payload = None
self.hdr = ""
self.handle.perform()
self.payload = self.payload_io.getvalue()
return self.payload
def get(self, url="", params=None):
"Ship a GET request for a specified URL, capture the response."
if params:
url += "?" + urllib_parse.urlencode(params)
self.set_option(pycurl.HTTPGET, 1)
return self.__request(url)
def post(self, cgi, params):
"Ship a POST request to a specified CGI, capture the response."
self.set_option(pycurl.POST, 1)
self.set_option(pycurl.POSTFIELDS, urllib_parse.urlencode(params))
return self.__request(cgi)
def body(self):
"Return the body from the last response."
return self.payload
def header(self):
"Return the header from the last response."
return self.hdr
def get_info(self, *args):
"Get information about retrieval."
return self.handle.getinfo(*args)
def info(self):
"Return a dictionary with all info on the last response."
m = {}
m['effective-url'] = self.handle.getinfo(pycurl.EFFECTIVE_URL)
m['http-code'] = self.handle.getinfo(pycurl.HTTP_CODE)
m['total-time'] = self.handle.getinfo(pycurl.TOTAL_TIME)
m['namelookup-time'] = self.handle.getinfo(pycurl.NAMELOOKUP_TIME)
m['connect-time'] = self.handle.getinfo(pycurl.CONNECT_TIME)
m['pretransfer-time'] = self.handle.getinfo(pycurl.PRETRANSFER_TIME)
m['redirect-time'] = self.handle.getinfo(pycurl.REDIRECT_TIME)
m['redirect-count'] = self.handle.getinfo(pycurl.REDIRECT_COUNT)
m['size-upload'] = self.handle.getinfo(pycurl.SIZE_UPLOAD)
m['size-download'] = self.handle.getinfo(pycurl.SIZE_DOWNLOAD)
m['speed-upload'] = self.handle.getinfo(pycurl.SPEED_UPLOAD)
m['header-size'] = self.handle.getinfo(pycurl.HEADER_SIZE)
m['request-size'] = self.handle.getinfo(pycurl.REQUEST_SIZE)
m['content-length-download'] = self.handle.getinfo(pycurl.CONTENT_LENGTH_DOWNLOAD)
m['content-length-upload'] = self.handle.getinfo(pycurl.CONTENT_LENGTH_UPLOAD)
m['content-type'] = self.handle.getinfo(pycurl.CONTENT_TYPE)
m['response-code'] = self.handle.getinfo(pycurl.RESPONSE_CODE)
m['speed-download'] = self.handle.getinfo(pycurl.SPEED_DOWNLOAD)
m['ssl-verifyresult'] = self.handle.getinfo(pycurl.SSL_VERIFYRESULT)
m['filetime'] = self.handle.getinfo(pycurl.INFO_FILETIME)
m['starttransfer-time'] = self.handle.getinfo(pycurl.STARTTRANSFER_TIME)
m['redirect-time'] = self.handle.getinfo(pycurl.REDIRECT_TIME)
m['redirect-count'] = self.handle.getinfo(pycurl.REDIRECT_COUNT)
m['http-connectcode'] = self.handle.getinfo(pycurl.HTTP_CONNECTCODE)
m['httpauth-avail'] = self.handle.getinfo(pycurl.HTTPAUTH_AVAIL)
m['proxyauth-avail'] = self.handle.getinfo(pycurl.PROXYAUTH_AVAIL)
m['os-errno'] = self.handle.getinfo(pycurl.OS_ERRNO)
m['num-connects'] = self.handle.getinfo(pycurl.NUM_CONNECTS)
m['ssl-engines'] = self.handle.getinfo(pycurl.SSL_ENGINES)
m['cookielist'] = self.handle.getinfo(pycurl.INFO_COOKIELIST)
m['lastsocket'] = self.handle.getinfo(pycurl.LASTSOCKET)
m['ftp-entry-path'] = self.handle.getinfo(pycurl.FTP_ENTRY_PATH)
return m
def answered(self, check):
"Did a given check string occur in the last payload?"
return self.payload.find(check) >= 0
def close(self):
"Close a session, freeing resources."
if self.handle:
self.handle.close()
self.handle = None
self.hdr = ""
self.payload = ""
def __del__(self):
self.close()
if __name__ == "__main__":
if len(sys.argv) < 2:
url = 'http://curl.haxx.se'
else:
url = sys.argv[1]
c = Curl()
c.get(url)
print(c.body())
print('='*74 + '\n')
import pprint
pprint.pprint(c.info())
print(c.get_info(pycurl.OS_ERRNO))
print(c.info()['os-errno'])
c.close()
pycurl-7.19.3/src/ 0000755 0004705 0004705 00000000000 12263734634 013314 5 ustar pie pie 0000000 0000000 pycurl-7.19.3/src/Makefile 0000644 0004705 0004705 00000000641 12255756661 014762 0 ustar pie pie 0000000 0000000 CC=gcc
RM=rm
CP=cp
PYINCLUDE=/usr/include/python2.2
CURLINCLUDE=/usr/include/curl
INCLUDE=-I$(PYINCLUDE) -I$(CURLINCLUDE)
LIBS=-L/usr/lib -lcurl
LDOPTS=-shared
CCOPTS=-g -O2 -Wall -Wstrict-prototypes -fPIC
all:
$(CC) $(INCLUDE) $(CCOPTS) -c pycurl.c -o pycurl.o
$(CC) $(LIBS) $(LDOPTS) -lcurl pycurl.o -o pycurl.so
install: all
$(CP) pycurl.so /usr/lib/python2.2/site-packages
clean:
$(RM) -f *~ *.o *obj *.so
pycurl-7.19.3/src/pycurl.c 0000644 0004705 0004705 00000501435 12262777702 015010 0 ustar pie pie 0000000 0000000 /* PycURL -- cURL Python module
*
* Authors:
* Copyright (C) 2001-2008 by Kjetil Jacobsen
* Copyright (C) 2001-2008 by Markus F.X.J. Oberhumer
* Copyright (C) 2013-2014 by Oleg Pudeyev
*
* All rights reserved.
*
* Contributions:
* Tino Lange
* Matt King
* Conrad Steenberg
* Amit Mongia
* Eric S. Raymond
* Martin Muenstermann
* Domenico Andreoli
* Dominique
* Paul Pacheco
* Victor Lascurain
* K.S.Sreeram
* Jayne
* Bastian Kleineidam
* Mark Eichin
* Aaron Hill
* Daniel Pena Arteaga
* Jim Patterson
* Yuhui H
* Nick Pilon
* Thomas Hunger
* Wim Lewis
*
* See file README for license information.
*/
#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32)
# define WIN32 1
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#if !defined(WIN32)
#include
#include
#include
#endif
#include
#include
#include
#undef NDEBUG
#include
#if defined(WIN32)
/* supposedly not present in errno.h provided with VC */
# if !defined(EAFNOSUPPORT)
# define EAFNOSUPPORT 97
# endif
#endif
/* The inet_ntop() was added in ws2_32.dll on Windows Vista [1]. Hence the
* Windows SDK targeting lesser OS'es doesn't provide that prototype.
* Maybe we should use the local hidden inet_ntop() for all OS'es thus
* making a pycurl.pyd work across OS'es w/o rebuilding?
*
* [1] http://msdn.microsoft.com/en-us/library/windows/desktop/cc805843(v=vs.85).aspx
*/
#if defined(WIN32) && ((_WIN32_WINNT < 0x0600) || (NTDDI_VERSION < NTDDI_VISTA))
static const char * pycurl_inet_ntop (int family, void *addr, char *string, size_t string_size);
#define inet_ntop(fam,addr,string,size) pycurl_inet_ntop(fam,addr,string,size)
#endif
/* Ensure we have updated versions */
#if !defined(PY_VERSION_HEX) || (PY_VERSION_HEX < 0x02040000)
# error "Need Python version 2.4 or greater to compile pycurl."
#endif
#if !defined(LIBCURL_VERSION_NUM) || (LIBCURL_VERSION_NUM < 0x071300)
# error "Need libcurl version 7.19.0 or greater to compile pycurl."
#endif
#if LIBCURL_VERSION_NUM >= 0x071301 /* check for 7.19.1 or greater */
#define HAVE_CURLOPT_USERNAME
#define HAVE_CURLOPT_PROXYUSERNAME
#define HAVE_CURLOPT_CERTINFO
#define HAVE_CURLOPT_POSTREDIR
#endif
#if LIBCURL_VERSION_NUM >= 0x071303 /* check for 7.19.3 or greater */
#define HAVE_CURLAUTH_DIGEST_IE
#endif
#if LIBCURL_VERSION_NUM >= 0x071500 /* check for 7.21.0 or greater */
#define HAVE_CURLINFO_LOCAL_PORT
#define HAVE_CURLINFO_PRIMARY_PORT
#define HAVE_CURLINFO_LOCAL_IP
#endif
#if LIBCURL_VERSION_NUM >= 0x071304 /* check for 7.19.4 or greater */
#define HAVE_CURLOPT_NOPROXY
#endif
#if LIBCURL_VERSION_NUM >= 0x071503 /* check for 7.21.3 or greater */
#define HAVE_CURLOPT_RESOLVE
#endif
#if LIBCURL_VERSION_NUM >= 0x071800 /* check for 7.24.0 or greater */
#define HAVE_CURLOPT_DNS_SERVERS
#endif
#if LIBCURL_VERSION_NUM >= 0x071A00 /* check for 7.26.0 or greater */
#define HAVE_CURL_REDIR_POST_303
#endif
/* Python < 2.5 compat for Py_ssize_t */
#if PY_VERSION_HEX < 0x02050000
typedef int Py_ssize_t;
#endif
/* Py_TYPE is defined by Python 2.6+ */
#if PY_VERSION_HEX < 0x02060000 && !defined(Py_TYPE)
# define Py_TYPE(x) (x)->ob_type
#endif
#undef UNUSED
#define UNUSED(var) ((void)&var)
/* Cruft for thread safe SSL crypto locks, snapped from the PHP curl extension */
#if defined(HAVE_CURL_SSL)
# if defined(HAVE_CURL_OPENSSL)
# define PYCURL_NEED_SSL_TSL
# define PYCURL_NEED_OPENSSL_TSL
# include
# define COMPILE_SSL_LIB "openssl"
# elif defined(HAVE_CURL_GNUTLS)
# include
# if GNUTLS_VERSION_NUMBER <= 0x020b00
# define PYCURL_NEED_SSL_TSL
# define PYCURL_NEED_GNUTLS_TSL
# include
# endif
# define COMPILE_SSL_LIB "gnutls"
# elif defined(HAVE_CURL_NSS)
# define COMPILE_SSL_LIB "nss"
# else
# warning \
"libcurl was compiled with SSL support, but configure could not determine which " \
"library was used; thus no SSL crypto locking callbacks will be set, which may " \
"cause random crashes on SSL requests"
/* since we have no crypto callbacks for other ssl backends,
* no reason to require users match those */
# define COMPILE_SSL_LIB "none/other"
# endif /* HAVE_CURL_OPENSSL || HAVE_CURL_GNUTLS || HAVE_CURL_NSS */
#else
# define COMPILE_SSL_LIB "none/other"
#endif /* HAVE_CURL_SSL */
#if defined(PYCURL_NEED_SSL_TSL)
static void pycurl_ssl_init(void);
static void pycurl_ssl_cleanup(void);
#endif
#ifdef WITH_THREAD
# define PYCURL_DECLARE_THREAD_STATE PyThreadState *tmp_state
# define PYCURL_ACQUIRE_THREAD() acquire_thread(self, &tmp_state)
# define PYCURL_ACQUIRE_THREAD_MULTI() acquire_thread_multi(self, &tmp_state)
# define PYCURL_RELEASE_THREAD() release_thread(tmp_state)
/* Replacement for Py_BEGIN_ALLOW_THREADS/Py_END_ALLOW_THREADS when python
callbacks are expected during blocking i/o operations: self->state will hold
the handle to current thread to be used as context */
# define PYCURL_BEGIN_ALLOW_THREADS \
self->state = PyThreadState_Get(); \
assert(self->state != NULL); \
Py_BEGIN_ALLOW_THREADS
# define PYCURL_END_ALLOW_THREADS \
Py_END_ALLOW_THREADS \
self->state = NULL;
#else
# define PYCURL_DECLARE_THREAD_STATE
# define PYCURL_ACQUIRE_THREAD() (1)
# define PYCURL_ACQUIRE_THREAD_MULTI() (1)
# define PYCURL_RELEASE_THREAD()
# define PYCURL_BEGIN_ALLOW_THREADS
# define PYCURL_END_ALLOW_THREADS
#endif
#if PY_MAJOR_VERSION >= 3
#define PyInt_Type PyLong_Type
#define PyInt_Check(op) PyLong_Check(op)
#define PyInt_FromLong PyLong_FromLong
#define PyInt_AsLong PyLong_AsLong
#endif
/* Calculate the number of OBJECTPOINT options we need to store */
#define OPTIONS_SIZE ((int)CURLOPT_LASTENTRY % 10000)
#define MOPTIONS_SIZE ((int)CURLMOPT_LASTENTRY % 10000)
/* Memory groups */
/* Attributes dictionary */
#define PYCURL_MEMGROUP_ATTRDICT 1
/* multi_stack */
#define PYCURL_MEMGROUP_MULTI 2
/* Python callbacks */
#define PYCURL_MEMGROUP_CALLBACK 4
/* Python file objects */
#define PYCURL_MEMGROUP_FILE 8
/* Share objects */
#define PYCURL_MEMGROUP_SHARE 16
/* httppost buffer references */
#define PYCURL_MEMGROUP_HTTPPOST 32
/* Postfields object */
#define PYCURL_MEMGROUP_POSTFIELDS 64
#define PYCURL_MEMGROUP_EASY \
(PYCURL_MEMGROUP_CALLBACK | PYCURL_MEMGROUP_FILE | \
PYCURL_MEMGROUP_HTTPPOST | PYCURL_MEMGROUP_POSTFIELDS)
#define PYCURL_MEMGROUP_ALL \
(PYCURL_MEMGROUP_ATTRDICT | PYCURL_MEMGROUP_EASY | \
PYCURL_MEMGROUP_MULTI | PYCURL_MEMGROUP_SHARE)
#if defined(WIN32)
# define PYCURL_STRINGIZE_IMP(x) #x
# define PYCURL_STRINGIZE(x) PYCURL_STRINGIZE_IMP(x)
# define PYCURL_VERSION_STRING PYCURL_STRINGIZE(PYCURL_VERSION)
#else
# define PYCURL_VERSION_STRING PYCURL_VERSION
#endif
#define PYCURL_VERSION_PREFIX "PycURL/" PYCURL_VERSION_STRING
/* Initialized during module init */
static char *g_pycurl_useragent = NULL;
/* Type objects */
static PyObject *ErrorObject = NULL;
static PyTypeObject *p_Curl_Type = NULL;
static PyTypeObject *p_CurlMulti_Type = NULL;
static PyTypeObject *p_CurlShare_Type = NULL;
typedef struct {
PyThread_type_lock locks[CURL_LOCK_DATA_LAST];
} ShareLock;
typedef struct {
PyObject_HEAD
PyObject *dict; /* Python attributes dictionary */
CURLSH *share_handle;
#ifdef WITH_THREAD
ShareLock *lock; /* lock object to implement CURLSHOPT_LOCKFUNC */
#endif
} CurlShareObject;
typedef struct {
PyObject_HEAD
PyObject *dict; /* Python attributes dictionary */
CURLM *multi_handle;
#ifdef WITH_THREAD
PyThreadState *state;
#endif
fd_set read_fd_set;
fd_set write_fd_set;
fd_set exc_fd_set;
/* callbacks */
PyObject *t_cb;
PyObject *s_cb;
} CurlMultiObject;
typedef struct {
PyObject_HEAD
PyObject *dict; /* Python attributes dictionary */
CURL *handle;
#ifdef WITH_THREAD
PyThreadState *state;
#endif
CurlMultiObject *multi_stack;
CurlShareObject *share;
struct curl_httppost *httppost;
/* List of INC'ed references associated with httppost. */
PyObject *httppost_ref_list;
struct curl_slist *httpheader;
struct curl_slist *http200aliases;
struct curl_slist *quote;
struct curl_slist *postquote;
struct curl_slist *prequote;
#ifdef HAVE_CURLOPT_RESOLVE
struct curl_slist *resolve;
#endif
/* callbacks */
PyObject *w_cb;
PyObject *h_cb;
PyObject *r_cb;
PyObject *pro_cb;
PyObject *debug_cb;
PyObject *ioctl_cb;
PyObject *opensocket_cb;
PyObject *seek_cb;
/* file objects */
PyObject *readdata_fp;
PyObject *writedata_fp;
PyObject *writeheader_fp;
/* reference to the object used for CURLOPT_POSTFIELDS */
PyObject *postfields_obj;
/* misc */
char error[CURL_ERROR_SIZE+1];
} CurlObject;
/* Raise exception based on return value `res' and `self->error' */
#define CURLERROR_RETVAL() do {\
PyObject *v; \
self->error[sizeof(self->error) - 1] = 0; \
v = Py_BuildValue("(is)", (int) (res), self->error); \
if (v != NULL) { PyErr_SetObject(ErrorObject, v); Py_DECREF(v); } \
return NULL; \
} while (0)
/* Raise exception based on return value `res' and custom message */
#define CURLERROR_MSG(msg) do {\
PyObject *v; const char *m = (msg); \
v = Py_BuildValue("(is)", (int) (res), (m)); \
if (v != NULL) { PyErr_SetObject(ErrorObject, v); Py_DECREF(v); } \
return NULL; \
} while (0)
/*************************************************************************
// python 2/3 compatibility
**************************************************************************/
#if PY_MAJOR_VERSION >= 3
# define PyText_FromFormat(format, str) PyUnicode_FromFormat((format), (str))
# define PyText_FromString(str) PyUnicode_FromString(str)
# define PyByteStr_Check(obj) PyBytes_Check(obj)
# define PyByteStr_AsStringAndSize(obj, buffer, length) PyBytes_AsStringAndSize((obj), (buffer), (length))
#else
# define PyText_FromFormat(format, str) PyString_FromFormat((format), (str))
# define PyText_FromString(str) PyString_FromString(str)
# define PyByteStr_Check(obj) PyString_Check(obj)
# define PyByteStr_AsStringAndSize(obj, buffer, length) PyString_AsStringAndSize((obj), (buffer), (length))
#endif
#define PyText_EncodedDecref(encoded) Py_XDECREF(encoded)
/*************************************************************************
// python utility functions
**************************************************************************/
int PyText_AsStringAndSize(PyObject *obj, char **buffer, Py_ssize_t *length, PyObject **encoded_obj)
{
if (PyByteStr_Check(obj)) {
*encoded_obj = NULL;
return PyByteStr_AsStringAndSize(obj, buffer, length);
} else {
int rv;
assert(PyUnicode_Check(obj));
*encoded_obj = PyUnicode_AsEncodedString(obj, "ascii", "strict");
if (*encoded_obj == NULL) {
return -1;
}
rv = PyByteStr_AsStringAndSize(*encoded_obj, buffer, length);
if (rv != 0) {
/* If we free the object, pointer must be reset to NULL */
Py_CLEAR(*encoded_obj);
}
return rv;
}
}
/* Like PyString_AsString(), but set an exception if the string contains
* embedded NULs. Actually PyString_AsStringAndSize() already does that for
* us if the `len' parameter is NULL - see Objects/stringobject.c.
*/
static char *PyText_AsString_NoNUL(PyObject *obj, PyObject **encoded_obj)
{
char *s = NULL;
Py_ssize_t r;
r = PyText_AsStringAndSize(obj, &s, NULL, encoded_obj);
if (r != 0)
return NULL; /* exception already set */
assert(s != NULL);
return s;
}
/* Returns true if the object is of a type that can be given to
* curl_easy_setopt and such - either a byte string or a Unicode string
* with ASCII code points only.
*/
#if PY_MAJOR_VERSION >= 3
static int PyText_Check(PyObject *o) {
return PyUnicode_Check(o) || PyBytes_Check(o);
}
#else
static int PyText_Check(PyObject *o) {
return PyUnicode_Check(o) || PyString_Check(o);
}
#endif
/* Convert a curl slist (a list of strings) to a Python list.
* In case of error return NULL with an exception set.
*/
static PyObject *convert_slist(struct curl_slist *slist, int free_flags)
{
PyObject *ret = NULL;
struct curl_slist *slist_start = slist;
ret = PyList_New((Py_ssize_t)0);
if (ret == NULL) goto error;
for ( ; slist != NULL; slist = slist->next) {
PyObject *v = NULL;
if (slist->data == NULL) {
v = Py_None; Py_INCREF(v);
} else {
v = PyText_FromString(slist->data);
}
if (v == NULL || PyList_Append(ret, v) != 0) {
Py_XDECREF(v);
goto error;
}
Py_DECREF(v);
}
if ((free_flags & 1) && slist_start)
curl_slist_free_all(slist_start);
return ret;
error:
Py_XDECREF(ret);
if ((free_flags & 2) && slist_start)
curl_slist_free_all(slist_start);
return NULL;
}
#ifdef HAVE_CURLOPT_CERTINFO
/* Convert a struct curl_certinfo into a Python data structure.
* In case of error return NULL with an exception set.
*/
static PyObject *convert_certinfo(struct curl_certinfo *cinfo)
{
PyObject *certs;
int cert_index;
if (!cinfo)
Py_RETURN_NONE;
certs = PyList_New((Py_ssize_t)(cinfo->num_of_certs));
if (!certs)
return NULL;
for (cert_index = 0; cert_index < cinfo->num_of_certs; cert_index ++) {
struct curl_slist *fields = cinfo->certinfo[cert_index];
struct curl_slist *field_cursor;
int field_count, field_index;
PyObject *cert;
field_count = 0;
field_cursor = fields;
while (field_cursor != NULL) {
field_cursor = field_cursor->next;
field_count ++;
}
cert = PyTuple_New((Py_ssize_t)field_count);
if (!cert)
goto error;
PyList_SetItem(certs, cert_index, cert); /* Eats the ref from New() */
for(field_index = 0, field_cursor = fields;
field_cursor != NULL;
field_index ++, field_cursor = field_cursor->next) {
const char *field = field_cursor->data;
PyObject *field_tuple;
if (!field) {
field_tuple = Py_None; Py_INCREF(field_tuple);
} else {
const char *sep = strchr(field, ':');
if (!sep) {
field_tuple = PyText_FromString(field);
} else {
/* XXX check */
field_tuple = Py_BuildValue("s#s", field, (int)(sep - field), sep+1);
}
if (!field_tuple)
goto error;
}
PyTuple_SET_ITEM(cert, field_index, field_tuple); /* Eats the ref */
}
}
return certs;
error:
Py_DECREF(certs);
return NULL;
}
#endif
#ifdef WITH_THREAD
/*************************************************************************
// static utility functions
**************************************************************************/
static PyThreadState *
get_thread_state(const CurlObject *self)
{
/* Get the thread state for callbacks to run in.
* This is either `self->state' when running inside perform() or
* `self->multi_stack->state' when running inside multi_perform().
* When the result is != NULL we also implicitly assert
* a valid `self->handle'.
*/
if (self == NULL)
return NULL;
assert(Py_TYPE(self) == p_Curl_Type);
if (self->state != NULL)
{
/* inside perform() */
assert(self->handle != NULL);
if (self->multi_stack != NULL) {
assert(self->multi_stack->state == NULL);
}
return self->state;
}
if (self->multi_stack != NULL && self->multi_stack->state != NULL)
{
/* inside multi_perform() */
assert(self->handle != NULL);
assert(self->multi_stack->multi_handle != NULL);
assert(self->state == NULL);
return self->multi_stack->state;
}
return NULL;
}
static PyThreadState *
get_thread_state_multi(const CurlMultiObject *self)
{
/* Get the thread state for callbacks to run in when given
* multi handles instead of regular handles
*/
if (self == NULL)
return NULL;
assert(Py_TYPE(self) == p_CurlMulti_Type);
if (self->state != NULL)
{
/* inside multi_perform() */
assert(self->multi_handle != NULL);
return self->state;
}
return NULL;
}
static int acquire_thread(const CurlObject *self, PyThreadState **state)
{
*state = get_thread_state(self);
if (*state == NULL)
return 0;
PyEval_AcquireThread(*state);
return 1;
}
static int acquire_thread_multi(const CurlMultiObject *self, PyThreadState **state)
{
*state = get_thread_state_multi(self);
if (*state == NULL)
return 0;
PyEval_AcquireThread(*state);
return 1;
}
static void release_thread(PyThreadState *state)
{
PyEval_ReleaseThread(state);
}
#endif
/* assert some CurlShareObject invariants */
static void
assert_share_state(const CurlShareObject *self)
{
assert(self != NULL);
assert(Py_TYPE(self) == p_CurlShare_Type);
#ifdef WITH_THREAD
assert(self->lock != NULL);
#endif
}
/* assert some CurlObject invariants */
static void
assert_curl_state(const CurlObject *self)
{
assert(self != NULL);
assert(Py_TYPE(self) == p_Curl_Type);
#ifdef WITH_THREAD
(void) get_thread_state(self);
#endif
}
/* assert some CurlMultiObject invariants */
static void
assert_multi_state(const CurlMultiObject *self)
{
assert(self != NULL);
assert(Py_TYPE(self) == p_CurlMulti_Type);
#ifdef WITH_THREAD
if (self->state != NULL) {
assert(self->multi_handle != NULL);
}
#endif
}
/* check state for methods */
static int
check_curl_state(const CurlObject *self, int flags, const char *name)
{
assert_curl_state(self);
if ((flags & 1) && self->handle == NULL) {
PyErr_Format(ErrorObject, "cannot invoke %s() - no curl handle", name);
return -1;
}
#ifdef WITH_THREAD
if ((flags & 2) && get_thread_state(self) != NULL) {
PyErr_Format(ErrorObject, "cannot invoke %s() - perform() is currently running", name);
return -1;
}
#endif
return 0;
}
static int
check_multi_state(const CurlMultiObject *self, int flags, const char *name)
{
assert_multi_state(self);
if ((flags & 1) && self->multi_handle == NULL) {
PyErr_Format(ErrorObject, "cannot invoke %s() - no multi handle", name);
return -1;
}
#ifdef WITH_THREAD
if ((flags & 2) && self->state != NULL) {
PyErr_Format(ErrorObject, "cannot invoke %s() - multi_perform() is currently running", name);
return -1;
}
#endif
return 0;
}
static int
check_share_state(const CurlShareObject *self, int flags, const char *name)
{
assert_share_state(self);
return 0;
}
/*************************************************************************
// SSL TSL
**************************************************************************/
#ifdef WITH_THREAD
#ifdef PYCURL_NEED_OPENSSL_TSL
static PyThread_type_lock *pycurl_openssl_tsl = NULL;
static void pycurl_ssl_lock(int mode, int n, const char * file, int line)
{
if (mode & CRYPTO_LOCK) {
PyThread_acquire_lock(pycurl_openssl_tsl[n], 1);
} else {
PyThread_release_lock(pycurl_openssl_tsl[n]);
}
}
static unsigned long pycurl_ssl_id(void)
{
return (unsigned long) PyThread_get_thread_ident();
}
static void pycurl_ssl_init(void)
{
int i, c = CRYPTO_num_locks();
pycurl_openssl_tsl = PyMem_Malloc(c * sizeof(PyThread_type_lock));
for (i = 0; i < c; ++i) {
pycurl_openssl_tsl[i] = PyThread_allocate_lock();
}
CRYPTO_set_id_callback(pycurl_ssl_id);
CRYPTO_set_locking_callback(pycurl_ssl_lock);
}
static void pycurl_ssl_cleanup(void)
{
if (pycurl_openssl_tsl) {
int i, c = CRYPTO_num_locks();
CRYPTO_set_id_callback(NULL);
CRYPTO_set_locking_callback(NULL);
for (i = 0; i < c; ++i) {
PyThread_free_lock(pycurl_openssl_tsl[i]);
}
PyMem_Free(pycurl_openssl_tsl);
pycurl_openssl_tsl = NULL;
}
}
#endif
#ifdef PYCURL_NEED_GNUTLS_TSL
static int pycurl_ssl_mutex_create(void **m)
{
if ((*((PyThread_type_lock *) m) = PyThread_allocate_lock()) == NULL) {
return -1;
} else {
return 0;
}
}
static int pycurl_ssl_mutex_destroy(void **m)
{
PyThread_free_lock(*((PyThread_type_lock *) m));
return 0;
}
static int pycurl_ssl_mutex_lock(void **m)
{
return !PyThread_acquire_lock(*((PyThread_type_lock *) m), 1);
}
static int pycurl_ssl_mutex_unlock(void **m)
{
PyThread_release_lock(*((PyThread_type_lock *) m));
return 0;
}
static struct gcry_thread_cbs pycurl_gnutls_tsl = {
GCRY_THREAD_OPTION_USER,
NULL,
pycurl_ssl_mutex_create,
pycurl_ssl_mutex_destroy,
pycurl_ssl_mutex_lock,
pycurl_ssl_mutex_unlock
};
static void pycurl_ssl_init(void)
{
gcry_control(GCRYCTL_SET_THREAD_CBS, &pycurl_gnutls_tsl);
}
static void pycurl_ssl_cleanup(void)
{
return;
}
#endif
/*************************************************************************
// CurlShareObject
**************************************************************************/
static void
share_lock_lock(ShareLock *lock, curl_lock_data data)
{
PyThread_acquire_lock(lock->locks[data], 1);
}
static void
share_lock_unlock(ShareLock *lock, curl_lock_data data)
{
PyThread_release_lock(lock->locks[data]);
}
static
ShareLock *share_lock_new(void)
{
int i;
ShareLock *lock = (ShareLock*)PyMem_Malloc(sizeof(ShareLock));
assert(lock);
for (i = 0; i < CURL_LOCK_DATA_LAST; ++i) {
lock->locks[i] = PyThread_allocate_lock();
if (lock->locks[i] == NULL) {
goto error;
}
}
return lock;
error:
for (--i; i >= 0; --i) {
PyThread_free_lock(lock->locks[i]);
lock->locks[i] = NULL;
}
PyMem_Free(lock);
return NULL;
}
static void
share_lock_destroy(ShareLock *lock)
{
int i;
assert(lock);
for (i = 0; i < CURL_LOCK_DATA_LAST; ++i){
assert(lock->locks[i] != NULL);
PyThread_free_lock(lock->locks[i]);
}
PyMem_Free(lock);
lock = NULL;
}
static void
share_lock_callback(CURL *handle, curl_lock_data data, curl_lock_access locktype, void *userptr)
{
CurlShareObject *share = (CurlShareObject*)userptr;
share_lock_lock(share->lock, data);
}
static void
share_unlock_callback(CURL *handle, curl_lock_data data, void *userptr)
{
CurlShareObject *share = (CurlShareObject*)userptr;
share_lock_unlock(share->lock, data);
}
#else /* WITH_THREAD */
#if defined(PYCURL_NEED_SSL_TSL)
static void pycurl_ssl_init(void)
{
return;
}
static void pycurl_ssl_cleanup(void)
{
return;
}
#endif
#endif /* WITH_THREAD */
/* constructor - this is a module-level function returning a new instance */
static CurlShareObject *
do_share_new(PyObject *dummy)
{
int res;
CurlShareObject *self;
#ifdef WITH_THREAD
const curl_lock_function lock_cb = share_lock_callback;
const curl_unlock_function unlock_cb = share_unlock_callback;
#endif
UNUSED(dummy);
/* Allocate python curl-share object */
self = (CurlShareObject *) PyObject_GC_New(CurlShareObject, p_CurlShare_Type);
if (self) {
PyObject_GC_Track(self);
}
else {
return NULL;
}
/* Initialize object attributes */
self->dict = NULL;
#ifdef WITH_THREAD
self->lock = share_lock_new();
assert(self->lock != NULL);
#endif
/* Allocate libcurl share handle */
self->share_handle = curl_share_init();
if (self->share_handle == NULL) {
Py_DECREF(self);
PyErr_SetString(ErrorObject, "initializing curl-share failed");
return NULL;
}
#ifdef WITH_THREAD
/* Set locking functions and data */
res = curl_share_setopt(self->share_handle, CURLSHOPT_LOCKFUNC, lock_cb);
assert(res == CURLE_OK);
res = curl_share_setopt(self->share_handle, CURLSHOPT_USERDATA, self);
assert(res == CURLE_OK);
res = curl_share_setopt(self->share_handle, CURLSHOPT_UNLOCKFUNC, unlock_cb);
assert(res == CURLE_OK);
#endif
return self;
}
static int
do_share_traverse(CurlShareObject *self, visitproc visit, void *arg)
{
int err;
#undef VISIT
#define VISIT(v) if ((v) != NULL && ((err = visit(v, arg)) != 0)) return err
VISIT(self->dict);
return 0;
#undef VISIT
}
/* Drop references that may have created reference cycles. */
static int
do_share_clear(CurlShareObject *self)
{
Py_CLEAR(self->dict);
return 0;
}
static void
util_share_close(CurlShareObject *self){
if (self->share_handle != NULL) {
CURLSH *share_handle = self->share_handle;
self->share_handle = NULL;
curl_share_cleanup(share_handle);
}
}
static void
do_share_dealloc(CurlShareObject *self){
PyObject_GC_UnTrack(self);
Py_TRASHCAN_SAFE_BEGIN(self);
Py_CLEAR(self->dict);
util_share_close(self);
#ifdef WITH_THREAD
share_lock_destroy(self->lock);
#endif
PyObject_GC_Del(self);
Py_TRASHCAN_SAFE_END(self)
}
static PyObject *
do_share_close(CurlShareObject *self)
{
if (check_share_state(self, 2, "close") != 0) {
return NULL;
}
util_share_close(self);
Py_RETURN_NONE;
}
/* setopt, unsetopt*/
/* --------------- unsetopt/setopt/getinfo --------------- */
static PyObject *
do_curlshare_setopt(CurlShareObject *self, PyObject *args)
{
int option;
PyObject *obj;
if (!PyArg_ParseTuple(args, "iO:setopt", &option, &obj))
return NULL;
if (check_share_state(self, 1 | 2, "sharesetopt") != 0)
return NULL;
/* early checks of option value */
if (option <= 0)
goto error;
if (option >= (int)CURLOPTTYPE_OFF_T + OPTIONS_SIZE)
goto error;
if (option % 10000 >= OPTIONS_SIZE)
goto error;
#if 0 /* XXX - should we ??? */
/* Handle the case of None */
if (obj == Py_None) {
return util_curl_unsetopt(self, option);
}
#endif
/* Handle the case of integer arguments */
if (PyInt_Check(obj)) {
long d = PyInt_AsLong(obj);
if (d != CURL_LOCK_DATA_COOKIE && d != CURL_LOCK_DATA_DNS && d != CURL_LOCK_DATA_SSL_SESSION) {
goto error;
}
switch(option) {
case CURLSHOPT_SHARE:
case CURLSHOPT_UNSHARE:
curl_share_setopt(self->share_handle, option, d);
break;
default:
PyErr_SetString(PyExc_TypeError, "integers are not supported for this option");
return NULL;
}
Py_RETURN_NONE;
}
/* Failed to match any of the function signatures -- return error */
error:
PyErr_SetString(PyExc_TypeError, "invalid arguments to setopt");
return NULL;
}
/*************************************************************************
// CurlObject
**************************************************************************/
/* --------------- construct/destruct (i.e. open/close) --------------- */
/* Allocate a new python curl object */
static CurlObject *
util_curl_new(void)
{
CurlObject *self;
self = (CurlObject *) PyObject_GC_New(CurlObject, p_Curl_Type);
if (self == NULL)
return NULL;
PyObject_GC_Track(self);
/* Set python curl object initial values */
self->dict = NULL;
self->handle = NULL;
#ifdef WITH_THREAD
self->state = NULL;
#endif
self->share = NULL;
self->multi_stack = NULL;
self->httppost = NULL;
self->httppost_ref_list = NULL;
self->httpheader = NULL;
self->http200aliases = NULL;
self->quote = NULL;
self->postquote = NULL;
self->prequote = NULL;
#ifdef HAVE_CURLOPT_RESOLVE
self->resolve = NULL;
#endif
/* Set callback pointers to NULL by default */
self->w_cb = NULL;
self->h_cb = NULL;
self->r_cb = NULL;
self->pro_cb = NULL;
self->debug_cb = NULL;
self->ioctl_cb = NULL;
self->opensocket_cb = NULL;
self->seek_cb = NULL;
/* Set file object pointers to NULL by default */
self->readdata_fp = NULL;
self->writedata_fp = NULL;
self->writeheader_fp = NULL;
/* Set postfields object pointer to NULL by default */
self->postfields_obj = NULL;
/* Zero string pointer memory buffer used by setopt */
memset(self->error, 0, sizeof(self->error));
return self;
}
/* initializer - used to intialize curl easy handles for use with pycurl */
static int
util_curl_init(CurlObject *self)
{
int res;
/* Set curl error buffer and zero it */
res = curl_easy_setopt(self->handle, CURLOPT_ERRORBUFFER, self->error);
if (res != CURLE_OK) {
return (-1);
}
memset(self->error, 0, sizeof(self->error));
/* Set backreference */
res = curl_easy_setopt(self->handle, CURLOPT_PRIVATE, (char *) self);
if (res != CURLE_OK) {
return (-1);
}
/* Enable NOPROGRESS by default, i.e. no progress output */
res = curl_easy_setopt(self->handle, CURLOPT_NOPROGRESS, (long)1);
if (res != CURLE_OK) {
return (-1);
}
/* Disable VERBOSE by default, i.e. no verbose output */
res = curl_easy_setopt(self->handle, CURLOPT_VERBOSE, (long)0);
if (res != CURLE_OK) {
return (-1);
}
/* Set FTP_ACCOUNT to NULL by default */
res = curl_easy_setopt(self->handle, CURLOPT_FTP_ACCOUNT, NULL);
if (res != CURLE_OK) {
return (-1);
}
/* Set default USERAGENT */
assert(g_pycurl_useragent);
res = curl_easy_setopt(self->handle, CURLOPT_USERAGENT, g_pycurl_useragent);
if (res != CURLE_OK) {
return (-1);
}
return (0);
}
/* constructor - this is a module-level function returning a new instance */
static CurlObject *
do_curl_new(PyObject *dummy)
{
CurlObject *self = NULL;
int res;
UNUSED(dummy);
/* Allocate python curl object */
self = util_curl_new();
if (self == NULL)
return NULL;
/* Initialize curl handle */
self->handle = curl_easy_init();
if (self->handle == NULL)
goto error;
res = util_curl_init(self);
if (res < 0)
goto error;
/* Success - return new object */
return self;
error:
Py_DECREF(self); /* this also closes self->handle */
PyErr_SetString(ErrorObject, "initializing curl failed");
return NULL;
}
/* util function shared by close() and clear() */
static void
util_curl_xdecref(CurlObject *self, int flags, CURL *handle)
{
if (flags & PYCURL_MEMGROUP_ATTRDICT) {
/* Decrement refcount for attributes dictionary. */
Py_CLEAR(self->dict);
}
if (flags & PYCURL_MEMGROUP_MULTI) {
/* Decrement refcount for multi_stack. */
if (self->multi_stack != NULL) {
CurlMultiObject *multi_stack = self->multi_stack;
self->multi_stack = NULL;
if (multi_stack->multi_handle != NULL && handle != NULL) {
(void) curl_multi_remove_handle(multi_stack->multi_handle, handle);
}
Py_DECREF(multi_stack);
}
}
if (flags & PYCURL_MEMGROUP_CALLBACK) {
/* Decrement refcount for python callbacks. */
Py_CLEAR(self->w_cb);
Py_CLEAR(self->h_cb);
Py_CLEAR(self->r_cb);
Py_CLEAR(self->pro_cb);
Py_CLEAR(self->debug_cb);
Py_CLEAR(self->ioctl_cb);
}
if (flags & PYCURL_MEMGROUP_FILE) {
/* Decrement refcount for python file objects. */
Py_CLEAR(self->readdata_fp);
Py_CLEAR(self->writedata_fp);
Py_CLEAR(self->writeheader_fp);
}
if (flags & PYCURL_MEMGROUP_POSTFIELDS) {
/* Decrement refcount for postfields object */
Py_CLEAR(self->postfields_obj);
}
if (flags & PYCURL_MEMGROUP_SHARE) {
/* Decrement refcount for share objects. */
if (self->share != NULL) {
CurlShareObject *share = self->share;
self->share = NULL;
if (share->share_handle != NULL && handle != NULL) {
curl_easy_setopt(handle, CURLOPT_SHARE, NULL);
}
Py_DECREF(share);
}
}
if (flags & PYCURL_MEMGROUP_HTTPPOST) {
/* Decrement refcounts for httppost related references. */
Py_CLEAR(self->httppost_ref_list);
}
}
static void
util_curl_close(CurlObject *self)
{
CURL *handle;
/* Zero handle and thread-state to disallow any operations to be run
* from now on */
assert(self != NULL);
assert(Py_TYPE(self) == p_Curl_Type);
handle = self->handle;
self->handle = NULL;
if (handle == NULL) {
/* Some paranoia assertions just to make sure the object
* deallocation problem is finally really fixed... */
#ifdef WITH_THREAD
assert(self->state == NULL);
#endif
assert(self->multi_stack == NULL);
assert(self->share == NULL);
return; /* already closed */
}
#ifdef WITH_THREAD
self->state = NULL;
#endif
/* Decref multi stuff which uses this handle */
util_curl_xdecref(self, PYCURL_MEMGROUP_MULTI, handle);
/* Decref share which uses this handle */
util_curl_xdecref(self, PYCURL_MEMGROUP_SHARE, handle);
/* Cleanup curl handle - must be done without the gil */
Py_BEGIN_ALLOW_THREADS
curl_easy_cleanup(handle);
Py_END_ALLOW_THREADS
handle = NULL;
/* Decref easy related objects */
util_curl_xdecref(self, PYCURL_MEMGROUP_EASY, handle);
/* Free all variables allocated by setopt */
#undef SFREE
#define SFREE(v) if ((v) != NULL) (curl_formfree(v), (v) = NULL)
SFREE(self->httppost);
#undef SFREE
#define SFREE(v) if ((v) != NULL) (curl_slist_free_all(v), (v) = NULL)
SFREE(self->httpheader);
SFREE(self->http200aliases);
SFREE(self->quote);
SFREE(self->postquote);
SFREE(self->prequote);
#ifdef HAVE_CURLOPT_RESOLVE
SFREE(self->resolve);
#endif
#undef SFREE
}
static void
do_curl_dealloc(CurlObject *self)
{
PyObject_GC_UnTrack(self);
Py_TRASHCAN_SAFE_BEGIN(self)
Py_CLEAR(self->dict);
util_curl_close(self);
PyObject_GC_Del(self);
Py_TRASHCAN_SAFE_END(self)
}
static PyObject *
do_curl_close(CurlObject *self)
{
if (check_curl_state(self, 2, "close") != 0) {
return NULL;
}
util_curl_close(self);
Py_RETURN_NONE;
}
static PyObject *
do_curl_errstr(CurlObject *self)
{
if (check_curl_state(self, 1 | 2, "errstr") != 0) {
return NULL;
}
self->error[sizeof(self->error) - 1] = 0;
return PyText_FromString(self->error);
}
/* --------------- GC support --------------- */
/* Drop references that may have created reference cycles. */
static int
do_curl_clear(CurlObject *self)
{
#ifdef WITH_THREAD
assert(get_thread_state(self) == NULL);
#endif
util_curl_xdecref(self, PYCURL_MEMGROUP_ALL, self->handle);
return 0;
}
/* Traverse all refcounted objects. */
static int
do_curl_traverse(CurlObject *self, visitproc visit, void *arg)
{
int err;
#undef VISIT
#define VISIT(v) if ((v) != NULL && ((err = visit(v, arg)) != 0)) return err
VISIT(self->dict);
VISIT((PyObject *) self->multi_stack);
VISIT((PyObject *) self->share);
VISIT(self->w_cb);
VISIT(self->h_cb);
VISIT(self->r_cb);
VISIT(self->pro_cb);
VISIT(self->debug_cb);
VISIT(self->ioctl_cb);
VISIT(self->opensocket_cb);
VISIT(self->seek_cb);
VISIT(self->readdata_fp);
VISIT(self->writedata_fp);
VISIT(self->writeheader_fp);
VISIT(self->postfields_obj);
return 0;
#undef VISIT
}
/* --------------- perform --------------- */
static PyObject *
do_curl_perform(CurlObject *self)
{
int res;
if (check_curl_state(self, 1 | 2, "perform") != 0) {
return NULL;
}
PYCURL_BEGIN_ALLOW_THREADS
res = curl_easy_perform(self->handle);
PYCURL_END_ALLOW_THREADS
if (res != CURLE_OK) {
CURLERROR_RETVAL();
}
Py_RETURN_NONE;
}
/* --------------- callback handlers --------------- */
/* IMPORTANT NOTE: due to threading issues, we cannot call _any_ Python
* function without acquiring the thread state in the callback handlers.
*/
static size_t
util_write_callback(int flags, char *ptr, size_t size, size_t nmemb, void *stream)
{
CurlObject *self;
PyObject *arglist;
PyObject *result = NULL;
size_t ret = 0; /* assume error */
PyObject *cb;
int total_size;
PYCURL_DECLARE_THREAD_STATE;
/* acquire thread */
self = (CurlObject *)stream;
if (!PYCURL_ACQUIRE_THREAD())
return ret;
/* check args */
cb = flags ? self->h_cb : self->w_cb;
if (cb == NULL)
goto silent_error;
if (size <= 0 || nmemb <= 0)
goto done;
total_size = (int)(size * nmemb);
if (total_size < 0 || (size_t)total_size / size != nmemb) {
PyErr_SetString(ErrorObject, "integer overflow in write callback");
goto verbose_error;
}
/* run callback */
#if PY_MAJOR_VERSION >= 3
arglist = Py_BuildValue("(y#)", ptr, total_size);
#else
arglist = Py_BuildValue("(s#)", ptr, total_size);
#endif
if (arglist == NULL)
goto verbose_error;
result = PyEval_CallObject(cb, arglist);
Py_DECREF(arglist);
if (result == NULL)
goto verbose_error;
/* handle result */
if (result == Py_None) {
ret = total_size; /* None means success */
}
else if (PyInt_Check(result) || PyLong_Check(result)) {
/* if the cast to long fails, PyLong_AsLong() returns -1L */
ret = (size_t) PyLong_AsLong(result);
}
else {
PyErr_SetString(ErrorObject, "write callback must return int or None");
goto verbose_error;
}
done:
silent_error:
Py_XDECREF(result);
PYCURL_RELEASE_THREAD();
return ret;
verbose_error:
PyErr_Print();
goto silent_error;
}
static size_t
write_callback(char *ptr, size_t size, size_t nmemb, void *stream)
{
return util_write_callback(0, ptr, size, nmemb, stream);
}
static size_t
header_callback(char *ptr, size_t size, size_t nmemb, void *stream)
{
return util_write_callback(1, ptr, size, nmemb, stream);
}
/* convert protocol address from C to python, returns a tuple of protocol
specific values */
static PyObject *
convert_protocol_address(struct sockaddr* saddr, unsigned int saddrlen)
{
PyObject *res_obj = NULL;
switch (saddr->sa_family)
{
case AF_INET:
{
struct sockaddr_in* sin = (struct sockaddr_in*)saddr;
char *addr_str = (char *)PyMem_Malloc(INET_ADDRSTRLEN);
if (addr_str == NULL) {
PyErr_SetString(ErrorObject, "Out of memory");
goto error;
}
if (inet_ntop(saddr->sa_family, &sin->sin_addr, addr_str, INET_ADDRSTRLEN) == NULL) {
PyErr_SetFromErrno(ErrorObject);
PyMem_Free(addr_str);
goto error;
}
res_obj = Py_BuildValue("(si)", addr_str, ntohs(sin->sin_port));
PyMem_Free(addr_str);
}
break;
case AF_INET6:
{
struct sockaddr_in6* sin6 = (struct sockaddr_in6*)saddr;
char *addr_str = (char *)PyMem_Malloc(INET6_ADDRSTRLEN);
if (addr_str == NULL) {
PyErr_SetString(ErrorObject, "Out of memory");
goto error;
}
if (inet_ntop(saddr->sa_family, &sin6->sin6_addr, addr_str, INET6_ADDRSTRLEN) == NULL) {
PyErr_SetFromErrno(ErrorObject);
PyMem_Free(addr_str);
goto error;
}
res_obj = Py_BuildValue("(si)", addr_str, ntohs(sin6->sin6_port));
PyMem_Free(addr_str);
}
break;
default:
/* We (currently) only support IPv4/6 addresses. Can curl even be used
with anything else? */
PyErr_SetString(ErrorObject, "Unsupported address family.");
}
error:
return res_obj;
}
#if defined(WIN32)
static SOCKET
dup_winsock(SOCKET sock, const struct curl_sockaddr *address)
{
int rv;
WSAPROTOCOL_INFO pi;
rv = WSADuplicateSocket(sock, GetCurrentProcessId(), &pi);
if (rv) {
return CURL_SOCKET_BAD;
}
/* not sure if WSA_FLAG_OVERLAPPED is needed, but it does not seem to hurt */
return WSASocket(address->family, address->socktype, address->protocol, &pi, 0, WSA_FLAG_OVERLAPPED);
}
#endif
/* curl_socket_t is just an int on unix/windows (with limitations that
* are not important here) */
static curl_socket_t
opensocket_callback(void *clientp, curlsocktype purpose,
struct curl_sockaddr *address)
{
PyObject *arglist;
PyObject *result = NULL;
PyObject *fileno_result = NULL;
CurlObject *self;
int ret = CURL_SOCKET_BAD;
PYCURL_DECLARE_THREAD_STATE;
self = (CurlObject *)clientp;
PYCURL_ACQUIRE_THREAD();
arglist = Py_BuildValue("(iiiN)", address->family, address->socktype, address->protocol, convert_protocol_address(&address->addr, address->addrlen));
if (arglist == NULL)
goto verbose_error;
result = PyEval_CallObject(self->opensocket_cb, arglist);
Py_DECREF(arglist);
if (result == NULL) {
goto verbose_error;
}
if (PyObject_HasAttrString(result, "fileno")) {
fileno_result = PyObject_CallMethod(result, "fileno", NULL);
if (fileno_result == NULL) {
ret = CURL_SOCKET_BAD;
goto verbose_error;
}
// normal operation:
if (PyInt_Check(fileno_result)) {
int sockfd = PyInt_AsLong(fileno_result);
#if defined(WIN32)
ret = dup_winsock(sockfd, address);
#else
ret = dup(sockfd);
#endif
goto done;
}
} else {
PyErr_SetString(ErrorObject, "Return value must be a socket.");
ret = CURL_SOCKET_BAD;
goto verbose_error;
}
silent_error:
done:
Py_XDECREF(result);
Py_XDECREF(fileno_result);
PYCURL_RELEASE_THREAD();
return ret;
verbose_error:
PyErr_Print();
goto silent_error;
}
static int
seek_callback(void *stream, curl_off_t offset, int origin)
{
CurlObject *self;
PyObject *arglist;
PyObject *result = NULL;
int ret = 2; /* assume error 2 (can't seek, libcurl free to work around). */
PyObject *cb;
int source = 0; /* assume beginning */
PYCURL_DECLARE_THREAD_STATE;
/* acquire thread */
self = (CurlObject *)stream;
if (!PYCURL_ACQUIRE_THREAD())
return ret;
/* check arguments */
switch (origin)
{
case SEEK_SET:
source = 0;
break;
case SEEK_CUR:
source = 1;
break;
case SEEK_END:
source = 2;
break;
default:
source = origin;
break;
}
/* run callback */
cb = self->seek_cb;
if (cb == NULL)
goto silent_error;
arglist = Py_BuildValue("(i,i)", offset, source);
if (arglist == NULL)
goto verbose_error;
result = PyEval_CallObject(cb, arglist);
Py_DECREF(arglist);
if (result == NULL)
goto verbose_error;
/* handle result */
if (result == Py_None) {
ret = 0; /* None means success */
}
else if (PyInt_Check(result)) {
int ret_code = PyInt_AsLong(result);
if (ret_code < 0 || ret_code > 2) {
PyErr_Format(ErrorObject, "invalid return value for seek callback %d not in (0, 1, 2)", ret_code);
goto verbose_error;
}
ret = ret_code; /* pass the return code from the callback */
}
else {
PyErr_SetString(ErrorObject, "seek callback must return 0 (CURL_SEEKFUNC_OK), 1 (CURL_SEEKFUNC_FAIL), 2 (CURL_SEEKFUNC_CANTSEEK) or None");
goto verbose_error;
}
silent_error:
Py_XDECREF(result);
PYCURL_RELEASE_THREAD();
return ret;
verbose_error:
PyErr_Print();
goto silent_error;
}
static size_t
read_callback(char *ptr, size_t size, size_t nmemb, void *stream)
{
CurlObject *self;
PyObject *arglist;
PyObject *result = NULL;
size_t ret = CURL_READFUNC_ABORT; /* assume error, this actually works */
int total_size;
PYCURL_DECLARE_THREAD_STATE;
/* acquire thread */
self = (CurlObject *)stream;
if (!PYCURL_ACQUIRE_THREAD())
return ret;
/* check args */
if (self->r_cb == NULL)
goto silent_error;
if (size <= 0 || nmemb <= 0)
goto done;
total_size = (int)(size * nmemb);
if (total_size < 0 || (size_t)total_size / size != nmemb) {
PyErr_SetString(ErrorObject, "integer overflow in read callback");
goto verbose_error;
}
/* run callback */
arglist = Py_BuildValue("(i)", total_size);
if (arglist == NULL)
goto verbose_error;
result = PyEval_CallObject(self->r_cb, arglist);
Py_DECREF(arglist);
if (result == NULL)
goto verbose_error;
/* handle result */
if (PyByteStr_Check(result)) {
char *buf = NULL;
Py_ssize_t obj_size = -1;
Py_ssize_t r;
r = PyByteStr_AsStringAndSize(result, &buf, &obj_size);
if (r != 0 || obj_size < 0 || obj_size > total_size) {
PyErr_Format(ErrorObject, "invalid return value for read callback (%ld bytes returned when at most %ld bytes were wanted)", (long)obj_size, (long)total_size);
goto verbose_error;
}
memcpy(ptr, buf, obj_size);
ret = obj_size; /* success */
}
else if (PyUnicode_Check(result)) {
char *buf = NULL;
Py_ssize_t obj_size = -1;
Py_ssize_t r;
/*
Encode with ascii codec.
HTTP requires sending content-length for request body to the server
before the request body is sent, therefore typically content length
is given via POSTFIELDSIZE before read function is invoked to
provide the data.
However, if we encode the string using any encoding other than ascii,
the length of encoded string may not match the length of unicode
string we are encoding. Therefore, if client code does a simple
len(source_string) to determine the value to supply in content-length,
the length of bytes read may be different.
To avoid this situation, we only accept ascii bytes in the string here.
Encode data yourself to bytes when dealing with non-ascii data.
*/
PyObject *encoded = PyUnicode_AsEncodedString(result, "ascii", "strict");
if (encoded == NULL) {
goto verbose_error;
}
r = PyByteStr_AsStringAndSize(encoded, &buf, &obj_size);
if (r != 0 || obj_size < 0 || obj_size > total_size) {
Py_DECREF(encoded);
PyErr_Format(ErrorObject, "invalid return value for read callback (%ld bytes returned after encoding to utf-8 when at most %ld bytes were wanted)", (long)obj_size, (long)total_size);
goto verbose_error;
}
memcpy(ptr, buf, obj_size);
Py_DECREF(encoded);
ret = obj_size; /* success */
}
#if PY_MAJOR_VERSION < 3
else if (PyInt_Check(result)) {
long r = PyInt_AsLong(result);
if (r != CURL_READFUNC_ABORT && r != CURL_READFUNC_PAUSE)
goto type_error;
ret = r; /* either CURL_READFUNC_ABORT or CURL_READFUNC_PAUSE */
}
#endif
else if (PyLong_Check(result)) {
long r = PyLong_AsLong(result);
if (r != CURL_READFUNC_ABORT && r != CURL_READFUNC_PAUSE)
goto type_error;
ret = r; /* either CURL_READFUNC_ABORT or CURL_READFUNC_PAUSE */
}
else {
type_error:
PyErr_SetString(ErrorObject, "read callback must return a byte string or Unicode string with ASCII code points only");
goto verbose_error;
}
done:
silent_error:
Py_XDECREF(result);
PYCURL_RELEASE_THREAD();
return ret;
verbose_error:
PyErr_Print();
goto silent_error;
}
static int
progress_callback(void *stream,
double dltotal, double dlnow, double ultotal, double 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->pro_cb == NULL)
goto silent_error;
/* run callback */
arglist = Py_BuildValue("(dddd)", dltotal, dlnow, ultotal, ulnow);
if (arglist == NULL)
goto verbose_error;
result = PyEval_CallObject(self->pro_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;
}
static int
debug_callback(CURL *curlobj, curl_infotype type,
char *buffer, size_t total_size, void *stream)
{
CurlObject *self;
PyObject *arglist;
PyObject *result = NULL;
int ret = 0; /* always success */
PYCURL_DECLARE_THREAD_STATE;
UNUSED(curlobj);
/* acquire thread */
self = (CurlObject *)stream;
if (!PYCURL_ACQUIRE_THREAD())
return ret;
/* check args */
if (self->debug_cb == NULL)
goto silent_error;
if ((int)total_size < 0 || (size_t)((int)total_size) != total_size) {
PyErr_SetString(ErrorObject, "integer overflow in debug callback");
goto verbose_error;
}
/* run callback */
arglist = Py_BuildValue("(is#)", (int)type, buffer, (int)total_size);
if (arglist == NULL)
goto verbose_error;
result = PyEval_CallObject(self->debug_cb, arglist);
Py_DECREF(arglist);
if (result == NULL)
goto verbose_error;
/* return values from debug callbacks should be ignored */
silent_error:
Py_XDECREF(result);
PYCURL_RELEASE_THREAD();
return ret;
verbose_error:
PyErr_Print();
goto silent_error;
}
static curlioerr
ioctl_callback(CURL *curlobj, int cmd, void *stream)
{
CurlObject *self;
PyObject *arglist;
PyObject *result = NULL;
int ret = CURLIOE_FAILRESTART; /* assume error */
PYCURL_DECLARE_THREAD_STATE;
UNUSED(curlobj);
/* acquire thread */
self = (CurlObject *)stream;
if (!PYCURL_ACQUIRE_THREAD())
return (curlioerr) ret;
/* check args */
if (self->ioctl_cb == NULL)
goto silent_error;
/* run callback */
arglist = Py_BuildValue("(i)", cmd);
if (arglist == NULL)
goto verbose_error;
result = PyEval_CallObject(self->ioctl_cb, arglist);
Py_DECREF(arglist);
if (result == NULL)
goto verbose_error;
/* handle result */
if (result == Py_None) {
ret = CURLIOE_OK; /* None means success */
}
else if (PyInt_Check(result)) {
ret = (int) PyInt_AsLong(result);
if (ret >= CURLIOE_LAST || ret < 0) {
PyErr_SetString(ErrorObject, "ioctl callback returned invalid value");
goto verbose_error;
}
}
silent_error:
Py_XDECREF(result);
PYCURL_RELEASE_THREAD();
return (curlioerr) ret;
verbose_error:
PyErr_Print();
goto silent_error;
}
/* ------------------------ reset ------------------------ */
static PyObject*
do_curl_reset(CurlObject *self)
{
int res;
curl_easy_reset(self->handle);
/* Decref easy interface related objects */
util_curl_xdecref(self, PYCURL_MEMGROUP_EASY, self->handle);
/* Free all variables allocated by setopt */
#undef SFREE
#define SFREE(v) if ((v) != NULL) (curl_formfree(v), (v) = NULL)
SFREE(self->httppost);
#undef SFREE
#define SFREE(v) if ((v) != NULL) (curl_slist_free_all(v), (v) = NULL)
SFREE(self->httpheader);
SFREE(self->http200aliases);
SFREE(self->quote);
SFREE(self->postquote);
SFREE(self->prequote);
#ifdef HAVE_CURLOPT_RESOLVE
SFREE(self->resolve);
#endif
#undef SFREE
res = util_curl_init(self);
if (res < 0) {
Py_DECREF(self); /* this also closes self->handle */
PyErr_SetString(ErrorObject, "resetting curl failed");
return NULL;
}
Py_RETURN_NONE;
}
/* --------------- unsetopt/setopt/getinfo --------------- */
static PyObject *
util_curl_unsetopt(CurlObject *self, int option)
{
int res;
#define SETOPT2(o,x) \
if ((res = curl_easy_setopt(self->handle, (o), (x))) != CURLE_OK) goto error
#define SETOPT(x) SETOPT2((CURLoption)option, (x))
/* FIXME: implement more options. Have to carefully check lib/url.c in the
* libcurl source code to see if it's actually safe to simply
* unset the option. */
switch (option)
{
case CURLOPT_SHARE:
SETOPT((CURLSH *) NULL);
Py_XDECREF(self->share);
self->share = NULL;
break;
case CURLOPT_HTTPPOST:
SETOPT((void *) 0);
curl_formfree(self->httppost);
util_curl_xdecref(self, PYCURL_MEMGROUP_HTTPPOST, self->handle);
self->httppost = NULL;
/* FIXME: what about data->set.httpreq ?? */
break;
case CURLOPT_INFILESIZE:
SETOPT((long) -1);
break;
case CURLOPT_WRITEHEADER:
SETOPT((void *) 0);
Py_CLEAR(self->writeheader_fp);
break;
case CURLOPT_CAINFO:
case CURLOPT_CAPATH:
case CURLOPT_COOKIE:
case CURLOPT_COOKIEJAR:
case CURLOPT_CUSTOMREQUEST:
case CURLOPT_EGDSOCKET:
case CURLOPT_FTPPORT:
case CURLOPT_PROXYUSERPWD:
#ifdef HAVE_CURLOPT_PROXYUSERNAME
case CURLOPT_PROXYUSERNAME:
case CURLOPT_PROXYPASSWORD:
#endif
case CURLOPT_RANDOM_FILE:
case CURLOPT_SSL_CIPHER_LIST:
case CURLOPT_USERPWD:
#ifdef HAVE_CURLOPT_USERNAME
case CURLOPT_USERNAME:
case CURLOPT_PASSWORD:
#endif
case CURLOPT_RANGE:
SETOPT((char *) 0);
break;
#ifdef HAVE_CURLOPT_CERTINFO
case CURLOPT_CERTINFO:
SETOPT((long) 0);
break;
#endif
/* info: we explicitly list unsupported options here */
case CURLOPT_COOKIEFILE:
default:
PyErr_SetString(PyExc_TypeError, "unsetopt() is not supported for this option");
return NULL;
}
Py_RETURN_NONE;
error:
CURLERROR_RETVAL();
#undef SETOPT
#undef SETOPT2
}
static PyObject *
do_curl_unsetopt(CurlObject *self, PyObject *args)
{
int option;
if (!PyArg_ParseTuple(args, "i:unsetopt", &option)) {
return NULL;
}
if (check_curl_state(self, 1 | 2, "unsetopt") != 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;
return util_curl_unsetopt(self, option);
error:
PyErr_SetString(PyExc_TypeError, "invalid arguments to unsetopt");
return NULL;
}
static PyObject *
do_curl_setopt(CurlObject *self, PyObject *args)
{
int option;
PyObject *obj;
int res;
PyObject *encoded_obj;
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;
/* 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_KRB4LEVEL:
case CURLOPT_NETRC_FILE:
case CURLOPT_PROXY:
case CURLOPT_PROXYUSERPWD:
#ifdef HAVE_CURLOPT_PROXYUSERNAME
case CURLOPT_PROXYUSERNAME:
case CURLOPT_PROXYPASSWORD:
#endif
case CURLOPT_RANDOM_FILE:
case CURLOPT_RANGE:
case CURLOPT_REFERER:
case CURLOPT_SSLCERT:
case CURLOPT_SSLCERTTYPE:
case CURLOPT_SSLENGINE:
case CURLOPT_SSLKEY:
case CURLOPT_SSLKEYPASSWD:
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:
#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:
#ifdef HAVE_CURLOPT_DNS_SERVERS
case CURLOPT_DNS_SERVERS:
#endif
#ifdef HAVE_CURLOPT_NOPROXY
case CURLOPT_NOPROXY:
#endif
/* 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) {
CURLERROR_RETVAL();
}
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 PY_MAJOR_VERSION >= 3
/* 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 {
store_obj = obj;
Py_INCREF(store_obj);
}
#else
/* no encoding is performed, incref the original object. */
store_obj = obj;
Py_INCREF(store_obj);
#endif
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;
}
/* 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;
}
#undef IS_LONG_OPTION
#undef IS_OFF_T_OPTION
#if PY_MAJOR_VERSION < 3
/* Handle the case of file objects */
if (PyFile_Check(obj)) {
FILE *fp;
/* 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);
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 objects */
if (PyList_Check(obj)) {
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_QUOTE:
old_slist = &self->quote;
break;
case CURLOPT_POSTQUOTE:
old_slist = &self->postquote;
break;
case CURLOPT_PREQUOTE:
old_slist = &self->prequote;
break;
#ifdef HAVE_CURLOPT_RESOLVE
case CURLOPT_RESOLVE:
old_slist = &self->resolve;
break;
#endif
case CURLOPT_HTTPPOST:
break;
default:
/* None of the list options were recognized, raise exception */
PyErr_SetString(PyExc_TypeError, "lists are not supported for this option");
return NULL;
}
len = PyList_Size(obj);
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;
for (i = 0; i < len; i++) {
char *nstr = NULL, *cstr = NULL;
Py_ssize_t nlen = -1, clen = -1;
PyObject *listitem = PyList_GetItem(obj, i);
if (!PyTuple_Check(listitem)) {
curl_formfree(post);
Py_XDECREF(ref_params);
PyErr_SetString(PyExc_TypeError, "list items must be tuple objects");
return NULL;
}
if (PyTuple_GET_SIZE(listitem) != 2) {
curl_formfree(post);
Py_XDECREF(ref_params);
PyErr_SetString(PyExc_TypeError, "tuple must contain two elements (name, value)");
return NULL;
}
if (PyText_AsStringAndSize(PyTuple_GET_ITEM(listitem, 0), &nstr, &nlen, &nencoded_obj) != 0) {
curl_formfree(post);
Py_XDECREF(ref_params);
PyErr_SetString(PyExc_TypeError, "tuple must contain a byte string or Unicode string with ASCII code points only as first element");
return NULL;
}
if (PyText_Check(PyTuple_GET_ITEM(listitem, 1))) {
/* Handle strings as second argument for backwards compatibility */
if (PyText_AsStringAndSize(PyTuple_GET_ITEM(listitem, 1), &cstr, &clen, &cencoded_obj)) {
curl_formfree(post);
Py_XDECREF(ref_params);
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);
CURLERROR_RETVAL();
}
}
else if (PyTuple_Check(PyTuple_GET_ITEM(listitem, 1))) {
/* Supports content, file and content-type */
PyObject *t = PyTuple_GET_ITEM(listitem, 1);
Py_ssize_t tlen = PyTuple_Size(t);
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);
PyErr_SetString(PyExc_TypeError, "tuple must contain at least one option and one value");
return NULL;
}
/* 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);
PyErr_NoMemory();
return 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;
if (j == (tlen-1)) {
PyErr_SetString(PyExc_TypeError, "expected value");
PyMem_Free(forms);
curl_formfree(post);
Py_XDECREF(ref_params);
return NULL;
}
if (!PyInt_Check(PyTuple_GET_ITEM(t, j))) {
PyErr_SetString(PyExc_TypeError, "option must be long");
PyMem_Free(forms);
curl_formfree(post);
Py_XDECREF(ref_params);
return NULL;
}
if (!PyText_Check(PyTuple_GET_ITEM(t, j+1))) {
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);
return NULL;
}
val = PyLong_AsLong(PyTuple_GET_ITEM(t, j));
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);
return NULL;
}
if (PyText_AsStringAndSize(PyTuple_GET_ITEM(t, j+1), &ostr, &olen, &oencoded_obj)) {
/* exception should be already set */
PyMem_Free(forms);
curl_formfree(post);
Py_XDECREF(ref_params);
return NULL;
}
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 = PyTuple_GET_ITEM(t, j+1);
ref_params = PyList_New((Py_ssize_t)0);
if (ref_params == NULL) {
PyText_EncodedDecref(oencoded_obj);
PyMem_Free(forms);
curl_formfree(post);
return NULL;
}
/* 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);
return NULL;
}
/* As with CURLFORM_COPYCONTENTS, specify the length. */
forms[k].option = CURLFORM_BUFFERLENGTH;
forms[k].value = (const char *)olen;
++k;
}
}
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();
}
} else {
/* Some other type was given, ignore */
PyText_EncodedDecref(nencoded_obj);
curl_formfree(post);
Py_XDECREF(ref_params);
PyErr_SetString(PyExc_TypeError, "unsupported second type in tuple");
return NULL;
}
PyText_EncodedDecref(nencoded_obj);
}
res = curl_easy_setopt(self->handle, CURLOPT_HTTPPOST, post);
/* Check for errors */
if (res != CURLE_OK) {
curl_formfree(post);
Py_XDECREF(ref_params);
CURLERROR_RETVAL();
}
/* 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;
}
/* Just to be sure we do not bug off here */
assert(old_slist != NULL && slist == NULL);
/* Handle regular list operations on the other options */
for (i = 0; i < len; i++) {
PyObject *listitem = PyList_GetItem(obj, i);
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;
Py_RETURN_NONE;
}
/* 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;
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;
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;
}
/* handle the SHARE case */
if (option == CURLOPT_SHARE) {
CurlShareObject *share;
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();
}
self->share = NULL;
Py_DECREF(share);
Py_RETURN_NONE;
}
}
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;
}
/*
Handle the case of file-like objects for Python 3.
Given an object with a write method, we will call the write method
from the appropriate callback.
Files in Python 3 are no longer FILE * instances and therefore cannot
be directly given to curl.
For consistency, ability to use any file-like object is also available
on Python 2.
*/
if (option == CURLOPT_READDATA ||
option == CURLOPT_WRITEDATA ||
option == CURLOPT_WRITEHEADER)
{
PyObject *write_method = PyObject_GetAttrString(obj, "write");
if (write_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(write_method);
return NULL;
}
option = CURLOPT_HEADERFUNCTION;
break;
default:
PyErr_SetString(PyExc_TypeError, "objects are not supported for this option");
Py_DECREF(write_method);
return NULL;
}
arglist = Py_BuildValue("(iO)", option, write_method);
/* reference is now in arglist */
Py_DECREF(write_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;
}
}
/* Failed to match any of the function signatures -- return error */
error:
PyErr_SetString(PyExc_TypeError, "invalid arguments to setopt");
return NULL;
}
static PyObject *
do_curl_getinfo(CurlObject *self, PyObject *args)
{
int option;
int res;
if (!PyArg_ParseTuple(args, "i:getinfo", &option)) {
return NULL;
}
if (check_curl_state(self, 1 | 2, "getinfo") != 0) {
return NULL;
}
switch (option) {
case CURLINFO_FILETIME:
case CURLINFO_HEADER_SIZE:
case CURLINFO_HTTP_CODE:
case CURLINFO_REDIRECT_COUNT:
case CURLINFO_REQUEST_SIZE:
case CURLINFO_SSL_VERIFYRESULT:
case CURLINFO_HTTP_CONNECTCODE:
case CURLINFO_HTTPAUTH_AVAIL:
case CURLINFO_PROXYAUTH_AVAIL:
case CURLINFO_OS_ERRNO:
case CURLINFO_NUM_CONNECTS:
case CURLINFO_LASTSOCKET:
#ifdef HAVE_CURLINFO_LOCAL_PORT
case CURLINFO_LOCAL_PORT:
#endif
#ifdef HAVE_CURLINFO_PRIMARY_PORT
case CURLINFO_PRIMARY_PORT:
#endif
{
/* Return PyInt as result */
long l_res = -1;
res = curl_easy_getinfo(self->handle, (CURLINFO)option, &l_res);
/* Check for errors and return result */
if (res != CURLE_OK) {
CURLERROR_RETVAL();
}
return PyInt_FromLong(l_res);
}
case CURLINFO_CONTENT_TYPE:
case CURLINFO_EFFECTIVE_URL:
case CURLINFO_FTP_ENTRY_PATH:
case CURLINFO_REDIRECT_URL:
case CURLINFO_PRIMARY_IP:
#ifdef HAVE_CURLINFO_LOCAL_IP
case CURLINFO_LOCAL_IP:
#endif
{
/* Return PyString as result */
char *s_res = NULL;
res = curl_easy_getinfo(self->handle, (CURLINFO)option, &s_res);
if (res != CURLE_OK) {
CURLERROR_RETVAL();
}
/* If the resulting string is NULL, return None */
if (s_res == NULL) {
Py_RETURN_NONE;
}
return PyText_FromString(s_res);
}
case CURLINFO_CONNECT_TIME:
case CURLINFO_APPCONNECT_TIME:
case CURLINFO_CONTENT_LENGTH_DOWNLOAD:
case CURLINFO_CONTENT_LENGTH_UPLOAD:
case CURLINFO_NAMELOOKUP_TIME:
case CURLINFO_PRETRANSFER_TIME:
case CURLINFO_REDIRECT_TIME:
case CURLINFO_SIZE_DOWNLOAD:
case CURLINFO_SIZE_UPLOAD:
case CURLINFO_SPEED_DOWNLOAD:
case CURLINFO_SPEED_UPLOAD:
case CURLINFO_STARTTRANSFER_TIME:
case CURLINFO_TOTAL_TIME:
{
/* Return PyFloat as result */
double d_res = 0.0;
res = curl_easy_getinfo(self->handle, (CURLINFO)option, &d_res);
if (res != CURLE_OK) {
CURLERROR_RETVAL();
}
return PyFloat_FromDouble(d_res);
}
case CURLINFO_SSL_ENGINES:
case CURLINFO_COOKIELIST:
{
/* Return a list of strings */
struct curl_slist *slist = NULL;
res = curl_easy_getinfo(self->handle, (CURLINFO)option, &slist);
if (res != CURLE_OK) {
CURLERROR_RETVAL();
}
return convert_slist(slist, 1 | 2);
}
#ifdef HAVE_CURLOPT_CERTINFO
case CURLINFO_CERTINFO:
{
/* Return a list of lists of 2-tuples */
struct curl_certinfo *clist = NULL;
res = curl_easy_getinfo(self->handle, CURLINFO_CERTINFO, &clist);
if (res != CURLE_OK) {
CURLERROR_RETVAL();
} else {
return convert_certinfo(clist);
}
}
#endif
}
/* Got wrong option on the method call */
PyErr_SetString(PyExc_ValueError, "invalid argument to getinfo");
return NULL;
}
/* curl_easy_pause() can be called from inside a callback or outside */
static PyObject *
do_curl_pause(CurlObject *self, PyObject *args)
{
int bitmask;
CURLcode res;
#ifdef WITH_THREAD
PyThreadState *saved_state;
#endif
if (!PyArg_ParseTuple(args, "i:pause", &bitmask)) {
return NULL;
}
if (check_curl_state(self, 1, "pause") != 0) {
return NULL;
}
#ifdef WITH_THREAD
/* Save handle to current thread (used as context for python callbacks) */
saved_state = self->state;
PYCURL_BEGIN_ALLOW_THREADS
/* We must allow threads here because unpausing a handle can cause
some of its callbacks to be invoked immediately, from inside
curl_easy_pause() */
#endif
res = curl_easy_pause(self->handle, bitmask);
#ifdef WITH_THREAD
PYCURL_END_ALLOW_THREADS
/* Restore the thread-state to whatever it was on entry */
self->state = saved_state;
#endif
if (res != CURLE_OK) {
CURLERROR_MSG("pause/unpause failed");
} else {
Py_INCREF(Py_None);
return Py_None;
}
}
static const char co_pause_doc [] =
"pause(bitmask) -> None. "
"Pauses or unpauses a curl handle. Bitmask should be a value such as PAUSE_RECV or PAUSE_CONT. "
"Raises pycurl.error exception upon failure.\n";
/*************************************************************************
// CurlMultiObject
**************************************************************************/
/* --------------- construct/destruct (i.e. open/close) --------------- */
/* constructor - this is a module-level function returning a new instance */
static CurlMultiObject *
do_multi_new(PyObject *dummy)
{
CurlMultiObject *self;
UNUSED(dummy);
/* Allocate python curl-multi object */
self = (CurlMultiObject *) PyObject_GC_New(CurlMultiObject, p_CurlMulti_Type);
if (self) {
PyObject_GC_Track(self);
}
else {
return NULL;
}
/* Initialize object attributes */
self->dict = NULL;
#ifdef WITH_THREAD
self->state = NULL;
#endif
self->t_cb = NULL;
self->s_cb = NULL;
/* Allocate libcurl multi handle */
self->multi_handle = curl_multi_init();
if (self->multi_handle == NULL) {
Py_DECREF(self);
PyErr_SetString(ErrorObject, "initializing curl-multi failed");
return NULL;
}
return self;
}
static void
util_multi_close(CurlMultiObject *self)
{
assert(self != NULL);
#ifdef WITH_THREAD
self->state = NULL;
#endif
if (self->multi_handle != NULL) {
CURLM *multi_handle = self->multi_handle;
self->multi_handle = NULL;
curl_multi_cleanup(multi_handle);
}
}
static void
do_multi_dealloc(CurlMultiObject *self)
{
PyObject_GC_UnTrack(self);
Py_TRASHCAN_SAFE_BEGIN(self)
Py_CLEAR(self->dict);
util_multi_close(self);
PyObject_GC_Del(self);
Py_TRASHCAN_SAFE_END(self)
}
static PyObject *
do_multi_close(CurlMultiObject *self)
{
if (check_multi_state(self, 2, "close") != 0) {
return NULL;
}
util_multi_close(self);
Py_RETURN_NONE;
}
/* --------------- GC support --------------- */
/* Drop references that may have created reference cycles. */
static int
do_multi_clear(CurlMultiObject *self)
{
Py_CLEAR(self->dict);
return 0;
}
static int
do_multi_traverse(CurlMultiObject *self, visitproc visit, void *arg)
{
int err;
#undef VISIT
#define VISIT(v) if ((v) != NULL && ((err = visit(v, arg)) != 0)) return err
VISIT(self->dict);
return 0;
#undef VISIT
}
/* --------------- setopt --------------- */
static int
multi_socket_callback(CURL *easy,
curl_socket_t s,
int what,
void *userp,
void *socketp)
{
CurlMultiObject *self;
PyObject *arglist;
PyObject *result = NULL;
PYCURL_DECLARE_THREAD_STATE;
/* acquire thread */
self = (CurlMultiObject *)userp;
if (!PYCURL_ACQUIRE_THREAD_MULTI())
return 0;
/* check args */
if (self->s_cb == NULL)
goto silent_error;
if (socketp == NULL) {
Py_INCREF(Py_None);
socketp = Py_None;
}
/* run callback */
arglist = Py_BuildValue("(iiOO)", what, s, userp, (PyObject *)socketp);
if (arglist == NULL)
goto verbose_error;
result = PyEval_CallObject(self->s_cb, arglist);
Py_DECREF(arglist);
if (result == NULL)
goto verbose_error;
/* return values from socket callbacks should be ignored */
silent_error:
Py_XDECREF(result);
PYCURL_RELEASE_THREAD();
return 0;
verbose_error:
PyErr_Print();
goto silent_error;
return 0;
}
static int
multi_timer_callback(CURLM *multi,
long timeout_ms,
void *userp)
{
CurlMultiObject *self;
PyObject *arglist;
PyObject *result = NULL;
int ret = 0; /* always success */
PYCURL_DECLARE_THREAD_STATE;
UNUSED(multi);
/* acquire thread */
self = (CurlMultiObject *)userp;
if (!PYCURL_ACQUIRE_THREAD_MULTI())
return ret;
/* check args */
if (self->t_cb == NULL)
goto silent_error;
/* run callback */
arglist = Py_BuildValue("(i)", timeout_ms);
if (arglist == NULL)
goto verbose_error;
result = PyEval_CallObject(self->t_cb, arglist);
Py_DECREF(arglist);
if (result == NULL)
goto verbose_error;
/* return values from timer callbacks should be ignored */
silent_error:
Py_XDECREF(result);
PYCURL_RELEASE_THREAD();
return ret;
verbose_error:
PyErr_Print();
goto silent_error;
return 0;
}
static PyObject *
do_multi_setopt(CurlMultiObject *self, PyObject *args)
{
int option;
PyObject *obj;
if (!PyArg_ParseTuple(args, "iO:setopt", &option, &obj))
return NULL;
if (check_multi_state(self, 1 | 2, "setopt") != 0)
return NULL;
/* Early checks of option value */
if (option <= 0)
goto error;
if (option >= (int)CURLOPTTYPE_OFF_T + MOPTIONS_SIZE)
goto error;
if (option % 10000 >= MOPTIONS_SIZE)
goto error;
/* Handle the case of integer arguments */
if (PyInt_Check(obj)) {
long d = PyInt_AsLong(obj);
switch(option) {
case CURLMOPT_PIPELINING:
curl_multi_setopt(self->multi_handle, option, d);
break;
case CURLMOPT_MAXCONNECTS:
curl_multi_setopt(self->multi_handle, option, d);
break;
default:
PyErr_SetString(PyExc_TypeError, "integers are not supported for this option");
return NULL;
}
Py_RETURN_NONE;
}
if (PyFunction_Check(obj) || PyCFunction_Check(obj) ||
PyCallable_Check(obj) || PyMethod_Check(obj)) {
/* We use function types here to make sure that our callback
* definitions exactly match the interface.
*/
const curl_multi_timer_callback t_cb = multi_timer_callback;
const curl_socket_callback s_cb = multi_socket_callback;
switch(option) {
case CURLMOPT_SOCKETFUNCTION:
curl_multi_setopt(self->multi_handle, CURLMOPT_SOCKETFUNCTION, s_cb);
curl_multi_setopt(self->multi_handle, CURLMOPT_SOCKETDATA, self);
Py_INCREF(obj);
self->s_cb = obj;
break;
case CURLMOPT_TIMERFUNCTION:
curl_multi_setopt(self->multi_handle, CURLMOPT_TIMERFUNCTION, t_cb);
curl_multi_setopt(self->multi_handle, CURLMOPT_TIMERDATA, self);
Py_INCREF(obj);
self->t_cb = obj;
break;
default:
PyErr_SetString(PyExc_TypeError, "callables are not supported for this option");
return NULL;
}
Py_RETURN_NONE;
}
/* Failed to match any of the function signatures -- return error */
error:
PyErr_SetString(PyExc_TypeError, "invalid arguments to setopt");
return NULL;
}
/* --------------- timeout --------------- */
static PyObject *
do_multi_timeout(CurlMultiObject *self)
{
CURLMcode res;
long timeout;
if (check_multi_state(self, 1 | 2, "timeout") != 0) {
return NULL;
}
res = curl_multi_timeout(self->multi_handle, &timeout);
if (res != CURLM_OK) {
CURLERROR_MSG("timeout failed");
}
/* Return number of millisecs until timeout */
return Py_BuildValue("l", timeout);
}
/* --------------- assign --------------- */
static PyObject *
do_multi_assign(CurlMultiObject *self, PyObject *args)
{
CURLMcode res;
curl_socket_t socket;
PyObject *obj;
if (!PyArg_ParseTuple(args, "iO:assign", &socket, &obj))
return NULL;
if (check_multi_state(self, 1 | 2, "assign") != 0) {
return NULL;
}
Py_INCREF(obj);
res = curl_multi_assign(self->multi_handle, socket, obj);
if (res != CURLM_OK) {
CURLERROR_MSG("assign failed");
}
Py_RETURN_NONE;
}
/* --------------- socket_action --------------- */
static PyObject *
do_multi_socket_action(CurlMultiObject *self, PyObject *args)
{
CURLMcode res;
curl_socket_t socket;
int ev_bitmask;
int running = -1;
if (!PyArg_ParseTuple(args, "ii:socket_action", &socket, &ev_bitmask))
return NULL;
if (check_multi_state(self, 1 | 2, "socket_action") != 0) {
return NULL;
}
PYCURL_BEGIN_ALLOW_THREADS
res = curl_multi_socket_action(self->multi_handle, socket, ev_bitmask, &running);
PYCURL_END_ALLOW_THREADS
if (res != CURLM_OK) {
CURLERROR_MSG("multi_socket_action failed");
}
/* Return a tuple with the result and the number of running handles */
return Py_BuildValue("(ii)", (int)res, running);
}
/* --------------- socket_all --------------- */
static PyObject *
do_multi_socket_all(CurlMultiObject *self)
{
CURLMcode res;
int running = -1;
if (check_multi_state(self, 1 | 2, "socket_all") != 0) {
return NULL;
}
PYCURL_BEGIN_ALLOW_THREADS
res = curl_multi_socket_all(self->multi_handle, &running);
PYCURL_END_ALLOW_THREADS
/* We assume these errors are ok, otherwise raise exception */
if (res != CURLM_OK && res != CURLM_CALL_MULTI_PERFORM) {
CURLERROR_MSG("perform failed");
}
/* Return a tuple with the result and the number of running handles */
return Py_BuildValue("(ii)", (int)res, running);
}
/* --------------- perform --------------- */
static PyObject *
do_multi_perform(CurlMultiObject *self)
{
CURLMcode res;
int running = -1;
if (check_multi_state(self, 1 | 2, "perform") != 0) {
return NULL;
}
PYCURL_BEGIN_ALLOW_THREADS
res = curl_multi_perform(self->multi_handle, &running);
PYCURL_END_ALLOW_THREADS
/* We assume these errors are ok, otherwise raise exception */
if (res != CURLM_OK && res != CURLM_CALL_MULTI_PERFORM) {
CURLERROR_MSG("perform failed");
}
/* Return a tuple with the result and the number of running handles */
return Py_BuildValue("(ii)", (int)res, running);
}
/* --------------- add_handle/remove_handle --------------- */
/* static utility function */
static int
check_multi_add_remove(const CurlMultiObject *self, const CurlObject *obj)
{
/* check CurlMultiObject status */
assert_multi_state(self);
if (self->multi_handle == NULL) {
PyErr_SetString(ErrorObject, "cannot add/remove handle - multi-stack is closed");
return -1;
}
#ifdef WITH_THREAD
if (self->state != NULL) {
PyErr_SetString(ErrorObject, "cannot add/remove handle - multi_perform() already running");
return -1;
}
#endif
/* check CurlObject status */
assert_curl_state(obj);
#ifdef WITH_THREAD
if (obj->state != NULL) {
PyErr_SetString(ErrorObject, "cannot add/remove handle - perform() of curl object already running");
return -1;
}
#endif
if (obj->multi_stack != NULL && obj->multi_stack != self) {
PyErr_SetString(ErrorObject, "cannot add/remove handle - curl object already on another multi-stack");
return -1;
}
return 0;
}
static PyObject *
do_multi_add_handle(CurlMultiObject *self, PyObject *args)
{
CurlObject *obj;
CURLMcode res;
if (!PyArg_ParseTuple(args, "O!:add_handle", p_Curl_Type, &obj)) {
return NULL;
}
if (check_multi_add_remove(self, obj) != 0) {
return NULL;
}
if (obj->handle == NULL) {
PyErr_SetString(ErrorObject, "curl object already closed");
return NULL;
}
if (obj->multi_stack == self) {
PyErr_SetString(ErrorObject, "curl object already on this multi-stack");
return NULL;
}
assert(obj->multi_stack == NULL);
res = curl_multi_add_handle(self->multi_handle, obj->handle);
if (res != CURLM_OK) {
CURLERROR_MSG("curl_multi_add_handle() failed due to internal errors");
}
obj->multi_stack = self;
Py_INCREF(self);
Py_RETURN_NONE;
}
static PyObject *
do_multi_remove_handle(CurlMultiObject *self, PyObject *args)
{
CurlObject *obj;
CURLMcode res;
if (!PyArg_ParseTuple(args, "O!:remove_handle", p_Curl_Type, &obj)) {
return NULL;
}
if (check_multi_add_remove(self, obj) != 0) {
return NULL;
}
if (obj->handle == NULL) {
/* CurlObject handle already closed -- ignore */
goto done;
}
if (obj->multi_stack != self) {
PyErr_SetString(ErrorObject, "curl object not on this multi-stack");
return NULL;
}
res = curl_multi_remove_handle(self->multi_handle, obj->handle);
if (res != CURLM_OK) {
CURLERROR_MSG("curl_multi_remove_handle() failed due to internal errors");
}
assert(obj->multi_stack == self);
obj->multi_stack = NULL;
Py_DECREF(self);
done:
Py_RETURN_NONE;
}
/* --------------- fdset ---------------------- */
static PyObject *
do_multi_fdset(CurlMultiObject *self)
{
CURLMcode res;
int max_fd = -1, fd;
PyObject *ret = NULL;
PyObject *read_list = NULL, *write_list = NULL, *except_list = NULL;
PyObject *py_fd = NULL;
if (check_multi_state(self, 1 | 2, "fdset") != 0) {
return NULL;
}
/* Clear file descriptor sets */
FD_ZERO(&self->read_fd_set);
FD_ZERO(&self->write_fd_set);
FD_ZERO(&self->exc_fd_set);
/* Don't bother releasing the gil as this is just a data structure operation */
res = curl_multi_fdset(self->multi_handle, &self->read_fd_set,
&self->write_fd_set, &self->exc_fd_set, &max_fd);
if (res != CURLM_OK) {
CURLERROR_MSG("curl_multi_fdset() failed due to internal errors");
}
/* Allocate lists. */
if ((read_list = PyList_New((Py_ssize_t)0)) == NULL) goto error;
if ((write_list = PyList_New((Py_ssize_t)0)) == NULL) goto error;
if ((except_list = PyList_New((Py_ssize_t)0)) == NULL) goto error;
/* Populate lists */
for (fd = 0; fd < max_fd + 1; fd++) {
if (FD_ISSET(fd, &self->read_fd_set)) {
if ((py_fd = PyInt_FromLong((long)fd)) == NULL) goto error;
if (PyList_Append(read_list, py_fd) != 0) goto error;
Py_DECREF(py_fd);
py_fd = NULL;
}
if (FD_ISSET(fd, &self->write_fd_set)) {
if ((py_fd = PyInt_FromLong((long)fd)) == NULL) goto error;
if (PyList_Append(write_list, py_fd) != 0) goto error;
Py_DECREF(py_fd);
py_fd = NULL;
}
if (FD_ISSET(fd, &self->exc_fd_set)) {
if ((py_fd = PyInt_FromLong((long)fd)) == NULL) goto error;
if (PyList_Append(except_list, py_fd) != 0) goto error;
Py_DECREF(py_fd);
py_fd = NULL;
}
}
/* Return a tuple with the 3 lists */
ret = Py_BuildValue("(OOO)", read_list, write_list, except_list);
error:
Py_XDECREF(py_fd);
Py_XDECREF(except_list);
Py_XDECREF(write_list);
Py_XDECREF(read_list);
return ret;
}
/* --------------- info_read --------------- */
static PyObject *
do_multi_info_read(CurlMultiObject *self, PyObject *args)
{
PyObject *ret = NULL;
PyObject *ok_list = NULL, *err_list = NULL;
CURLMsg *msg;
int in_queue = 0, num_results = INT_MAX;
/* Sanity checks */
if (!PyArg_ParseTuple(args, "|i:info_read", &num_results)) {
return NULL;
}
if (num_results <= 0) {
PyErr_SetString(ErrorObject, "argument to info_read must be greater than zero");
return NULL;
}
if (check_multi_state(self, 1 | 2, "info_read") != 0) {
return NULL;
}
if ((ok_list = PyList_New((Py_ssize_t)0)) == NULL) goto error;
if ((err_list = PyList_New((Py_ssize_t)0)) == NULL) goto error;
/* Loop through all messages */
while ((msg = curl_multi_info_read(self->multi_handle, &in_queue)) != NULL) {
CURLcode res;
CurlObject *co = NULL;
/* Check for termination as specified by the user */
if (num_results-- <= 0) {
break;
}
/* Fetch the curl object that corresponds to the curl handle in the message */
res = curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &co);
if (res != CURLE_OK || co == NULL) {
Py_DECREF(err_list);
Py_DECREF(ok_list);
CURLERROR_MSG("Unable to fetch curl handle from curl object");
}
assert(Py_TYPE(co) == p_Curl_Type);
if (msg->msg != CURLMSG_DONE) {
/* FIXME: what does this mean ??? */
}
if (msg->data.result == CURLE_OK) {
/* Append curl object to list of objects which succeeded */
if (PyList_Append(ok_list, (PyObject *)co) != 0) {
goto error;
}
}
else {
/* Create a result tuple that will get added to err_list. */
PyObject *v = Py_BuildValue("(Ois)", (PyObject *)co, (int)msg->data.result, co->error);
/* Append curl object to list of objects which failed */
if (v == NULL || PyList_Append(err_list, v) != 0) {
Py_XDECREF(v);
goto error;
}
Py_DECREF(v);
}
}
/* Return (number of queued messages, [ok_objects], [error_objects]) */
ret = Py_BuildValue("(iOO)", in_queue, ok_list, err_list);
error:
Py_XDECREF(err_list);
Py_XDECREF(ok_list);
return ret;
}
/* --------------- select --------------- */
static PyObject *
do_multi_select(CurlMultiObject *self, PyObject *args)
{
int max_fd = -1, n;
double timeout = -1.0;
struct timeval tv, *tvp;
CURLMcode res;
if (!PyArg_ParseTuple(args, "d:select", &timeout)) {
return NULL;
}
if (check_multi_state(self, 1 | 2, "select") != 0) {
return NULL;
}
if (timeout < 0 || timeout >= 365 * 24 * 60 * 60) {
PyErr_SetString(PyExc_OverflowError, "invalid timeout period");
return NULL;
} else {
long seconds = (long)timeout;
timeout = timeout - (double)seconds;
assert(timeout >= 0.0); assert(timeout < 1.0);
tv.tv_sec = seconds;
tv.tv_usec = (long)(timeout*1000000.0);
tvp = &tv;
}
FD_ZERO(&self->read_fd_set);
FD_ZERO(&self->write_fd_set);
FD_ZERO(&self->exc_fd_set);
res = curl_multi_fdset(self->multi_handle, &self->read_fd_set,
&self->write_fd_set, &self->exc_fd_set, &max_fd);
if (res != CURLM_OK) {
CURLERROR_MSG("multi_fdset failed");
}
if (max_fd < 0) {
n = 0;
}
else {
Py_BEGIN_ALLOW_THREADS
n = select(max_fd + 1, &self->read_fd_set, &self->write_fd_set, &self->exc_fd_set, tvp);
Py_END_ALLOW_THREADS
/* info: like Python's socketmodule.c we do not raise an exception
* if select() fails - we'll leave it to the actual libcurl
* socket code to report any errors.
*/
}
return PyInt_FromLong(n);
}
/*************************************************************************
// type definitions
**************************************************************************/
/* --------------- methods --------------- */
static const char cso_close_doc [] =
"close() -> None. "
"Close shared handle.\n";
static const char cso_setopt_doc [] =
"setopt(option, parameter) -> None. "
"Set curl share option. Raises pycurl.error exception upon failure.\n";
static const char co_close_doc [] =
"close() -> None. "
"Close handle and end curl session.\n";
static const char co_errstr_doc [] =
"errstr() -> String. "
"Return the internal libcurl error buffer string.\n";
static const char co_getinfo_doc [] =
"getinfo(info) -> Res. "
"Extract and return information from a curl session. Raises pycurl.error exception upon failure.\n";
static const char co_perform_doc [] =
"perform() -> None. "
"Perform a file transfer. Raises pycurl.error exception upon failure.\n";
static const char co_setopt_doc [] =
"setopt(option, parameter) -> None. "
"Set curl session option. Raises pycurl.error exception upon failure.\n";
static const char co_unsetopt_doc [] =
"unsetopt(option) -> None. "
"Reset curl session option to default value. Raises pycurl.error exception upon failure.\n";
static const char co_reset_doc [] =
"reset() -> None. "
"Reset all options set on curl handle to default values, but preserves live connections, session ID cache, DNS cache, cookies, and shares.\n";
static const char co_multi_fdset_doc [] =
"fdset() -> Tuple. "
"Returns a tuple of three lists that can be passed to the select.select() method .\n";
static const char co_multi_info_read_doc [] =
"info_read([max_objects]) -> Tuple. "
"Returns a tuple (number of queued handles, [curl objects]).\n";
static const char co_multi_select_doc [] =
"select([timeout]) -> Int. "
"Returns result from doing a select() on the curl multi file descriptor with the given timeout.\n";
static const char co_multi_socket_action_doc [] =
"socket_action(sockfd, ev_bitmask) -> Tuple. "
"Returns result from doing a socket_action() on the curl multi file descriptor with the given timeout.\n";
static const char co_multi_socket_all_doc [] =
"socket_all() -> Tuple. "
"Returns result from doing a socket_all() on the curl multi file descriptor with the given timeout.\n";
static PyMethodDef curlshareobject_methods[] = {
{"close", (PyCFunction)do_share_close, METH_NOARGS, cso_close_doc},
{"setopt", (PyCFunction)do_curlshare_setopt, METH_VARARGS, cso_setopt_doc},
{NULL, NULL, 0, 0}
};
static PyMethodDef curlobject_methods[] = {
{"close", (PyCFunction)do_curl_close, METH_NOARGS, co_close_doc},
{"errstr", (PyCFunction)do_curl_errstr, METH_NOARGS, co_errstr_doc},
{"getinfo", (PyCFunction)do_curl_getinfo, METH_VARARGS, co_getinfo_doc},
{"pause", (PyCFunction)do_curl_pause, METH_VARARGS, co_pause_doc},
{"perform", (PyCFunction)do_curl_perform, METH_NOARGS, co_perform_doc},
{"setopt", (PyCFunction)do_curl_setopt, METH_VARARGS, co_setopt_doc},
{"unsetopt", (PyCFunction)do_curl_unsetopt, METH_VARARGS, co_unsetopt_doc},
{"reset", (PyCFunction)do_curl_reset, METH_NOARGS, co_reset_doc},
{NULL, NULL, 0, NULL}
};
static PyMethodDef curlmultiobject_methods[] = {
{"add_handle", (PyCFunction)do_multi_add_handle, METH_VARARGS, NULL},
{"close", (PyCFunction)do_multi_close, METH_NOARGS, NULL},
{"fdset", (PyCFunction)do_multi_fdset, METH_NOARGS, co_multi_fdset_doc},
{"info_read", (PyCFunction)do_multi_info_read, METH_VARARGS, co_multi_info_read_doc},
{"perform", (PyCFunction)do_multi_perform, METH_NOARGS, NULL},
{"socket_action", (PyCFunction)do_multi_socket_action, METH_VARARGS, co_multi_socket_action_doc},
{"socket_all", (PyCFunction)do_multi_socket_all, METH_NOARGS, co_multi_socket_all_doc},
{"setopt", (PyCFunction)do_multi_setopt, METH_VARARGS, NULL},
{"timeout", (PyCFunction)do_multi_timeout, METH_NOARGS, NULL},
{"assign", (PyCFunction)do_multi_assign, METH_VARARGS, NULL},
{"remove_handle", (PyCFunction)do_multi_remove_handle, METH_VARARGS, NULL},
{"select", (PyCFunction)do_multi_select, METH_VARARGS, co_multi_select_doc},
{NULL, NULL, 0, NULL}
};
/* --------------- setattr/getattr --------------- */
static PyObject *curlobject_constants = NULL;
static PyObject *curlmultiobject_constants = NULL;
static PyObject *curlshareobject_constants = NULL;
#if PY_MAJOR_VERSION >= 3
static PyObject *
my_getattro(PyObject *co, PyObject *name, PyObject *dict1, PyObject *dict2, PyMethodDef *m)
{
PyObject *v = NULL;
if( dict1 != NULL )
v = PyDict_GetItem(dict1, name);
if( v == NULL && dict2 != NULL )
v = PyDict_GetItem(dict2, name);
if( v != NULL )
{
Py_INCREF(v);
return v;
}
PyErr_SetString(PyExc_AttributeError, "trying to obtain a non-existing attribute");
return NULL;
}
static int
my_setattro(PyObject **dict, PyObject *name, PyObject *v)
{
if( *dict == NULL )
{
*dict = PyDict_New();
if( *dict == NULL )
return -1;
}
if (v != NULL)
return PyDict_SetItem(*dict, name, v);
else {
int v = PyDict_DelItem(*dict, name);
if (v != 0) {
/* need to convert KeyError to AttributeError */
if (PyErr_ExceptionMatches(PyExc_KeyError)) {
PyErr_SetString(PyExc_AttributeError, "trying to delete a non-existing attribute");
}
}
return v;
}
}
PyObject *do_curl_getattro(PyObject *o, PyObject *n)
{
PyObject *v = PyObject_GenericGetAttr(o, n);
if( !v && PyErr_ExceptionMatches(PyExc_AttributeError) )
{
PyErr_Clear();
v = my_getattro(o, n, ((CurlObject *)o)->dict,
curlobject_constants, curlobject_methods);
}
return v;
}
static int
do_curl_setattro(PyObject *o, PyObject *name, PyObject *v)
{
assert_curl_state((CurlObject *)o);
return my_setattro(&((CurlObject *)o)->dict, name, v);
}
static PyObject *
do_multi_getattro(PyObject *o, PyObject *n)
{
PyObject *v;
assert_multi_state((CurlMultiObject *)o);
v = PyObject_GenericGetAttr(o, n);
if( !v && PyErr_ExceptionMatches(PyExc_AttributeError) )
{
PyErr_Clear();
v = my_getattro(o, n, ((CurlMultiObject *)o)->dict,
curlmultiobject_constants, curlmultiobject_methods);
}
return v;
}
static int
do_multi_setattro(PyObject *o, PyObject *n, PyObject *v)
{
assert_multi_state((CurlMultiObject *)o);
return my_setattro(&((CurlMultiObject *)o)->dict, n, v);
}
static PyObject *
do_share_getattro(PyObject *o, PyObject *n)
{
PyObject *v;
assert_share_state((CurlShareObject *)o);
v = PyObject_GenericGetAttr(o, n);
if( !v && PyErr_ExceptionMatches(PyExc_AttributeError) )
{
PyErr_Clear();
v = my_getattro(o, n, ((CurlShareObject *)o)->dict,
curlshareobject_constants, curlshareobject_methods);
}
return v;
}
static int
do_share_setattro(PyObject *o, PyObject *n, PyObject *v)
{
assert_share_state((CurlShareObject *)o);
return my_setattro(&((CurlShareObject *)o)->dict, n, v);
}
#else
static int
my_setattr(PyObject **dict, char *name, PyObject *v)
{
if (v == NULL) {
int rv = -1;
if (*dict != NULL)
rv = PyDict_DelItemString(*dict, name);
if (rv < 0)
PyErr_SetString(PyExc_AttributeError, "delete non-existing attribute");
return rv;
}
if (*dict == NULL) {
*dict = PyDict_New();
if (*dict == NULL)
return -1;
}
return PyDict_SetItemString(*dict, name, v);
}
static PyObject *
my_getattr(PyObject *co, char *name, PyObject *dict1, PyObject *dict2, PyMethodDef *m)
{
PyObject *v = NULL;
if (v == NULL && dict1 != NULL)
v = PyDict_GetItemString(dict1, name);
if (v == NULL && dict2 != NULL)
v = PyDict_GetItemString(dict2, name);
if (v != NULL) {
Py_INCREF(v);
return v;
}
return Py_FindMethod(m, co, name);
}
static int
do_share_setattr(CurlShareObject *so, char *name, PyObject *v)
{
assert_share_state(so);
return my_setattr(&so->dict, name, v);
}
static int
do_curl_setattr(CurlObject *co, char *name, PyObject *v)
{
assert_curl_state(co);
return my_setattr(&co->dict, name, v);
}
static int
do_multi_setattr(CurlMultiObject *co, char *name, PyObject *v)
{
assert_multi_state(co);
return my_setattr(&co->dict, name, v);
}
static PyObject *
do_share_getattr(CurlShareObject *cso, char *name)
{
assert_share_state(cso);
return my_getattr((PyObject *)cso, name, cso->dict,
curlshareobject_constants, curlshareobject_methods);
}
static PyObject *
do_curl_getattr(CurlObject *co, char *name)
{
assert_curl_state(co);
return my_getattr((PyObject *)co, name, co->dict,
curlobject_constants, curlobject_methods);
}
static PyObject *
do_multi_getattr(CurlMultiObject *co, char *name)
{
assert_multi_state(co);
return my_getattr((PyObject *)co, name, co->dict,
curlmultiobject_constants, curlmultiobject_methods);
}
#endif
/* --------------- actual type definitions --------------- */
#if PY_MAJOR_VERSION >= 3
static PyTypeObject CurlShare_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"pycurl.CurlShare", /* tp_name */
sizeof(CurlShareObject), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)do_share_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
(getattrofunc)do_share_getattro, /* tp_getattro */
(setattrofunc)do_share_setattro, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_HAVE_GC, /* tp_flags */
0, /* tp_doc */
(traverseproc)do_share_traverse, /* tp_traverse */
(inquiry)do_share_clear, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
curlshareobject_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
};
#else
static PyTypeObject CurlShare_Type = {
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
"pycurl.CurlShare", /* tp_name */
sizeof(CurlMultiObject), /* tp_basicsize */
0, /* tp_itemsize */
/* Methods */
(destructor)do_share_dealloc, /* tp_dealloc */
0, /* tp_print */
(getattrfunc)do_share_getattr, /* tp_getattr */
(setattrfunc)do_share_setattr, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_HAVE_GC, /* tp_flags */
0, /* tp_doc */
(traverseproc)do_share_traverse, /* tp_traverse */
(inquiry)do_share_clear /* tp_clear */
/* More fields follow here, depending on your Python version. You can
* safely ignore any compiler warnings about missing initializers.
*/
};
#endif
#if PY_MAJOR_VERSION >= 3
static PyTypeObject Curl_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"pycurl.Curl", /* tp_name */
sizeof(CurlObject), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)do_curl_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
(getattrofunc)do_curl_getattro, /* tp_getattro */
(setattrofunc)do_curl_setattro, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_HAVE_GC, /* tp_flags */
0, /* tp_doc */
(traverseproc)do_curl_traverse, /* tp_traverse */
(inquiry)do_curl_clear, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
curlobject_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
};
#else
static PyTypeObject Curl_Type = {
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
"pycurl.Curl", /* tp_name */
sizeof(CurlObject), /* tp_basicsize */
0, /* tp_itemsize */
/* Methods */
(destructor)do_curl_dealloc, /* tp_dealloc */
0, /* tp_print */
(getattrfunc)do_curl_getattr, /* tp_getattr */
(setattrfunc)do_curl_setattr, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_HAVE_GC, /* tp_flags */
0, /* tp_doc */
(traverseproc)do_curl_traverse, /* tp_traverse */
(inquiry)do_curl_clear /* tp_clear */
/* More fields follow here, depending on your Python version. You can
* safely ignore any compiler warnings about missing initializers.
*/
};
#endif
#if PY_MAJOR_VERSION >= 3
static PyTypeObject CurlMulti_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"pycurl.CurlMulti", /* tp_name */
sizeof(CurlMultiObject), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)do_multi_dealloc, /* tp_dealloc */
0, /* tp_print */
0, // (getattrfunc)do_curl_getattr, /* tp_getattr */
0, //(setattrfunc)do_curl_setattr, /* tp_setattr */
0, /* tp_reserved */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
(getattrofunc)do_multi_getattro, //0, /* tp_getattro */
(setattrofunc)do_multi_setattro, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_HAVE_GC, /* tp_flags */
0, /* tp_doc */
(traverseproc)do_multi_traverse, /* tp_traverse */
(inquiry)do_multi_clear, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
curlmultiobject_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
};
#else
static PyTypeObject CurlMulti_Type = {
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
"pycurl.CurlMulti", /* tp_name */
sizeof(CurlMultiObject), /* tp_basicsize */
0, /* tp_itemsize */
/* Methods */
(destructor)do_multi_dealloc, /* tp_dealloc */
0, /* tp_print */
(getattrfunc)do_multi_getattr, /* tp_getattr */
(setattrfunc)do_multi_setattr, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_HAVE_GC, /* tp_flags */
0, /* tp_doc */
(traverseproc)do_multi_traverse, /* tp_traverse */
(inquiry)do_multi_clear /* tp_clear */
/* More fields follow here, depending on your Python version. You can
* safely ignore any compiler warnings about missing initializers.
*/
};
#endif
static int
are_global_init_flags_valid(int flags)
{
#ifdef CURL_GLOBAL_ACK_EINTR
/* CURL_GLOBAL_ACK_EINTR was introduced in libcurl-7.30.0 */
return !(flags & ~(CURL_GLOBAL_ALL | CURL_GLOBAL_ACK_EINTR));
#else
return !(flags & ~(CURL_GLOBAL_ALL));
#endif
}
/*************************************************************************
// module level
// Note that the object constructors (do_curl_new, do_multi_new)
// are module-level functions as well.
**************************************************************************/
static PyObject *
do_global_init(PyObject *dummy, PyObject *args)
{
int res, option;
UNUSED(dummy);
if (!PyArg_ParseTuple(args, "i:global_init", &option)) {
return NULL;
}
if (!are_global_init_flags_valid(option)) {
PyErr_SetString(PyExc_ValueError, "invalid option to global_init");
return NULL;
}
res = curl_global_init(option);
if (res != CURLE_OK) {
PyErr_SetString(ErrorObject, "unable to set global option");
return NULL;
}
Py_RETURN_NONE;
}
static PyObject *
do_global_cleanup(PyObject *dummy)
{
UNUSED(dummy);
curl_global_cleanup();
#ifdef PYCURL_NEED_SSL_TSL
pycurl_ssl_cleanup();
#endif
Py_RETURN_NONE;
}
static PyObject *vi_str(const char *s)
{
if (s == NULL)
Py_RETURN_NONE;
while (*s == ' ' || *s == '\t')
s++;
return PyText_FromString(s);
}
static PyObject *
do_version_info(PyObject *dummy, PyObject *args)
{
const curl_version_info_data *vi;
PyObject *ret = NULL;
PyObject *protocols = NULL;
PyObject *tmp;
Py_ssize_t i;
int stamp = CURLVERSION_NOW;
UNUSED(dummy);
if (!PyArg_ParseTuple(args, "|i:version_info", &stamp)) {
return NULL;
}
vi = curl_version_info((CURLversion) stamp);
if (vi == NULL) {
PyErr_SetString(ErrorObject, "unable to get version info");
return NULL;
}
/* INFO: actually libcurl in lib/version.c does ignore
* the "stamp" parameter, and so do we. */
for (i = 0; vi->protocols[i] != NULL; )
i++;
protocols = PyTuple_New(i);
if (protocols == NULL)
goto error;
for (i = 0; vi->protocols[i] != NULL; i++) {
tmp = vi_str(vi->protocols[i]);
if (tmp == NULL)
goto error;
PyTuple_SET_ITEM(protocols, i, tmp);
}
ret = PyTuple_New((Py_ssize_t)12);
if (ret == NULL)
goto error;
#define SET(i, v) \
tmp = (v); if (tmp == NULL) goto error; PyTuple_SET_ITEM(ret, i, tmp)
SET(0, PyInt_FromLong((long) vi->age));
SET(1, vi_str(vi->version));
SET(2, PyInt_FromLong(vi->version_num));
SET(3, vi_str(vi->host));
SET(4, PyInt_FromLong(vi->features));
SET(5, vi_str(vi->ssl_version));
SET(6, PyInt_FromLong(vi->ssl_version_num));
SET(7, vi_str(vi->libz_version));
SET(8, protocols);
SET(9, vi_str(vi->ares));
SET(10, PyInt_FromLong(vi->ares_num));
SET(11, vi_str(vi->libidn));
#undef SET
return ret;
error:
Py_XDECREF(ret);
Py_XDECREF(protocols);
return NULL;
}
/* Per function docstrings */
static const char pycurl_global_init_doc[] =
"global_init(option) -> None. "
"Initialize curl environment.\n";
static const char pycurl_global_cleanup_doc[] =
"global_cleanup() -> None. "
"Cleanup curl environment.\n";
static const char pycurl_version_info_doc[] =
"version_info() -> tuple. "
"Returns a 12-tuple with the version info.\n";
static const char pycurl_share_new_doc[] =
"CurlShare() -> New CurlShare object.";
static const char pycurl_curl_new_doc[] =
"Curl() -> New curl object. "
"Implicitly calls global_init() if not called.\n";
static const char pycurl_multi_new_doc[] =
"CurlMulti() -> New curl multi-object.\n";
/* List of functions defined in this module */
static PyMethodDef curl_methods[] = {
{"global_init", (PyCFunction)do_global_init, METH_VARARGS, pycurl_global_init_doc},
{"global_cleanup", (PyCFunction)do_global_cleanup, METH_NOARGS, pycurl_global_cleanup_doc},
{"version_info", (PyCFunction)do_version_info, METH_VARARGS, pycurl_version_info_doc},
{"Curl", (PyCFunction)do_curl_new, METH_NOARGS, pycurl_curl_new_doc},
{"CurlMulti", (PyCFunction)do_multi_new, METH_NOARGS, pycurl_multi_new_doc},
{"CurlShare", (PyCFunction)do_share_new, METH_NOARGS, pycurl_share_new_doc},
{NULL, NULL, 0, NULL}
};
/* Module docstring */
static const char module_doc [] =
"This module implements an interface to the cURL library.\n"
"\n"
"Types:\n"
"\n"
"Curl() -> New object. Create a new curl object.\n"
"CurlMulti() -> New object. Create a new curl multi-object.\n"
"\n"
"Functions:\n"
"\n"
"global_init(option) -> None. Initialize curl environment.\n"
"global_cleanup() -> None. Cleanup curl environment.\n"
"version_info() -> tuple. Return version information.\n"
;
/* Helper functions for inserting constants into the module namespace */
static void
insobj2(PyObject *dict1, PyObject *dict2, char *name, PyObject *value)
{
/* Insert an object into one or two dicts. Eats a reference to value.
* See also the implementation of PyDict_SetItemString(). */
PyObject *key = NULL;
if (dict1 == NULL && dict2 == NULL)
goto error;
if (value == NULL)
goto error;
key = PyText_FromString(name);
if (key == NULL)
goto error;
#if 0
PyString_InternInPlace(&key); /* XXX Should we really? */
#endif
if (dict1 != NULL) {
assert(PyDict_GetItem(dict1, key) == NULL);
if (PyDict_SetItem(dict1, key, value) != 0)
goto error;
}
if (dict2 != NULL && dict2 != dict1) {
assert(PyDict_GetItem(dict2, key) == NULL);
if (PyDict_SetItem(dict2, key, value) != 0)
goto error;
}
Py_DECREF(key);
Py_DECREF(value);
return;
error:
Py_FatalError("pycurl: insobj2() failed");
assert(0);
}
static void
insstr(PyObject *d, char *name, char *value)
{
PyObject *v = PyText_FromString(value);
insobj2(d, NULL, name, v);
}
static void
insint(PyObject *d, char *name, long value)
{
PyObject *v = PyInt_FromLong(value);
insobj2(d, NULL, name, v);
}
static void
insint_s(PyObject *d, char *name, long value)
{
PyObject *v = PyInt_FromLong(value);
insobj2(d, curlshareobject_constants, name, v);
}
static void
insint_c(PyObject *d, char *name, long value)
{
PyObject *v = PyInt_FromLong(value);
insobj2(d, curlobject_constants, name, v);
}
static void
insint_m(PyObject *d, char *name, long value)
{
PyObject *v = PyInt_FromLong(value);
insobj2(d, curlmultiobject_constants, name, v);
}
/* Used in Python 3 only, and even then this function seems to never get
* called. Python 2 has no module cleanup:
* http://stackoverflow.com/questions/20741856/run-a-function-when-a-c-extension-module-is-freed-on-python-2
*/
void do_curlmod_free(void *unused) {
PyMem_Free(g_pycurl_useragent);
g_pycurl_useragent = NULL;
}
#if PY_MAJOR_VERSION >= 3
static PyModuleDef curlmodule = {
PyModuleDef_HEAD_INIT,
"pycurl", /* m_name */
module_doc, /* m_doc */
-1, /* m_size */
curl_methods, /* m_methods */
NULL, /* m_reload */
NULL, /* m_traverse */
NULL, /* m_clear */
do_curlmod_free /* m_free */
};
#endif
#if PY_MAJOR_VERSION >= 3
#define PYCURL_MODINIT_RETURN_NULL return NULL
PyMODINIT_FUNC PyInit_pycurl(void)
#else
#define PYCURL_MODINIT_RETURN_NULL return
/* Initialization function for the module */
#if defined(PyMODINIT_FUNC)
PyMODINIT_FUNC
#else
#if defined(__cplusplus)
extern "C"
#endif
DL_EXPORT(void)
#endif
initpycurl(void)
#endif
{
PyObject *m, *d;
const curl_version_info_data *vi;
const char *libcurl_version, *runtime_ssl_lib;
int libcurl_version_len, pycurl_version_len;
/* Check the version, as this has caused nasty problems in
* some cases. */
vi = curl_version_info(CURLVERSION_NOW);
if (vi == NULL) {
PyErr_SetString(PyExc_ImportError, "pycurl: curl_version_info() failed");
PYCURL_MODINIT_RETURN_NULL;
}
if (vi->version_num < LIBCURL_VERSION_NUM) {
PyErr_Format(PyExc_ImportError, "pycurl: libcurl link-time version (%s) is older than compile-time version (%s)", vi->version, LIBCURL_VERSION);
PYCURL_MODINIT_RETURN_NULL;
}
/* Our compiled crypto locks should correspond to runtime ssl library. */
if (vi->ssl_version == NULL) {
runtime_ssl_lib = "none/other";
} else if (!strncmp(vi->ssl_version, "OpenSSL/", 8)) {
runtime_ssl_lib = "openssl";
} else if (!strncmp(vi->ssl_version, "GnuTLS/", 7)) {
runtime_ssl_lib = "gnutls";
} else if (!strncmp(vi->ssl_version, "NSS/", 4)) {
runtime_ssl_lib = "nss";
} else {
runtime_ssl_lib = "none/other";
}
if (strcmp(runtime_ssl_lib, COMPILE_SSL_LIB)) {
PyErr_Format(PyExc_ImportError, "pycurl: libcurl link-time ssl backend (%s) is different from compile-time ssl backend (%s)", runtime_ssl_lib, COMPILE_SSL_LIB);
PYCURL_MODINIT_RETURN_NULL;
}
/* Initialize the type of the new type objects here; doing it here
* is required for portability to Windows without requiring C++. */
p_Curl_Type = &Curl_Type;
p_CurlMulti_Type = &CurlMulti_Type;
p_CurlShare_Type = &CurlShare_Type;
Py_TYPE(&Curl_Type) = &PyType_Type;
Py_TYPE(&CurlMulti_Type) = &PyType_Type;
Py_TYPE(&CurlShare_Type) = &PyType_Type;
/* Create the module and add the functions */
#if PY_MAJOR_VERSION >= 3
if (PyType_Ready(&Curl_Type) < 0)
return NULL;
if (PyType_Ready(&CurlMulti_Type) < 0)
return NULL;
m = PyModule_Create(&curlmodule);
if (m == NULL)
return NULL;
Py_INCREF(&Curl_Type);
#else
m = Py_InitModule3("pycurl", curl_methods, module_doc);
assert(m != NULL && PyModule_Check(m));
#endif
/* Add error object to the module */
d = PyModule_GetDict(m);
assert(d != NULL);
ErrorObject = PyErr_NewException("pycurl.error", NULL, NULL);
assert(ErrorObject != NULL);
PyDict_SetItemString(d, "error", ErrorObject);
curlobject_constants = PyDict_New();
assert(curlobject_constants != NULL);
/* Add version strings to the module */
libcurl_version = curl_version();
libcurl_version_len = strlen(libcurl_version);
#define PYCURL_VERSION_PREFIX_SIZE sizeof(PYCURL_VERSION_PREFIX)
/* PYCURL_VERSION_PREFIX_SIZE includes terminating null which will be
* 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);
assert(g_pycurl_useragent != NULL);
memcpy(g_pycurl_useragent, PYCURL_VERSION_PREFIX, PYCURL_VERSION_PREFIX_SIZE);
g_pycurl_useragent[PYCURL_VERSION_PREFIX_SIZE-1] = ' ';
memcpy(g_pycurl_useragent + PYCURL_VERSION_PREFIX_SIZE,
libcurl_version, libcurl_version_len);
g_pycurl_useragent[pycurl_version_len - 1] = 0;
#undef PYCURL_VERSION_PREFIX_SIZE
insobj2(d, NULL, "version", PyText_FromString(g_pycurl_useragent));
insstr(d, "COMPILE_DATE", __DATE__ " " __TIME__);
insint(d, "COMPILE_PY_VERSION_HEX", PY_VERSION_HEX);
insint(d, "COMPILE_LIBCURL_VERSION_NUM", LIBCURL_VERSION_NUM);
/**
** the order of these constants mostly follows
**/
/* Abort curl_read_callback(). */
insint_c(d, "READFUNC_ABORT", CURL_READFUNC_ABORT);
insint_c(d, "READFUNC_PAUSE", CURL_READFUNC_PAUSE);
/* Pause curl_write_callback(). */
insint_c(d, "WRITEFUNC_PAUSE", CURL_WRITEFUNC_PAUSE);
/* constants for ioctl callback return values */
insint_c(d, "IOE_OK", CURLIOE_OK);
insint_c(d, "IOE_UNKNOWNCMD", CURLIOE_UNKNOWNCMD);
insint_c(d, "IOE_FAILRESTART", CURLIOE_FAILRESTART);
/* constants for ioctl callback argument values */
insint_c(d, "IOCMD_NOP", CURLIOCMD_NOP);
insint_c(d, "IOCMD_RESTARTREAD", CURLIOCMD_RESTARTREAD);
/* curl_infotype: the kind of data that is passed to information_callback */
/* XXX do we actually need curl_infotype in pycurl ??? */
insint_c(d, "INFOTYPE_TEXT", CURLINFO_TEXT);
insint_c(d, "INFOTYPE_HEADER_IN", CURLINFO_HEADER_IN);
insint_c(d, "INFOTYPE_HEADER_OUT", CURLINFO_HEADER_OUT);
insint_c(d, "INFOTYPE_DATA_IN", CURLINFO_DATA_IN);
insint_c(d, "INFOTYPE_DATA_OUT", CURLINFO_DATA_OUT);
insint_c(d, "INFOTYPE_SSL_DATA_IN", CURLINFO_SSL_DATA_IN);
insint_c(d, "INFOTYPE_SSL_DATA_OUT", CURLINFO_SSL_DATA_OUT);
/* CURLcode: error codes */
insint_c(d, "E_OK", CURLE_OK);
insint_c(d, "E_UNSUPPORTED_PROTOCOL", CURLE_UNSUPPORTED_PROTOCOL);
insint_c(d, "E_FAILED_INIT", CURLE_FAILED_INIT);
insint_c(d, "E_URL_MALFORMAT", CURLE_URL_MALFORMAT);
insint_c(d, "E_COULDNT_RESOLVE_PROXY", CURLE_COULDNT_RESOLVE_PROXY);
insint_c(d, "E_COULDNT_RESOLVE_HOST", CURLE_COULDNT_RESOLVE_HOST);
insint_c(d, "E_COULDNT_CONNECT", CURLE_COULDNT_CONNECT);
insint_c(d, "E_FTP_WEIRD_SERVER_REPLY", CURLE_FTP_WEIRD_SERVER_REPLY);
insint_c(d, "E_FTP_ACCESS_DENIED", CURLE_FTP_ACCESS_DENIED);
insint_c(d, "E_FTP_WEIRD_PASS_REPLY", CURLE_FTP_WEIRD_PASS_REPLY);
insint_c(d, "E_FTP_WEIRD_USER_REPLY", CURLE_FTP_WEIRD_USER_REPLY);
insint_c(d, "E_FTP_WEIRD_PASV_REPLY", CURLE_FTP_WEIRD_PASV_REPLY);
insint_c(d, "E_FTP_WEIRD_227_FORMAT", CURLE_FTP_WEIRD_227_FORMAT);
insint_c(d, "E_FTP_CANT_GET_HOST", CURLE_FTP_CANT_GET_HOST);
insint_c(d, "E_FTP_CANT_RECONNECT", CURLE_FTP_CANT_RECONNECT);
insint_c(d, "E_FTP_COULDNT_SET_BINARY", CURLE_FTP_COULDNT_SET_BINARY);
insint_c(d, "E_PARTIAL_FILE", CURLE_PARTIAL_FILE);
insint_c(d, "E_FTP_COULDNT_RETR_FILE", CURLE_FTP_COULDNT_RETR_FILE);
insint_c(d, "E_FTP_WRITE_ERROR", CURLE_FTP_WRITE_ERROR);
insint_c(d, "E_FTP_QUOTE_ERROR", CURLE_FTP_QUOTE_ERROR);
insint_c(d, "E_HTTP_RETURNED_ERROR", CURLE_HTTP_RETURNED_ERROR);
insint_c(d, "E_WRITE_ERROR", CURLE_WRITE_ERROR);
insint_c(d, "E_FTP_COULDNT_STOR_FILE", CURLE_FTP_COULDNT_STOR_FILE);
insint_c(d, "E_READ_ERROR", CURLE_READ_ERROR);
insint_c(d, "E_OUT_OF_MEMORY", CURLE_OUT_OF_MEMORY);
insint_c(d, "E_OPERATION_TIMEOUTED", CURLE_OPERATION_TIMEOUTED);
insint_c(d, "E_OPERATION_TIMEDOUT", CURLE_OPERATION_TIMEDOUT);
insint_c(d, "E_FTP_COULDNT_SET_ASCII", CURLE_FTP_COULDNT_SET_ASCII);
insint_c(d, "E_FTP_PORT_FAILED", CURLE_FTP_PORT_FAILED);
insint_c(d, "E_FTP_COULDNT_USE_REST", CURLE_FTP_COULDNT_USE_REST);
insint_c(d, "E_FTP_COULDNT_GET_SIZE", CURLE_FTP_COULDNT_GET_SIZE);
insint_c(d, "E_HTTP_RANGE_ERROR", CURLE_HTTP_RANGE_ERROR);
insint_c(d, "E_HTTP_POST_ERROR", CURLE_HTTP_POST_ERROR);
insint_c(d, "E_SSL_CONNECT_ERROR", CURLE_SSL_CONNECT_ERROR);
insint_c(d, "E_BAD_DOWNLOAD_RESUME", CURLE_BAD_DOWNLOAD_RESUME);
insint_c(d, "E_FILE_COULDNT_READ_FILE", CURLE_FILE_COULDNT_READ_FILE);
insint_c(d, "E_LDAP_CANNOT_BIND", CURLE_LDAP_CANNOT_BIND);
insint_c(d, "E_LDAP_SEARCH_FAILED", CURLE_LDAP_SEARCH_FAILED);
insint_c(d, "E_LIBRARY_NOT_FOUND", CURLE_LIBRARY_NOT_FOUND);
insint_c(d, "E_FUNCTION_NOT_FOUND", CURLE_FUNCTION_NOT_FOUND);
insint_c(d, "E_ABORTED_BY_CALLBACK", CURLE_ABORTED_BY_CALLBACK);
insint_c(d, "E_BAD_FUNCTION_ARGUMENT", CURLE_BAD_FUNCTION_ARGUMENT);
insint_c(d, "E_INTERFACE_FAILED", CURLE_INTERFACE_FAILED);
insint_c(d, "E_TOO_MANY_REDIRECTS", CURLE_TOO_MANY_REDIRECTS);
insint_c(d, "E_UNKNOWN_TELNET_OPTION", CURLE_UNKNOWN_TELNET_OPTION);
insint_c(d, "E_TELNET_OPTION_SYNTAX", CURLE_TELNET_OPTION_SYNTAX);
insint_c(d, "E_SSL_PEER_CERTIFICATE", CURLE_SSL_PEER_CERTIFICATE);
insint_c(d, "E_GOT_NOTHING", CURLE_GOT_NOTHING);
insint_c(d, "E_SSL_ENGINE_NOTFOUND", CURLE_SSL_ENGINE_NOTFOUND);
insint_c(d, "E_SSL_ENGINE_SETFAILED", CURLE_SSL_ENGINE_SETFAILED);
insint_c(d, "E_SEND_ERROR", CURLE_SEND_ERROR);
insint_c(d, "E_RECV_ERROR", CURLE_RECV_ERROR);
insint_c(d, "E_SHARE_IN_USE", CURLE_SHARE_IN_USE);
insint_c(d, "E_SSL_CERTPROBLEM", CURLE_SSL_CERTPROBLEM);
insint_c(d, "E_SSL_CIPHER", CURLE_SSL_CIPHER);
insint_c(d, "E_SSL_CACERT", CURLE_SSL_CACERT);
insint_c(d, "E_BAD_CONTENT_ENCODING", CURLE_BAD_CONTENT_ENCODING);
insint_c(d, "E_LDAP_INVALID_URL", CURLE_LDAP_INVALID_URL);
insint_c(d, "E_FILESIZE_EXCEEDED", CURLE_FILESIZE_EXCEEDED);
insint_c(d, "E_FTP_SSL_FAILED", CURLE_FTP_SSL_FAILED);
insint_c(d, "E_SEND_FAIL_REWIND", CURLE_SEND_FAIL_REWIND);
insint_c(d, "E_SSL_ENGINE_INITFAILED", CURLE_SSL_ENGINE_INITFAILED);
insint_c(d, "E_LOGIN_DENIED", CURLE_LOGIN_DENIED);
insint_c(d, "E_TFTP_NOTFOUND", CURLE_TFTP_NOTFOUND);
insint_c(d, "E_TFTP_PERM", CURLE_TFTP_PERM);
insint_c(d, "E_TFTP_DISKFULL",CURLE_TFTP_DISKFULL );
insint_c(d, "E_TFTP_ILLEGAL",CURLE_TFTP_ILLEGAL );
insint_c(d, "E_TFTP_UNKNOWNID",CURLE_TFTP_UNKNOWNID );
insint_c(d, "E_TFTP_EXISTS", CURLE_TFTP_EXISTS);
insint_c(d, "E_TFTP_NOSUCHUSER",CURLE_TFTP_NOSUCHUSER );
insint_c(d, "E_CONV_FAILED",CURLE_CONV_FAILED );
insint_c(d, "E_CONV_REQD",CURLE_CONV_REQD );
insint_c(d, "E_SSL_CACERT_BADFILE", CURLE_SSL_CACERT_BADFILE);
insint_c(d, "E_REMOTE_FILE_NOT_FOUND",CURLE_REMOTE_FILE_NOT_FOUND );
insint_c(d, "E_SSH",CURLE_SSH );
insint_c(d, "E_SSL_SHUTDOWN_FAILED",CURLE_SSL_SHUTDOWN_FAILED );
/* curl_proxytype: constants for setopt(PROXYTYPE, x) */
insint_c(d, "PROXYTYPE_HTTP", CURLPROXY_HTTP);
insint_c(d, "PROXYTYPE_SOCKS4", CURLPROXY_SOCKS4);
insint_c(d, "PROXYTYPE_SOCKS5", CURLPROXY_SOCKS5);
/* curl_httpauth: constants for setopt(HTTPAUTH, x) */
insint_c(d, "HTTPAUTH_NONE", CURLAUTH_NONE);
insint_c(d, "HTTPAUTH_BASIC", CURLAUTH_BASIC);
insint_c(d, "HTTPAUTH_DIGEST", CURLAUTH_DIGEST);
#ifdef HAVE_CURLAUTH_DIGEST_IE
insint_c(d, "HTTPAUTH_DIGEST_IE", CURLAUTH_DIGEST_IE);
#endif
insint_c(d, "HTTPAUTH_GSSNEGOTIATE", CURLAUTH_GSSNEGOTIATE);
insint_c(d, "HTTPAUTH_NTLM", CURLAUTH_NTLM);
insint_c(d, "HTTPAUTH_ANY", CURLAUTH_ANY);
insint_c(d, "HTTPAUTH_ANYSAFE", CURLAUTH_ANYSAFE);
/* curl_ftpssl: constants for setopt(FTP_SSL, x) */
insint_c(d, "FTPSSL_NONE", CURLFTPSSL_NONE);
insint_c(d, "FTPSSL_TRY", CURLFTPSSL_TRY);
insint_c(d, "FTPSSL_CONTROL", CURLFTPSSL_CONTROL);
insint_c(d, "FTPSSL_ALL", CURLFTPSSL_ALL);
/* curl_ftpauth: constants for setopt(FTPSSLAUTH, x) */
insint_c(d, "FTPAUTH_DEFAULT", CURLFTPAUTH_DEFAULT);
insint_c(d, "FTPAUTH_SSL", CURLFTPAUTH_SSL);
insint_c(d, "FTPAUTH_TLS", CURLFTPAUTH_TLS);
/* curl_ftpauth: constants for setopt(FTPSSLAUTH, x) */
insint_c(d, "FORM_BUFFER", CURLFORM_BUFFER);
insint_c(d, "FORM_BUFFERPTR", CURLFORM_BUFFERPTR);
insint_c(d, "FORM_CONTENTS", CURLFORM_COPYCONTENTS);
insint_c(d, "FORM_FILE", CURLFORM_FILE);
insint_c(d, "FORM_CONTENTTYPE", CURLFORM_CONTENTTYPE);
insint_c(d, "FORM_FILENAME", CURLFORM_FILENAME);
/* FTP_FILEMETHOD options */
insint_c(d, "FTPMETHOD_DEFAULT", CURLFTPMETHOD_DEFAULT);
insint_c(d, "FTPMETHOD_MULTICWD", CURLFTPMETHOD_MULTICWD);
insint_c(d, "FTPMETHOD_NOCWD", CURLFTPMETHOD_NOCWD);
insint_c(d, "FTPMETHOD_SINGLECWD", CURLFTPMETHOD_SINGLECWD);
/* CURLoption: symbolic constants for setopt() */
/* FIXME: reorder these to match */
insint_c(d, "FILE", CURLOPT_WRITEDATA);
insint_c(d, "URL", CURLOPT_URL);
insint_c(d, "PORT", CURLOPT_PORT);
insint_c(d, "PROXY", CURLOPT_PROXY);
insint_c(d, "USERPWD", CURLOPT_USERPWD);
#ifdef HAVE_CURLOPT_USERNAME
insint_c(d, "USERNAME", CURLOPT_USERNAME);
insint_c(d, "PASSWORD", CURLOPT_PASSWORD);
#endif
insint_c(d, "PROXYUSERPWD", CURLOPT_PROXYUSERPWD);
#ifdef HAVE_CURLOPT_PROXYUSERNAME
insint_c(d, "PROXYUSERNAME", CURLOPT_PROXYUSERNAME);
insint_c(d, "PROXYPASSWORD", CURLOPT_PROXYPASSWORD);
#endif
insint_c(d, "RANGE", CURLOPT_RANGE);
insint_c(d, "INFILE", CURLOPT_READDATA);
/* ERRORBUFFER is not supported */
insint_c(d, "WRITEFUNCTION", CURLOPT_WRITEFUNCTION);
insint_c(d, "READFUNCTION", CURLOPT_READFUNCTION);
insint_c(d, "TIMEOUT", CURLOPT_TIMEOUT);
insint_c(d, "INFILESIZE", CURLOPT_INFILESIZE_LARGE); /* _LARGE ! */
insint_c(d, "POSTFIELDS", CURLOPT_POSTFIELDS);
insint_c(d, "REFERER", CURLOPT_REFERER);
insint_c(d, "FTPPORT", CURLOPT_FTPPORT);
insint_c(d, "USERAGENT", CURLOPT_USERAGENT);
insint_c(d, "LOW_SPEED_LIMIT", CURLOPT_LOW_SPEED_LIMIT);
insint_c(d, "LOW_SPEED_TIME", CURLOPT_LOW_SPEED_TIME);
insint_c(d, "RESUME_FROM", CURLOPT_RESUME_FROM_LARGE); /* _LARGE ! */
insint_c(d, "WRITEDATA", CURLOPT_WRITEDATA);
insint_c(d, "READDATA", CURLOPT_READDATA);
insint_c(d, "PROXYPORT", CURLOPT_PROXYPORT);
insint_c(d, "HTTPPROXYTUNNEL", CURLOPT_HTTPPROXYTUNNEL);
insint_c(d, "VERBOSE", CURLOPT_VERBOSE);
insint_c(d, "HEADER", CURLOPT_HEADER);
insint_c(d, "NOPROGRESS", CURLOPT_NOPROGRESS);
insint_c(d, "NOBODY", CURLOPT_NOBODY);
insint_c(d, "FAILONERROR", CURLOPT_FAILONERROR);
insint_c(d, "UPLOAD", CURLOPT_UPLOAD);
insint_c(d, "POST", CURLOPT_POST);
insint_c(d, "FTPLISTONLY", CURLOPT_FTPLISTONLY);
insint_c(d, "FTPAPPEND", CURLOPT_FTPAPPEND);
insint_c(d, "NETRC", CURLOPT_NETRC);
insint_c(d, "FOLLOWLOCATION", CURLOPT_FOLLOWLOCATION);
insint_c(d, "TRANSFERTEXT", CURLOPT_TRANSFERTEXT);
insint_c(d, "PUT", CURLOPT_PUT);
insint_c(d, "POSTFIELDSIZE", CURLOPT_POSTFIELDSIZE_LARGE); /* _LARGE ! */
insint_c(d, "COOKIE", CURLOPT_COOKIE);
insint_c(d, "HTTPHEADER", CURLOPT_HTTPHEADER);
insint_c(d, "HTTPPOST", CURLOPT_HTTPPOST);
insint_c(d, "SSLCERT", CURLOPT_SSLCERT);
insint_c(d, "SSLCERTPASSWD", CURLOPT_SSLCERTPASSWD);
insint_c(d, "CRLF", CURLOPT_CRLF);
insint_c(d, "QUOTE", CURLOPT_QUOTE);
insint_c(d, "POSTQUOTE", CURLOPT_POSTQUOTE);
insint_c(d, "PREQUOTE", CURLOPT_PREQUOTE);
insint_c(d, "WRITEHEADER", CURLOPT_WRITEHEADER);
insint_c(d, "HEADERFUNCTION", CURLOPT_HEADERFUNCTION);
insint_c(d, "SEEKFUNCTION", CURLOPT_SEEKFUNCTION);
insint_c(d, "COOKIEFILE", CURLOPT_COOKIEFILE);
insint_c(d, "SSLVERSION", CURLOPT_SSLVERSION);
insint_c(d, "TIMECONDITION", CURLOPT_TIMECONDITION);
insint_c(d, "TIMEVALUE", CURLOPT_TIMEVALUE);
insint_c(d, "CUSTOMREQUEST", CURLOPT_CUSTOMREQUEST);
insint_c(d, "STDERR", CURLOPT_STDERR);
insint_c(d, "INTERFACE", CURLOPT_INTERFACE);
insint_c(d, "KRB4LEVEL", CURLOPT_KRB4LEVEL);
insint_c(d, "PROGRESSFUNCTION", CURLOPT_PROGRESSFUNCTION);
insint_c(d, "SSL_VERIFYPEER", CURLOPT_SSL_VERIFYPEER);
insint_c(d, "CAPATH", CURLOPT_CAPATH);
insint_c(d, "CAINFO", CURLOPT_CAINFO);
insint_c(d, "OPT_FILETIME", CURLOPT_FILETIME);
insint_c(d, "MAXREDIRS", CURLOPT_MAXREDIRS);
insint_c(d, "MAXCONNECTS", CURLOPT_MAXCONNECTS);
insint_c(d, "FRESH_CONNECT", CURLOPT_FRESH_CONNECT);
insint_c(d, "FORBID_REUSE", CURLOPT_FORBID_REUSE);
insint_c(d, "RANDOM_FILE", CURLOPT_RANDOM_FILE);
insint_c(d, "EGDSOCKET", CURLOPT_EGDSOCKET);
insint_c(d, "CONNECTTIMEOUT", CURLOPT_CONNECTTIMEOUT);
insint_c(d, "HTTPGET", CURLOPT_HTTPGET);
insint_c(d, "SSL_VERIFYHOST", CURLOPT_SSL_VERIFYHOST);
insint_c(d, "COOKIEJAR", CURLOPT_COOKIEJAR);
insint_c(d, "SSL_CIPHER_LIST", CURLOPT_SSL_CIPHER_LIST);
insint_c(d, "HTTP_VERSION", CURLOPT_HTTP_VERSION);
insint_c(d, "FTP_USE_EPSV", CURLOPT_FTP_USE_EPSV);
insint_c(d, "SSLCERTTYPE", CURLOPT_SSLCERTTYPE);
insint_c(d, "SSLKEY", CURLOPT_SSLKEY);
insint_c(d, "SSLKEYTYPE", CURLOPT_SSLKEYTYPE);
insint_c(d, "SSLKEYPASSWD", CURLOPT_SSLKEYPASSWD);
insint_c(d, "SSLENGINE", CURLOPT_SSLENGINE);
insint_c(d, "SSLENGINE_DEFAULT", CURLOPT_SSLENGINE_DEFAULT);
insint_c(d, "DNS_CACHE_TIMEOUT", CURLOPT_DNS_CACHE_TIMEOUT);
insint_c(d, "DNS_USE_GLOBAL_CACHE", CURLOPT_DNS_USE_GLOBAL_CACHE);
insint_c(d, "DEBUGFUNCTION", CURLOPT_DEBUGFUNCTION);
insint_c(d, "BUFFERSIZE", CURLOPT_BUFFERSIZE);
insint_c(d, "NOSIGNAL", CURLOPT_NOSIGNAL);
insint_c(d, "SHARE", CURLOPT_SHARE);
insint_c(d, "PROXYTYPE", CURLOPT_PROXYTYPE);
insint_c(d, "ENCODING", CURLOPT_ENCODING);
insint_c(d, "HTTP200ALIASES", CURLOPT_HTTP200ALIASES);
insint_c(d, "UNRESTRICTED_AUTH", CURLOPT_UNRESTRICTED_AUTH);
insint_c(d, "FTP_USE_EPRT", CURLOPT_FTP_USE_EPRT);
insint_c(d, "HTTPAUTH", CURLOPT_HTTPAUTH);
insint_c(d, "FTP_CREATE_MISSING_DIRS", CURLOPT_FTP_CREATE_MISSING_DIRS);
insint_c(d, "PROXYAUTH", CURLOPT_PROXYAUTH);
insint_c(d, "FTP_RESPONSE_TIMEOUT", CURLOPT_FTP_RESPONSE_TIMEOUT);
insint_c(d, "IPRESOLVE", CURLOPT_IPRESOLVE);
insint_c(d, "MAXFILESIZE", CURLOPT_MAXFILESIZE_LARGE); /* _LARGE ! */
insint_c(d, "INFILESIZE_LARGE", CURLOPT_INFILESIZE_LARGE);
insint_c(d, "RESUME_FROM_LARGE", CURLOPT_RESUME_FROM_LARGE);
insint_c(d, "MAXFILESIZE_LARGE", CURLOPT_MAXFILESIZE_LARGE);
insint_c(d, "NETRC_FILE", CURLOPT_NETRC_FILE);
insint_c(d, "FTP_SSL", CURLOPT_FTP_SSL);
insint_c(d, "POSTFIELDSIZE_LARGE", CURLOPT_POSTFIELDSIZE_LARGE);
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);
insint_c(d, "FTP_ACCOUNT", CURLOPT_FTP_ACCOUNT);
insint_c(d, "IGNORE_CONTENT_LENGTH", CURLOPT_IGNORE_CONTENT_LENGTH);
insint_c(d, "COOKIELIST", CURLOPT_COOKIELIST);
insint_c(d, "FTP_SKIP_PASV_IP", CURLOPT_FTP_SKIP_PASV_IP);
insint_c(d, "FTP_FILEMETHOD", CURLOPT_FTP_FILEMETHOD);
insint_c(d, "CONNECT_ONLY", CURLOPT_CONNECT_ONLY);
insint_c(d, "LOCALPORT", CURLOPT_LOCALPORT);
insint_c(d, "LOCALPORTRANGE", CURLOPT_LOCALPORTRANGE);
insint_c(d, "FTP_ALTERNATIVE_TO_USER", CURLOPT_FTP_ALTERNATIVE_TO_USER);
insint_c(d, "MAX_SEND_SPEED_LARGE", CURLOPT_MAX_SEND_SPEED_LARGE);
insint_c(d, "MAX_RECV_SPEED_LARGE", CURLOPT_MAX_RECV_SPEED_LARGE);
insint_c(d, "SSL_SESSIONID_CACHE", CURLOPT_SSL_SESSIONID_CACHE);
insint_c(d, "SSH_AUTH_TYPES", CURLOPT_SSH_AUTH_TYPES);
insint_c(d, "SSH_PUBLIC_KEYFILE", CURLOPT_SSH_PUBLIC_KEYFILE);
insint_c(d, "SSH_PRIVATE_KEYFILE", CURLOPT_SSH_PRIVATE_KEYFILE);
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);
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);
insint_c(d, "NEW_DIRECTORY_PERMS", CURLOPT_NEW_DIRECTORY_PERMS);
insint_c(d, "POST301", CURLOPT_POST301);
insint_c(d, "PROXY_TRANSFER_MODE", CURLOPT_PROXY_TRANSFER_MODE);
insint_c(d, "COPYPOSTFIELDS", CURLOPT_COPYPOSTFIELDS);
insint_c(d, "SSH_HOST_PUBLIC_KEY_MD5", CURLOPT_SSH_HOST_PUBLIC_KEY_MD5);
insint_c(d, "AUTOREFERER", CURLOPT_AUTOREFERER);
insint_c(d, "CRLFILE", CURLOPT_CRLFILE);
insint_c(d, "ISSUERCERT", CURLOPT_ISSUERCERT);
insint_c(d, "ADDRESS_SCOPE", CURLOPT_ADDRESS_SCOPE);
#ifdef HAVE_CURLOPT_RESOLVE
insint_c(d, "RESOLVE", CURLOPT_RESOLVE);
#endif
#ifdef HAVE_CURLOPT_CERTINFO
insint_c(d, "OPT_CERTINFO", CURLOPT_CERTINFO);
#endif
#ifdef HAVE_CURLOPT_POSTREDIR
insint_c(d, "POSTREDIR", CURLOPT_POSTREDIR);
#endif
#ifdef HAVE_CURLOPT_NOPROXY
insint_c(d, "NOPROXY", CURLOPT_NOPROXY);
#endif
insint_c(d, "M_TIMERFUNCTION", CURLMOPT_TIMERFUNCTION);
insint_c(d, "M_SOCKETFUNCTION", CURLMOPT_SOCKETFUNCTION);
insint_c(d, "M_PIPELINING", CURLMOPT_PIPELINING);
insint_c(d, "M_MAXCONNECTS", CURLMOPT_MAXCONNECTS);
/* constants for setopt(IPRESOLVE, x) */
insint_c(d, "IPRESOLVE_WHATEVER", CURL_IPRESOLVE_WHATEVER);
insint_c(d, "IPRESOLVE_V4", CURL_IPRESOLVE_V4);
insint_c(d, "IPRESOLVE_V6", CURL_IPRESOLVE_V6);
/* constants for setopt(HTTP_VERSION, x) */
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);
insint_c(d, "CURL_HTTP_VERSION_LAST", CURL_HTTP_VERSION_LAST);
/* CURL_NETRC_OPTION: constants for setopt(NETRC, x) */
insint_c(d, "NETRC_OPTIONAL", CURL_NETRC_OPTIONAL);
insint_c(d, "NETRC_IGNORED", CURL_NETRC_IGNORED);
insint_c(d, "NETRC_REQUIRED", CURL_NETRC_REQUIRED);
/* constants for setopt(SSLVERSION, x) */
insint_c(d, "SSLVERSION_DEFAULT", CURL_SSLVERSION_DEFAULT);
insint_c(d, "SSLVERSION_TLSv1", CURL_SSLVERSION_TLSv1);
insint_c(d, "SSLVERSION_SSLv2", CURL_SSLVERSION_SSLv2);
insint_c(d, "SSLVERSION_SSLv3", CURL_SSLVERSION_SSLv3);
/* curl_TimeCond: constants for setopt(TIMECONDITION, x) */
insint_c(d, "TIMECONDITION_NONE", CURL_TIMECOND_NONE);
insint_c(d, "TIMECONDITION_IFMODSINCE", CURL_TIMECOND_IFMODSINCE);
insint_c(d, "TIMECONDITION_IFUNMODSINCE", CURL_TIMECOND_IFUNMODSINCE);
insint_c(d, "TIMECONDITION_LASTMOD", CURL_TIMECOND_LASTMOD);
/* constants for setopt(CURLOPT_SSH_AUTH_TYPES, x) */
insint_c(d, "SSH_AUTH_ANY", CURLSSH_AUTH_ANY);
insint_c(d, "SSH_AUTH_NONE", CURLSSH_AUTH_NONE);
insint_c(d, "SSH_AUTH_PUBLICKEY", CURLSSH_AUTH_PUBLICKEY);
insint_c(d, "SSH_AUTH_PASSWORD", CURLSSH_AUTH_PASSWORD);
insint_c(d, "SSH_AUTH_HOST", CURLSSH_AUTH_HOST);
insint_c(d, "SSH_AUTH_KEYBOARD", CURLSSH_AUTH_KEYBOARD);
insint_c(d, "SSH_AUTH_DEFAULT", CURLSSH_AUTH_DEFAULT);
/* CURLINFO: symbolic constants for getinfo(x) */
insint_c(d, "EFFECTIVE_URL", CURLINFO_EFFECTIVE_URL);
insint_c(d, "HTTP_CODE", CURLINFO_HTTP_CODE);
insint_c(d, "RESPONSE_CODE", CURLINFO_HTTP_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);
insint_c(d, "APPCONNECT_TIME", CURLINFO_APPCONNECT_TIME);
insint_c(d, "PRETRANSFER_TIME", CURLINFO_PRETRANSFER_TIME);
insint_c(d, "SIZE_UPLOAD", CURLINFO_SIZE_UPLOAD);
insint_c(d, "SIZE_DOWNLOAD", CURLINFO_SIZE_DOWNLOAD);
insint_c(d, "SPEED_DOWNLOAD", CURLINFO_SPEED_DOWNLOAD);
insint_c(d, "SPEED_UPLOAD", CURLINFO_SPEED_UPLOAD);
insint_c(d, "HEADER_SIZE", CURLINFO_HEADER_SIZE);
insint_c(d, "REQUEST_SIZE", CURLINFO_REQUEST_SIZE);
insint_c(d, "SSL_VERIFYRESULT", CURLINFO_SSL_VERIFYRESULT);
insint_c(d, "INFO_FILETIME", CURLINFO_FILETIME);
insint_c(d, "CONTENT_LENGTH_DOWNLOAD", CURLINFO_CONTENT_LENGTH_DOWNLOAD);
insint_c(d, "CONTENT_LENGTH_UPLOAD", CURLINFO_CONTENT_LENGTH_UPLOAD);
insint_c(d, "STARTTRANSFER_TIME", CURLINFO_STARTTRANSFER_TIME);
insint_c(d, "CONTENT_TYPE", CURLINFO_CONTENT_TYPE);
insint_c(d, "REDIRECT_TIME", CURLINFO_REDIRECT_TIME);
insint_c(d, "REDIRECT_COUNT", CURLINFO_REDIRECT_COUNT);
insint_c(d, "REDIRECT_URL", CURLINFO_REDIRECT_URL);
insint_c(d, "PRIMARY_IP", CURLINFO_PRIMARY_IP);
#ifdef HAVE_CURLINFO_PRIMARY_PORT
insint_c(d, "PRIMARY_PORT", CURLINFO_PRIMARY_PORT);
#endif
#ifdef HAVE_CURLINFO_LOCAL_IP
insint_c(d, "LOCAL_IP", CURLINFO_LOCAL_IP);
#endif
#ifdef HAVE_CURLINFO_LOCAL_PORT
insint_c(d, "LOCAL_PORT", CURLINFO_LOCAL_PORT);
#endif
insint_c(d, "HTTP_CONNECTCODE", CURLINFO_HTTP_CONNECTCODE);
insint_c(d, "HTTPAUTH_AVAIL", CURLINFO_HTTPAUTH_AVAIL);
insint_c(d, "PROXYAUTH_AVAIL", CURLINFO_PROXYAUTH_AVAIL);
insint_c(d, "OS_ERRNO", CURLINFO_OS_ERRNO);
insint_c(d, "NUM_CONNECTS", CURLINFO_NUM_CONNECTS);
insint_c(d, "SSL_ENGINES", CURLINFO_SSL_ENGINES);
insint_c(d, "INFO_COOKIELIST", CURLINFO_COOKIELIST);
insint_c(d, "LASTSOCKET", CURLINFO_LASTSOCKET);
insint_c(d, "FTP_ENTRY_PATH", CURLINFO_FTP_ENTRY_PATH);
#ifdef HAVE_CURLOPT_CERTINFO
insint_c(d, "INFO_CERTINFO", CURLINFO_CERTINFO);
#endif
/* CURLPAUSE: symbolic constants for pause(bitmask) */
insint_c(d, "PAUSE_RECV", CURLPAUSE_RECV);
insint_c(d, "PAUSE_SEND", CURLPAUSE_SEND);
insint_c(d, "PAUSE_ALL", CURLPAUSE_ALL);
insint_c(d, "PAUSE_CONT", CURLPAUSE_CONT);
#ifdef HAVE_CURLOPT_DNS_SERVERS
insint_c(d, "DNS_SERVERS", CURLOPT_DNS_SERVERS);
#endif
#ifdef HAVE_CURLOPT_POSTREDIR
insint_c(d, "REDIR_POST_301", CURL_REDIR_POST_301);
insint_c(d, "REDIR_POST_302", CURL_REDIR_POST_302);
# ifdef HAVE_CURL_REDIR_POST_303
insint_c(d, "REDIR_POST_303", CURL_REDIR_POST_303);
# endif
insint_c(d, "REDIR_POST_ALL", CURL_REDIR_POST_ALL);
#endif
/* options for global_init() */
insint(d, "GLOBAL_SSL", CURL_GLOBAL_SSL);
insint(d, "GLOBAL_WIN32", CURL_GLOBAL_WIN32);
insint(d, "GLOBAL_ALL", CURL_GLOBAL_ALL);
insint(d, "GLOBAL_NOTHING", CURL_GLOBAL_NOTHING);
insint(d, "GLOBAL_DEFAULT", CURL_GLOBAL_DEFAULT);
#ifdef CURL_GLOBAL_ACK_EINTR
/* CURL_GLOBAL_ACK_EINTR was introduced in libcurl-7.30.0 */
insint(d, "GLOBAL_ACK_EINTR", CURL_GLOBAL_ACK_EINTR);
#endif
/* constants for curl_multi_socket interface */
insint(d, "CSELECT_IN", CURL_CSELECT_IN);
insint(d, "CSELECT_OUT", CURL_CSELECT_OUT);
insint(d, "CSELECT_ERR", CURL_CSELECT_ERR);
insint(d, "SOCKET_TIMEOUT", CURL_SOCKET_TIMEOUT);
insint(d, "POLL_NONE", CURL_POLL_NONE);
insint(d, "POLL_IN", CURL_POLL_IN);
insint(d, "POLL_OUT", CURL_POLL_OUT);
insint(d, "POLL_INOUT", CURL_POLL_INOUT);
insint(d, "POLL_REMOVE", CURL_POLL_REMOVE);
/* curl_lock_data: XXX do we need this in pycurl ??? */
/* curl_lock_access: XXX do we need this in pycurl ??? */
/* CURLSHcode: XXX do we need this in pycurl ??? */
/* CURLSHoption: XXX do we need this in pycurl ??? */
/* CURLversion: constants for curl_version_info(x) */
#if 0
/* XXX - do we need these ?? */
insint(d, "VERSION_FIRST", CURLVERSION_FIRST);
insint(d, "VERSION_SECOND", CURLVERSION_SECOND);
insint(d, "VERSION_THIRD", CURLVERSION_THIRD);
insint(d, "VERSION_NOW", CURLVERSION_NOW);
#endif
/* version features - bitmasks for curl_version_info_data.features */
#if 0
/* XXX - do we need these ?? */
/* XXX - should we really rename these ?? */
insint(d, "VERSION_FEATURE_IPV6", CURL_VERSION_IPV6);
insint(d, "VERSION_FEATURE_KERBEROS4", CURL_VERSION_KERBEROS4);
insint(d, "VERSION_FEATURE_SSL", CURL_VERSION_SSL);
insint(d, "VERSION_FEATURE_LIBZ", CURL_VERSION_LIBZ);
insint(d, "VERSION_FEATURE_NTLM", CURL_VERSION_NTLM);
insint(d, "VERSION_FEATURE_GSSNEGOTIATE", CURL_VERSION_GSSNEGOTIATE);
insint(d, "VERSION_FEATURE_DEBUG", CURL_VERSION_DEBUG);
insint(d, "VERSION_FEATURE_ASYNCHDNS", CURL_VERSION_ASYNCHDNS);
insint(d, "VERSION_FEATURE_SPNEGO", CURL_VERSION_SPNEGO);
insint(d, "VERSION_FEATURE_LARGEFILE", CURL_VERSION_LARGEFILE);
insint(d, "VERSION_FEATURE_IDN", CURL_VERSION_IDN);
#endif
/**
** the order of these constants mostly follows
**/
/* CURLMcode: multi error codes */
curlmultiobject_constants = PyDict_New();
assert(curlmultiobject_constants != NULL);
insint_m(d, "E_CALL_MULTI_PERFORM", CURLM_CALL_MULTI_PERFORM);
insint_m(d, "E_MULTI_OK", CURLM_OK);
insint_m(d, "E_MULTI_BAD_HANDLE", CURLM_BAD_HANDLE);
insint_m(d, "E_MULTI_BAD_EASY_HANDLE", CURLM_BAD_EASY_HANDLE);
insint_m(d, "E_MULTI_OUT_OF_MEMORY", CURLM_OUT_OF_MEMORY);
insint_m(d, "E_MULTI_INTERNAL_ERROR", CURLM_INTERNAL_ERROR);
/* curl shared constants */
curlshareobject_constants = PyDict_New();
assert(curlshareobject_constants != NULL);
insint_s(d, "SH_SHARE", CURLSHOPT_SHARE);
insint_s(d, "SH_UNSHARE", CURLSHOPT_UNSHARE);
insint_s(d, "LOCK_DATA_COOKIE", CURL_LOCK_DATA_COOKIE);
insint_s(d, "LOCK_DATA_DNS", CURL_LOCK_DATA_DNS);
insint_s(d, "LOCK_DATA_SSL_SESSION", CURL_LOCK_DATA_SSL_SESSION);
/* Initialize callback locks if ssl is enabled */
#if defined(PYCURL_NEED_SSL_TSL)
pycurl_ssl_init();
#endif
#ifdef WITH_THREAD
/* Finally initialize global interpreter lock */
PyEval_InitThreads();
#endif
#if PY_MAJOR_VERSION >= 3
return m;
#endif
}
#if defined(WIN32) && ((_WIN32_WINNT < 0x0600) || (NTDDI_VERSION < NTDDI_VISTA))
/*
* Only Winsock on Vista+ has inet_ntop().
*/
static const char * pycurl_inet_ntop (int family, void *addr, char *string, size_t string_size)
{
SOCKADDR *sa;
int sa_len;
if (family == AF_INET6) {
struct sockaddr_in6 sa6;
memset(&sa6, 0, sizeof(sa6));
sa6.sin6_family = AF_INET6;
memcpy(&sa6.sin6_addr, addr, sizeof(sa6.sin6_addr));
sa = (SOCKADDR*) &sa6;
sa_len = sizeof(sa6);
} else if (family == AF_INET) {
struct sockaddr_in sa4;
memset(&sa4, 0, sizeof(sa4));
sa4.sin_family = AF_INET;
memcpy(&sa4.sin_addr, addr, sizeof(sa4.sin_addr));
sa = (SOCKADDR*) &sa4;
sa_len = sizeof(sa4);
} else {
errno = EAFNOSUPPORT;
return NULL;
}
if (WSAAddressToString(sa, sa_len, NULL, string, &string_size))
return NULL;
return string;
}
#endif
/* vi:ts=4:et:nowrap
*/
pycurl-7.19.3/tests/ 0000755 0004705 0004705 00000000000 12263734634 013667 5 ustar pie pie 0000000 0000000 pycurl-7.19.3/tests/certs/ 0000755 0004705 0004705 00000000000 12263734634 015007 5 ustar pie pie 0000000 0000000 pycurl-7.19.3/tests/certs/server.crt 0000644 0004705 0004705 00000001426 12256232314 017020 0 ustar pie pie 0000000 0000000 -----BEGIN CERTIFICATE-----
MIICGzCCAYQCCQCdeJzNRLLLvDANBgkqhkiG9w0BAQUFADBSMQswCQYDVQQGEwJB
VTETMBEGA1UECBMKU29tZS1TdGF0ZTEaMBgGA1UEChMRUHljVVJMIHRlc3Qgc3Vp
dGUxEjAQBgNVBAMTCWxvY2FsaG9zdDAeFw0xMzEyMjIxMzA0MTVaFw0xNzA5MTcx
MzA0MTVaMFIxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMRowGAYD
VQQKExFQeWNVUkwgdGVzdCBzdWl0ZTESMBAGA1UEAxMJbG9jYWxob3N0MIGfMA0G
CSqGSIb3DQEBAQUAA4GNADCBiQKBgQDETT7n0p/bP1xxnJQC4oZCmRBpe0QkLXd7
FjhJYh+vvwV8JK12qll1+abrp2MUQ2GZoBlgaw8cZesQgdCqNrbspiQejhxcvGSx
HgNvTXI8y0xT3EfODfs5MhLaUZGYzEt24pzlcqzED3w9X8PWTiSyotwsMf/9h0tH
SJhRgs4iAwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAGhX0iIMCrRcI6T6S4YydkXf
7LrXZpjSJwVGSCd5ehx3Qvc1LQNJjpB68F0MhVTqbDP1o3CAHaTa2s/8NC9j3tV7
bUynoKJT3srpHisfdd/SV538mWvFDtGRctbmmqp8qT4On+kr76dKj+/d3HyfOKIK
Aasa7ODxFKbbY542yYHu
-----END CERTIFICATE-----
pycurl-7.19.3/tests/certs/server.key 0000644 0004705 0004705 00000001567 12255756661 017045 0 ustar pie pie 0000000 0000000 -----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQDETT7n0p/bP1xxnJQC4oZCmRBpe0QkLXd7FjhJYh+vvwV8JK12
qll1+abrp2MUQ2GZoBlgaw8cZesQgdCqNrbspiQejhxcvGSxHgNvTXI8y0xT3EfO
Dfs5MhLaUZGYzEt24pzlcqzED3w9X8PWTiSyotwsMf/9h0tHSJhRgs4iAwIDAQAB
AoGAMLNFTvgnJpqikaEZ61lNm8ojkze8oQkSolvR3QrV96D5eGIVEuKSTT2Blucx
In7RAO8CPLRyzEXQuoiqPwBSAxY2Xswd+zcAgN2Zi8uqWTmPNsW6451BJRemgLjK
OxLxCdVTOTxHfttj/CnwYQ6zn55oyZJGGmaVGykbvH/AgukCQQD3HfhOPExsI/6X
Bp7CeZOhmM+LBOQGQGDjRnBdRp0s3HiUfaDxU2mbEafGPI2OyuzpYAqxHVTJLai6
CQlJGuQXAkEAy1upObz2bcN2dUCHNufk2qdfRSCRkmKemuqznwCW3fSoRKB+qOu3
xyTLEkTvLBNnAFjoyd6B75QzL/7//qvo9QJAE0xV3dY7qZ5N/YFY2Jsh+layroqd
PBe++UDA+afQEnbNO9trvCzlbGS+k26bJ3GVeswzSY2e128nZA/cl8bv1QJAfTEO
uybjpqtAj+qL03drYljLw+jK9Y2VCtYWgnqAZmAp/yW3FBMZbpXuFm8ttrqzHHmf
xjcfUvivkoqv2n7GyQJBAKxbBVx/LQiSVpOTnXTEA1NJF8NS2NCF+3sm3kGhFKql
Hi/cCAFrhBl9MoPJF/6noukfIkq0SzjkWrYIcoBDoVg=
-----END RSA PRIVATE KEY-----
pycurl-7.19.3/tests/ext/ 0000755 0004705 0004705 00000000000 12263734634 014467 5 ustar pie pie 0000000 0000000 pycurl-7.19.3/tests/ext/test-lib.sh 0000644 0004705 0004705 00000003747 12257207035 016552 0 ustar pie pie 0000000 0000000 # shell test framework based on test framework in rpg:
# https://github.com/rtomayko/rpg
#
# Copyright (c) 2010 Ryan Tomayko
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
: ${VERBOSE:=false}
unset CDPATH
#cd "$(dirname $0)"
if test -z "$TESTDIR"; then
TESTDIR=$(realpath $(pwd))
fi
test_count=0
successes=0
failures=0
output="$TESTDIR/$(basename "$0" .sh).out"
trap "rm -f $output" 0
succeeds () {
test_count=$(( test_count + 1 ))
echo "\$ ${2:-$1}" > "$output"
eval "( ${2:-$1} )" 1>>"$output" 2>&1
ec=$?
if test $ec -eq 0
then successes=$(( successes + 1 ))
printf 'ok %d - %s\n' $test_count "$1"
else failures=$(( failures + 1 ))
printf 'not ok %d - %s [%d]\n' $test_count "$1" "$ec"
fi
$VERBOSE && dcat $output
return 0
}
fails () {
if test $# -eq 1
then succeeds "! $1"
else succeeds "$1" "! $2"
fi
}
diag () { echo "$@" | sed 's/^/# /'; }
dcat () { cat "$@" | sed 's/^/# /'; }
desc () { diag "$@"; }
setup () {
rm -rf "$TESTDIR/trash"
return 0
}
pycurl-7.19.3/tests/ext/test-suite.sh 0000755 0004705 0004705 00000001360 12257207035 017125 0 ustar pie pie 0000000 0000000 #
dir=$(dirname "$0")
. "$dir"/test-lib.sh
setup
desc 'setup.py without arguments'
fails 'python setup.py'
succeeds 'python setup.py 2>&1 |grep "usage: setup.py"'
desc 'setup.py --help'
succeeds 'python setup.py --help'
# .* = Unix|Windows
succeeds 'python setup.py --help |grep "PycURL .* options:"'
# distutils help
succeeds 'python setup.py --help |grep "Common commands:"'
desc 'setup.py --help with bogus --curl-config'
succeeds 'python setup.py --help --curl-config=/dev/null'
succeeds 'python setup.py --help --curl-config=/dev/null |grep "PycURL .* options:"'
# this checks that --curl-config is consumed prior to
# distutils processing --help
fails 'python setup.py --help --curl-config=/dev/null 2>&1 |grep "option .* not recognized"'
pycurl-7.19.3/tests/matrix/ 0000755 0004705 0004705 00000000000 12263734634 015173 5 ustar pie pie 0000000 0000000 pycurl-7.19.3/tests/matrix/curl-7.19.0-sslv2-c66b0b32fba-modified.patch 0000644 0004705 0004705 00000001111 12256232314 024316 0 ustar pie pie 0000000 0000000 diff --git a/lib/ssluse.c b/lib/ssluse.c
index cee78bb..967fc57 100644
--- a/lib/ssluse.c
+++ b/lib/ssluse.c
@@ -1327,8 +1327,13 @@ ossl_connect_step1(struct connectdata *conn,
req_method = TLSv1_client_method();
break;
case CURL_SSLVERSION_SSLv2:
+#ifdef OPENSSL_NO_SSL2
+ failf(data, "OpenSSL was built without SSLv2 support");
+ return CURLE_UNSUPPORTED_PROTOCOL /* CURLE_NOT_BUILT_IN not defined in 7.19.0 */;
+#else
req_method = SSLv2_client_method();
break;
+#endif
case CURL_SSLVERSION_SSLv3:
req_method = SSLv3_client_method();
break;
pycurl-7.19.3/tests/matrix/nose-1.3.0-python24.patch 0000644 0004705 0004705 00000004346 12255756661 021316 0 ustar pie pie 0000000 0000000 diff --git a/nose/failure.py b/nose/failure.py
index d24401c..c5fabfd 100644
--- a/nose/failure.py
+++ b/nose/failure.py
@@ -1,6 +1,7 @@
import logging
import unittest
from traceback import format_tb
+from nose.pyversion import is_base_exception
log = logging.getLogger(__name__)
@@ -34,7 +35,7 @@ def address(self):
def runTest(self):
if self.tb is not None:
- if isinstance(self.exc_val, BaseException):
+ if is_base_exception(self.exc_val):
raise self.exc_val, None, self.tb
raise self.exc_class, self.exc_val, self.tb
else:
diff --git a/nose/plugins/capture.py b/nose/plugins/capture.py
index 224f0a5..d9041d8 100644
--- a/nose/plugins/capture.py
+++ b/nose/plugins/capture.py
@@ -13,6 +13,7 @@
import os
import sys
from nose.plugins.base import Plugin
+from nose.pyversion import is_base_exception
from nose.util import ln
from StringIO import StringIO
@@ -86,7 +87,7 @@ def formatFailure(self, test, err):
return self.formatError(test, err)
def addCaptureToErr(self, ev, output):
- if isinstance(ev, BaseException):
+ if is_base_exception(ev):
if hasattr(ev, '__unicode__'):
# 2.6+
try:
diff --git a/nose/pyversion.py b/nose/pyversion.py
index a6ec3f7..14a133e 100644
--- a/nose/pyversion.py
+++ b/nose/pyversion.py
@@ -9,7 +9,7 @@
__all__ = ['make_instancemethod', 'cmp_to_key', 'sort_list', 'ClassType',
'TypeType', 'UNICODE_STRINGS', 'unbound_method', 'ismethod',
- 'bytes_']
+ 'bytes_', 'is_base_exception']
# In Python 3.x, all strings are unicode (the call to 'unicode()' in the 2.x
# source will be replaced with 'str()' when running 2to3, so this test will
@@ -147,3 +147,12 @@ def isgenerator(func):
return func.func_code.co_flags & CO_GENERATOR != 0
except AttributeError:
return False
+
+# Make a function to help check if an exception is derived from BaseException.
+# In Python 2.4, we just use Exception instead.
+if sys.version_info[:2] < (2, 5):
+ def is_base_exception(exc):
+ return isinstance(exc, Exception)
+else:
+ def is_base_exception(exc):
+ return isinstance(exc, BaseException)
pycurl-7.19.3/tests/matrix/python25.patch 0000644 0004705 0004705 00000004051 12255756661 017711 0 ustar pie pie 0000000 0000000
# HG changeset patch
# User Benjamin Peterson
# Date 1243106677 0
# Node ID 150986ab3db284cfd38d67a1ace2f04c2780bae4
# Parent 54bbfd41be668dfc23c083510211accda7e54133
support building with subversion 1.7 #6094
diff --git a/Makefile.pre.in b/Makefile.pre.in
--- a/Makefile.pre.in
+++ b/Makefile.pre.in
@@ -505,7 +505,7 @@ Modules/getbuildinfo.o: $(PARSER_OBJS) \
$(SIGNAL_OBJS) \
$(MODOBJS) \
$(srcdir)/Modules/getbuildinfo.c
- $(CC) -c $(PY_CFLAGS) -DSVNVERSION=\"`LC_ALL=C $(SVNVERSION)`\" -o $@ $(srcdir)/Modules/getbuildinfo.c
+ $(CC) -c $(PY_CFLAGS) -DSVNVERSION="\"`LC_ALL=C $(SVNVERSION)`\"" -o $@ $(srcdir)/Modules/getbuildinfo.c
Modules/getpath.o: $(srcdir)/Modules/getpath.c Makefile
$(CC) -c $(PY_CFLAGS) -DPYTHONPATH='"$(PYTHONPATH)"' \
diff --git a/Modules/getbuildinfo.c b/Modules/getbuildinfo.c
--- a/Modules/getbuildinfo.c
+++ b/Modules/getbuildinfo.c
@@ -48,5 +48,5 @@ const char *
static const char svnversion[] = SVNVERSION;
if (svnversion[0] != '$')
return svnversion; /* it was interpolated, or passed on command line */
- return "exported";
+ return "Unversioned directory";
}
diff --git a/Python/sysmodule.c b/Python/sysmodule.c
--- a/Python/sysmodule.c
+++ b/Python/sysmodule.c
@@ -1158,7 +1158,7 @@ svnversion_init(void)
svnversion = _Py_svnversion();
- if (strcmp(svnversion, "exported") != 0)
+ if (strcmp(svnversion, "Unversioned directory") != 0 && strcmp(svnversion, "exported") != 0)
svn_revision = svnversion;
else if (istag) {
len = strlen(_patchlevel_revision);
diff --git a/configure b/configure
--- a/configure
+++ b/configure
@@ -4385,7 +4385,7 @@ if test $SVNVERSION = found
then
SVNVERSION="svnversion \$(srcdir)"
else
- SVNVERSION="echo exported"
+ SVNVERSION="echo Unversioned directory"
fi
case $MACHDEP in
diff --git a/configure.in b/configure.in
--- a/configure.in
+++ b/configure.in
@@ -802,7 +802,7 @@ if test $SVNVERSION = found
then
SVNVERSION="svnversion \$(srcdir)"
else
- SVNVERSION="echo exported"
+ SVNVERSION="echo Unversioned directory"
fi
case $MACHDEP in
pycurl-7.19.3/tests/matrix/python30.patch 0000644 0004705 0004705 00000001237 12256232314 017671 0 ustar pie pie 0000000 0000000 Python 3.0's version.py uses cmp which is not defined by the interpreter.
The only difference between version.py in 3.0 and 3.1 is this fix.
--- Python-3.0.1/Lib/distutils/version.py 2013-12-14 21:06:23.000000000 -0500
+++ Python-3.1.5/Lib/distutils/version.py 2013-12-14 21:01:10.000000000 -0500
@@ -338,7 +338,12 @@
if isinstance(other, str):
other = LooseVersion(other)
- return cmp(self.version, other.version)
+ if self.version == other.version:
+ return 0
+ if self.version < other.version:
+ return -1
+ if self.version > other.version:
+ return 1
# end class LooseVersion
pycurl-7.19.3/tests/__init__.py 0000644 0004705 0004705 00000000445 12260666417 016003 0 ustar pie pie 0000000 0000000 def setup_package():
# import here, not globally, so that running
# python -m tests.appmanager
# to launch the app manager is possible without having pycurl installed
# (as the test app does not depend on pycurl)
import pycurl
print('Testing %s' % pycurl.version)
pycurl-7.19.3/tests/app.py 0000644 0004705 0004705 00000005244 12262015545 015016 0 ustar pie pie 0000000 0000000 import time as _time, sys
import bottle
try:
import json
except ImportError:
import simplejson as json
py3 = sys.version_info[0] == 3
app = bottle.Bottle()
app.debug = True
@app.route('/success')
def ok():
return 'success'
@app.route('/short_wait')
def ok():
_time.sleep(0.1)
return 'success'
@app.route('/status/403')
def forbidden():
return bottle.HTTPResponse('forbidden', 403)
@app.route('/status/404')
def not_found():
return bottle.HTTPResponse('not found', 404)
@app.route('/postfields', method='post')
def postfields():
return json.dumps(dict(bottle.request.forms))
@app.route('/raw_utf8', method='post')
def raw_utf8():
data = bottle.request.body.getvalue().decode('utf8')
return json.dumps(data)
# XXX file is not a bottle FileUpload instance, but FieldStorage?
def convert_file(key, file):
return {
'key': key,
'name': file.name,
'raw_filename': file.raw_filename,
'headers': file.headers,
'content_type': file.content_type,
'content_length': file.content_length,
'data': file.read(),
}
def convert_file(key, file):
return {
'name': file.name,
'filename': file.filename,
'data': file.file.read().decode(),
}
@app.route('/files', method='post')
def files():
files = [convert_file(key, bottle.request.files[key]) for key in bottle.request.files]
return json.dumps(files)
@app.route('/header')
def header():
return bottle.request.headers[bottle.request.query['h']]
# This is a hacky endpoint to test non-ascii text being given to libcurl
# via headers.
# HTTP RFC requires headers to be latin1-encoded.
# Any string can be decoded as latin1; here we encode the header value
# back into latin1 to obtain original bytestring, then decode it in utf-8.
# Thanks to bdarnell for the idea: https://github.com/pycurl/pycurl/issues/124
@app.route('/header_utf8')
def header():
header_value = bottle.request.headers[bottle.request.query['h']]
if py3:
# header_value is a string, headers are decoded in latin1
header_value = header_value.encode('latin1').decode('utf8')
else:
# header_value is a binary string, decode in utf-8 directly
header_value = header_value.decode('utf8')
return header_value
@app.route('/param_utf8_hack', method='post')
def param_utf8_hack():
param = bottle.request.forms['p']
if py3:
# python 3 decodes bytes as latin1 perhaps?
# apply the latin1-utf8 hack
param = param.encode('latin').decode('utf8')
return param
def pause_writer():
yield 'part1'
_time.sleep(0.5)
yield 'part2'
@app.route('/pause')
def pause():
return pause_writer()
pycurl-7.19.3/tests/appmanager.py 0000644 0004705 0004705 00000002521 12260666417 016354 0 ustar pie pie 0000000 0000000 import sys, time, os
def noop(*args):
pass
def setup(*specs):
if os.environ.get('PYCURL_STANDALONE_APP') and os.environ['PYCURL_STANDALONE_APP'].lower() in ['1', 'yes', 'true']:
return (noop, noop)
else:
return perform_setup(*specs)
def perform_setup(*specs):
from . import runwsgi
app_specs = []
for spec in specs:
app_module = __import__(spec[0], globals(), locals(), ['app'], 1)
app = getattr(app_module, 'app')
app_specs.append([app] + list(spec[1:]))
return runwsgi.app_runner_setup(*app_specs)
quit = False
def sigterm_handler(*args):
global quit
quit = True
def run_standalone():
import signal
funcs = []
signal.signal(signal.SIGTERM, sigterm_handler)
funcs.append(setup(('app', 8380)))
funcs.append(setup(('app', 8381)))
funcs.append(setup(('app', 8382)))
funcs.append(setup(('app', 8383, dict(ssl=True))))
for setup_func, teardown_func in funcs:
setup_func(sys.modules[__name__])
sys.stdout.write("Running, use SIGTERM or SIGINT to stop\n")
try:
while not quit:
time.sleep(1)
except KeyboardInterrupt:
pass
for setup_func, teardown_func in funcs:
teardown_func(sys.modules[__name__])
if __name__ == '__main__':
run_standalone()
pycurl-7.19.3/tests/certinfo_test.py 0000644 0004705 0004705 00000004403 12262015545 017102 0 ustar pie pie 0000000 0000000 #! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et
import pycurl
import unittest
import nose.plugins.skip
from . import appmanager
from . import util
setup_module, teardown_module = appmanager.setup(('app', 8383, dict(ssl=True)))
class CertinfoTest(unittest.TestCase):
def setUp(self):
self.curl = pycurl.Curl()
def tearDown(self):
self.curl.close()
# CURLOPT_CERTINFO was introduced in libcurl-7.19.1
@util.min_libcurl(7, 19, 1)
def test_certinfo_option(self):
assert hasattr(pycurl, 'OPT_CERTINFO')
# CURLOPT_CERTINFO was introduced in libcurl-7.19.1
@util.min_libcurl(7, 19, 1)
@util.only_ssl
def test_request_without_certinfo(self):
self.curl.setopt(pycurl.URL, 'https://localhost:8383/success')
sio = util.BytesIO()
self.curl.setopt(pycurl.WRITEFUNCTION, sio.write)
# self signed certificate
self.curl.setopt(pycurl.SSL_VERIFYPEER, 0)
self.curl.perform()
assert sio.getvalue().decode() == 'success'
certinfo = self.curl.getinfo(pycurl.INFO_CERTINFO)
self.assertEqual([], certinfo)
# CURLOPT_CERTINFO was introduced in libcurl-7.19.1
@util.min_libcurl(7, 19, 1)
@util.only_ssl
def test_request_with_certinfo(self):
# CURLOPT_CERTINFO only works with OpenSSL
if 'openssl' not in pycurl.version.lower():
raise nose.plugins.skip.SkipTest('libcurl does not use openssl')
self.curl.setopt(pycurl.URL, 'https://localhost:8383/success')
sio = util.BytesIO()
self.curl.setopt(pycurl.WRITEFUNCTION, sio.write)
self.curl.setopt(pycurl.OPT_CERTINFO, 1)
# self signed certificate
self.curl.setopt(pycurl.SSL_VERIFYPEER, 0)
self.curl.perform()
assert sio.getvalue().decode() == 'success'
certinfo = self.curl.getinfo(pycurl.INFO_CERTINFO)
# self signed certificate, one certificate in chain
assert len(certinfo) == 1
certinfo = certinfo[0]
# convert to a dictionary
certinfo_dict = {}
for entry in certinfo:
certinfo_dict[entry[0]] = entry[1]
assert 'Subject' in certinfo_dict
assert 'PycURL test suite' in certinfo_dict['Subject']
pycurl-7.19.3/tests/debug_test.py 0000644 0004705 0004705 00000003111 12262015545 016352 0 ustar pie pie 0000000 0000000 #! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et
import pycurl
import unittest
from . import appmanager
from . import util
setup_module, teardown_module = appmanager.setup(('app', 8380))
class DebugTest(unittest.TestCase):
def setUp(self):
self.curl = pycurl.Curl()
self.debug_entries = []
def tearDown(self):
self.curl.close()
def debug_function(self, t, b):
self.debug_entries.append((t, b))
def test_perform_get_with_debug_function(self):
self.curl.setopt(pycurl.VERBOSE, 1)
self.curl.setopt(pycurl.DEBUGFUNCTION, self.debug_function)
self.curl.setopt(pycurl.URL, 'http://localhost:8380/success')
sio = util.BytesIO()
self.curl.setopt(pycurl.WRITEFUNCTION, sio.write)
self.curl.perform()
# Some checks with no particular intent
self.check(0, 'Trying')
if util.pycurl_version_less_than(7, 24):
self.check(0, 'connected')
else:
self.check(0, 'Connected to localhost')
self.check(0, 'port 8380')
# request
self.check(2, 'GET /success HTTP/1.1')
# response
self.check(1, 'HTTP/1.0 200 OK')
self.check(1, 'Content-Length: 7')
# result
self.check(3, 'success')
def check(self, wanted_t, wanted_b):
for t, b in self.debug_entries:
if t == wanted_t and wanted_b in b:
return
assert False, "%d: %s not found in debug entries\nEntries are:\n%s" % \
(wanted_t, wanted_b, repr(self.debug_entries))
pycurl-7.19.3/tests/default_write_function_test.py 0000644 0004705 0004705 00000005730 12262015545 022040 0 ustar pie pie 0000000 0000000 #! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et
import unittest
import pycurl
import sys
import tempfile
import os
from . import appmanager
setup_module, teardown_module = appmanager.setup(('app', 8380))
STDOUT_FD_NUM = 1
def try_fsync(fd):
try:
os.fsync(fd)
except OSError:
# On travis:
# OSError: [Errno 22] Invalid argument
# ignore
pass
class DefaultWriteFunctionTest(unittest.TestCase):
def setUp(self):
self.curl = pycurl.Curl()
def tearDown(self):
self.curl.close()
def test_perform_get(self):
# This test performs a GET request without doing anything else.
# Unfortunately, the default curl behavior is to print response
# body to standard output, which spams test output.
# As a result this test is commented out. Uncomment for debugging.
# test_perform_get_with_default_write_function is the test
# which exercises default curl write handler.
self.curl.setopt(pycurl.URL, 'http://localhost:8380/success')
self.curl.perform()
# If this flush is not done, stdout output bleeds into the next test
# that is executed (without nose output capture)
sys.stdout.flush()
try_fsync(STDOUT_FD_NUM)
# I have a really hard time getting this to work with nose output capture
def skip_perform_get_with_default_write_function(self):
self.curl.setopt(pycurl.URL, 'http://localhost:8380/success')
f = tempfile.NamedTemporaryFile()
try:
#with open('w', 'w+') as f:
# nose output capture plugin replaces sys.stdout with a StringIO
# instance. We want to redirect the underlying file descriptor
# anyway because underlying C code uses it.
# Therefore:
# 1. Use file descriptor 1 rather than sys.stdout.fileno() to
# reference the standard output file descriptor.
# 2. We do not touch sys.stdout. This means anything written to
# sys.stdout will be captured by nose, and not make it to our code.
# But the output we care about happens at libcurl level, below
# nose, therefore this is fine.
saved_stdout_fd = os.dup(STDOUT_FD_NUM)
os.dup2(f.fileno(), STDOUT_FD_NUM)
#os.dup2(1, 100)
#os.dup2(2, 1)
# We also need to flush the output that libcurl wrote to stdout.
# Since sys.stdout might be nose's StringIO instance, open the
# stdout file descriptor manually.
try:
self.curl.perform()
sys.stdout.flush()
finally:
try_fsync(STDOUT_FD_NUM)
os.dup2(saved_stdout_fd, STDOUT_FD_NUM)
os.close(saved_stdout_fd)
#os.dup2(100, 1)
f.seek(0)
body = f.read()
finally:
f.close()
self.assertEqual('success', body)
pycurl-7.19.3/tests/easy_test.py 0000644 0004705 0004705 00000000462 12262015545 016233 0 ustar pie pie 0000000 0000000 #! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et
import pycurl
import unittest
class EasyTest(unittest.TestCase):
def test_easy_close(self):
c = pycurl.Curl()
c.close()
def test_easy_close_twice(self):
c = pycurl.Curl()
c.close()
c.close()
pycurl-7.19.3/tests/error_test.py 0000644 0004705 0004705 00000005452 12262015545 016427 0 ustar pie pie 0000000 0000000 #! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et
import pycurl
import sys
import unittest
class ErrorTest(unittest.TestCase):
def setUp(self):
self.curl = pycurl.Curl()
def tearDown(self):
self.curl.close()
# error originating in libcurl
def test_pycurl_error_libcurl(self):
try:
# perform without a url
self.curl.perform()
except pycurl.error:
exc_type, exc = sys.exc_info()[:2]
assert exc_type == pycurl.error
# pycurl.error's arguments are libcurl errno and message
self.assertEqual(2, len(exc.args))
self.assertEqual(int, type(exc.args[0]))
self.assertEqual(str, type(exc.args[1]))
# unpack
err, msg = exc.args
self.assertEqual(pycurl.E_URL_MALFORMAT, err)
# possibly fragile
self.assertEqual('No URL set!', msg)
else:
self.fail('Expected pycurl.error to be raised')
def test_pycurl_errstr_initially_empty(self):
self.assertEqual('', self.curl.errstr())
def test_pycurl_errstr_type(self):
self.assertEqual('', self.curl.errstr())
try:
# perform without a url
self.curl.perform()
except pycurl.error:
# might be fragile
self.assertEqual('No URL set!', self.curl.errstr())
# repeated checks do not clear value
self.assertEqual('No URL set!', self.curl.errstr())
# check the type - on all python versions
self.assertEqual(str, type(self.curl.errstr()))
else:
self.fail('no exception')
# pycurl raises standard library exceptions in some cases
def test_pycurl_error_stdlib(self):
try:
# set an option of the wrong type
self.curl.setopt(pycurl.WRITEFUNCTION, True)
except TypeError:
exc_type, exc = sys.exc_info()[:2]
else:
self.fail('Expected TypeError to be raised')
# error originating in pycurl
def test_pycurl_error_pycurl(self):
try:
# invalid option combination
self.curl.setopt(pycurl.WRITEFUNCTION, lambda x: x)
f = open(__file__)
try:
self.curl.setopt(pycurl.WRITEHEADER, f)
finally:
f.close()
except pycurl.error:
exc_type, exc = sys.exc_info()[:2]
assert exc_type == pycurl.error
# for non-libcurl errors, arguments are just the error string
self.assertEqual(1, len(exc.args))
self.assertEqual(str, type(exc.args[0]))
self.assertEqual('cannot combine WRITEHEADER with WRITEFUNCTION.', exc.args[0])
else:
self.fail('Expected pycurl.error to be raised')
pycurl-7.19.3/tests/ftp_test.py 0000644 0004705 0004705 00000003002 12262015545 016054 0 ustar pie pie 0000000 0000000 #! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et
# Note: this test is meant to be run from pycurl project root.
import pycurl
import unittest
from . import util
from . import procmgr
setup_module, teardown_module = procmgr.vsftpd_setup()
class FtpTest(unittest.TestCase):
def setUp(self):
self.curl = pycurl.Curl()
def tearDown(self):
self.curl.close()
def test_get_ftp(self):
self.curl.setopt(pycurl.URL, 'ftp://localhost:8321')
sio = util.BytesIO()
self.curl.setopt(pycurl.WRITEFUNCTION, sio.write)
self.curl.perform()
result = sio.getvalue().decode()
assert 'README.rst' in result
assert 'INSTALL' in result
# XXX this test needs to be fixed
def test_quote(self):
self.curl.setopt(pycurl.URL, 'ftp://localhost:8321')
sio = util.BytesIO()
self.curl.setopt(pycurl.WRITEFUNCTION, sio.write)
self.curl.setopt(pycurl.QUOTE, ['CWD tests'])
self.curl.perform()
result = sio.getvalue().decode()
assert 'README.rst' not in result
assert 'ftp_test.py' in result
def test_epsv(self):
self.curl.setopt(pycurl.URL, 'ftp://localhost:8321')
sio = util.BytesIO()
self.curl.setopt(pycurl.WRITEFUNCTION, sio.write)
self.curl.setopt(pycurl.FTP_USE_EPSV, 1)
self.curl.perform()
result = sio.getvalue().decode()
assert 'README.rst' in result
assert 'INSTALL' in result
pycurl-7.19.3/tests/functools_backport.py 0000644 0004705 0004705 00000004565 12260666420 020146 0 ustar pie pie 0000000 0000000 # partial implementation from
# http://stackoverflow.com/questions/12274814/functools-wraps-for-python-2-4
def partial(func, *args, **kwds):
"""Emulate Python2.6's functools.partial"""
return lambda *fargs, **fkwds: func(*(args+fargs), **dict(kwds, **fkwds))
# functools from python 2.5
"""functools.py - Tools for working with functions and callable objects
"""
# Python module wrapper for _functools C module
# to allow utilities written in Python to be added
# to the functools module.
# Written by Nick Coghlan
# Copyright (C) 2006 Python Software Foundation.
# See C source code for _functools credits/copyright
# update_wrapper() and wraps() are tools to help write
# wrapper functions that can handle naive introspection
WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__')
WRAPPER_UPDATES = ('__dict__',)
def update_wrapper(wrapper,
wrapped,
assigned = WRAPPER_ASSIGNMENTS,
updated = WRAPPER_UPDATES):
"""Update a wrapper function to look like the wrapped function
wrapper is the function to be updated
wrapped is the original function
assigned is a tuple naming the attributes assigned directly
from the wrapped function to the wrapper function (defaults to
functools.WRAPPER_ASSIGNMENTS)
updated is a tuple naming the attributes off the wrapper that
are updated with the corresponding attribute from the wrapped
function (defaults to functools.WRAPPER_UPDATES)
"""
for attr in assigned:
setattr(wrapper, attr, getattr(wrapped, attr))
for attr in updated:
getattr(wrapper, attr).update(getattr(wrapped, attr, {}))
# Return the wrapper so this can be used as a decorator via partial()
return wrapper
def wraps(wrapped,
assigned = WRAPPER_ASSIGNMENTS,
updated = WRAPPER_UPDATES):
"""Decorator factory to apply update_wrapper() to a wrapper function
Returns a decorator that invokes update_wrapper() with the decorated
function as the wrapper argument and the arguments to wraps() as the
remaining arguments. Default arguments are as for update_wrapper().
This is a convenience function to simplify applying partial() to
update_wrapper().
"""
return partial(update_wrapper, wrapped=wrapped,
assigned=assigned, updated=updated)
pycurl-7.19.3/tests/getinfo_test.py 0000644 0004705 0004705 00000004032 12262540746 016730 0 ustar pie pie 0000000 0000000 #! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et
import pycurl
import unittest
from . import appmanager
from . import util
setup_module, teardown_module = appmanager.setup(('app', 8380))
class GetinfoTest(unittest.TestCase):
def setUp(self):
self.curl = pycurl.Curl()
def tearDown(self):
self.curl.close()
def test_getinfo(self):
self.make_request()
self.assertEqual(200, self.curl.getinfo(pycurl.HTTP_CODE))
assert type(self.curl.getinfo(pycurl.TOTAL_TIME)) is float
assert self.curl.getinfo(pycurl.TOTAL_TIME) > 0
assert self.curl.getinfo(pycurl.TOTAL_TIME) < 1
assert type(self.curl.getinfo(pycurl.SPEED_DOWNLOAD)) is float
assert self.curl.getinfo(pycurl.SPEED_DOWNLOAD) > 0
self.assertEqual(7, self.curl.getinfo(pycurl.SIZE_DOWNLOAD))
self.assertEqual('http://localhost:8380/success', self.curl.getinfo(pycurl.EFFECTIVE_URL))
self.assertEqual('text/html; charset=utf-8', self.curl.getinfo(pycurl.CONTENT_TYPE).lower())
assert type(self.curl.getinfo(pycurl.NAMELOOKUP_TIME)) is float
assert self.curl.getinfo(pycurl.NAMELOOKUP_TIME) > 0
assert self.curl.getinfo(pycurl.NAMELOOKUP_TIME) < 1
self.assertEqual(0, self.curl.getinfo(pycurl.REDIRECT_TIME))
self.assertEqual(0, self.curl.getinfo(pycurl.REDIRECT_COUNT))
# time not requested
self.assertEqual(-1, self.curl.getinfo(pycurl.INFO_FILETIME))
@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()
self.curl.setopt(pycurl.WRITEFUNCTION, sio.write)
self.curl.perform()
self.assertEqual('success', sio.getvalue().decode())
pycurl-7.19.3/tests/global_init_test.py 0000644 0004705 0004705 00000002047 12262015545 017556 0 ustar pie pie 0000000 0000000 #! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et
import pycurl
import unittest
import nose.tools
import nose.plugins.skip
from . import util
class GlobalInitTest(unittest.TestCase):
def test_global_init_default(self):
# initialize libcurl with DEFAULT flags
pycurl.global_init(pycurl.GLOBAL_DEFAULT)
pycurl.global_cleanup()
def test_global_init_ack_eintr(self):
# the GLOBAL_ACK_EINTR flag was introduced in libcurl-7.30, but can also
# be backported for older versions of libcurl at the distribution level
if util.pycurl_version_less_than(7, 30) and not hasattr(pycurl, 'GLOBAL_ACK_EINTR'):
raise nose.plugins.skip.SkipTest('libcurl < 7.30.0 or no GLOBAL_ACK_EINTR')
# initialize libcurl with the GLOBAL_ACK_EINTR flag
pycurl.global_init(pycurl.GLOBAL_ACK_EINTR)
pycurl.global_cleanup()
@nose.tools.raises(ValueError)
def test_global_init_bogus(self):
# initialize libcurl with bogus flags
pycurl.global_init(0xffff)
pycurl-7.19.3/tests/header_function_test.py 0000644 0004705 0004705 00000003024 12262015545 020424 0 ustar pie pie 0000000 0000000 #! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et
import pycurl
import unittest
import time as _time
from . import appmanager
from . import util
setup_module, teardown_module = appmanager.setup(('app', 8380))
class HeaderFunctionTest(unittest.TestCase):
def setUp(self):
self.curl = pycurl.Curl()
self.header_lines = []
def tearDown(self):
self.curl.close()
def header_function(self, line):
self.header_lines.append(line.decode())
def test_get(self):
self.curl.setopt(pycurl.URL, 'http://localhost:8380/success')
sio = util.BytesIO()
self.curl.setopt(pycurl.WRITEFUNCTION, sio.write)
self.curl.setopt(pycurl.HEADERFUNCTION, self.header_function)
self.curl.perform()
self.assertEqual('success', sio.getvalue().decode())
assert len(self.header_lines) > 0
self.assertEqual("HTTP/1.0 200 OK\r\n", self.header_lines[0])
# day of week
# important: must be in utc
todays_day = _time.strftime('%a', _time.gmtime())
# Date: Sun, 03 Mar 2013 05:38:12 GMT\r\n
self.check('Date: %s' % todays_day)
# Server: WSGIServer/0.1 Python/2.7.3\r\n
self.check('Server: WSGIServer')
self.check('Content-Length: 7')
self.check('Content-Type: text/html')
def check(self, wanted_text):
for line in self.header_lines:
if wanted_text in line:
return
assert False, "%s not found in header lines" % wanted_text
pycurl-7.19.3/tests/header_test.py 0000644 0004705 0004705 00000003154 12262015545 016523 0 ustar pie pie 0000000 0000000 #! /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))
# NB: HTTP RFC requires headers to be latin1 encoded, which we violate.
# See the comments under /header_utf8 route in app.py.
class HeaderTest(unittest.TestCase):
def setUp(self):
self.curl = pycurl.Curl()
def tearDown(self):
self.curl.close()
def test_ascii_string_header(self):
self.check('x-test-header: ascii', 'ascii')
def test_ascii_unicode_header(self):
self.check(util.u('x-test-header: ascii'), 'ascii')
# on python 2 unicode is accepted in strings because strings are byte strings
@util.only_python3
@nose.tools.raises(UnicodeEncodeError)
def test_unicode_string_header(self):
self.check('x-test-header: Москва', 'Москва')
@nose.tools.raises(UnicodeEncodeError)
def test_unicode_unicode_header(self):
self.check(util.u('x-test-header: Москва'), util.u('Москва'))
def test_encoded_unicode_header(self):
self.check(util.u('x-test-header: Москва').encode('utf-8'), util.u('Москва'))
def check(self, send, expected):
self.curl.setopt(pycurl.URL, 'http://localhost:8380/header_utf8?h=x-test-header')
sio = util.BytesIO()
self.curl.setopt(pycurl.WRITEFUNCTION, sio.write)
self.curl.setopt(pycurl.HTTPHEADER, [send])
self.curl.perform()
self.assertEqual(expected, sio.getvalue().decode('utf-8'))
pycurl-7.19.3/tests/internals_test.py 0000644 0004705 0004705 00000011416 12262015545 017272 0 ustar pie pie 0000000 0000000 #! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et
import pycurl
import unittest
try:
import cPickle
except ImportError:
cPickle = None
import pickle
import copy
from . import util
class InternalsTest(unittest.TestCase):
def setUp(self):
self.curl = pycurl.Curl()
def tearDown(self):
self.curl.close()
del self.curl
# /***********************************************************************
# // test misc
# ************************************************************************/
def test_constant_aliasing(self):
assert self.curl.URL is pycurl.URL
# /***********************************************************************
# // test handles
# ************************************************************************/
def test_remove_invalid_handle(self):
m = pycurl.CurlMulti()
try:
m.remove_handle(self.curl)
except pycurl.error:
pass
else:
assert False, "No exception when trying to remove a handle that is not in CurlMulti"
del m
def test_remove_invalid_closed_handle(self):
m = pycurl.CurlMulti()
c = pycurl.Curl()
c.close()
m.remove_handle(c)
del m, c
def test_add_closed_handle(self):
m = pycurl.CurlMulti()
c = pycurl.Curl()
c.close()
try:
m.add_handle(c)
except pycurl.error:
pass
else:
assert 0, "No exception when trying to add a close handle to CurlMulti"
m.close()
del m, c
def test_add_handle_twice(self):
m = pycurl.CurlMulti()
m.add_handle(self.curl)
try:
m.add_handle(self.curl)
except pycurl.error:
pass
else:
assert 0, "No exception when trying to add the same handle twice"
del m
def test_add_handle_on_multiple_stacks(self):
m1 = pycurl.CurlMulti()
m2 = pycurl.CurlMulti()
m1.add_handle(self.curl)
try:
m2.add_handle(self.curl)
except pycurl.error:
pass
else:
assert 0, "No exception when trying to add the same handle on multiple stacks"
del m1, m2
def test_move_handle(self):
m1 = pycurl.CurlMulti()
m2 = pycurl.CurlMulti()
m1.add_handle(self.curl)
m1.remove_handle(self.curl)
m2.add_handle(self.curl)
del m1, m2
# /***********************************************************************
# // test copying and pickling - copying and pickling of
# // instances of Curl and CurlMulti is not allowed
# ************************************************************************/
def test_copy_curl(self):
try:
copy.copy(self.curl)
# python 2 raises copy.Error, python 3 raises TypeError
except (copy.Error, TypeError):
pass
else:
assert False, "No exception when trying to copy a Curl handle"
def test_copy_multi(self):
m = pycurl.CurlMulti()
try:
copy.copy(m)
except (copy.Error, TypeError):
pass
else:
assert False, "No exception when trying to copy a CurlMulti handle"
def test_pickle_curl(self):
fp = util.StringIO()
p = pickle.Pickler(fp, 1)
try:
p.dump(self.curl)
# python 2 raises pickle.PicklingError, python 3 raises TypeError
except (pickle.PicklingError, TypeError):
pass
else:
assert 0, "No exception when trying to pickle a Curl handle"
del fp, p
def test_pickle_multi(self):
m = pycurl.CurlMulti()
fp = util.StringIO()
p = pickle.Pickler(fp, 1)
try:
p.dump(m)
except (pickle.PicklingError, TypeError):
pass
else:
assert 0, "No exception when trying to pickle a CurlMulti handle"
del m, fp, p
if cPickle is not None:
def test_cpickle_curl(self):
fp = util.StringIO()
p = cPickle.Pickler(fp, 1)
try:
p.dump(self.curl)
except cPickle.PicklingError:
pass
else:
assert 0, "No exception when trying to pickle a Curl handle via cPickle"
del fp, p
def test_cpickle_multi(self):
m = pycurl.CurlMulti()
fp = util.StringIO()
p = cPickle.Pickler(fp, 1)
try:
p.dump(m)
except cPickle.PicklingError:
pass
else:
assert 0, "No exception when trying to pickle a CurlMulti handle via cPickle"
del m, fp, p
pycurl-7.19.3/tests/matrix.py 0000644 0004705 0004705 00000017453 12260666420 015551 0 ustar pie pie 0000000 0000000 import os, os.path, subprocess, shutil, re
try:
from urllib.request import urlopen
except ImportError:
from urllib import urlopen
python_versions = ['2.4.6', '2.5.6', '2.6.8', '2.7.5', '3.0.1', '3.1.5', '3.2.5', '3.3.3']
libcurl_versions = ['7.19.0', '7.33.0']
python_meta = {
'2.5.6': {
'patches': ['python25.patch'],
},
'3.0.1': {
'patches': ['python25.patch', 'python30.patch'],
},
}
root = os.path.abspath(os.path.dirname(__file__))
class in_dir:
def __init__(self, dir):
self.dir = dir
def __enter__(self):
self.oldwd = os.getcwd()
os.chdir(self.dir)
def __exit__(self, type, value, traceback):
os.chdir(self.oldwd)
def fetch(url, archive=None):
if archive is None:
archive = os.path.basename(url)
if not os.path.exists(archive):
sys.stdout.write("Fetching %s\n" % url)
io = urlopen(url)
with open('.tmp.%s' % archive, 'wb') as f:
while True:
chunk = io.read(65536)
if len(chunk) == 0:
break
f.write(chunk)
os.rename('.tmp.%s' % archive, archive)
def build(archive, dir, prefix, meta=None):
if not os.path.exists(dir):
sys.stdout.write("Building %s\n" % archive)
subprocess.check_call(['tar', 'xf', archive])
with in_dir(dir):
if meta and 'patches' in meta:
for patch in meta['patches']:
patch_path = os.path.join(root, 'matrix', patch)
subprocess.check_call(['patch', '-p1', '-i', patch_path])
subprocess.check_call(['./configure', '--prefix=%s' % prefix])
subprocess.check_call(['make'])
subprocess.check_call(['make', 'install'])
def patch_pycurl_for_24():
# change relative imports to old syntax as python 2.4 does not
# support relative imports
for root, dirs, files in os.walk('tests'):
for file in files:
if file.endswith('.py'):
path = os.path.join(root, file)
with open(path, 'r') as f:
contents = f.read()
contents = re.compile(r'^(\s*)from \. import', re.M).sub(r'\1import', contents)
contents = re.compile(r'^(\s*)from \.(\w+) import', re.M).sub(r'\1from \2 import', contents)
with open(path, 'w') as f:
f.write(contents)
def run_matrix(python_versions, libcurl_versions):
for python_version in python_versions:
url = 'http://www.python.org/ftp/python/%s/Python-%s.tgz' % (python_version, python_version)
archive = os.path.basename(url)
fetch(url, archive)
dir = archive.replace('.tgz', '')
prefix = os.path.abspath('i/%s' % dir)
build(archive, dir, prefix, meta=python_meta.get(python_version))
for libcurl_version in libcurl_versions:
url = 'http://curl.haxx.se/download/curl-%s.tar.gz' % libcurl_version
archive = os.path.basename(url)
fetch(url, archive)
dir = archive.replace('.tar.gz', '')
prefix = os.path.abspath('i/%s' % dir)
build(archive, dir, prefix)
fetch('https://raw.github.com/pypa/virtualenv/1.7/virtualenv.py', 'virtualenv-1.7.py')
fetch('https://raw.github.com/pypa/virtualenv/1.9.1/virtualenv.py', 'virtualenv-1.9.1.py')
if not os.path.exists('venv'):
os.mkdir('venv')
for python_version in python_versions:
python_version_pieces = [int(piece) for piece in python_version.split('.')[:2]]
for libcurl_version in libcurl_versions:
python_prefix = os.path.abspath('i/Python-%s' % python_version)
libcurl_prefix = os.path.abspath('i/curl-%s' % libcurl_version)
venv = os.path.abspath('venv/Python-%s-curl-%s' % (python_version, libcurl_version))
if os.path.exists(venv):
shutil.rmtree(venv)
if python_version_pieces >= [2, 5]:
fetch('https://pypi.python.org/packages/2.5/s/setuptools/setuptools-0.6c11-py2.5.egg')
fetch('https://pypi.python.org/packages/2.6/s/setuptools/setuptools-0.6c11-py2.6.egg')
fetch('https://pypi.python.org/packages/2.7/s/setuptools/setuptools-0.6c11-py2.7.egg')
# I had virtualenv 1.8.2 installed systemwide which
# did not work with python 3.0:
# http://stackoverflow.com/questions/14224361/why-am-i-getting-this-error-related-to-pip-and-easy-install-when-trying-to-set
# so, use known versions everywhere
# md5=89e68df89faf1966bcbd99a0033fbf8e
fetch('https://pypi.python.org/packages/source/d/distribute/distribute-0.6.49.tar.gz')
subprocess.check_call(['python', 'virtualenv-1.9.1.py', venv, '-p', '%s/bin/python%d.%d' % (python_prefix, python_version_pieces[0], python_version_pieces[1]), '--no-site-packages', '--never-download'])
else:
# md5=bd639f9b0eac4c42497034dec2ec0c2b
fetch('https://pypi.python.org/packages/2.4/s/setuptools/setuptools-0.6c11-py2.4.egg')
# md5=6afbb46aeb48abac658d4df742bff714
fetch('https://pypi.python.org/packages/source/p/pip/pip-1.4.1.tar.gz')
subprocess.check_call(['python', 'virtualenv-1.7.py', venv, '-p', '%s/bin/python' % python_prefix, '--no-site-packages', '--never-download'])
curl_config_path = os.path.join(libcurl_prefix, 'bin/curl-config')
curl_lib_path = os.path.join(libcurl_prefix, 'lib')
with in_dir('pycurl'):
extra_patches = []
extra_env = []
if python_version_pieces >= [2, 6]:
deps_cmd = 'pip install -r requirements-dev.txt'
elif python_version_pieces >= [2, 5]:
deps_cmd = 'pip install -r requirements-dev-2.5.txt'
else:
deps_cmd = 'easy_install nose simplejson==2.1.0'
patch_pycurl_for_24()
extra_patches.append('(cd %s/lib/python2.4/site-packages/nose-* && patch -p1) 1 and sys.argv[1] == 'patch-24':
patch_pycurl_for_24()
else:
main()
pycurl-7.19.3/tests/memory_mgmt_test.py 0000644 0004705 0004705 00000012670 12262015545 017632 0 ustar pie pie 0000000 0000000 #! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et
import pycurl
import unittest
import gc
debug = False
class MemoryMgmtTest(unittest.TestCase):
def maybe_enable_debug(self):
if debug:
flags = gc.DEBUG_COLLECTABLE | gc.DEBUG_UNCOLLECTABLE
# python 3 has no DEBUG_OBJECTS
if hasattr(gc, 'DEBUG_OBJECTS'):
flags |= gc.DEBUG_OBJECTS
flags |= gc.DEBUG_STATS
gc.set_debug(flags)
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()
multi = pycurl.CurlMulti()
t = []
searches = []
for a in range(100):
curl = pycurl.Curl()
multi.add_handle(curl)
t.append(curl)
c_id = id(curl)
searches.append(c_id)
m_id = id(multi)
searches.append(m_id)
self.maybe_print_objects()
for curl in t:
curl.close()
multi.remove_handle(curl)
self.maybe_print_objects()
del curl
del t
del multi
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()
multi = pycurl.CurlMulti()
t = []
searches = []
for a in range(100):
curl = pycurl.Curl()
multi.add_handle(curl)
t.append(curl)
c_id = id(curl)
searches.append(c_id)
m_id = id(multi)
searches.append(m_id)
self.maybe_print_objects()
del curl
del t
del multi
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()
share = pycurl.CurlShare()
t = []
searches = []
for a in range(100):
curl = pycurl.Curl()
curl.setopt(curl.SHARE, share)
t.append(curl)
c_id = id(curl)
searches.append(c_id)
m_id = id(share)
searches.append(m_id)
self.maybe_print_objects()
for curl in t:
curl.unsetopt(curl.SHARE)
curl.close()
self.maybe_print_objects()
del curl
del t
del share
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()
share = pycurl.CurlShare()
t = []
searches = []
for a in range(100):
curl = pycurl.Curl()
curl.setopt(curl.SHARE, share)
t.append(curl)
c_id = id(curl)
searches.append(c_id)
m_id = id(share)
searches.append(m_id)
self.maybe_print_objects()
del curl
del t
del share
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)
# basic check of reference counting (use a memory checker like valgrind)
def test_reference_counting(self):
c = pycurl.Curl()
m = pycurl.CurlMulti()
m.add_handle(c)
del m
m = pycurl.CurlMulti()
c.close()
del m, c
def test_cyclic_gc(self):
gc.collect()
c = pycurl.Curl()
c.m = pycurl.CurlMulti()
c.m.add_handle(c)
# create some nasty cyclic references
c.c = c
c.c.c1 = c
c.c.c2 = c
c.c.c3 = c.c
c.c.c4 = c.m
c.m.c = c
c.m.m = c.m
c.m.c = c
# delete
gc.collect()
self.maybe_enable_debug()
##print gc.get_referrers(c)
##print gc.get_objects()
#if opts.verbose >= 1:
#print("Tracked objects:", len(gc.get_objects()))
c_id = id(c)
# The `del' below should delete these 4 objects:
# Curl + internal dict, CurlMulti + internal dict
del c
gc.collect()
objects = gc.get_objects()
for object in objects:
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
except NameError:
range_generator = range
# Ensure that the refcounting error in "reset" is fixed:
for i in range_generator(100000):
c = pycurl.Curl()
c.reset()
pycurl-7.19.3/tests/multi_socket_select_test.py 0000644 0004705 0004705 00000007360 12262015545 021337 0 ustar pie pie 0000000 0000000 #! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et
import pycurl
import unittest
import select
from . import appmanager
from . import util
setup_module_1, teardown_module_1 = appmanager.setup(('app', 8380))
setup_module_2, teardown_module_2 = appmanager.setup(('app', 8381))
setup_module_3, teardown_module_3 = appmanager.setup(('app', 8382))
def setup_module(mod):
setup_module_1(mod)
setup_module_2(mod)
setup_module_3(mod)
def teardown_module(mod):
teardown_module_3(mod)
teardown_module_2(mod)
teardown_module_1(mod)
class MultiSocketSelectTest(unittest.TestCase):
def test_multi_socket_select(self):
sockets = set()
timeout = 0
urls = [
# we need libcurl to actually wait on the handles,
# and initiate polling.
# thus use urls that sleep for a bit.
'http://localhost:8380/short_wait',
'http://localhost:8381/short_wait',
'http://localhost:8382/short_wait',
]
socket_events = []
# socket callback
def socket(event, socket, multi, data):
if event == pycurl.POLL_REMOVE:
#print("Remove Socket %d"%socket)
sockets.remove(socket)
else:
if socket not in sockets:
#print("Add socket %d"%socket)
sockets.add(socket)
socket_events.append((event, multi))
# init
m = pycurl.CurlMulti()
m.setopt(pycurl.M_SOCKETFUNCTION, socket)
m.handles = []
for url in urls:
c = pycurl.Curl()
# save info in standard Python attributes
c.url = url
c.body = util.BytesIO()
c.http_code = -1
m.handles.append(c)
# pycurl API calls
c.setopt(c.URL, c.url)
c.setopt(c.WRITEFUNCTION, c.body.write)
m.add_handle(c)
# get data
num_handles = len(m.handles)
while (pycurl.E_CALL_MULTI_PERFORM==m.socket_all()[0]):
pass
timeout = m.timeout()
# timeout might be -1, indicating that all work is done
# XXX make sure there is always work to be done here?
while timeout >= 0:
(rr, wr, er) = select.select(sockets,sockets,sockets,timeout/1000.0)
socketSet = set(rr+wr+er)
if socketSet:
for s in socketSet:
while True:
(ret,running) = m.socket_action(s,0)
if ret!=pycurl.E_CALL_MULTI_PERFORM:
break
else:
(ret,running) = m.socket_action(pycurl.SOCKET_TIMEOUT,0)
if running==0:
break
for c in m.handles:
# save info in standard Python attributes
c.http_code = c.getinfo(c.HTTP_CODE)
# at least in and remove events per socket
assert len(socket_events) >= 6, 'Less than 6 socket events: %s' % repr(socket_events)
# print result
for c in m.handles:
self.assertEqual('success', c.body.getvalue().decode())
self.assertEqual(200, c.http_code)
# multi, not curl handle
self.check(pycurl.POLL_IN, m, socket_events)
self.check(pycurl.POLL_REMOVE, m, socket_events)
# close handles
for c in m.handles:
# pycurl API calls
m.remove_handle(c)
c.close()
m.close()
def check(self, event, multi, socket_events):
for event_, multi_ in socket_events:
if event == event_ and multi == multi_:
return
assert False, '%d %s not found in socket events' % (event, multi)
pycurl-7.19.3/tests/multi_socket_test.py 0000644 0004705 0004705 00000005725 12262015545 020003 0 ustar pie pie 0000000 0000000 #! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et
import pycurl
import unittest
from . import appmanager
from . import util
setup_module_1, teardown_module_1 = appmanager.setup(('app', 8380))
setup_module_2, teardown_module_2 = appmanager.setup(('app', 8381))
setup_module_3, teardown_module_3 = appmanager.setup(('app', 8382))
def setup_module(mod):
setup_module_1(mod)
setup_module_2(mod)
setup_module_3(mod)
def teardown_module(mod):
teardown_module_3(mod)
teardown_module_2(mod)
teardown_module_1(mod)
class MultiSocketTest(unittest.TestCase):
def test_multi_socket(self):
urls = [
# not sure why requesting /success produces no events.
# see multi_socket_select_test.py for a longer explanation
# why short wait is used there.
'http://localhost:8380/short_wait',
'http://localhost:8381/short_wait',
'http://localhost:8382/short_wait',
]
socket_events = []
# socket callback
def socket(event, socket, multi, data):
#print(event, socket, multi, data)
socket_events.append((event, multi))
# init
m = pycurl.CurlMulti()
m.setopt(pycurl.M_SOCKETFUNCTION, socket)
m.handles = []
for url in urls:
c = pycurl.Curl()
# save info in standard Python attributes
c.url = url
c.body = util.BytesIO()
c.http_code = -1
m.handles.append(c)
# pycurl API calls
c.setopt(c.URL, c.url)
c.setopt(c.WRITEFUNCTION, c.body.write)
m.add_handle(c)
# get data
num_handles = len(m.handles)
while num_handles:
while 1:
ret, num_handles = m.socket_all()
if ret != pycurl.E_CALL_MULTI_PERFORM:
break
# currently no more I/O is pending, could do something in the meantime
# (display a progress bar, etc.)
m.select(0.1)
for c in m.handles:
# save info in standard Python attributes
c.http_code = c.getinfo(c.HTTP_CODE)
# at least in and remove events per socket
assert len(socket_events) >= 6
# print result
for c in m.handles:
self.assertEqual('success', c.body.getvalue().decode())
self.assertEqual(200, c.http_code)
# multi, not curl handle
self.check(pycurl.POLL_IN, m, socket_events)
self.check(pycurl.POLL_REMOVE, m, socket_events)
# close handles
for c in m.handles:
# pycurl API calls
m.remove_handle(c)
c.close()
m.close()
def check(self, event, multi, socket_events):
for event_, multi_ in socket_events:
if event == event_ and multi == multi_:
return
assert False, '%d %s not found in socket events' % (event, multi)
pycurl-7.19.3/tests/multi_test.py 0000644 0004705 0004705 00000027241 12262015545 016430 0 ustar pie pie 0000000 0000000 #! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et
import pycurl
import unittest
import select
from . import appmanager
from . import util
setup_module_1, teardown_module_1 = appmanager.setup(('app', 8380))
setup_module_2, teardown_module_2 = appmanager.setup(('app', 8381))
setup_module_3, teardown_module_3 = appmanager.setup(('app', 8382))
def setup_module(mod):
setup_module_1(mod)
setup_module_2(mod)
setup_module_3(mod)
def teardown_module(mod):
teardown_module_3(mod)
teardown_module_2(mod)
teardown_module_1(mod)
class MultiTest(unittest.TestCase):
def test_multi(self):
io1 = util.BytesIO()
io2 = util.BytesIO()
m = pycurl.CurlMulti()
handles = []
c1 = pycurl.Curl()
c2 = pycurl.Curl()
c1.setopt(c1.URL, 'http://localhost:8380/success')
c1.setopt(c1.WRITEFUNCTION, io1.write)
c2.setopt(c2.URL, 'http://localhost:8381/success')
c2.setopt(c1.WRITEFUNCTION, io2.write)
m.add_handle(c1)
m.add_handle(c2)
handles.append(c1)
handles.append(c2)
num_handles = len(handles)
while num_handles:
while 1:
ret, num_handles = m.perform()
if ret != pycurl.E_CALL_MULTI_PERFORM:
break
m.select(1.0)
m.remove_handle(c2)
m.remove_handle(c1)
m.close()
c1.close()
c2.close()
self.assertEqual('success', io1.getvalue().decode())
self.assertEqual('success', io2.getvalue().decode())
def test_multi_select_fdset(self):
c1 = pycurl.Curl()
c2 = pycurl.Curl()
c3 = pycurl.Curl()
c1.setopt(c1.URL, "http://localhost:8380/success")
c2.setopt(c2.URL, "http://localhost:8381/success")
c3.setopt(c3.URL, "http://localhost:8382/success")
c1.body = util.BytesIO()
c2.body = util.BytesIO()
c3.body = util.BytesIO()
c1.setopt(c1.WRITEFUNCTION, c1.body.write)
c2.setopt(c2.WRITEFUNCTION, c2.body.write)
c3.setopt(c3.WRITEFUNCTION, c3.body.write)
m = pycurl.CurlMulti()
m.add_handle(c1)
m.add_handle(c2)
m.add_handle(c3)
# Number of seconds to wait for a timeout to happen
SELECT_TIMEOUT = 0.1
# Stir the state machine into action
while 1:
ret, num_handles = m.perform()
if ret != pycurl.E_CALL_MULTI_PERFORM:
break
# Keep going until all the connections have terminated
while num_handles:
select.select(*m.fdset() + (SELECT_TIMEOUT,))
while 1:
ret, num_handles = m.perform()
if ret != pycurl.E_CALL_MULTI_PERFORM:
break
# Cleanup
m.remove_handle(c3)
m.remove_handle(c2)
m.remove_handle(c1)
m.close()
c1.close()
c2.close()
c3.close()
self.assertEqual('success', c1.body.getvalue().decode())
self.assertEqual('success', c2.body.getvalue().decode())
self.assertEqual('success', c3.body.getvalue().decode())
def test_multi_status_codes(self):
# init
m = pycurl.CurlMulti()
m.handles = []
urls = [
'http://localhost:8380/success',
'http://localhost:8381/status/403',
'http://localhost:8382/status/404',
]
for url in urls:
c = pycurl.Curl()
# save info in standard Python attributes
c.url = url.rstrip()
c.body = util.BytesIO()
c.http_code = -1
m.handles.append(c)
# pycurl API calls
c.setopt(c.URL, c.url)
c.setopt(c.WRITEFUNCTION, c.body.write)
m.add_handle(c)
# get data
num_handles = len(m.handles)
while num_handles:
while 1:
ret, num_handles = m.perform()
if ret != pycurl.E_CALL_MULTI_PERFORM:
break
# currently no more I/O is pending, could do something in the meantime
# (display a progress bar, etc.)
m.select(0.1)
# close handles
for c in m.handles:
# save info in standard Python attributes
c.http_code = c.getinfo(c.HTTP_CODE)
# pycurl API calls
m.remove_handle(c)
c.close()
m.close()
# check result
self.assertEqual('success', m.handles[0].body.getvalue().decode())
self.assertEqual(200, m.handles[0].http_code)
# bottle generated response body
self.assertEqual('forbidden', m.handles[1].body.getvalue().decode())
self.assertEqual(403, m.handles[1].http_code)
# bottle generated response body
self.assertEqual('not found', m.handles[2].body.getvalue().decode())
self.assertEqual(404, m.handles[2].http_code)
def check_adding_closed_handle(self, close_fn):
# init
m = pycurl.CurlMulti()
m.handles = []
urls = [
'http://localhost:8380/success',
'http://localhost:8381/status/403',
'http://localhost:8382/status/404',
]
for url in urls:
c = pycurl.Curl()
# save info in standard Python attributes
c.url = url
c.body = util.BytesIO()
c.http_code = -1
c.debug = 0
m.handles.append(c)
# pycurl API calls
c.setopt(c.URL, c.url)
c.setopt(c.WRITEFUNCTION, c.body.write)
m.add_handle(c)
# debug - close a handle
c = m.handles[2]
c.debug = 1
c.close()
# get data
num_handles = len(m.handles)
while num_handles:
while 1:
ret, num_handles = m.perform()
if ret != pycurl.E_CALL_MULTI_PERFORM:
break
# currently no more I/O is pending, could do something in the meantime
# (display a progress bar, etc.)
m.select(0.1)
# close handles
for c in m.handles:
# save info in standard Python attributes
try:
c.http_code = c.getinfo(c.HTTP_CODE)
except pycurl.error:
# handle already closed - see debug above
assert c.debug
c.http_code = -1
# pycurl API calls
close_fn(m, c)
m.close()
# check result
self.assertEqual('success', m.handles[0].body.getvalue().decode())
self.assertEqual(200, m.handles[0].http_code)
# bottle generated response body
self.assertEqual('forbidden', m.handles[1].body.getvalue().decode())
self.assertEqual(403, m.handles[1].http_code)
# bottle generated response body
self.assertEqual('', m.handles[2].body.getvalue().decode())
self.assertEqual(-1, m.handles[2].http_code)
def _remove_then_close(self, m, c):
m.remove_handle(c)
c.close()
def _close_then_remove(self, m, c):
# in the C API this is the wrong calling order, but pycurl
# handles this automatically
c.close()
m.remove_handle(c)
def _close_without_removing(self, m, c):
# actually, remove_handle is called automatically on close
c.close
def test_adding_closed_handle_remove_then_close(self):
self.check_adding_closed_handle(self._remove_then_close)
def test_adding_closed_handle_close_then_remove(self):
self.check_adding_closed_handle(self._close_then_remove)
def test_adding_closed_handle_close_without_removing(self):
self.check_adding_closed_handle(self._close_without_removing)
def test_multi_select(self):
c1 = pycurl.Curl()
c2 = pycurl.Curl()
c3 = pycurl.Curl()
c1.setopt(c1.URL, "http://localhost:8380/success")
c2.setopt(c2.URL, "http://localhost:8381/success")
c3.setopt(c3.URL, "http://localhost:8382/success")
c1.body = util.BytesIO()
c2.body = util.BytesIO()
c3.body = util.BytesIO()
c1.setopt(c1.WRITEFUNCTION, c1.body.write)
c2.setopt(c2.WRITEFUNCTION, c2.body.write)
c3.setopt(c3.WRITEFUNCTION, c3.body.write)
m = pycurl.CurlMulti()
m.add_handle(c1)
m.add_handle(c2)
m.add_handle(c3)
# Number of seconds to wait for a timeout to happen
SELECT_TIMEOUT = 1.0
# Stir the state machine into action
while 1:
ret, num_handles = m.perform()
if ret != pycurl.E_CALL_MULTI_PERFORM:
break
# Keep going until all the connections have terminated
while num_handles:
# The select method uses fdset internally to determine which file descriptors
# to check.
m.select(SELECT_TIMEOUT)
while 1:
ret, num_handles = m.perform()
if ret != pycurl.E_CALL_MULTI_PERFORM:
break
# Cleanup
m.remove_handle(c3)
m.remove_handle(c2)
m.remove_handle(c1)
m.close()
c1.close()
c2.close()
c3.close()
self.assertEqual('success', c1.body.getvalue().decode())
self.assertEqual('success', c2.body.getvalue().decode())
self.assertEqual('success', c3.body.getvalue().decode())
def test_multi_info_read(self):
c1 = pycurl.Curl()
c2 = pycurl.Curl()
c3 = pycurl.Curl()
c1.setopt(c1.URL, "http://localhost:8380/short_wait")
c2.setopt(c2.URL, "http://localhost:8381/short_wait")
c3.setopt(c3.URL, "http://localhost:8382/short_wait")
c1.body = util.BytesIO()
c2.body = util.BytesIO()
c3.body = util.BytesIO()
c1.setopt(c1.WRITEFUNCTION, c1.body.write)
c2.setopt(c2.WRITEFUNCTION, c2.body.write)
c3.setopt(c3.WRITEFUNCTION, c3.body.write)
m = pycurl.CurlMulti()
m.add_handle(c1)
m.add_handle(c2)
m.add_handle(c3)
# Number of seconds to wait for a timeout to happen
SELECT_TIMEOUT = 1.0
# Stir the state machine into action
while 1:
ret, num_handles = m.perform()
if ret != pycurl.E_CALL_MULTI_PERFORM:
break
infos = []
# Keep going until all the connections have terminated
while num_handles:
# The select method uses fdset internally to determine which file descriptors
# to check.
m.select(SELECT_TIMEOUT)
while 1:
ret, num_handles = m.perform()
info = m.info_read()
infos.append(info)
if ret != pycurl.E_CALL_MULTI_PERFORM:
break
all_handles = []
for info in infos:
handles = info[1]
# last info is an empty array
if handles:
all_handles.extend(handles)
self.assertEqual(3, len(all_handles))
assert c1 in all_handles
assert c2 in all_handles
assert c3 in all_handles
# Cleanup
m.remove_handle(c3)
m.remove_handle(c2)
m.remove_handle(c1)
m.close()
c1.close()
c2.close()
c3.close()
self.assertEqual('success', c1.body.getvalue().decode())
self.assertEqual('success', c2.body.getvalue().decode())
self.assertEqual('success', c3.body.getvalue().decode())
def test_multi_close(self):
m = pycurl.CurlMulti()
m.close()
def test_multi_close_twice(self):
m = pycurl.CurlMulti()
m.close()
m.close()
pycurl-7.19.3/tests/multi_timer_test.py 0000644 0004705 0004705 00000005153 12262015545 017626 0 ustar pie pie 0000000 0000000 #! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et
import pycurl
import unittest
from . import appmanager
from . import util
setup_module_1, teardown_module_1 = appmanager.setup(('app', 8380))
setup_module_2, teardown_module_2 = appmanager.setup(('app', 8381))
setup_module_3, teardown_module_3 = appmanager.setup(('app', 8382))
def setup_module(mod):
setup_module_1(mod)
setup_module_2(mod)
setup_module_3(mod)
def teardown_module(mod):
teardown_module_3(mod)
teardown_module_2(mod)
teardown_module_1(mod)
class MultiSocketTest(unittest.TestCase):
def test_multi_timer(self):
urls = [
'http://localhost:8380/success',
'http://localhost:8381/success',
'http://localhost:8382/success',
]
timers = []
# timer callback
def timer(msecs):
#print('Timer callback msecs:', msecs)
timers.append(msecs)
# init
m = pycurl.CurlMulti()
m.setopt(pycurl.M_TIMERFUNCTION, timer)
m.handles = []
for url in urls:
c = pycurl.Curl()
# save info in standard Python attributes
c.url = url
c.body = util.BytesIO()
c.http_code = -1
m.handles.append(c)
# pycurl API calls
c.setopt(c.URL, c.url)
c.setopt(c.WRITEFUNCTION, c.body.write)
m.add_handle(c)
# get data
num_handles = len(m.handles)
while num_handles:
while 1:
ret, num_handles = m.perform()
if ret != pycurl.E_CALL_MULTI_PERFORM:
break
# currently no more I/O is pending, could do something in the meantime
# (display a progress bar, etc.)
m.select(1.0)
for c in m.handles:
# save info in standard Python attributes
c.http_code = c.getinfo(c.HTTP_CODE)
# print result
for c in m.handles:
self.assertEqual('success', c.body.getvalue().decode())
self.assertEqual(200, c.http_code)
assert len(timers) > 0
# libcurl 7.23.0 produces a 0 timer
assert timers[0] >= 0
# this assertion does not appear to hold on older libcurls
# or apparently on any linuxes, see
# https://github.com/p/pycurl/issues/19
#if not util.pycurl_version_less_than(7, 24):
# self.assertEqual(-1, timers[-1])
# close handles
for c in m.handles:
# pycurl API calls
m.remove_handle(c)
c.close()
m.close()
pycurl-7.19.3/tests/option_constants_test.py 0000644 0004705 0004705 00000005256 12262560234 020704 0 ustar pie pie 0000000 0000000 #! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et
import pycurl
import unittest
import nose.plugins.skip
from . import util
class OptionConstantsTest(unittest.TestCase):
# CURLOPT_USERNAME was introduced in libcurl-7.19.1
@util.min_libcurl(7, 19, 1)
def test_username(self):
assert hasattr(pycurl, 'USERNAME')
assert hasattr(pycurl, 'PASSWORD')
assert hasattr(pycurl, 'PROXYUSERNAME')
assert hasattr(pycurl, 'PROXYPASSWORD')
# CURLOPT_DNS_SERVERS was introduced in libcurl-7.24.0
@util.min_libcurl(7, 24, 0)
def test_dns_servers(self):
assert hasattr(pycurl, 'DNS_SERVERS')
# Does not work unless libcurl was built against c-ares
#c = pycurl.Curl()
#c.setopt(c.DNS_SERVERS, '1.2.3.4')
#c.close()
# CURLOPT_POSTREDIR was introduced in libcurl-7.19.1
@util.min_libcurl(7, 19, 1)
def test_postredir(self):
assert hasattr(pycurl, 'POSTREDIR')
assert hasattr(pycurl, 'REDIR_POST_301')
assert hasattr(pycurl, 'REDIR_POST_302')
assert hasattr(pycurl, 'REDIR_POST_ALL')
# CURLOPT_POSTREDIR was introduced in libcurl-7.19.1
@util.min_libcurl(7, 19, 1)
def test_postredir_setopt(self):
curl = pycurl.Curl()
curl.setopt(curl.POSTREDIR, curl.REDIR_POST_301)
curl.close()
# CURL_REDIR_POST_303 was introduced in libcurl-7.26.0
@util.min_libcurl(7, 26, 0)
def test_redir_post_303(self):
assert hasattr(pycurl, 'REDIR_POST_303')
# CURLOPT_POSTREDIR was introduced in libcurl-7.19.1
@util.min_libcurl(7, 19, 1)
def test_postredir_flags(self):
self.assertEqual(pycurl.REDIR_POST_301, pycurl.REDIR_POST_ALL & pycurl.REDIR_POST_301)
self.assertEqual(pycurl.REDIR_POST_302, pycurl.REDIR_POST_ALL & pycurl.REDIR_POST_302)
# CURL_REDIR_POST_303 was introduced in libcurl-7.26.0
@util.min_libcurl(7, 26, 0)
def test_postredir_flags(self):
self.assertEqual(pycurl.REDIR_POST_303, pycurl.REDIR_POST_ALL & pycurl.REDIR_POST_303)
# HTTPAUTH_DIGEST_IE was introduced in libcurl-7.19.3
@util.min_libcurl(7, 19, 3)
def test_httpauth_digest_ie(self):
assert hasattr(pycurl, 'HTTPAUTH_DIGEST_IE')
# CURLE_OPERATION_TIMEDOUT was introduced in libcurl-7.10.2
# to replace CURLE_OPERATION_TIMEOUTED
def test_operation_timedout_constant(self):
self.assertEqual(pycurl.E_OPERATION_TIMEDOUT, pycurl.E_OPERATION_TIMEOUTED)
# CURLOPT_NOPROXY was introduced in libcurl-7.19.4
@util.min_libcurl(7, 19, 4)
def test_noproxy_setopt(self):
curl = pycurl.Curl()
curl.setopt(curl.NOPROXY, 'localhost')
curl.close()
pycurl-7.19.3/tests/pause_test.py 0000644 0004705 0004705 00000005624 12262015545 016414 0 ustar pie pie 0000000 0000000 #! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et
import pycurl
import unittest, signal
import time as _time
from . import appmanager
from . import util
setup_module, teardown_module = appmanager.setup(('app', 8380))
class PauseTest(unittest.TestCase):
def setUp(self):
self.curl = pycurl.Curl()
def tearDown(self):
self.curl.close()
def test_pause_via_call(self):
self.check_pause(True)
def test_pause_via_return(self):
self.check_pause(False)
def check_pause(self, call):
# the app sleeps for 0.5 seconds
self.curl.setopt(pycurl.URL, 'http://localhost:8380/pause')
sio = util.BytesIO()
state = dict(paused=False, resumed=False)
if call:
def writefunc(data):
rv = sio.write(data)
if not state['paused']:
self.curl.pause(pycurl.PAUSE_ALL)
state['paused'] = True
return rv
else:
def writefunc(data):
if not state['paused']:
# cannot write to sio here, because
# curl takes pause return value to mean that
# nothing was written
state['paused'] = True
return pycurl.READFUNC_PAUSE
else:
return sio.write(data)
def resume(*args):
state['resumed'] = True
self.curl.pause(pycurl.PAUSE_CONT)
signal.signal(signal.SIGALRM, resume)
# alarm for 1 second which is 0.5 seconds more than the server side
# should sleep for
signal.alarm(1)
start = _time.time()
self.curl.setopt(pycurl.WRITEFUNCTION, writefunc)
m = pycurl.CurlMulti()
m.add_handle(self.curl)
# Number of seconds to wait for a timeout to happen
SELECT_TIMEOUT = 1.0
# Stir the state machine into action
while 1:
ret, num_handles = m.perform()
if ret != pycurl.E_CALL_MULTI_PERFORM:
break
# Keep going until all the connections have terminated
while num_handles:
# The select method uses fdset internally to determine which file descriptors
# to check.
m.select(SELECT_TIMEOUT)
while 1:
if _time.time() - start > 2:
# test is taking too long, fail
assert False, 'Test is taking too long'
ret, num_handles = m.perform()
if ret != pycurl.E_CALL_MULTI_PERFORM:
break
# Cleanup
m.remove_handle(self.curl)
m.close()
self.assertEqual('part1part2', sio.getvalue().decode())
end = _time.time()
# check that client side waited
self.assertTrue(end-start > 1)
assert state['resumed']
pycurl-7.19.3/tests/post_test.py 0000644 0004705 0004705 00000011013 12262015545 016251 0 ustar pie pie 0000000 0000000 #! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et
import os.path
import pycurl
import unittest
try:
import json
except ImportError:
import simplejson as json
try:
import urllib.parse as urllib_parse
except ImportError:
import urllib as urllib_parse
from . import appmanager
from . import util
setup_module, teardown_module = appmanager.setup(('app', 8380))
class PostTest(unittest.TestCase):
def setUp(self):
self.curl = pycurl.Curl()
def tearDown(self):
self.curl.close()
def test_post_single_field(self):
pf = {'field1': 'value1'}
self.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:
# {'\x00\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00': ''}
# Traceback (most recent call last):
# File "/usr/local/bin/bottle.py", line 744, in _handle
# return route.call(**args)
# File "/usr/local/bin/bottle.py", line 1479, in wrapper
# rv = callback(*a, **ka)
# File "/home/pie/apps/pycurl/tests/app.py", line 21, in postfields
# return json.dumps(dict(bottle.request.forms))
# File "/usr/local/lib/python2.7/json/__init__.py", line 231, in dumps
# return _default_encoder.encode(obj)
# File "/usr/local/lib/python2.7/json/encoder.py", line 201, in encode
# chunks = self.iterencode(o, _one_shot=True)
# File "/usr/local/lib/python2.7/json/encoder.py", line 264, in iterencode
# return _iterencode(o, 0)
# UnicodeDecodeError: 'utf8' codec can't decode byte 0x80 in position 4: invalid start byte
#self.curl.setopt(pycurl.VERBOSE, 1)
sio = util.BytesIO()
self.curl.setopt(pycurl.WRITEFUNCTION, sio.write)
self.curl.perform()
self.assertEqual(200, self.curl.getinfo(pycurl.HTTP_CODE))
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'))
]
expect = {
'field3': 'this is wei\000rd, but null-bytes are okay',
}
self.check_post(send, expect, 'http://localhost:8380/postfields')
def test_post_file(self):
path = os.path.join(os.path.dirname(__file__), '..', 'README.rst')
f = open(path)
try:
contents = f.read()
finally:
f.close()
send = [
#('field2', (pycurl.FORM_FILE, 'test_post.py', pycurl.FORM_FILE, 'test_post2.py')),
('field2', (pycurl.FORM_FILE, path)),
]
expect = [{
'name': 'field2',
'filename': 'README.rst',
'data': contents,
}]
self.check_post(send, expect, 'http://localhost:8380/files')
def test_post_buffer(self):
contents = 'hello, world!'
send = [
('field2', (pycurl.FORM_BUFFER, 'uploaded.file', pycurl.FORM_BUFFERPTR, contents)),
]
expect = [{
'name': 'field2',
'filename': 'uploaded.file',
'data': contents,
}]
self.check_post(send, expect, 'http://localhost:8380/files')
# XXX this test takes about a second to run, check keep-alives?
def check_post(self, send, expect, endpoint):
self.curl.setopt(pycurl.URL, endpoint)
self.curl.setopt(pycurl.HTTPPOST, send)
#self.curl.setopt(pycurl.VERBOSE, 1)
sio = util.BytesIO()
self.curl.setopt(pycurl.WRITEFUNCTION, sio.write)
self.curl.perform()
self.assertEqual(200, self.curl.getinfo(pycurl.HTTP_CODE))
body = sio.getvalue().decode()
returned_fields = json.loads(body)
self.assertEqual(expect, returned_fields)
pycurl-7.19.3/tests/procmgr.py 0000644 0004705 0004705 00000005074 12260666420 015712 0 ustar pie pie 0000000 0000000 import threading
import subprocess
import os
import sys
import signal
import nose.plugins.skip
from . import util
class ProcessManager(object):
def __init__(self, cmd):
self.cmd = cmd
self.running = False
def start(self):
self.process = subprocess.Popen(self.cmd)
self.running = True
self.thread = threading.Thread(target=self.run)
self.thread.daemon = True
self.thread.start()
def run(self):
self.process.communicate()
def stop(self):
try:
os.kill(self.process.pid, signal.SIGTERM)
except OSError:
pass
self.running = False
managers = {}
def start(cmd):
if str(cmd) in managers and managers[str(cmd)].running:
# already started
return
manager = ProcessManager(cmd)
managers[str(cmd)] = manager
manager.start()
def start_setup(cmd):
def do_start():
start(cmd)
return do_start
# Example on FreeBSD:
# PYCURL_VSFTPD_PATH=/usr/local/libexec/vsftpd nosetests
if 'PYCURL_VSFTPD_PATH' in os.environ:
vsftpd_path = os.environ['PYCURL_VSFTPD_PATH']
else:
vsftpd_path = None
try:
# python 2
exception_base = StandardError
except NameError:
# python 3
exception_base = Exception
class VsftpdNotConfigured(exception_base):
pass
def vsftpd_setup():
config_file_path = os.path.join(os.path.dirname(__file__), 'vsftpd.conf')
root_path = os.path.join(os.path.dirname(__file__), '..')
cmd = [
vsftpd_path,
config_file_path,
'-oanon_root=%s' % root_path,
]
setup_module = start_setup(cmd)
def do_setup_module():
if vsftpd_path is None:
raise nose.plugins.skip.SkipTest('PYCURL_VSFTPD_PATH environment variable not set')
try:
setup_module()
except OSError:
import errno
e = sys.exc_info()[1]
if e.errno == errno.ENOENT:
msg = "Tried to execute `%s`\nTry specifying path to vsftpd via PYCURL_VSFTPD_PATH environment variable\n" % vsftpd_path
raise OSError(e.errno, e.strerror + "\n" + msg)
else:
raise
ok = util.wait_for_network_service(('127.0.0.1', 8321), 0.1, 10)
if not ok:
import warnings
warnings.warn('vsftpd did not start after 1 second')
def teardown_module():
try:
manager = managers[str(cmd)]
except KeyError:
pass
else:
manager.stop()
return do_setup_module, teardown_module
pycurl-7.19.3/tests/pycurl_object_test.py 0000644 0004705 0004705 00000010571 12262015545 020140 0 ustar pie pie 0000000 0000000 #! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et
import pycurl
import unittest
import sys
class PycurlObjectTest(unittest.TestCase):
def setUp(self):
self.curl = pycurl.Curl()
def tearDown(self):
self.curl.close()
def test_set_attribute_curl(self):
self.instantiate_and_check(self.check_set_attribute, 'Curl')
def test_get_attribute_curl(self):
self.instantiate_and_check(self.check_get_attribute, 'Curl')
def test_get_missing_attribute_curl(self):
self.instantiate_and_check(self.check_get_missing_attribute, 'Curl')
def test_delete_attribute_curl(self):
self.instantiate_and_check(self.check_delete_attribute, 'Curl')
def test_delete_missing_attribute_curl(self):
self.instantiate_and_check(self.check_delete_missing_attribute, 'Curl')
def test_set_attribute_multi(self):
self.instantiate_and_check(self.check_set_attribute, 'CurlMulti')
def test_get_attribute_multi(self):
self.instantiate_and_check(self.check_get_attribute, 'CurlMulti')
def test_get_missing_attribute_curl(self):
self.instantiate_and_check(self.check_get_missing_attribute, 'CurlMulti')
def test_delete_attribute_multi(self):
self.instantiate_and_check(self.check_delete_attribute, 'CurlMulti')
def test_delete_missing_attribute_curl(self):
self.instantiate_and_check(self.check_delete_missing_attribute, 'CurlMulti')
def test_set_attribute_share(self):
self.instantiate_and_check(self.check_set_attribute, 'CurlShare')
def test_get_attribute_share(self):
self.instantiate_and_check(self.check_get_attribute, 'CurlShare')
def test_get_missing_attribute_curl(self):
self.instantiate_and_check(self.check_get_missing_attribute, 'CurlShare')
def test_delete_attribute_share(self):
self.instantiate_and_check(self.check_delete_attribute, 'CurlShare')
def test_delete_missing_attribute_curl(self):
self.instantiate_and_check(self.check_delete_missing_attribute, 'CurlShare')
def instantiate_and_check(self, fn, cls_name):
cls = getattr(pycurl, cls_name)
instance = cls()
try:
fn(instance)
finally:
instance.close()
def check_set_attribute(self, pycurl_obj):
assert not hasattr(pycurl_obj, 'attr')
pycurl_obj.attr = 1
assert hasattr(pycurl_obj, 'attr')
def check_get_attribute(self, pycurl_obj):
assert not hasattr(pycurl_obj, 'attr')
pycurl_obj.attr = 1
self.assertEqual(1, pycurl_obj.attr)
def check_get_missing_attribute(self, pycurl_obj):
try:
getattr(pycurl_obj, 'doesnotexist')
self.fail('Expected an AttributeError exception to be raised')
except AttributeError:
pass
def check_delete_attribute(self, pycurl_obj):
assert not hasattr(pycurl_obj, 'attr')
pycurl_obj.attr = 1
self.assertEqual(1, pycurl_obj.attr)
assert hasattr(pycurl_obj, 'attr')
del pycurl_obj.attr
assert not hasattr(pycurl_obj, 'attr')
def check_delete_missing_attribute(self, pycurl_obj):
try:
del pycurl_obj.doesnotexist
self.fail('Expected an AttributeError exception to be raised')
except AttributeError:
pass
def test_modify_attribute_curl(self):
self.check_modify_attribute(pycurl.Curl, 'READFUNC_PAUSE')
def test_modify_attribute_multi(self):
self.check_modify_attribute(pycurl.CurlMulti, 'E_MULTI_OK')
def test_modify_attribute_share(self):
self.check_modify_attribute(pycurl.CurlShare, 'SH_SHARE')
def check_modify_attribute(self, cls, name):
obj1 = cls()
obj2 = cls()
old_value = getattr(obj1, name)
self.assertNotEqual('helloworld', old_value)
# value should be identical to pycurl global
self.assertEqual(old_value, getattr(pycurl, name))
setattr(obj1, name, 'helloworld')
self.assertEqual('helloworld', getattr(obj1, name))
# change does not affect other existing objects
self.assertEqual(old_value, getattr(obj2, name))
# change does not affect objects created later
obj3 = cls()
self.assertEqual(old_value, getattr(obj3, name))
pycurl-7.19.3/tests/read_callback_test.py 0000644 0004705 0004705 00000010436 12262015545 020023 0 ustar pie pie 0000000 0000000 #! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et
import pycurl
import unittest
import sys
try:
import json
except ImportError:
import simplejson as json
try:
import urllib.parse as urllib_parse
except ImportError:
import urllib as urllib_parse
from . import appmanager
from . import util
setup_module, teardown_module = appmanager.setup(('app', 8380))
POSTFIELDS = {
'field1':'value1',
'field2':'value2 with blanks',
'field3':'value3',
}
POSTSTRING = urllib_parse.urlencode(POSTFIELDS)
class DataProvider(object):
def __init__(self, data):
self.data = data
self.finished = False
def read_cb(self, size):
assert len(self.data) <= size
if not self.finished:
self.finished = True
return self.data
else:
# Nothing more to read
return ""
class ReadCallbackTest(unittest.TestCase):
def setUp(self):
self.curl = pycurl.Curl()
def tearDown(self):
self.curl.close()
def test_post_with_read_callback(self):
d = DataProvider(POSTSTRING)
self.curl.setopt(self.curl.URL, 'http://localhost:8380/postfields')
self.curl.setopt(self.curl.POST, 1)
self.curl.setopt(self.curl.POSTFIELDSIZE, len(POSTSTRING))
self.curl.setopt(self.curl.READFUNCTION, d.read_cb)
#self.curl.setopt(self.curl.VERBOSE, 1)
sio = util.BytesIO()
self.curl.setopt(pycurl.WRITEFUNCTION, sio.write)
self.curl.perform()
actual = json.loads(sio.getvalue().decode())
self.assertEqual(POSTFIELDS, actual)
def test_post_with_read_callback_returning_bytes(self):
self.check_bytes('world')
def test_post_with_read_callback_returning_bytes_with_nulls(self):
self.check_bytes("wor\0ld")
def test_post_with_read_callback_returning_bytes_with_multibyte(self):
self.check_bytes(util.u("Пушкин"))
def check_bytes(self, poststring):
data = poststring.encode('utf8')
assert type(data) == util.binary_type
d = DataProvider(data)
self.curl.setopt(self.curl.URL, 'http://localhost:8380/raw_utf8')
self.curl.setopt(self.curl.POST, 1)
self.curl.setopt(self.curl.HTTPHEADER, ['Content-Type: application/octet-stream'])
# length of bytes
self.curl.setopt(self.curl.POSTFIELDSIZE, len(data))
self.curl.setopt(self.curl.READFUNCTION, d.read_cb)
#self.curl.setopt(self.curl.VERBOSE, 1)
sio = util.BytesIO()
self.curl.setopt(pycurl.WRITEFUNCTION, sio.write)
self.curl.perform()
# json should be ascii
actual = json.loads(sio.getvalue().decode('ascii'))
self.assertEqual(poststring, actual)
def test_post_with_read_callback_returning_unicode(self):
self.check_unicode(util.u('world'))
def test_post_with_read_callback_returning_unicode_with_nulls(self):
self.check_unicode(util.u("wor\0ld"))
def test_post_with_read_callback_returning_unicode_with_multibyte(self):
try:
self.check_unicode(util.u("Пушкин"))
# prints:
# UnicodeEncodeError: 'ascii' codec can't encode characters in position 6-11: ordinal not in range(128)
except pycurl.error:
err, msg = sys.exc_info()[1].args
# we expect pycurl.E_WRITE_ERROR as the response
self.assertEqual(pycurl.E_ABORTED_BY_CALLBACK, err)
self.assertEqual('operation aborted by callback', msg)
def check_unicode(self, poststring):
assert type(poststring) == util.text_type
d = DataProvider(poststring)
self.curl.setopt(self.curl.URL, 'http://localhost:8380/raw_utf8')
self.curl.setopt(self.curl.POST, 1)
self.curl.setopt(self.curl.HTTPHEADER, ['Content-Type: application/octet-stream'])
self.curl.setopt(self.curl.POSTFIELDSIZE, len(poststring))
self.curl.setopt(self.curl.READFUNCTION, d.read_cb)
#self.curl.setopt(self.curl.VERBOSE, 1)
sio = util.BytesIO()
self.curl.setopt(pycurl.WRITEFUNCTION, sio.write)
self.curl.perform()
# json should be ascii
actual = json.loads(sio.getvalue().decode('ascii'))
self.assertEqual(poststring, actual)
pycurl-7.19.3/tests/relative_url_test.py 0000644 0004705 0004705 00000001012 12262015545 017757 0 ustar pie pie 0000000 0000000 #! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et
# uses the high level interface
import curl
import unittest
from . import appmanager
setup_module, teardown_module = appmanager.setup(('app', 8380))
class RelativeUrlTest(unittest.TestCase):
def setUp(self):
self.curl = curl.Curl('http://localhost:8380/')
def tearDown(self):
self.curl.close()
def test_get_relative(self):
self.curl.get('/success')
self.assertEqual('success', self.curl.body().decode())
pycurl-7.19.3/tests/reset_test.py 0000644 0004705 0004705 00000003722 12262015545 016416 0 ustar pie pie 0000000 0000000 #! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et
import pycurl
import unittest
try:
import urllib.parse as urllib_parse
except ImportError:
import urllib as urllib_parse
from . import appmanager
from . import util
setup_module, teardown_module = appmanager.setup(('app', 8380))
class ResetTest(unittest.TestCase):
# XXX this test was broken when it was test_reset.py
def skip_reset(self):
outf = util.BytesIO()
cm = pycurl.CurlMulti()
eh = pycurl.Curl()
for x in range(1, 20):
eh.setopt(pycurl.WRITEFUNCTION, outf.write)
eh.setopt(pycurl.URL, 'http://localhost:8380/success')
cm.add_handle(eh)
while 1:
ret, active_handles = cm.perform()
if ret != pycurl.E_CALL_MULTI_PERFORM:
break
while active_handles:
ret = cm.select(1.0)
if ret == -1:
continue
while 1:
ret, active_handles = cm.perform()
if ret != pycurl.E_CALL_MULTI_PERFORM:
break
count, good, bad = cm.info_read()
for h, en, em in bad:
print("Transfer to %s failed with %d, %s\n" % \
(h.getinfo(pycurl.EFFECTIVE_URL), en, em))
raise RuntimeError
for h in good:
httpcode = h.getinfo(pycurl.RESPONSE_CODE)
if httpcode != 200:
print("Transfer to %s failed with code %d\n" %\
(h.getinfo(pycurl.EFFECTIVE_URL), httpcode))
raise RuntimeError
else:
print("Recd %d bytes from %s" % \
(h.getinfo(pycurl.SIZE_DOWNLOAD),
h.getinfo(pycurl.EFFECTIVE_URL)))
cm.remove_handle(eh)
eh.reset()
eh.close()
cm.close()
outf.close()
pycurl-7.19.3/tests/resolve_test.py 0000644 0004705 0004705 00000001427 12262015545 016753 0 ustar pie pie 0000000 0000000 # -*- coding: utf-8 -*-
import pycurl
import unittest
import nose.plugins.skip
from . import appmanager
from . import util
setup_module, teardown_module = appmanager.setup(('app', 8380))
class ResolveTest(unittest.TestCase):
def setUp(self):
self.curl = pycurl.Curl()
def tearDown(self):
self.curl.close()
def test_resolve(self):
if util.pycurl_version_less_than(7, 21, 3) and not hasattr(pycurl, 'RESOLVE'):
raise nose.plugins.skip.SkipTest('libcurl < 7.21.3 or no RESOLVE')
self.curl.setopt(pycurl.URL, 'http://p.localhost:8380/success')
self.curl.setopt(pycurl.RESOLVE, ['p.localhost:8380:127.0.0.1'])
self.curl.perform()
self.assertEqual(200, self.curl.getinfo(pycurl.RESPONSE_CODE))
pycurl-7.19.3/tests/runwsgi.py 0000644 0004705 0004705 00000010033 12260666420 015726 0 ustar pie pie 0000000 0000000 # Run a WSGI application in a daemon thread
import sys
import bottle
import threading
import os.path
from . import util
global_stop = False
class Server(bottle.WSGIRefServer):
def run(self, handler): # pragma: no cover
from wsgiref.simple_server import make_server, WSGIRequestHandler
if self.quiet:
base = self.options.get('handler_class', WSGIRequestHandler)
class QuietHandler(base):
def log_request(*args, **kw):
pass
self.options['handler_class'] = QuietHandler
self.srv = make_server(self.host, self.port, handler, **self.options)
if sys.version_info[0] == 2 and sys.version_info[1] < 6:
# python 2.5 has no poll_interval
# and thus no way to stop the server
while not global_stop:
self.srv.handle_request()
else:
self.srv.serve_forever(poll_interval=0.1)
class SslServer(bottle.CherryPyServer):
def run(self, handler):
import cherrypy.wsgiserver, cherrypy.wsgiserver.ssl_builtin
server = cherrypy.wsgiserver.CherryPyWSGIServer((self.host, self.port), handler)
cert_dir = os.path.join(os.path.dirname(__file__), 'certs')
ssl_adapter = cherrypy.wsgiserver.ssl_builtin.BuiltinSSLAdapter(
os.path.join(cert_dir, 'server.crt'),
os.path.join(cert_dir, 'server.key'),
)
server.ssl_adapter = ssl_adapter
try:
server.start()
finally:
server.stop()
def start_bottle_server(app, port, server, **kwargs):
server_thread = ServerThread(app, port, server, kwargs)
server_thread.daemon = True
server_thread.start()
ok = util.wait_for_network_service(('127.0.0.1', port), 0.1, 10)
if not ok:
import warnings
warnings.warn('Server did not start after 1 second')
return server_thread.server
class ServerThread(threading.Thread):
def __init__(self, app, port, server, server_kwargs):
threading.Thread.__init__(self)
self.app = app
self.port = port
self.server_kwargs = server_kwargs
self.server = server(host='127.0.0.1', port=self.port, **self.server_kwargs)
def run(self):
bottle.run(self.app, server=self.server, quiet=True)
started_servers = {}
def app_runner_setup(*specs):
'''Returns setup and teardown methods for running a list of WSGI
applications in a daemon thread.
Each argument is an (app, port) pair.
Return value is a (setup, teardown) function pair.
The setup and teardown functions expect to be called with an argument
on which server state will be stored.
Example usage with nose:
>>> setup_module, teardown_module = \
runwsgi.app_runner_setup((app_module.app, 8050))
'''
def setup(self):
self.servers = []
for spec in specs:
if len(spec) == 2:
app, port = spec
kwargs = {}
else:
app, port, kwargs = spec
if port in started_servers:
assert started_servers[port] == (app, kwargs)
else:
server = Server
if 'server' in kwargs:
server = kwargs['server']
del kwargs['server']
elif 'ssl' in kwargs:
if kwargs['ssl']:
server = SslServer
del kwargs['ssl']
self.servers.append(start_bottle_server(app, port, server, **kwargs))
started_servers[port] = (app, kwargs)
def teardown(self):
return
for server in self.servers:
# if no tests from module were run, there is no server to shut down
if hasattr(server, 'srv'):
if hasattr(server.srv, 'shutdown'):
server.srv.shutdown()
else:
# python 2.5
global global_stop
global_stop = True
return [setup, teardown]
pycurl-7.19.3/tests/seek_function_test.py 0000644 0004705 0004705 00000004315 12262015545 020127 0 ustar pie pie 0000000 0000000 #! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et
# Note: this test is meant to be run from pycurl project root.
import pycurl
import unittest
import os.path
from . import procmgr
setup_module, teardown_module = procmgr.vsftpd_setup()
class PartialFileSource:
def __init__(self):
self.__buf = '1234567890.1234567890'
self.__maxread = None
self.__bufptr = 0
def read(self, size):
p = self.__bufptr
end = p+size
if self.__maxread:
end = min(self.__maxread, end)
ret = self.__buf[p:end]
self.__bufptr+= len(ret)
#print 20*">>>", "read(%s) ==> %s" % (size, len(ret))
return ret
def seek(self, offset, origin):
#print 20*">>>", "seek(%s, %s)" % (offset, origin)
self.__bufptr = offset
def set_maxread(self, maxread):
self.__maxread = maxread
class SeekFunctionTest(unittest.TestCase):
def test_seek_function(self):
c = pycurl.Curl()
c.setopt(pycurl.UPLOAD, 1)
c.setopt(pycurl.URL, "ftp://localhost:8321/tests/tmp/upload.txt")
c.setopt(pycurl.RESUME_FROM, 0)
#c.setopt(pycurl.VERBOSE, 1)
upload_file = PartialFileSource()
c.setopt(pycurl.READFUNCTION, upload_file.read)
upload_file.set_maxread(10)
c.perform()
f = open(os.path.join(os.path.dirname(__file__), 'tmp', 'upload.txt'))
try:
content = f.read()
finally:
f.close()
self.assertEqual('1234567890', content)
c.close()
del c
del upload_file
c = pycurl.Curl()
c.setopt(pycurl.URL, "ftp://localhost:8321/tests/tmp/upload.txt")
c.setopt(pycurl.RESUME_FROM, -1)
c.setopt(pycurl.UPLOAD, 1)
#c.setopt(pycurl.VERBOSE, 1)
upload_file = PartialFileSource()
c.setopt(pycurl.READFUNCTION, upload_file.read)
c.setopt(pycurl.SEEKFUNCTION, upload_file.seek)
c.perform()
c.close()
f = open(os.path.join(os.path.dirname(__file__), 'tmp', 'upload.txt'))
try:
content = f.read()
finally:
f.close()
self.assertEqual('1234567890.1234567890', content)
pycurl-7.19.3/tests/setopt_lifecycle_test.py 0000644 0004705 0004705 00000003406 12262015545 020630 0 ustar pie pie 0000000 0000000 #! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et
import gc
import os.path
import pycurl
import unittest
try:
import json
except ImportError:
import simplejson as json
from . import appmanager
from . import util
setup_module, teardown_module = appmanager.setup(('app', 8380))
class TestString(str):
def __del__(self):
self.replace('1', '2')
#print self
#print 'd'
class SetoptLifecycleTest(unittest.TestCase):
# separate method to permit pf to go out of scope and be
# garbage collected before perform call
def do_setopt(self, curl, index):
pf = TestString('&'.join(50*['field=value%d' % (index,)]))
curl.setopt(pycurl.URL, 'http://localhost:8380/postfields')
curl.setopt(pycurl.POSTFIELDS, pf)
# This test takes 6+ seconds to run.
# It seems to pass with broken pycurl code when run by itself,
# but fails when run as part of the entire test suite.
def test_postfields_lifecycle(self):
requests = []
for i in range(1000):
curl = pycurl.Curl()
self.do_setopt(curl, i)
gc.collect()
requests.append(curl)
# send requests here to permit maximum garbage recycling
for i in range(100):
curl = requests[i]
#self.curl.setopt(pycurl.VERBOSE, 1)
sio = util.BytesIO()
curl.setopt(pycurl.WRITEFUNCTION, sio.write)
curl.perform()
self.assertEqual(200, curl.getinfo(pycurl.HTTP_CODE))
body = sio.getvalue().decode()
returned_fields = json.loads(body)
self.assertEqual(dict(field='value%d' % i), returned_fields)
for i in range(100):
curl = requests[i]
curl.close()
pycurl-7.19.3/tests/setopt_unicode_test.py 0000644 0004705 0004705 00000002175 12262015545 020321 0 ustar pie pie 0000000 0000000 #! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et
import pycurl
import unittest
import nose.tools
try:
import json
except ImportError:
import simplejson as json
from . import appmanager
from . import util
setup_module, teardown_module = appmanager.setup(('app', 8380))
class SetoptUnicodeTest(unittest.TestCase):
def setUp(self):
self.curl = pycurl.Curl()
def tearDown(self):
self.curl.close()
def test_ascii_string(self):
self.check('p=test', 'test')
@nose.tools.raises(UnicodeEncodeError)
def test_unicode_string(self):
self.check(util.u('p=Москва'), util.u('Москва'))
def test_unicode_encoded(self):
self.check(util.u('p=Москва').encode('utf8'), util.u('Москва'))
def check(self, send, expected):
self.curl.setopt(pycurl.URL, 'http://localhost:8380/param_utf8_hack')
sio = util.BytesIO()
self.curl.setopt(pycurl.WRITEFUNCTION, sio.write)
self.curl.setopt(pycurl.POSTFIELDS, send)
self.curl.perform()
self.assertEqual(expected, sio.getvalue().decode('utf-8'))
pycurl-7.19.3/tests/share_test.py 0000644 0004705 0004705 00000002761 12262015545 016400 0 ustar pie pie 0000000 0000000 #! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et
import threading
import pycurl
import unittest
try:
import urllib.parse as urllib_parse
except ImportError:
import urllib as urllib_parse
from . import appmanager
from . import util
setup_module, teardown_module = appmanager.setup(('app', 8380))
class WorkerThread(threading.Thread):
def __init__(self, share):
threading.Thread.__init__(self)
self.curl = pycurl.Curl()
self.curl.setopt(pycurl.URL, 'http://localhost:8380/success')
self.curl.setopt(pycurl.SHARE, share)
self.sio = util.BytesIO()
self.curl.setopt(pycurl.WRITEFUNCTION, self.sio.write)
def run(self):
self.curl.perform()
self.curl.close()
class ShareTest(unittest.TestCase):
def test_share(self):
s = pycurl.CurlShare()
s.setopt(pycurl.SH_SHARE, pycurl.LOCK_DATA_COOKIE)
s.setopt(pycurl.SH_SHARE, pycurl.LOCK_DATA_DNS)
s.setopt(pycurl.SH_SHARE, pycurl.LOCK_DATA_SSL_SESSION)
t1 = WorkerThread(s)
t2 = WorkerThread(s)
t1.start()
t2.start()
t1.join()
t2.join()
del s
self.assertEqual('success', t1.sio.getvalue().decode())
self.assertEqual('success', t2.sio.getvalue().decode())
def test_share_close(self):
s = pycurl.CurlShare()
s.close()
def test_share_close_twice(self):
s = pycurl.CurlShare()
s.close()
s.close()
pycurl-7.19.3/tests/socket_open_test.py 0000644 0004705 0004705 00000002472 12262015545 017606 0 ustar pie pie 0000000 0000000 #! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et
import socket
import pycurl
import unittest
try:
import urllib.parse as urllib_parse
except ImportError:
import urllib as urllib_parse
from . import appmanager
from . import util
setup_module, teardown_module = appmanager.setup(('app', 8380))
socket_open_called = False
socket_open_address = None
def socket_open(family, socktype, protocol, address):
global socket_open_called
global socket_open_address
socket_open_called = True
socket_open_address = address
#print(family, socktype, protocol, address)
s = socket.socket(family, socktype, protocol)
s.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
return s
class SocketOpenTest(unittest.TestCase):
def setUp(self):
self.curl = pycurl.Curl()
def tearDown(self):
self.curl.close()
def test_socket_open(self):
self.curl.setopt(pycurl.OPENSOCKETFUNCTION, socket_open)
self.curl.setopt(self.curl.URL, 'http://localhost:8380/success')
sio = util.BytesIO()
self.curl.setopt(pycurl.WRITEFUNCTION, sio.write)
self.curl.perform()
assert socket_open_called
self.assertEqual(("127.0.0.1", 8380), socket_open_address)
self.assertEqual('success', sio.getvalue().decode())
pycurl-7.19.3/tests/unset_range_test.py 0000644 0004705 0004705 00000002705 12262015545 017606 0 ustar pie pie 0000000 0000000 #! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et
import os.path
import pycurl
import sys
import unittest
class UnsetRangeTest(unittest.TestCase):
def setUp(self):
self.curl = pycurl.Curl()
def tearDown(self):
self.curl.close()
def test_unset_range(self):
def write_cb(data):
self.read += len(data)
return None
# download bytes 0-9 of the script itself through the file:// protocol
self.read = 0
self.curl.setopt(pycurl.URL, 'file://' + os.path.abspath(sys.argv[0]))
self.curl.setopt(pycurl.WRITEFUNCTION, write_cb)
self.curl.setopt(pycurl.RANGE, '0-9')
self.curl.perform()
assert 10 == self.read
# the RANGE setting should be preserved from the previous transfer
self.read = 0
self.curl.perform()
assert 10 == self.read
# drop the RANGE setting using unsetopt() and download entire script
self.read = 0
self.curl.unsetopt(pycurl.RANGE)
self.curl.perform()
assert 10 < self.read
# now set the RANGE again and check that pycurl takes it into account
self.read = 0
self.curl.setopt(pycurl.RANGE, '0-9')
self.curl.perform()
assert 10 == self.read
# now drop the RANGE setting using setopt(..., None)
self.read = 0
self.curl.setopt(pycurl.RANGE, None)
self.curl.perform()
assert 10 < self.read
pycurl-7.19.3/tests/user_agent_string_test.py 0000644 0004705 0004705 00000001432 12262015545 021012 0 ustar pie pie 0000000 0000000 #! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et
import unittest
import pycurl
from . import appmanager
from . import util
setup_module, teardown_module = appmanager.setup(('app', 8380))
class UserAgentStringTest(unittest.TestCase):
def setUp(self):
self.curl = pycurl.Curl()
def tearDown(self):
self.curl.close()
def test_pycurl_user_agent_string(self):
self.curl.setopt(pycurl.URL, 'http://localhost:8380/header?h=user-agent')
sio = util.BytesIO()
self.curl.setopt(pycurl.WRITEFUNCTION, sio.write)
self.curl.perform()
user_agent = sio.getvalue().decode()
assert user_agent.startswith('PycURL/')
assert 'libcurl/' in user_agent, 'User agent did not include libcurl/: %s' % user_agent
pycurl-7.19.3/tests/util.py 0000644 0004705 0004705 00000010240 12262015545 015203 0 ustar pie pie 0000000 0000000 # -*- coding: utf-8 -*-
# vi:ts=4:et
import os, sys, socket
import time as _time
try:
import functools
except ImportError:
import functools_backport as functools
py3 = sys.version_info[0] == 3
# python 2/3 compatibility
if py3:
from io import StringIO, BytesIO
# borrowed from six
def b(s):
'''Byte literal'''
return s.encode("latin-1")
def u(s):
'''Text literal'''
return s
text_type = str
binary_type = bytes
else:
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO
BytesIO = StringIO
# borrowed from six
def b(s):
'''Byte literal'''
return s
# Workaround for standalone backslash
def u(s):
'''Text literal'''
return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape")
text_type = unicode
binary_type = str
def version_less_than_spec(version_tuple, spec_tuple):
# spec_tuple may have 2 elements, expect version_tuple to have 3 elements
assert len(version_tuple) >= len(spec_tuple)
for i in range(len(spec_tuple)):
if version_tuple[i] < spec_tuple[i]:
return True
if version_tuple[i] > spec_tuple[i]:
return False
return False
def pycurl_version_less_than(*spec):
import pycurl
version = [int(part) for part in pycurl.version_info()[1].split('.')]
return version_less_than_spec(version, spec)
def only_python3(fn):
import nose.plugins.skip
@functools.wraps(fn)
def decorated(*args, **kwargs):
if sys.version_info[0] < 3:
raise nose.plugins.skip.SkipTest('python < 3')
return fn(*args, **kwargs)
return decorated
def min_libcurl(major, minor, patch):
import nose.plugins.skip
def decorator(fn):
@functools.wraps(fn)
def decorated(*args, **kwargs):
if pycurl_version_less_than(major, minor, patch):
raise nose.plugins.skip.SkipTest('libcurl < %d.%d.%d' % (major, minor, patch))
return fn(*args, **kwargs)
return decorated
return decorator
def only_ssl(fn):
import nose.plugins.skip
import pycurl
@functools.wraps(fn)
def decorated(*args, **kwargs):
# easier to check that pycurl supports https, although
# theoretically it is not the same test.
# pycurl.version_info()[8] is a tuple of protocols supported by libcurl
if 'https' not in pycurl.version_info()[8]:
raise nose.plugins.skip.SkipTest('libcurl does not support ssl')
return fn(*args, **kwargs)
return decorated
try:
create_connection = socket.create_connection
except AttributeError:
# python 2.5
def create_connection(netloc, timeout=None):
# XXX ipv4 only
s = socket.socket()
if timeout is not None:
s.settimeout(timeout)
s.connect(netloc)
return s
def wait_for_network_service(netloc, check_interval, num_attempts):
ok = False
for i in range(num_attempts):
try:
conn = create_connection(netloc, check_interval)
except socket.error:
#e = sys.exc_info()[1]
_time.sleep(check_interval)
else:
conn.close()
ok = True
break
return ok
#
# prepare sys.path in case we are still in the build directory
# see also: distutils/command/build.py (build_platlib)
#
def get_sys_path(p=None):
if p is None:
p = sys.path
p = p[:]
try:
from distutils.util import get_platform
except ImportError:
return p
p0 = ""
if p:
p0 = p[0]
#
plat = get_platform()
plat_specifier = "%s-%s" % (plat, sys.version[:3])
##print plat, plat_specifier
#
for prefix in (p0, os.curdir, os.pardir,):
if not prefix:
continue
d = os.path.join(prefix, "build")
for subdir in ("lib", "lib." + plat_specifier, "lib." + plat):
dir = os.path.normpath(os.path.join(d, subdir))
if os.path.isdir(dir):
if dir not in p:
p.insert(1, dir)
#
return p
pycurl-7.19.3/tests/version_comparison_test.py 0000644 0004705 0004705 00000001002 12262015545 021200 0 ustar pie pie 0000000 0000000 #! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et
import unittest
from . import util
class VersionComparisonTest(unittest.TestCase):
def test_comparison(self):
assert util.version_less_than_spec((7, 22, 0), (7, 23, 0))
assert util.version_less_than_spec((7, 22, 0), (7, 23))
assert util.version_less_than_spec((7, 22, 0), (7, 22, 1))
assert not util.version_less_than_spec((7, 22, 0), (7, 22, 0))
assert not util.version_less_than_spec((7, 22, 0), (7, 22))
pycurl-7.19.3/tests/version_test.py 0000644 0004705 0004705 00000000471 12262015545 016757 0 ustar pie pie 0000000 0000000 #! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et
import unittest
import pycurl
class VersionTest(unittest.TestCase):
def test_pycurl_presence_and_case(self):
assert pycurl.version.startswith('PycURL/')
def test_libcurl_presence(self):
assert 'libcurl/' in pycurl.version
pycurl-7.19.3/tests/vsftpd.conf 0000644 0004705 0004705 00000000446 12260666420 016042 0 ustar pie pie 0000000 0000000 anon_world_readable_only=yes
anonymous_enable=yes
background=no
# currently we only list files
download_enable=no
listen=yes
run_as_launching_user=yes
write_enable=yes
anon_upload_enable=yes
anon_other_write_enable=yes
listen_port=8321
# should be supplied on command line
anon_root=/var/empty
pycurl-7.19.3/tests/write_abort_test.py 0000644 0004705 0004705 00000002232 12262015545 017610 0 ustar pie pie 0000000 0000000 #! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et
import os.path
import pycurl
import sys
import unittest
class WriteAbortTest(unittest.TestCase):
def setUp(self):
self.curl = pycurl.Curl()
def tearDown(self):
self.curl.close()
def test_write_abort(self):
def write_cb(_):
# this should cause pycurl.WRITEFUNCTION (without any range errors)
return -1
try:
# set when running full test suite if any earlier tests
# failed in Python code called from C
del sys.last_value
except AttributeError:
pass
# download the script itself through the file:// protocol into write_cb
self.curl.setopt(pycurl.URL, 'file://' + os.path.abspath(sys.argv[0]))
self.curl.setopt(pycurl.WRITEFUNCTION, write_cb)
try:
self.curl.perform()
except pycurl.error:
err, msg = sys.exc_info()[1].args
# we expect pycurl.E_WRITE_ERROR as the response
assert pycurl.E_WRITE_ERROR == err
# no additional errors should be reported
assert not hasattr(sys, 'last_value')
pycurl-7.19.3/tests/write_cb_bogus_test.py 0000644 0004705 0004705 00000002603 12262015545 020266 0 ustar pie pie 0000000 0000000 #! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et
import os.path
import pycurl
import sys
import unittest
class WriteAbortTest(unittest.TestCase):
def setUp(self):
self.curl = pycurl.Curl()
def tearDown(self):
self.curl.close()
def write_cb_returning_string(self, data):
return 'foo'
def write_cb_returning_float(self, data):
return 0.5
def test_write_cb_returning_string(self):
self.check(self.write_cb_returning_string)
def test_write_cb_returning_float(self):
self.check(self.write_cb_returning_float)
def check(self, write_cb):
# download the script itself through the file:// protocol into write_cb
c = pycurl.Curl()
self.curl.setopt(pycurl.URL, 'file://' + os.path.abspath(sys.argv[0]))
self.curl.setopt(pycurl.WRITEFUNCTION, write_cb)
try:
self.curl.perform()
self.fail('Should not get here')
except pycurl.error:
err, msg = sys.exc_info()[1].args
# we expect pycurl.E_WRITE_ERROR as the response
assert pycurl.E_WRITE_ERROR == err
# actual error
assert hasattr(sys, 'last_type')
self.assertEqual(pycurl.error, sys.last_type)
assert hasattr(sys, 'last_value')
self.assertEqual('write callback must return int or None', str(sys.last_value))
pycurl-7.19.3/tests/write_to_file_test.py 0000644 0004705 0004705 00000005353 12262015545 020131 0 ustar pie pie 0000000 0000000 #! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et
import unittest
import pycurl
import tempfile
import shutil
import os.path
from . import appmanager
from . import util
setup_module, teardown_module = appmanager.setup(('app', 8380))
class Acceptor(object):
def __init__(self):
self.buffer = ''
def write(self, chunk):
self.buffer += chunk.decode()
class WriteToFileTest(unittest.TestCase):
def setUp(self):
self.curl = pycurl.Curl()
def tearDown(self):
self.curl.close()
def test_write_to_tempfile_via_function(self):
self.curl.setopt(pycurl.URL, 'http://localhost:8380/success')
f = tempfile.NamedTemporaryFile()
try:
self.curl.setopt(pycurl.WRITEFUNCTION, f.write)
self.curl.perform()
f.seek(0)
body = f.read()
finally:
f.close()
self.assertEqual('success', body.decode())
@util.only_python3
def test_write_to_tempfile_via_object(self):
self.curl.setopt(pycurl.URL, 'http://localhost:8380/success')
f = tempfile.NamedTemporaryFile()
try:
self.curl.setopt(pycurl.WRITEDATA, f)
self.curl.perform()
f.seek(0)
body = f.read()
finally:
f.close()
self.assertEqual('success', body.decode())
def test_write_to_file_via_function(self):
self.curl.setopt(pycurl.URL, 'http://localhost:8380/success')
dir = tempfile.mkdtemp()
try:
path = os.path.join(dir, 'pycurltest')
f = open(path, 'wb+')
try:
self.curl.setopt(pycurl.WRITEFUNCTION, f.write)
self.curl.perform()
f.seek(0)
body = f.read()
finally:
f.close()
finally:
shutil.rmtree(dir)
self.assertEqual('success', body.decode())
def test_write_to_file_via_object(self):
self.curl.setopt(pycurl.URL, 'http://localhost:8380/success')
dir = tempfile.mkdtemp()
try:
path = os.path.join(dir, 'pycurltest')
f = open(path, 'wb+')
try:
self.curl.setopt(pycurl.WRITEDATA, f)
self.curl.perform()
f.seek(0)
body = f.read()
finally:
f.close()
finally:
shutil.rmtree(dir)
self.assertEqual('success', body.decode())
def test_write_to_file_like(self):
self.curl.setopt(pycurl.URL, 'http://localhost:8380/success')
acceptor = Acceptor()
self.curl.setopt(pycurl.WRITEDATA, acceptor)
self.curl.perform()
self.assertEqual('success', acceptor.buffer)
pycurl-7.19.3/tests/write_to_stringio_test.py 0000644 0004705 0004705 00000002275 12262015545 021050 0 ustar pie pie 0000000 0000000 #! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et
import pycurl
import unittest
import sys
from . import appmanager
from . import util
setup_module, teardown_module = appmanager.setup(('app', 8380))
class WriteToStringioTest(unittest.TestCase):
def setUp(self):
self.curl = pycurl.Curl()
def tearDown(self):
self.curl.close()
def test_write_to_bytesio(self):
self.curl.setopt(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())
@util.only_python3
def test_write_to_stringio(self):
self.curl.setopt(pycurl.URL, 'http://localhost:8380/success')
# stringio in python 3
sio = util.StringIO()
self.curl.setopt(pycurl.WRITEFUNCTION, sio.write)
try:
self.curl.perform()
self.fail('Should have received a write error')
except pycurl.error:
err, msg = sys.exc_info()[1].args
# we expect pycurl.E_WRITE_ERROR as the response
assert pycurl.E_WRITE_ERROR == err
pycurl-7.19.3/COPYING-LGPL 0000644 0004705 0004705 00000063636 12256232314 014320 0 ustar pie pie 0000000 0000000 GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
Copyright (C)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
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
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!
pycurl-7.19.3/COPYING-MIT 0000644 0004705 0004705 00000002245 12262777656 014224 0 ustar pie pie 0000000 0000000 COPYRIGHT AND PERMISSION NOTICE
Copyright (C) 2001-2008 by Kjetil Jacobsen
Copyright (C) 2001-2008 by Markus F.X.J. Oberhumer
Copyright (C) 2013-2014 by Oleg Pudeyev
All rights reserved.
Permission to use, copy, modify, and distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright
notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of a copyright holder shall not
be used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization of the copyright holder.
pycurl-7.19.3/ChangeLog 0000644 0004705 0004705 00000105365 12263706003 014276 0 ustar pie pie 0000000 0000000 Version 7.19.3 [requires libcurl-7.19.0 or better] - 2014-01-09
---------------------------------------------------------------
* Added CURLOPT_NOPROXY.
* Added CURLINFO_LOCAL_PORT, CURLINFO_PRIMARY_PORT and
CURLINFO_LOCAL_IP (patch by Adam Jacob Muller).
* When running on Python 2.x, for compatibility with Python 3.x,
Unicode strings containing ASCII code points only are now accepted
in setopt() calls.
* PycURL now requires that compile time SSL backend used by libcurl
is the same as the one used at runtime. setup.py supports
--with-ssl, --with-gnutls and --with-nss options like libcurl does,
to specify which backend libcurl uses. On some systems PycURL can
automatically figure out libcurl's backend.
If the backend is not one for which PycURL provides crypto locks
(i.e., any of the other backends supported by libcurl),
no runtime SSL backend check is performed.
* Default PycURL user agent string is now built at runtime, and will
include the user agent string of libcurl loaded at runtime rather
than the one present at compile time.
* PycURL will now use WSAduplicateSocket rather than dup on Windows
to duplicate sockets obtained from OPENSOCKETFUNCTION.
Using dup may have caused crashes, OPENSOCKETFUNCTION should
now be usable on Windows.
* A new script, winbuild.py, was added to build PycURL on Windows
against Python 2.6, 2.7, 3.2 and 3.3.
* Added CURL_LOCK_DATA_SSL_SESSION (patch by Tom Pierce).
* Added E_OPERATION_TIMEDOUT (patch by Romuald Brunet).
* setup.py now handles --help argument and will print PycURL-specific
configuration options in addition to distutils help.
* Windows build configuration has been redone:
PYCURL_USE_LIBCURL_DLL #define is gone, use --use-libcurl-dll
argument to setup.py to build against a libcurl DLL.
CURL_STATICLIB is now #defined only when --use-libcurl-dll is not
given to setup.py, and PycURL is built against libcurl statically.
--libcurl-lib-name option can be used to override libcurl import
library name.
* Added CURLAUTH_DIGEST_IE as pycurl.HTTPAUTH_DIGEST_IE.
* Added CURLOPT_POSTREDIR option and CURL_REDIR_POST_301,
CURL_REDIR_POST_302, CURL_REDIR_POST_303 and CURL_REDIR_POST_ALL
constants. CURL_REDIR_POST_303 requires libcurl 7.26.0 or higher,
all others require libcurl 7.19.1 or higher.
* PycURL now supports Python 3.1 through 3.3. Python 3.0 might
work but it appears to ship with broken distutils, making virtualenv
not function on it.
* PycURL multi objects now have the multi constants defined on them.
Previously the constants were only available on pycurl module.
The new behavior matches that of curl and share objects.
* PycURL share objects can now be closed via the close() method.
* PycURL will no longer call `curl-config --static-libs` if
`curl-config --libs` succeeds and returns output.
Systems on which neither `curl-config --libs` nor
`curl-config --static-libs` do the right thing should provide
a `curl-config` wrapper that is sane.
* Added CURLFORM_BUFFER and CURLFORM_BUFFERPTR.
* pycurl.version and user agent string now include both
PycURL version and libcurl version as separate items.
* Added CURLOPT_DNS_SERVERS.
* PycURL can now be dynamically linked against libcurl on Windows
if PYCURL_USE_LIBCURL_DLL is #defined during compilation.
* Breaking change: opensocket callback now takes an additional
(address, port) tuple argument. Existing callbacks will need to
be modified to accept this new argument.
https://github.com/pycurl/pycurl/pull/18
Version 7.19.0.3 [requires libcurl-7.19.0 or better] - 2013-12-24
-----------------------------------------------------------------
* Re-release of 7.19.0.2 with minor changes to build Windows packages
due to botched 7.19.0.2 files on PyPi.
http://curl.haxx.se/mail/curlpython-2013-12/0021.html
Version 7.19.0.2 [requires libcurl-7.19.0 or better] - 2013-10-08
-----------------------------------------------------------------
* Fixed a bug in a commit made in 2008 but not released until 7.19.0.1
which caused CURLOPT_POSTFIELDS to not correctly increment reference
count of the object being given as its argument, despite libcurl not
copying the data provided by said object.
* Added support for libcurl pause/unpause functionality,
via curl_easy_pause call and returning READFUNC_PAUSE from
read callback function.
Version 7.19.0.1 [requires libcurl-7.19.0 or better] - 2013-09-23
-----------------------------------------------------------------
* Test matrix tool added to test against all supported Python and
libcurl versions.
* Python 2.4 is now the minimum required version.
* Source code, bugs and patches are now kept on GitHub.
* Added CURLINFO_CERTINFO and CURLOPT_CERTINFO.
* Added CURLOPT_RESOLVE.
* PycURL can now be used with Python binaries without thread
support.
* gcrypt is no longer initialized when a newer version of gnutls
is used.
* Marked NSS as supported.
* Fixed relative URL request logic.
* Fixed a memory leak in util_curl_init.
* Added CURLOPT_USERNAME and CURLOPT_PASSWORD.
* Fixed handling of big timeout values.
* Added GLOBAL_ACK_EINTR.
* setopt(..., None) can be used as unsetopt().
* CURLOPT_RANGE can now be unset.
* Write callback can return -1 to signal user abort.
* Reorganized tests into an automated test suite.
* Added CURLOPT_SEEKFUNCTION and CURLOPT_SEEKDATA.
* Cleaned up website.
* Fix pycurl.reset() (patch by ).
* Fix install routine in setup.py where
certain platforms (Solaris, Mac OSX, etc)
would search for a static copy of libcurl (dbp).
* Fixed build on OpenSolaris 0906 and other platforms on which
curl-config does not have a --static-libs option.
* No longer keep string options copies in the
Curl Python objects, since string options are
now managed by libcurl.
Version 7.19.0 [requires libcurl-7.19.0 or better]
--------------------------------------------------
* Added CURLFILE, ADDRESS_SCOPE and ISSUERCERT options,
as well as the APPCONNECT_TIME info.
* Added PRIMARY_IP info (patch by
Yuhui H ).
* Added support for curl_easy_reset through a
new 'reset' method on curl objects
(patch by Nick Pilon ).
* Added support for OPENSOCKET callbacks.
See 'tests/test_opensocket.py' for example
usage (patch by Thomas Hunger ).
Version 7.18.2
--------------
* Added REDIRECT_URL info and M_MAXCONNECTS option
(patch by Yuhui H ).
* Added socket_action() method to CurlMulti objects.
See 'tests/test_multi_socket_select.py' for example
usage (patch by Yuhui H ).
* Added AUTOREFERER option.
* Allow resetting some list operations (HTTPHEADER,
QUOTE, POSTQUOTE, PREQUOTE) by passing an empty
list to setopt (patch by Jim Patterson).
Version 7.18.1
--------------
* Added POST301, SSH_HOST_PUBLIC_KEY_MD5,
COPYPOSTFIELDS and PROXY_TRANSFER_MODE options.
* Check for static libs in setup.py to better detect
whether libcurl was linked with OpenSSL or GNUTLS.
* PycURL is now dual licensed under the LGPL and
a license similar to the cURL license (an MIT/X
derivative).
Version 7.16.4
--------------
* Allow any callable object as the callback function.
This change comes handy when you would like to use objects
which are callable but are not functions or methods, for
example those objects created by the functions in the functools
module (patch by Daniel Pena Arteaga ).
* Added NEW_DIRECTORY_PERMS and NEW_FILE_PERMS options.
Version 7.16.2.1
----------------
* Added IOCMD_NOP and IOCMD_RESTARTREAD for ioctl callback
handling (patch by Mark Eichin).
* Use Py_ssize_t where appropriate for Python 2.5 and 64-bit
compatibility. This fixes the problem reported by Aaron
Hill, where the exception "pycurl.error: (2, '')" is thrown
when calling setopt(pycurl.POSTFIELDS,...) on 64-bit
platforms.
Version 7.16.2
--------------
* Added options HTTP_TRANSFER_DECODING, HTTP_CONTENT_DECODING,
TIMEOUT_MS, CONNECTTIMEOUT_MS from libcurl 7.16.2.
* Right-strip URLs read from files in the test scripts
to avoid sending requests with '\n' at the end.
Version 7.16.1
--------------
* Added constants for all libcurl (error) return codes. They
are named the same as the macro constants in curl.h but prefixed
with E_ instead of CURLE. Return codes for the multi API are
prefixed with M_ instead of CURLM.
* Added CURLOPT_FTP_SSL_CCC, CURLOPT_SSH_PUBLIC_KEYFILE,
CURLOPT_SSH_PRIVATE_KEYFILE, CURLOPT_SSH_AUTH_TYPES.
* Removed CLOSEPOLICY and friends since this option is now
deprecated in libcurl.
* Set the _use_datetime attribute on the CURLTransport class
to unbreak xmlrpc_curl.py on Python 2.5.
Version 7.16.0 [no public release]
--------------
* Added CURLOPT_SSL_SESSIONID_CACHE.
* Removed SOURCE_* options since they are no longer
supported by libcurl.
Version 7.15.5.1
----------------
* Added test for basic ftp usage (tests/test_ftp.py).
* Fix broken ssl mutex lock function when using
GNU TLS (Debian bug #380156, fix by Bastian Kleineidam)
Version 7.15.5
--------------
* Added CURLOPT_FTP_ALTERNATIVE_TO_USER,
CURLOPT_MAX_SEND_SPEED_LARGE,
and CURLOPT_MAX_RECV_SPEED_LARGE.
Version 7.15.4.2
----------------
* Use SSL locking callbacks, fixes random
crashes for multithreaded SSL connections
(patch by Jayne ).
Version 7.15.4.1
----------------
* Fixed compilation problem with C compilers
not allowing declarations in the middle of
code blocks (patch by
K.S.Sreeram ).
* Fixed bug in curl_multi_fdset wrapping,
max_fd < 0 is not an error (patch by
K.S.Sreeram ).
Version 7.15.4
--------------
* Added support for libcurl shares, patch from
Victor Lascurain . See the
file tests/test_share.py for example usage.
* Added support for CURLINFO_FTP_ENTRY_PATH.
Version 7.15.2
--------------
* Added CURLOPT_CONNECT_ONLY, CURLINFO_LASTSOCKET,
CURLOPT_LOCALPORT and CURLOPT_LOCALPORTRANGE.
Version 7.15.1
--------------
2006-01-31 Kjetil Jacobsen
* Fixed memory leak for getinfo calls that return a
list as result. Patch by Paul Pacheco.
Version 7.15.0
--------------
2005-10-18 Kjetil Jacobsen
* Added CURLOPT_FTP_SKIP_PASV_IP.
Version 7.14.1
--------------
2005-09-05 Kjetil Jacobsen
* Added CURLOPT_IGNORE_CONTENT_LENGTH, CURLOPT_COOKIELIST as
COOKIELIST and CURLINFO_COOKIELIST as INFO_COOKIELIST.
Version 7.14.0
--------------
2005-05-18 Kjetil Jacobsen
* Added missing information returned from the info() method
in the high-level interface.
* Added the FORM_FILENAME option to the CURLFORM API
with HTTPPOST.
Version 7.13.2
--------------
2005-03-30 Kjetil Jacobsen
* Unbreak tests/test_gtk.py and require pygtk >= 2.0.
2005-03-15 Kjetil Jacobsen
* Cleaned up several of the examples.
2005-03-11 Kjetil Jacobsen
* WARNING: multi.select() now requires the previously optional
timeout parameter. Updated the tests and examples to reflect
this change. If the timeout is not set, select could block
infinitely and cause problems for the internal timeout handling
in the multi stack. The problem was identified by
.
Version 7.13.1
--------------
2005-03-04 Kjetil Jacobsen
* Use METH_NOARGS where appropriate.
2005-03-03 Kjetil Jacobsen
* Added support for CURLFORM API with HTTPPOST: Supports a
a tuple with pairs of options and values instead of just
supporting string contents. See tests/test_post2.py
for example usage. Options are FORM_CONTENTS, FORM_FILE and
FORM_CONTENTTYPE, corresponding to the CURLFORM_* options,
and values are strings.
2005-02-13 Markus F.X.J. Oberhumer
* Read callbacks (pycurl.READFUNCTION) can now return
pycurl.READFUNC_ABORT to immediately abort the current transfer.
* The INFILESIZE, MAXFILESIZE, POSTFIELDSIZE and RESUME_FROM
options now automatically use the largefile version to handle
files > 2GB.
* Added missing pycurl.PORT constant.
Version 7.13.0
--------------
2005-02-10 Kjetil Jacobsen
* Added file_upload.py to examples, shows how to upload
a file.
* Added CURLOPT_IOCTLFUNCTION/DATA.
* Added options from libcurl 7.13.0: FTP_ACCOUNT, SOURCE_URL,
SOURCE_QUOTE.
* Obsoleted options: SOURCE_HOST, SOURCE_PATH, SOURCE_PORT,
PASV_HOST.
Version 7.12.3
--------------
2004-12-22 Markus F.X.J. Oberhumer
* Added CURLINFO_NUM_CONNECTS and CURLINFO_SSL_ENGINES.
* Added some other missing constants.
* Updated pycurl.version_info() to return a 12-tuple
instead of a 9-tuple.
Version 7.12.2
--------------
2004-10-15 Kjetil Jacobsen
* Added CURLOPT_FTPSSLAUTH (and CURLFTPAUTH_*).
* Added CURLINFO_OS_ERRNO.
2004-08-17 Kjetil Jacobsen
* Use LONG_LONG instead of PY_LONG_LONG to make pycurl compile
on Python versions < 2.3 (fix from Domenico Andreoli
).
Version 7.12.1
--------------
2004-08-02 Kjetil Jacobsen
* Added INFOTYPE_SSL_DATA_IN/OUT.
2004-07-16 Markus F.X.J. Oberhumer
* WARNING: removed deprecated PROXY_, TIMECOND_ and non-prefixed
INFOTYPE constant names. See ChangeLog entry 2003-06-10.
2004-06-21 Kjetil Jacobsen
* Added test program for HTTP post using the read callback (see
tests/test_post3.py for details).
* Use the new CURL_READFUNC_ABORT return code where appropriate
to avoid hanging in perform() when read callbacks are used.
* Added support for libcurl 7.12.1 CURLOPT features:
SOURCE_HOST, SOURCE_USERPWD, SOURCE_PATH, SOURCE_PORT,
PASV_HOST, SOURCE_PREQUOTE, SOURCE_POSTQUOTE.
2004-06-08 Markus F.X.J. Oberhumer
* Setting CURLOPT_POSTFIELDS now allows binary data and
automatically sets CURLOPT_POSTFIELDSIZE for you. If you really
want a different size you have to manually set POSTFIELDSIZE
after setting POSTFIELDS.
(Based on a patch by Martin Muenstermann).
2004-06-05 Markus F.X.J. Oberhumer
* Added stricter checks within the callback handlers.
* Unify the behaviour of int and long parameters where appropriate.
Version 7.12
------------
2004-05-18 Kjetil Jacobsen
* WARNING: To simplify code maintenance pycurl now requires
libcurl 7.11.2 and Python 2.2 or newer to work.
* GC support is now always enabled.
Version 7.11.3
--------------
2004-04-30 Kjetil Jacobsen
* Do not use the deprecated curl_formparse function.
API CHANGE: HTTPPOST now takes a list of tuples where each
tuple contains a form name and a form value, both strings
(see test/test_post2.py for example usage).
* Found a possible reference count bug in the multithreading
code which may have contributed to the long-standing GC
segfault which has haunted pycurl. Fingers crossed.
Version 7.11.2
--------------
2004-04-21 Kjetil Jacobsen
* Added support for libcurl 7.11.2 CURLOPT features:
CURLOPT_TCP_NODELAY.
2004-03-25 Kjetil Jacobsen
* Store Python longs in off_t with PyLong_AsLongLong instead
of PyLong_AsLong. Should make the options which deal
with large files behave a little better. Note that this
requires the long long support in Python 2.2 or newer to
work properly.
Version 7.11.1
--------------
2004-03-16 Kjetil Jacobsen
* WARNING: Removed support for the PASSWDFUNCTION callback, which
is no longer supported by libcurl.
2004-03-15 Kjetil Jacobsen
* Added support for libcurl 7.11.1 CURLOPT features:
CURLOPT_POSTFIELDSIZE_LARGE.
Version 7.11.0
--------------
2004-02-11 Kjetil Jacobsen
* Added support for libcurl 7.11.0 CURLOPT features:
INFILESIZE_LARGE, RESUME_FROM_LARGE, MAXFILESIZE_LARGE
and FTP_SSL.
* Circular garbage collection support can now be enabled or
disabled by passing the '--use-gc=[1|0]' parameter to setup.py
when building pycurl.
* HTTP_VERSION options are known as CURL_HTTP_VERSION_NONE,
CURL_HTTP_VERSION_1_0, CURL_HTTP_VERSION_1_1 and
CURL_HTTP_VERSION_LAST.
2003-11-16 Markus F.X.J. Oberhumer
* Added support for these new libcurl 7.11.0 features:
CURLOPT_NETRC_FILE.
Version 7.10.8
--------------
2003-11-04 Markus F.X.J. Oberhumer
* Added support for these new libcurl 7.10.8 features:
CURLOPT_FTP_RESPONSE_TIMEOUT, CURLOPT_IPRESOLVE,
CURLOPT_MAXFILESIZE,
CURLINFO_HTTPAUTH_AVAIL, CURLINFO_PROXYAUTH_AVAIL,
CURL_IPRESOLVE_* constants.
* Added support for these new libcurl 7.10.7 features:
CURLOPT_FTP_CREATE_MISSING_DIRS, CURLOPT_PROXYAUTH,
CURLINFO_HTTP_CONNECTCODE.
2003-10-28 Kjetil Jacobsen
* Added missing CURLOPT_ENCODING option (patch by Martijn
Boerwinkel )
Version 7.10.6
--------------
2003-07-29 Markus F.X.J. Oberhumer
* Started working on support for CURLOPT_SSL_CTX_FUNCTION and
CURLOPT_SSL_CTX_DATA (libcurl-7.10.6) - not yet finished.
2003-06-10 Markus F.X.J. Oberhumer
* Added support for CURLOPT_HTTPAUTH (libcurl-7.10.6), including
the new HTTPAUTH_BASIC, HTTPAUTH_DIGEST, HTTPAUTH_GSSNEGOTIATE
and HTTPAUTH_NTML constants.
* Some constants were renamed for consistency:
All curl_infotype constants are now prefixed with "INFOTYPE_",
all curl_proxytype constants are prefixed with "PROXYTYPE_" instead
of "PROXY_", and all curl_TimeCond constants are now prefixed
with "TIMECONDITION_" instead of "TIMECOND_".
(The old names are still available but will get removed
in a future release.)
* WARNING: Removed the deprecated pycurl.init() and pycurl.multi_init()
names - use pycurl.Curl() and pycurl.CurlMulti() instead.
* WARNING: Removed the deprecated Curl.cleanup() and
CurlMulti.cleanup() methods - use Curl.close() and
CurlMulti.close() instead.
Version 7.10.5
--------------
2003-05-15 Markus F.X.J. Oberhumer
* Added support for CURLOPT_FTP_USE_EPRT (libcurl-7.10.5).
* Documentation updates.
2003-05-07 Eric S. Raymond
* Lifted all HTML docs to clean XHTML, verified by tidy.
2003-05-02 Markus F.X.J. Oberhumer
* Fixed some `int' vs. `long' mismatches that affected 64-bit systems.
* Fixed wrong pycurl.CAPATH constant.
2003-05-01 Markus F.X.J. Oberhumer
* Added new method Curl.errstr() which returns the internal
libcurl error buffer string of the handle.
Version 7.10.4.2
----------------
2003-04-15 Markus F.X.J. Oberhumer
* Allow compilation against the libcurl-7.10.3 release - some
recent Linux distributions (e.g. Mandrake 9.1) ship with 7.10.3,
and apart from the new CURLOPT_UNRESTRICTED_AUTH option there is
no need that we require libcurl-7.10.4.
Version 7.10.4
--------------
2003-04-01 Kjetil Jacobsen
* Markus added CURLOPT_UNRESTRICTED_AUTH (libcurl-7.10.4).
2003-02-25 Kjetil Jacobsen
* Fixed some broken test code and removed the fileupload test
since it didn't work properly.
2003-01-28 Kjetil Jacobsen
* Some documentation updates by Markus and me.
2003-01-22 Kjetil Jacobsen
* API CHANGE: the CurlMulti.info_read() method now returns
a separate array with handles that failed. Each entry in this array
is a tuple with (curl object, error number, error message).
This addition makes it simpler to do error checking of individual
curl objects when using the multi interface.
Version 7.10.3
--------------
2003-01-13 Kjetil Jacobsen
* PycURL memory usage has been reduced.
2003-01-10 Kjetil Jacobsen
* Added 'examples/retriever-multi.py' which shows how to retrieve
a set of URLs concurrently using the multi interface.
2003-01-09 Kjetil Jacobsen
* Added support for CURLOPT_HTTP200ALIASES.
2002-11-22 Kjetil Jacobsen
* Updated pycurl documentation in the 'doc' directory.
2002-11-21 Kjetil Jacobsen
* Updated and improved 'examples/curl.py'.
* Added 'tests/test_multi6.py' which shows how to use the
info_read method with CurlMulti.
2002-11-19 Kjetil Jacobsen
* Added new method CurlMulti.info_read().
Version 7.10.2
--------------
2002-11-14 Kjetil Jacobsen
* Free options set with setopt after cleanup is called, as cleanup
assumes that options are still valid when invoked. This fixes the
bug with COOKIEJAR reported by Bastiaan Naber
.
2002-11-06 Markus F.X.J. Oberhumer
* Install documentation under /usr/share/doc instead of /usr/doc.
Also, start shipping the (unfinished) HTML docs and some
basic test scripts.
2002-10-30 Markus F.X.J. Oberhumer
* API CHANGE: For integral values, Curl.getinfo() now returns a
Python-int instead of a Python-long.
Version 7.10.1
--------------
2002-10-03 Markus F.X.J. Oberhumer
* Added new module-level function version_info() from
libcurl-7.10.
Version 7.10
------------
2002-09-13 Kjetil Jacobsen
* Added commandline options to setup.py for specifying the path to
'curl-config' (non-windows) and the curl installation directory
(windows). See the 'INSTALL' file for details.
* Added CURLOPT_ENCODING, CURLOPT_NOSIGNAL and CURLOPT_BUFFERSIZE
from libcurl-7.10 (by Markus Oberhumer).
Version 7.9.8.4
---------------
2002-08-28 Kjetil Jacobsen
* Added a simple web-browser example based on gtkhtml and pycurl.
See the file 'examples/gtkhtml_demo.py' for details. The example
requires a working installation of gnome-python with gtkhtml
bindings enabled (pass --with-gtkhtml to gnome-python configure).
2002-08-14 Kjetil Jacobsen
* Added new method 'select' on CurlMulti objects. Example usage
in 'tests/test_multi5.py'. This method is just an optimization of
the combined use of fdset and select.
2002-08-12 Kjetil Jacobsen
* Added support for curl_multi_fdset. See the file
'tests/test_multi4.py' for example usage. Contributed by Conrad
Steenberg .
* perform() on multi objects now returns a tuple (result, number
of handles) like the libcurl interface does.
2002-08-08 Kjetil Jacobsen
* Added the 'sfquery' script which retrieves a SourceForge XML
export object for a given project. See the file 'examples/sfquery.py'
for details and usage. 'sfquery' was contributed by Eric
S. Raymond .
2002-07-20 Markus F.X.J. Oberhumer
* API enhancements: added Curl() and CurlMulti() as aliases for
init() and multi_init(), and added close() methods as aliases
for the cleanup() methods. The new names much better match
the actual intended use of the objects, and they also nicely
correspond to Python's file object.
* Also, all constants for Curl.setopt() and Curl.getinfo() are now
visible from within Curl objects.
All changes are fully backward-compatible.
Version 7.9.8.3
---------------
2002-07-16 Markus F.X.J. Oberhumer
* Under Python 2.2 or better, Curl and CurlMulti objects now
automatically participate in cyclic garbarge collection
(using the gc module).
Version 7.9.8.2
---------------
2002-07-05 Markus F.X.J. Oberhumer
* Curl and CurlMulti objects now support standard Python attributes.
See tests/test_multi2.py for an example.
2002-07-02 Kjetil Jacobsen
* Added support for the multi-interface.
Version 7.9.8.1
---------------
2002-06-25 Markus F.X.J. Oberhumer
* Fixed a couple of `int' vs. `size_t' mismatches in callbacks
and Py_BuildValue() calls.
2002-06-25 Kjetil Jacobsen
* Use 'double' type instead of 'size_t' for progress callbacks
(by Conrad Steenberg ). Also cleaned up
some other type mismatches in the callback interfaces.
2002-06-24 Kjetil Jacobsen
* Added example code on how to upload a file using HTTPPOST in
pycurl (code by Amit Mongia ). See the
file 'test_fileupload.py' for details.
Version 7.9.8
-------------
2002-06-24 Kjetil Jacobsen
* Resolved some build problems on Windows (by Markus Oberhumer).
2002-06-19 Kjetil Jacobsen
* Added CURLOPT_CAPATH.
* Added option constants for CURLOPT_NETRC: CURL_NETRC_OPTIONAL,
CURL_NETRC_IGNORED and CURL_NETRC_REQUIRED.
* Added option constants for CURLOPT_TIMECONDITION:
TIMECOND_IFMODSINCE and TIMECOND_IFUNMODSINCE.
* Added an simple example crawler, which downloads documents
listed in a file with a configurable number of worker threads.
See the file 'crawler.py' in the 'tests' directory for details.
* Removed the redundant 'test_xmlrpc2.py' test script.
* Disallow recursive callback invocations (by Markus Oberhumer).
2002-06-18 Kjetil Jacobsen
* Made some changes to setup.py which should fix the build
problems on RedHat 7.3 (suggested by Benji ).
* Use CURLOPT_READDATA instead of CURLOPT_INFILE, and
CURLOPT_WRITEDATA instead of CURLOPT_FILE. Also fixed some
reference counting bugs with file objects.
* CURLOPT_FILETIME and CURLINFO_FILETIME had a namespace clash
which caused them not to work. Use OPT_FILETIME for setopt() and
INFO_FILETIME for getinfo(). See example usage in
'test_getinfo.py' for details.
Version 7.9.7
-------------
2002-05-20 Kjetil Jacobsen
* New versioning scheme. Pycurl now has the same version number
as the libcurl version it was built with. The pycurl version
number thus indicates which version of libcurl is required to run.
2002-05-17 Kjetil Jacobsen
* Added CURLINFO_REDIRECT_TIME and CURLINFO_REDIRECT_COUNT.
2002-04-27 Kjetil Jacobsen
* Fixed potential memory leak and thread race (by Markus
Oberhumer).
Version 0.4.9
-------------
2002-04-15 Kjetil Jacobsen
* Added CURLOPT_DEBUGFUNCTION to allow debug callbacks to be
specified (see the file 'test_debug.py' for details on how to use
debug callbacks).
* Added CURLOPT_DNS_USE_GLOBAL_CACHE and
CURLOPT_DNS_CACHE_TIMEOUT.
* Fixed a segfault when finalizing curl objects in Python 1.5.2.
* Now requires libcurl 7.9.6 or greater.
2002-04-12 Kjetil Jacobsen
* Added 'test_post2.py' file which is another example on how to
issue POST requests.
2002-04-11 Markus F.X.J. Oberhumer
* Added the 'test_post.py' file which demonstrates the use of
POST requests.
Version 0.4.8
-------------
2002-03-07 Kjetil Jacobsen
* Added CURLOPT_PREQUOTE.
* Now requires libcurl 7.9.5 or greater.
* Other minor code cleanups and bugfixes.
2002-03-05 Kjetil Jacobsen
* Do not allow WRITEFUNCTION and WRITEHEADER on the same handle.
Version 0.4.7
-------------
2002-02-27 Kjetil Jacobsen
* Abort callback if the thread state of the calling thread cannot
be determined.
* Check that the installed version of libcurl matches the
requirements of pycurl.
2002-02-26 Kjetil Jacobsen
* Clarence Garnder found a bug where string
arguments to setopt sometimes were prematurely deallocated, this
should now be fixed.
2002-02-21 Kjetil Jacobsen
* Added the 'xmlrpc_curl.py' file which implements a transport
for xmlrpclib (xmlrpclib is part of Python 2.2).
* Added CURLINFO_CONTENT_TYPE.
* Added CURLOPT_SSLCERTTYPE, CURLOPT_SSLKEY, CURLOPT_SSLKEYTYPE,
CURLOPT_SSLKEYPASSWD, CURLOPT_SSLENGINE and
CURLOPT_SSLENGINE_DEFAULT.
* When thrown, the pycurl.error exception is now a tuple consisting
of the curl error code and the error message.
* Now requires libcurl 7.9.4 or greater.
2002-02-19 Kjetil Jacobsen
* Fixed docstring for getopt() function.
2001-12-18 Kjetil Jacobsen
* Updated the INSTALL information for Win32.
2001-12-12 Kjetil Jacobsen
* Added missing link flag to make pycurl build on MacOS X (by Matt
King ).
2001-12-06 Kjetil Jacobsen
* Added CURLINFO_STARTTRANSFER_TIME and CURLOPT_FTP_USE_EPSV from
libcurl 7.9.2.
2001-12-01 Markus F.X.J. Oberhumer
* Added the 'test_stringio.py' file which demonstrates the use of
StringIO objects as callback.
2001-12-01 Markus F.X.J. Oberhumer
* setup.py: Do not remove entries from a list while iterating
over it.
2001-11-29 Kjetil Jacobsen
* Added code in setup.py to install on Windows. Requires some
manual configuration (by Tino Lange ).
2001-11-27 Kjetil Jacobsen
* Improved detection of where libcurl is installed in setup.py.
Should make it easier to install pycurl when libcurl is not
located in regular lib/include paths.
2001-11-05 Kjetil Jacobsen
* Some of the newer options to setopt were missing, this should
now be fixed.
2001-11-04 Kjetil Jacobsen
* Exception handling has been improved and should no longer throw
spurious exceptions (by Markus F.X.J. Oberhumer
).
2001-10-15 Kjetil Jacobsen
* Refactored the test_gtk.py script to avoid global variables.
2001-10-12 Kjetil Jacobsen
* Added module docstrings, terse perhaps, but better than nothing.
* Added the 'basicfirst.py' file which is a Python version of the
corresponding Perl script by Daniel.
* PycURL now works properly under Python 1.5 and 1.6 (by Markus
F.X.J. Oberhumer ).
* Allow C-functions and Python methods as callbacks (by Markus
F.X.J. Oberhumer ).
* Allow None as success result of write, header and progress
callback invocations (by Markus F.X.J. Oberhumer
).
* Added the 'basicfirst2.py' file which demonstrates the use of a
class method as callback instead of just a function.
2001-08-21 Kjetil Jacobsen
* Cleaned up the script with GNOME/PycURL integration.
2001-08-20 Kjetil Jacobsen
* Added another test script for shipping XML-RPC requests which
uses py-xmlrpc to encode the arguments (tests/test_xmlrpc2.py).
2001-08-20 Kjetil Jacobsen
* Added test script for using PycURL and GNOME (tests/test_gtk.py).
2001-08-20 Kjetil Jacobsen
* Added test script for using XML-RPC (tests/test_xmlrpc.py).
* Added more comments to the test sources.
2001-08-06 Kjetil Jacobsen
* Renamed module namespace to pycurl instead of curl.
2001-08-06 Kjetil Jacobsen
* Set CURLOPT_VERBOSE to 0 by default.
2001-06-29 Kjetil Jacobsen
* Updated INSTALL, curl version 7.8 or greater is now mandatory to
use pycurl.
2001-06-13 Kjetil Jacobsen
* Set NOPROGRESS to 1 by default.
2001-06-07 Kjetil Jacobsen
* Added global_init/cleanup.
2001-06-06 Kjetil Jacobsen
* Added HEADER/PROGRESSFUNCTION callbacks (see files in tests/).
* Added PASSWDFUNCTION callback (untested).
* Added READFUNCTION callback (untested).
2001-06-05 Kjetil Jacobsen
* WRITEFUNCTION callbacks now work (see tests/test_cb.py for details).
* Preliminary distutils installation.
* Added CLOSEPOLICY constants to module namespace.
2001-06-04 Kjetil Jacobsen
* Return -1 on error from Python callback in WRITEFUNCTION callback.
2001-06-01 Kjetil Jacobsen
* Moved source to src and tests to tests directory.
2001-05-31 Kjetil Jacobsen
* Added better type checking for setopt.
2001-05-30 Kjetil Jacobsen
* Moved code to sourceforge.
* Added getinfo support.
# vi:ts=8:et
pycurl-7.19.3/INSTALL 0000644 0004705 0004705 00000006545 12257354374 013572 0 ustar pie pie 0000000 0000000 NOTE: You need Python and libcurl installed on your system to use or
build pycurl. Some RPM distributions of curl/libcurl do not include
everything necessary to build pycurl, in which case you need to
install the developer specific RPM which is usually called curl-dev.
Distutils
---------
Build and install pycurl with the following commands:
(if necessary, become root)
tar -zxvf pycurl-$VER.tar.gz
cd pycurl-$VER
python setup.py install
$VER should be substituted with the pycurl version number, e.g. 7.10.5.
Note that the installation script assumes that 'curl-config' can be
located in your path setting. If curl-config is installed outside
your path or you want to force installation to use a particular
version of curl-config, use the '--curl-config' commandline option to
specify the location of curl-config. Example:
python setup.py install --curl-config=/usr/local/bin/curl-config
If libcurl is linked dynamically with pycurl, you may have to alter the
LD_LIBRARY_PATH environment variable accordingly. This normally
applies only if there is more than one version of libcurl installed,
e.g. one in /usr/lib and one in /usr/local/lib.
easy_install / pip
----------------
easy_install pycurl
pip install pycurl
If you need to specify an alternate curl-config, it can be done via an
environment variable:
export PYCURL_CURL_CONFIG=/usr/local/bin/curl-config
easy_install pycurl
The same applies to the SSL backend, if you need to specify it (see the SSL
section below):
export PYCURL_SSL_LIBRARY=openssl
easy_install pycurl
SSL
---
PycURL has locks around crypto functions. In order to compile correct locking
code, it has to know which SSL library is going to be used by libcurl at
runtime. setup.py will attempt to automatically detect the SSL library that
libcurl uses, but this does not always work. In the cases when setup.py cannot
figure out the SSL library, it must be provided via --with-ssl/--with-gnutls/
--with-nss arguments, just like libcurl's configure script uses, or via
PYCURL_SSL_LIBRARY=openssl|gnutls|nss environment variable.
Please note the difference in spelling that concerns OpenSSL: the command-line
argument is --with-ssl, to match libcurl, but the environment variable value is
"openssl".
Windows
-------
First, you will need to obtain dependencies. These can be precompiled binaries
or source packages that you are going to compile yourself.
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,
then configure PycURL as follows to use it:
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
and compiled libraries.
Additional Windows setup.py options:
--use-libcurl-dll - build against libcurl DLL, if not given PycURL will be built
against libcurl statically.
--libcurl-lib-name=libcurl_imp.lib - specify a different name for libcurl
import library. The default is libcurl.lib which is appropriate for static
linking and is sometimes the correct choice for dynamic linking as well. The
other possibility for dynamic linking is libcurl_imp.lib.
A good setup.py target to use is bdist_wininst which produces an executable
installer that you can run to install PycURL.
pycurl-7.19.3/MANIFEST.in 0000644 0004705 0004705 00000001107 12263706154 014256 0 ustar pie pie 0000000 0000000 #
# MANIFEST.in
# Manifest template for creating the source distribution.
#
include COPYING-LGPL
include COPYING-MIT
include ChangeLog
include INSTALL
include MANIFEST.in
include Makefile
include README.rst
include RELEASE-NOTES.rst
include doc/*.html
include doc/*.rst
include examples/*.py
include examples/tests/*.py
include src/Makefile
include src/pycurl.c
include python/curl/*.py
include requirements*.txt
include tests/*.py
include tests/certs/*.crt
include tests/certs/*.key
include tests/ext/*.sh
include tests/matrix/*.patch
include tests/vsftpd.conf
include winbuild.py
pycurl-7.19.3/Makefile 0000644 0004705 0004705 00000003203 12263707127 014160 0 ustar pie pie 0000000 0000000 #
# to use a specific python version call
# `make PYTHON=python2.2'
#
SHELL = /bin/sh
PYTHON = python
NOSETESTS = nosetests
all build:
$(PYTHON) setup.py build
build-7.10.8:
$(PYTHON) setup.py build --curl-config=/home/hosts/localhost/packages/curl-7.10.8/bin/curl-config
test: build
mkdir -p tests/tmp
PYTHONSUFFIX=$$(python -V 2>&1 |awk '{print $$2}' |awk -F. '{print $$1 "." $$2}') && \
PYTHONPATH=$$(ls -d build/lib.*$$PYTHONSUFFIX):$$PYTHONPATH \
$(PYTHON) -c 'import pycurl; print(pycurl.version)'
PYTHONPATH=$$(ls -d build/lib.*$$PYTHONSUFFIX):$$PYTHONPATH \
$(NOSETESTS)
./tests/ext/test-suite.sh
# (needs GNU binutils)
strip: build
strip -p --strip-unneeded build/lib*/*.so
chmod -x build/lib*/*.so
install install_lib:
$(PYTHON) setup.py $@
clean:
-rm -rf build dist
-rm -f *.pyc *.pyo */*.pyc */*.pyo */*/*.pyc */*/*.pyo
-rm -f MANIFEST
cd src && $(MAKE) clean
distclean: clean
maintainer-clean: distclean
dist sdist: distclean
$(PYTHON) setup.py sdist
# target for maintainer
windist: distclean
rm -rf build
python2.2 setup.py bdist_wininst
rm -rf build
python2.3 setup.py bdist_wininst
rm -rf build
python2.4 setup.py bdist_wininst
rm -rf build
python2.2 setup_win32_ssl.py bdist_wininst
rm -rf build
python2.3 setup_win32_ssl.py bdist_wininst
rm -rf build
python2.4 setup_win32_ssl.py bdist_wininst
rm -rf build
docs:
cd doc && for file in *.rst; do rst2html "$$file" ../www/htdocs/doc/`echo "$$file" |sed -e 's/.rst$$/.html/'`; done
rst2html RELEASE-NOTES.rst www/htdocs/release-notes.html
.PHONY: all build test strip install install_lib clean distclean maintainer-clean dist sdist windist
.NOEXPORT:
pycurl-7.19.3/README.rst 0000644 0004705 0004705 00000020417 12262777671 014227 0 ustar pie pie 0000000 0000000 PycURL: Python interface to libcurl
====================================
.. 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
Requirements
------------
- Python 2.4 through 2.7 or 3.1 through 3.3.
- libcurl 7.19.0 or better.
Installation
------------
You can install the most recent PycURL version using `easy_install`_::
easy_install pycurl
or `pip`_::
pip install pycurl
Installing from source is performed via ``setup.py``::
python setup.py install
You will need libcurl headers and libraries installed to install PycURL
from source. PycURL uses ``curl-config`` to determine correct flags/libraries
to use during compilation; you can override the location of ``curl-config``
if it is not in PATH or you want to use a custom libcurl installation::
python setup.py --curl-config=/path/to/curl-config install
Sometimes it is more convenient to use an environment variable, if
you are not directly invoking ``setup.py``::
PYCURL_CURL_CONFIG=/path/to/curl-config python setup.py install
``curl-config`` is expected to support the following options:
- ``--version``
- ``--cflags``
- ``--libs``
- ``--static-libs`` (if ``--libs`` does not work)
PycURL requires that the SSL library that it is built against is the same
one libcurl, and therefore PycURL, uses at runtime. PycURL's ``setup.py``
uses ``curl-config`` to attempt to figure out which SSL library libcurl
was compiled against, however this does not always work. If PycURL is unable
to determine the SSL library in use it will print a warning similar to
the following::
src/pycurl.c:137:4: warning: #warning "libcurl was compiled with SSL support, but configure could not determine which " "library was used; thus no SSL crypto locking callbacks will be set, which may " "cause random crashes on SSL requests" [-Wcpp]
It will then fail at runtime as follows::
ImportError: pycurl: libcurl link-time ssl backend (openssl) is different from compile-time ssl backend (none/other)
To fix this, you need to tell ``setup.py`` what SSL backend is used::
python setup.py --with-[ssl|gnutls|nss] install
Or use an environment variable::
PYCURL_SSL_LIBRARY=openssl|gnutls|nss python setup.py installl
Note the difference between ``--with-ssl`` (for compatibility with libcurl) and
``PYCURL_SSL_LIBRARY=openssl``.
.. _easy_install: http://peak.telecommunity.com/DevCenter/EasyInstall
.. _pip: http://pypi.python.org/pypi/pip
Support
-------
For support questions, please use `curl-and-python mailing list`_.
`Mailing list archives`_ are available for your perusal 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 you inquiry to the mailing list.
.. _curl-and-python mailing list: http://cool.haxx.se/mailman/listinfo/curl-and-python
.. _Mailing list archives: http://curl.haxx.se/mail/list.cgi?list=curl-and-python
.. _via GitHub: https://github.com/pycurl/pycurl/issues
Automated Tests
---------------
PycURL comes with an automated test suite. To run the tests, execute::
make test
The suite depends on packages `nose`_, `bottle`_ and `cherrypy`_.
Some tests use vsftpd configured to accept anonymous uploads. These tests
are not run by default. As configured, vsftpd will allow reads and writes to
anything the user running the tests has read and write access. To run
vsftpd tests you must explicitly set PYCURL_VSFTPD_PATH variable like so::
# use vsftpd in PATH
export PYCURL_VSFTPD_PATH=vsftpd
# specify full path to vsftpd
export PYCURL_VSFTPD_PATH=/usr/local/libexec/vsftpd
These instructions work for Python 2.5 through 2.7 and 3.1 through 3.3.
.. _nose: https://nose.readthedocs.org/
.. _bottle: http://bottlepy.org/
.. _cherrypy: http://www.cherrypy.org/
Test Matrix
-----------
The test matrix is a separate framework that runs tests on more esoteric
configurations. It supports:
- Testing against Python 2.4, which bottle does not support.
- Testing against Python compiled without threads, which requires an out of
process test server.
- Testing against locally compiled libcurl with arbitrary options.
To use the test matrix, first you need to start the test server from
Python 2.5+ by running:::
python -m tests.appmanager
Then in a different shell, and preferably in a separate user account,
run the test matrix:::
# run ftp tests, etc.
export PYCURL_VSFTPD_PATH=vsftpd
# create a new work directory, preferably not under pycurl tree
mkdir testmatrix
cd testmatrix
# run the matrix specifying absolute path
python /path/to/pycurl/tests/matrix.py
The test matrix will download, build and install supported Python versions
and supported libcurl versions, then run pycurl tests against each combination.
To see what the combinations are, look in
`tests/matrix.py `_.
Contribute
----------
For smaller changes:
#. Fork `the repository`_ on Github.
#. Create a branch off **master**.
#. Make your changes.
#. Write a test which shows that the bug was fixed or that the feature
works as expected.
#. Send a pull request.
#. Check back after 10-15 minutes to see if tests passed on Travis CI.
PycURL supports old Python and libcurl releases and their support is tested
on Travis.
For larger changes:
#. Join the `mailing list`_.
#. Discuss your proposal on the mailing list.
#. When consensus is reached, implement it as described above.
Please contribute binary distributions for your system to the
`downloads repository`_.
License
-------
::
Copyright (C) 2001-2008 by Kjetil Jacobsen
Copyright (C) 2001-2008 by Markus F.X.J. Oberhumer
Copyright (C) 2013-2014 by Oleg Pudeyev
All rights reserved.
PycURL is dual licensed under the LGPL and an MIT/X derivative license
based on the cURL license. A full copy of the LGPL license is included
in the file COPYING-LGPL. A full copy of the MIT/X derivative license is
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/
.. _libcurl: http://curl.haxx.se/libcurl/
.. _urllib: http://docs.python.org/library/urllib.html
.. _`the repository`: https://github.com/pycurl/pycurl
.. _`mailing list`: http://cool.haxx.se/mailman/listinfo/curl-and-python
.. _`downloads repository`: https://github.com/pycurl/downloads
pycurl-7.19.3/RELEASE-NOTES.rst 0000644 0004705 0004705 00000002474 12263710604 015223 0 ustar pie pie 0000000 0000000 Release Notes
=============
PycURL 7.19.3 - 2014-01-09
--------------------------
This release brings official Python 3 support to PycURL.
Several GNU/Linux distributions provided Python 3 packages of PycURL
previously; these packages were based on patches that were incomplete and
in some places incorrect. Behavior of PycURL 7.19.3 and later may therefore
differ from behavior of unofficial Python 3 packages of previous PycURL
versions.
To summarize the behavior under Python 3, PycURL will accept ``bytes`` where
it accepted strings under Python 2, and will also accept Unicode strings
containing ASCII codepoints only for convenience. Please refer to
`Unicode`_ and `file`_ documentation for further details.
In the interests of compatibility, PycURL will also accept Unicode data on
Python 2 given the same constraints as under Python 3.
While Unicode and file handling rules are expected to be sensible for
all use cases, and retain backwards compatibility with previous PycURL
versions, please treat behavior of this versions under Python 3 as experimental
and subject to change.
Another potentially disruptive change in PycURL is the requirement for
compile time and runtime SSL backends to match. Please see the readme for
how to indicate the SSL backend to setup.py.
.. _Unicode: doc/unicode.html
.. _file: doc/files.html
pycurl-7.19.3/requirements-dev-2.4.txt 0000644 0004705 0004705 00000000027 12255756661 017072 0 ustar pie pie 0000000 0000000 nose
simplejson==2.1.0
pycurl-7.19.3/requirements-dev-2.5.txt 0000644 0004705 0004705 00000000043 12255756661 017071 0 ustar pie pie 0000000 0000000 -r requirements-dev.txt
simplejson
pycurl-7.19.3/requirements-dev.txt 0000644 0004705 0004705 00000000025 12255756661 016567 0 ustar pie pie 0000000 0000000 bottle
nose
cherrypy
pycurl-7.19.3/setup.py 0000644 0004705 0004705 00000042765 12263706570 014253 0 ustar pie pie 0000000 0000000 #! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et
"""Setup script for the PycURL module distribution."""
PACKAGE = "pycurl"
PY_PACKAGE = "curl"
VERSION = "7.19.3"
import glob, os, re, sys, string, subprocess
import distutils
from distutils.core import setup
from distutils.extension import Extension
from distutils.util import split_quoted
from distutils.version import LooseVersion
try:
# python 2
exception_base = StandardError
except NameError:
# python 3
exception_base = Exception
class ConfigurationError(exception_base):
pass
include_dirs = []
define_macros = [("PYCURL_VERSION", '"%s"' % VERSION)]
library_dirs = []
libraries = []
runtime_library_dirs = []
extra_objects = []
extra_compile_args = []
extra_link_args = []
def fail(msg):
sys.stderr.write(msg + "\n")
exit(10)
def scan_argv(s, default=None):
p = default
i = 1
while i < len(sys.argv):
arg = sys.argv[i]
if str.find(arg, s) == 0:
if s.endswith('='):
# --option=value
p = arg[len(s):]
assert p, arg
else:
# --option
# set value to True
p = True
del sys.argv[i]
else:
i = i + 1
##print sys.argv
return p
# append contents of an environment variable to library_dirs[]
def add_libdirs(envvar, sep, fatal=False):
v = os.environ.get(envvar)
if not v:
return
for dir in str.split(v, sep):
dir = str.strip(dir)
if not dir:
continue
dir = os.path.normpath(dir)
if os.path.isdir(dir):
if not dir in library_dirs:
library_dirs.append(dir)
elif fatal:
fail("FATAL: bad directory %s in environment variable %s" % (dir, envvar))
def configure_windows():
# Windows users have to pass --curl-dir parameter to specify path
# to libcurl, because there is no curl-config on windows at all.
curl_dir = scan_argv("--curl-dir=")
if curl_dir is None:
fail("Please specify --curl-dir=/path/to/built/libcurl")
if not os.path.exists(curl_dir):
fail("Curl directory does not exist: %s" % curl_dir)
if not os.path.isdir(curl_dir):
fail("Curl directory is not a directory: %s" % curl_dir)
print("Using curl directory: %s" % curl_dir)
include_dirs.append(os.path.join(curl_dir, "include"))
# libcurl windows documentation states that for linking against libcurl
# dll, the import library name is libcurl_imp.lib.
# in practice, the library name sometimes is libcurl.lib.
# override with: --libcurl-lib-name=libcurl_imp.lib
curl_lib_name = scan_argv('--libcurl-lib-name=', 'libcurl.lib')
if scan_argv("--use-libcurl-dll") is not None:
libcurl_lib_path = os.path.join(curl_dir, "lib", curl_lib_name)
extra_link_args.extend(["ws2_32.lib"])
if str.find(sys.version, "MSC") >= 0:
# build a dll
extra_compile_args.append("-MD")
else:
extra_compile_args.append("-DCURL_STATICLIB")
libcurl_lib_path = os.path.join(curl_dir, "lib", curl_lib_name)
extra_link_args.extend(["gdi32.lib", "wldap32.lib", "winmm.lib", "ws2_32.lib",])
if not os.path.exists(libcurl_lib_path):
fail("libcurl.lib does not exist at %s.\nCurl directory must point to compiled libcurl (bin/include/lib subdirectories): %s" %(libcurl_lib_path, curl_dir))
extra_objects.append(libcurl_lib_path)
# make pycurl binary work on windows xp.
# we use inet_ntop which was added in vista and implement a fallback.
# our implementation will not be compiled with _WIN32_WINNT targeting
# vista or above, thus said binary won't work on xp.
# http://curl.haxx.se/mail/curlpython-2013-12/0007.html
extra_compile_args.append("-D_WIN32_WINNT=0x0501")
if str.find(sys.version, "MSC") >= 0:
extra_compile_args.append("-O2")
extra_compile_args.append("-GF") # enable read-only string pooling
extra_compile_args.append("-WX") # treat warnings as errors
p = subprocess.Popen(['cl.exe'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = p.communicate()
match = re.search(r'Version (\d+)', err.decode().split("\n")[0])
if match and int(match.group(1)) < 16:
# option removed in vs 2010:
# connect.microsoft.com/VisualStudio/feedback/details/475896/link-fatal-error-lnk1117-syntax-error-in-option-opt-nowin98/
extra_link_args.append("/opt:nowin98") # use small section alignment
def get_bdist_msi_version_hack():
# workaround for distutils/msi version requirement per
# epydoc.sourceforge.net/stdlib/distutils.version.StrictVersion-class.html -
# only x.y.z version numbers are supported, whereas our versions might be x.y.z.p.
# bugs.python.org/issue6040#msg133094
from distutils.command.bdist_msi import bdist_msi
import inspect
import types
import re
class bdist_msi_version_hack(bdist_msi):
""" MSI builder requires version to be in the x.x.x format """
def run(self):
def monkey_get_version(self):
""" monkey patch replacement for metadata.get_version() that
returns MSI compatible version string for bdist_msi
"""
# get filename of the calling function
if inspect.stack()[1][1].endswith('bdist_msi.py'):
# strip revision from version (if any), e.g. 11.0.0-r31546
match = re.match(r'(\d+\.\d+\.\d+)', self.version)
assert match
return match.group(1)
else:
return self.version
# monkeypatching get_version() call for DistributionMetadata
self.distribution.metadata.get_version = \
types.MethodType(monkey_get_version, self.distribution.metadata)
bdist_msi.run(self)
return bdist_msi_version_hack
def configure_unix():
# Find out the rest the hard way
OPENSSL_DIR = scan_argv("--openssl-dir=")
if OPENSSL_DIR is not None:
include_dirs.append(os.path.join(OPENSSL_DIR, "include"))
CURL_CONFIG = os.environ.get('PYCURL_CURL_CONFIG', "curl-config")
CURL_CONFIG = scan_argv("--curl-config=", CURL_CONFIG)
try:
p = subprocess.Popen((CURL_CONFIG, '--version'),
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
except OSError:
exc = sys.exc_info()[1]
msg = 'Could not run curl-config: %s' % str(exc)
raise ConfigurationError(msg)
stdout, stderr = p.communicate()
if p.wait() != 0:
msg = "`%s' not found -- please install the libcurl development files or specify --curl-config=/path/to/curl-config" % CURL_CONFIG
if stderr:
msg += ":\n" + stderr.decode()
raise ConfigurationError(msg)
libcurl_version = stdout.decode().strip()
print("Using %s (%s)" % (CURL_CONFIG, libcurl_version))
p = subprocess.Popen((CURL_CONFIG, '--cflags'),
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = p.communicate()
if p.wait() != 0:
msg = "Problem running `%s' --cflags" % CURL_CONFIG
if stderr:
msg += ":\n" + stderr.decode()
raise ConfigurationError(msg)
for arg in split_quoted(stdout.decode()):
if arg[:2] == "-I":
# do not add /usr/include
if not re.search(r"^\/+usr\/+include\/*$", arg[2:]):
include_dirs.append(arg[2:])
else:
extra_compile_args.append(arg)
# Obtain linker flags/libraries to link against.
# In theory, all we should need is `curl-config --libs`.
# Apparently on some platforms --libs fails and --static-libs works,
# so try that.
# If --libs succeeds do not try --static libs; see
# https://github.com/pycurl/pycurl/issues/52 for more details.
# If neither --libs nor --static-libs work, fail.
optbuf = ""
errtext = ''
for option in ["--libs", "--static-libs"]:
p = subprocess.Popen((CURL_CONFIG, option),
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = p.communicate()
if p.wait() == 0:
optbuf = stdout.decode()
break
else:
errtext += stderr.decode()
if optbuf == "":
msg = "Neither curl-config --libs nor curl-config --static-libs" +\
" succeeded and produced output"
if errtext:
msg += ":\n" + errtext
raise ConfigurationError(msg)
libs = split_quoted(optbuf)
ssl_lib_detected = False
if 'PYCURL_SSL_LIBRARY' in os.environ:
ssl_lib = os.environ['PYCURL_SSL_LIBRARY']
if ssl_lib in ['openssl', 'gnutls', 'nss']:
ssl_lib_detected = True
define_macros.append(('HAVE_CURL_%s' % ssl_lib.upper(), 1))
else:
raise ConfigurationError('Invalid value "%s" for PYCURL_SSL_LIBRARY' % ssl_lib)
ssl_options = {
'--with-ssl': 'HAVE_CURL_OPENSSL',
'--with-gnutls': 'HAVE_CURL_GNUTLS',
'--with-nss': 'HAVE_CURL_NSS',
}
for option in ssl_options:
if scan_argv(option) is not None:
for other_option in ssl_options:
if option != other_option:
if scan_argv(other_option) is not None:
raise ConfigurationError('Cannot give both %s and %s' % (option, other_option))
ssl_lib_detected = True
define_macros.append((ssl_options[option], 1))
for arg in libs:
if arg[:2] == "-l":
libraries.append(arg[2:])
if not ssl_lib_detected and arg[2:] == 'ssl':
define_macros.append(('HAVE_CURL_OPENSSL', 1))
ssl_lib_detected = True
if not ssl_lib_detected and arg[2:] == 'gnutls':
define_macros.append(('HAVE_CURL_GNUTLS', 1))
ssl_lib_detected = True
if not ssl_lib_detected and arg[2:] == 'ssl3':
define_macros.append(('HAVE_CURL_NSS', 1))
ssl_lib_detected = True
elif arg[:2] == "-L":
library_dirs.append(arg[2:])
else:
extra_link_args.append(arg)
if not ssl_lib_detected:
p = subprocess.Popen((CURL_CONFIG, '--features'),
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = p.communicate()
if p.wait() != 0:
msg = "Problem running `%s' --features" % CURL_CONFIG
if stderr:
msg += ":\n" + stderr.decode()
raise ConfigurationError(msg)
for feature in split_quoted(stdout.decode()):
if feature == 'SSL':
# this means any ssl library, not just openssl
define_macros.append(('HAVE_CURL_SSL', 1))
else:
# if we are configuring for a particular ssl library,
# we can assume that ssl is being used
define_macros.append(('HAVE_CURL_SSL', 1))
if not libraries:
libraries.append("curl")
# Add extra compile flag for MacOS X
if sys.platform[:-1] == "darwin":
extra_link_args.append("-flat_namespace")
def configure():
if sys.platform == "win32":
configure_windows()
else:
configure_unix()
def strip_pycurl_options():
if sys.platform == 'win32':
options = ['--curl-dir=', '--curl-lib-name=', '--use-libcurl-dll']
else:
options = ['--openssl-dir', '--curl-config']
for option in options:
scan_argv(option)
###############################################################################
def get_extension():
ext = Extension(
name=PACKAGE,
sources=[
os.path.join("src", "pycurl.c"),
],
include_dirs=include_dirs,
define_macros=define_macros,
library_dirs=library_dirs,
libraries=libraries,
runtime_library_dirs=runtime_library_dirs,
extra_objects=extra_objects,
extra_compile_args=extra_compile_args,
extra_link_args=extra_link_args,
)
##print(ext.__dict__); sys.exit(1)
return ext
###############################################################################
# prepare data_files
def get_data_files():
# a list of tuples with (path to install to, a list of local files)
data_files = []
if sys.platform == "win32":
datadir = os.path.join("doc", PACKAGE)
else:
datadir = os.path.join("share", "doc", PACKAGE)
#
files = ["ChangeLog", "COPYING-LGPL", "COPYING-MIT", "INSTALL", "README.rst"]
if files:
data_files.append((os.path.join(datadir), files))
files = glob.glob(os.path.join("doc", "*.html"))
if files:
data_files.append((os.path.join(datadir, "html"), files))
files = glob.glob(os.path.join("examples", "*.py"))
if files:
data_files.append((os.path.join(datadir, "examples"), files))
files = glob.glob(os.path.join("tests", "*.py"))
if files:
data_files.append((os.path.join(datadir, "tests"), files))
#
assert data_files
for install_dir, files in data_files:
assert files
for f in files:
assert os.path.isfile(f), (f, install_dir)
return data_files
##print get_data_files(); sys.exit(1)
###############################################################################
def check_manifest():
import fnmatch
f = open('MANIFEST.in')
globs = []
try:
for line in f.readlines():
stripped = line.strip()
if stripped == '' or stripped.startswith('#'):
continue
assert stripped.startswith('include ')
glob = stripped[8:]
globs.append(glob)
finally:
f.close()
paths = []
start = os.path.abspath(os.path.dirname(__file__))
for root, dirs, files in os.walk(start):
if '.git' in dirs:
dirs.remove('.git')
for file in files:
if file.endswith('.pyc'):
continue
rel = os.path.join(root, file)[len(start)+1:]
paths.append(rel)
for path in paths:
included = False
for glob in globs:
if fnmatch.fnmatch(path, glob):
included = True
break
if not included:
print(path)
###############################################################################
setup_args = dict(
name=PACKAGE,
version=VERSION,
description="PycURL -- cURL library module for Python",
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/",
license="LGPL/MIT",
keywords=['curl', 'libcurl', 'urllib', 'wget', 'download', 'file transfer',
'http', 'www'],
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Web Environment',
'Intended Audience :: Developers',
'License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)',
'License :: OSI Approved :: MIT License',
'Operating System :: Microsoft :: Windows',
'Operating System :: POSIX',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 3',
'Topic :: Internet :: File Transfer Protocol (FTP)',
'Topic :: Internet :: WWW/HTTP',
],
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":
setup_args['cmdclass'] = {'bdist_msi': get_bdist_msi_version_hack()}
##print distutils.__version__
if LooseVersion(distutils.__version__) > LooseVersion("1.0.1"):
setup_args["platforms"] = "All"
if LooseVersion(distutils.__version__) < LooseVersion("1.0.3"):
setup_args["licence"] = setup_args["license"]
unix_help = '''\
PycURL Unix options:
--curl-config=/path/to/curl-config use specified curl-config binary
--openssl-dir=/path/to/openssl/dir path to OpenSSL headers and libraries
--with-ssl libcurl is linked against OpenSSL
--with-gnutls libcurl is linked against GnuTLS
--with-nss libcurl is linked against NSS
'''
windows_help = '''\
PycURL Windows options:
--curl-dir=/path/to/compiled/libcurl path to libcurl headers and libraries
--use-libcurl-dll link against libcurl DLL, if not given
link against libcurl statically
--libcurl-lib-name=libcurl_imp.lib override libcurl import library name
'''
if __name__ == "__main__":
if '--help' in sys.argv:
# unfortunately this help precedes distutils help
if sys.platform == "win32":
print(windows_help)
else:
print(unix_help)
# invoke setup without configuring pycurl because
# configuration might fail, and we want to display help anyway.
# we need to remove our options because distutils complains about them
strip_pycurl_options()
setup(**setup_args)
elif len(sys.argv) > 1 and sys.argv[1] == 'manifest':
check_manifest()
else:
configure()
setup_args['data_files'] = get_data_files()
ext = get_extension()
setup_args['ext_modules'] = [ext]
for o in ext.extra_objects:
assert os.path.isfile(o), o
setup(**setup_args)
pycurl-7.19.3/winbuild.py 0000644 0004705 0004705 00000015547 12263706602 014722 0 ustar pie pie 0000000 0000000 # work directory for downloading dependencies and building everything
root = 'c:/dev/build-pycurl'
# where msysgit is installed
git_root = 'c:/program files/git'
# which versions of python to build against
python_versions = ['2.6', '2.7', '3.2', '3.3']
# where pythons are installed
python_path_template = 'c:/python%s/python'
vc_paths = {
# where msvc 9 is installed, for python 2.6 through 3.2
'vc9': 'c:/program files/microsoft visual studio 9.0',
# where msvc 10 is installed, for python 3.3
'vc10': 'c:/program files/microsoft visual studio 10.0',
}
# whether to link libcurl against zlib
use_zlib = False
# which version of zlib to use, will be downloaded from internet
zlib_version = '1.2.8'
# which version of libcurl to use, will be downloaded from the internet
libcurl_version = '7.34.0'
# pycurl version to build, we should know this ourselves
pycurl_version = '7.19.3'
import os, os.path, sys, subprocess, shutil, contextlib
archives_path = os.path.join(root, 'archives')
state_path = os.path.join(root, 'state')
git_bin_path = os.path.join(git_root, 'bin')
git_path = os.path.join(git_bin_path, 'git')
rm_path = os.path.join(git_bin_path, 'rm')
tar_path = os.path.join(git_bin_path, 'tar')
for key in vc_paths:
vc_paths[key] = {
'root': vc_paths[key],
'vsvars': os.path.join(vc_paths[key], 'common7/tools/vsvars32.bat'),
}
python_vc_versions = {
'2.6': 'vc9',
'2.7': 'vc9',
'3.2': 'vc9',
'3.3': 'vc10',
}
vc_versions = vc_paths.keys()
try:
from urllib.request import urlopen
except ImportError:
from urllib import urlopen
def fetch(url, archive=None):
if archive is None:
archive = os.path.basename(url)
if not os.path.exists(archive):
sys.stdout.write("Fetching %s\n" % url)
io = urlopen(url)
with open('.tmp.%s' % archive, 'wb') as f:
while True:
chunk = io.read(65536)
if len(chunk) == 0:
break
f.write(chunk)
os.rename('.tmp.%s' % archive, archive)
@contextlib.contextmanager
def in_dir(dir):
old_cwd = os.getcwd()
try:
os.chdir(dir)
yield
finally:
os.chdir(old_cwd)
@contextlib.contextmanager
def step(step_fn, *args):
step = step_fn.__name__
if args:
step += '-' + '-'.join(args)
if not os.path.exists(state_path):
os.makedirs(state_path)
state_file_path = os.path.join(state_path, step)
if not os.path.exists(state_file_path):
step_fn(*args)
with open(state_file_path, 'w') as f:
pass
def untar(basename):
if os.path.exists(basename):
shutil.rmtree(basename)
subprocess.check_call([tar_path, 'xf', '%s.tar.gz' % basename])
def rename_for_vc(basename, vc_version):
suffixed_dir = '%s-%s' % (basename, vc_version)
if os.path.exists(suffixed_dir):
shutil.rmtree(suffixed_dir)
os.rename(basename, suffixed_dir)
return suffixed_dir
def work():
os.environ['PATH'] += ";%s" % git_bin_path
if not os.path.exists(archives_path):
os.makedirs(archives_path)
with in_dir(archives_path):
def build_zlib(vc_version):
fetch('http://downloads.sourceforge.net/project/libpng/zlib/%s/zlib-%s.tar.gz' % (zlib_version, zlib_version))
untar('zlib-%s' % zlib_version)
zlib_dir = rename_for_vc('zlib-%s' % zlib_version, vc_version)
with in_dir(zlib_dir):
with open('doit.bat', 'w') as f:
f.write("call \"%s\"\n" % vc_paths[vc_version]['vsvars'])
f.write("nmake /f win32/Makefile.msc\n")
subprocess.check_call(['doit.bat'])
def build_curl(vc_version):
fetch('http://curl.haxx.se/download/curl-%s.tar.gz' % libcurl_version)
untar('curl-%s' % libcurl_version)
curl_dir = rename_for_vc('curl-%s' % libcurl_version, vc_version)
with in_dir(os.path.join(curl_dir, 'winbuild')):
with open('doit.bat', 'w') as f:
f.write("call \"%s\"\n" % vc_paths[vc_version]['vsvars'])
f.write("set include=%%include%%;%s\n" % os.path.join(archives_path, 'zlib-%s-%s' % (zlib_version, vc_version)))
f.write("set lib=%%lib%%;%s\n" % os.path.join(archives_path, 'zlib-%s-%s' % (zlib_version, vc_version)))
if use_zlib:
extra_options = ' WITH_ZLIB=dll'
else:
extra_options = ''
f.write("nmake /f Makefile.vc mode=dll ENABLE_IDN=no\n")
subprocess.check_call(['doit.bat'])
for vc_version in vc_versions:
if use_zlib:
step(build_zlib, vc_version)
step(build_curl, vc_version)
def prepare_pycurl():
#fetch('http://pycurl.sourceforge.net/download/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)
def build_pycurl(python_version, target):
python_path = python_path_template % python_version.replace('.', '')
vc_version = python_vc_versions[python_version]
with in_dir(os.path.join('pycurl-%s' % pycurl_version)):
if use_zlib:
libcurl_build_name = 'libcurl-vc-x86-release-dll-zlib-dll-ipv6-sspi-spnego-winssl'
else:
libcurl_build_name = 'libcurl-vc-x86-release-dll-ipv6-sspi-spnego-winssl'
curl_dir = '../curl-%s-%s/builds/%s' % (libcurl_version, vc_version, libcurl_build_name)
if not os.path.exists('build/lib.win32-%s' % python_version):
# exists for building additional targets for the same python version
os.makedirs('build/lib.win32-%s' % python_version)
shutil.copy(os.path.join(curl_dir, 'bin', 'libcurl.dll'), 'build/lib.win32-%s' % python_version)
with open('doit.bat', 'w') as f:
f.write("call \"%s\"\n" % vc_paths[vc_version]['vsvars'])
f.write("%s setup.py %s --curl-dir=%s --use-libcurl-dll\n" % (python_path, target, curl_dir))
subprocess.check_call(['doit.bat'])
if target == 'bdist':
os.rename('dist/pycurl-%s.win32.zip' % pycurl_version, 'dist/pycurl-%s.win32-py%s.zip' % (pycurl_version, python_version))
prepare_pycurl()
for python_version in python_versions:
for target in ['bdist', 'bdist_wininst', 'bdist_msi']:
build_pycurl(python_version, target)
work()
pycurl-7.19.3/PKG-INFO 0000644 0004705 0004705 00000001756 12263734634 013633 0 ustar pie pie 0000000 0000000 Metadata-Version: 1.1
Name: pycurl
Version: 7.19.3
Summary: PycURL -- cURL library module for Python
Home-page: http://pycurl.sourceforge.net/
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.
Keywords: curl,libcurl,urllib,wget,download,file transfer,http,www
Platform: All
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Internet :: File Transfer Protocol (FTP)
Classifier: Topic :: Internet :: WWW/HTTP