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,
XFERINFOFUNCTION, IOCTLFUNCTION, DEBUGFUNCTION,
PREREQFUNCTION, TRAILERFUNCTION, RESOLVER_START_FUNCTION,
FNMATCH_FUNCTION, HSTSREADFUNCTION or HSTSWRITEFUNCTION.
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.
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 documented below.
Error Reporting¶
PycURL callbacks are invoked as follows:
Python application -> perform() -> libcurl (C code) -> Python callback
Because callbacks are invoked by libcurl, they should not raise exceptions on failure but instead return appropriate values indicating failure. The documentation for individual callbacks below specifies expected success and failure return values.
Unhandled exceptions propagated out of Python callbacks will be intercepted
by PycURL or the Python runtime. This will fail the callback with a
generic failure status, in turn failing the perform() operation.
A failing perform() will raise pycurl.error, but the error code
used depends on the specific callback.
KeyboardInterrupt and other BaseException subclasses (for example, SystemExit)
are handled specially: if they are raised inside a callback, they are preserved and re-raised
to the caller instead of being converted into a pycurl.error.
Rich context information like exception objects can be stored in various ways, for example the following example stores OPENSOCKET callback exception on the Curl object:
import pycurl, random, socket
class ConnectionRejected(Exception):
pass
def opensocket(curl, purpose, curl_address):
# always fail
curl.exception = ConnectionRejected('Rejecting connection attempt in opensocket callback')
return pycurl.SOCKET_BAD
# the callback must create a socket if it does not fail,
# see examples/opensocketexception.py
c = pycurl.Curl()
c.setopt(c.URL, 'http://pycurl.io')
c.exception = None
c.setopt(c.OPENSOCKETFUNCTION,
lambda purpose, address: opensocket(c, purpose, address))
try:
c.perform()
except pycurl.error as e:
if e.args[0] == pycurl.E_COULDNT_CONNECT and c.exception:
print(c.exception)
else:
print(e)
WRITEFUNCTION¶
- WRITEFUNCTION(byte string) number of characters written¶
Callback for writing data. Corresponds to CURLOPT_WRITEFUNCTION in libcurl.
On Python 3, the argument is of type
bytes.The
WRITEFUNCTIONcallback may return the number of bytes written. If this number is not equal to the size of the byte string, this signifies an error and libcurl will abort the request. ReturningNoneis an alternate way of indicating that the callback has consumed all of the string passed to it and, hence, succeeded.write_test.py test shows how to use
WRITEFUNCTION.
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()
HEADERFUNCTION¶
- HEADERFUNCTION(byte string) number of characters written¶
Callback for writing received headers. Corresponds to CURLOPT_HEADERFUNCTION in libcurl.
On Python 3, the argument is of type
bytes.The
HEADERFUNCTIONcallback may return the number of bytes written. If this number is not equal to the size of the byte string, this signifies an error and libcurl will abort the request. ReturningNoneis an alternate way of indicating that the callback has consumed all of the string passed to it and, hence, succeeded.header_test.py test shows how to use
WRITEFUNCTION.
READFUNCTION¶
- READFUNCTION(number of characters to read) byte string¶
Callback for reading data. Corresponds to CURLOPT_READFUNCTION in libcurl.
The callback must return either a C-contiguous object supporting the buffer protocol (e.g. bytes, bytearray, memoryview, or a C-contiguous numpy array) or a Unicode string consisting of ASCII code points only.
In addition,
READFUNCTIONmay returnREADFUNC_ABORTorREADFUNC_PAUSE. See the libcurl documentation for an explanation of these values.The file_upload.py example in the distribution contains example code for using
READFUNCTION.
SEEKFUNCTION¶
- SEEKFUNCTION(offset, origin) status¶
Callback for seek operations. Corresponds to CURLOPT_SEEKFUNCTION in libcurl.
originis0(beginning),1(current position) or2(end).Return one of:
SEEKFUNC_OK(seek succeeded)SEEKFUNC_FAIL(hard failure)SEEKFUNC_CANTSEEK(seek not possible; libcurl may fall back)
IOCTLFUNCTION¶
- IOCTLFUNCTION(ioctl cmd) status¶
Callback for I/O operations. Corresponds to CURLOPT_IOCTLFUNCTION in libcurl.
Note: this callback is deprecated. Use SEEKFUNCTION instead.
DEBUGFUNCTION¶
- DEBUGFUNCTION(debug message type, debug message byte string) None¶
Callback for debug information. Corresponds to CURLOPT_DEBUGFUNCTION in libcurl.
Changed in version 7.19.5.2: The second argument to a
DEBUGFUNCTIONcallback is now of typebyteson Python 3. Previously the argument was of typestr.debug_test.py test shows how to use
DEBUGFUNCTION.
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, "https://curl.haxx.se/")
c.setopt(pycurl.VERBOSE, 1)
c.setopt(pycurl.DEBUGFUNCTION, test)
c.perform()
PROGRESSFUNCTION¶
- PROGRESSFUNCTION(download total, downloaded, upload total, uploaded) status¶
Callback for progress meter. Corresponds to CURLOPT_PROGRESSFUNCTION in libcurl.
PROGRESSFUNCTIONreceives amounts as floating point arguments to the callback. Since libcurl 7.32.0PROGRESSFUNCTIONis deprecated;XFERINFOFUNCTIONshould be used instead which receives amounts as long integers.NOPROGRESSoption must be set for False libcurl to invoke a progress callback, as PycURL by default setsNOPROGRESSto True.
XFERINFOFUNCTION¶
- XFERINFOFUNCTION(download total, downloaded, upload total, uploaded) status¶
Callback for progress meter. Corresponds to CURLOPT_XFERINFOFUNCTION in libcurl.
XFERINFOFUNCTIONreceives amounts as long integers.NOPROGRESSoption must be set for False libcurl to invoke a progress callback, as PycURL by default setsNOPROGRESSto True.
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 = pycurl.Curl()
c.setopt(c.URL, "http://slashdot.org/")
c.setopt(c.NOPROGRESS, False)
c.setopt(c.XFERINFOFUNCTION, progress)
c.perform()
OPENSOCKETFUNCTION¶
- OPENSOCKETFUNCTION(purpose, address) int¶
Callback for opening sockets. Corresponds to CURLOPT_OPENSOCKETFUNCTION in libcurl.
purpose is a
SOCKTYPE_*value.address is a namedtuple with
family,socktype,protocolandaddrfields, per CURLOPT_OPENSOCKETFUNCTION documentation.addr is an object representing the address. Currently the following address families are supported:
AF_INET: addr is a 2-tuple of(host, port).AF_INET6: addr is a 4-tuple of(host, port, flow info, scope id).AF_UNIX: addr is a byte string containing path to the Unix socket.Availability: Unix.
This behavior matches that of Python’s socket module.
The callback should return a socket object, a socket file descriptor or a Python object with a
filenoproperty containing the socket file descriptor.The callback may be unset by calling setopt with
Noneas the value or by calling unsetopt.open_socket_cb_test.py test shows how to use
OPENSOCKETFUNCTION.Changed in version 7.21.5: Previously, the callback received
family,socktype,protocolandaddrparameters (purposewas not passed andaddresswas flattened). Also,AF_INET6addresses were exposed as 2-tuples of(host, port)rather than 4-tuples.Changed in version 7.19.3:
addrparameter added to the callback.
CLOSESOCKETFUNCTION¶
- CLOSESOCKETFUNCTION(curlfd) int¶
Callback for setting socket options. Corresponds to CURLOPT_CLOSESOCKETFUNCTION in libcurl.
curlfd is the file descriptor to be closed.
The callback should return an
int.The callback may be unset by calling setopt with
Noneas the value or by calling unsetopt.close_socket_cb_test.py test shows how to use
CLOSESOCKETFUNCTION.
SOCKOPTFUNCTION¶
- SOCKOPTFUNCTION(curlfd, purpose) int¶
Callback for setting socket options. Corresponds to CURLOPT_SOCKOPTFUNCTION in libcurl.
curlfd is the file descriptor of the newly created socket.
purpose is a
SOCKTYPE_*value.The callback should return an
int.The callback may be unset by calling setopt with
Noneas the value or by calling unsetopt.sockopt_cb_test.py test shows how to use
SOCKOPTFUNCTION.
SSH_KEYFUNCTION¶
- SSH_KEYFUNCTION(known_key, found_key, match) int¶
Callback for known host matching logic. Corresponds to CURLOPT_SSH_KEYFUNCTION in libcurl.
known_key and found_key are instances of
KhKeyclass which is a namedtuple withkeyandkeytypefields, corresponding to libcurl’sstruct curl_khkey:KhKey = namedtuple('KhKey', ('key', 'keytype'))
The key field of
KhKeyisbytes. keytype is anint.known_key may be
Nonewhen there is no known matching host key.SSH_KEYFUNCTIONcallback should return aKHSTAT_*value.The callback may be unset by calling setopt with
Noneas the value or by calling unsetopt.ssh_key_cb_test.py test shows how to use
SSH_KEYFUNCTION.
TIMERFUNCTION¶
- TIMERFUNCTION(timeout_ms) int | None¶
Callback for installing a timer requested by libcurl. Corresponds to CURLMOPT_TIMERFUNCTION.
The application should arrange for a non-repeating timer to fire in
timeout_msmilliseconds, at which point the application should call either socket_action or perform. See the CURLMOPT_TIMERFUNCTION docs for the special values oftimeout_ms.Return
0orNonefor success, or-1to abort all in-progress transfers in the multi handle. Exceptions raised in the callback are printed to stderr and treated as-1.See
examples/multi-socket_action-select.pyfor an example program that uses the timer function and the socket function.
SOCKETFUNCTION¶
- SOCKETFUNCTION(what, sock_fd, multi, socketp) int | None¶
Callback notifying the application about activity on libcurl sockets. Corresponds to CURLMOPT_SOCKETFUNCTION.
Note that the PycURL callback takes
whatas the first argument andsock_fdas the second argument, whereas the libcurl callback takessock_fdas the first argument andwhatas the second argument.what is one of
pycurl.POLL_IN,POLL_OUT,POLL_INOUTorPOLL_REMOVE; see the CURLMOPT_SOCKETFUNCTION docs for their meaning.The
userp(“private callback pointer”) argument, as described in theCURLMOPT_SOCKETFUNCTIONdocumentation, is set to theCurlMultiinstance.The
socketp(“private socket pointer”) argument, as described in theCURLMOPT_SOCKETFUNCTIONdocumentation, is set to the value provided to the assign method for the correspondingsock_fd, orNoneif no value was assigned.Return
0orNonefor success, or-1to abort all in-progress transfers in the multi handle. Exceptions raised in the callback are printed to stderr and treated as-1.See
examples/multi-socket_action-select.pyfor an example program that uses the timer function and the socket function.
PREREQFUNCTION¶
- PREREQFUNCTION(conn_primary_ip, conn_local_ip, conn_primary_port, conn_local_port) int¶
Callback called when a connection has been established, but before a request has been made. Corresponds to CURLOPT_PREREQFUNCTION in libcurl.
conn_primary_ip is the primary IP address of the remote server established with this connection (as a string).
conn_local_ip is the originating IP address for this connection (as a string).
conn_primary_port is the primary port number on the remote server established with this connection.
conn_local_port is the originating port number for this connection.
The callback should return an
int, which must be eitherPREREQFUNC_OK(on success) orPREREQFUNC_ABORTto cause the transfer to fail with resultABORTED_BY_CALLBACK.The callback may be unset by calling setopt with
Noneas the value or by calling unsetopt.prereq_cb_test.py test shows how to use
PREREQFUNCTION.
TRAILERFUNCTION¶
- TRAILERFUNCTION() list of strings or None¶
Callback for supplying HTTP trailing headers on chunked uploads. Corresponds to CURLOPT_TRAILERFUNCTION in libcurl.
The callback takes no arguments and should return either
None(no trailing headers) or a list or tuple of header strings in"Name: value"form. The same rules apply to these strings as do toHTTPHEADERentries.Raising an exception from the callback, or returning any other type, causes the transfer to fail with
TRAILERFUNC_ABORT.The callback may be unset by calling setopt with
Noneas the value or by calling unsetopt.
RESOLVER_START_FUNCTION¶
- RESOLVER_START_FUNCTION() int¶
Callback invoked before each name resolution. Corresponds to CURLOPT_RESOLVER_START_FUNCTION in libcurl.
The callback takes no arguments. Return
0(orNone) to let the resolution proceed; return any non-zero value to abort the transfer.The callback may be unset by calling setopt with
Noneas the value or by calling unsetopt.
FNMATCH_FUNCTION¶
- FNMATCH_FUNCTION(pattern, string) int¶
Callback for wildcard matching used by the FTP wildcard transfer feature. Corresponds to CURLOPT_FNMATCH_FUNCTION in libcurl.
pattern and string are both
bytes.The callback should return one of:
FNMATCHFUNC_MATCH(pattern matched)FNMATCHFUNC_NOMATCH(pattern did not match)FNMATCHFUNC_FAIL(error during matching; aborts the transfer)
The callback may be unset by calling setopt with
Noneas the value or by calling unsetopt.
HSTSWRITEFUNCTION¶
- HSTSWRITEFUNCTION(entry, index) int¶
Callback for persisting the in-memory HSTS cache. Corresponds to CURLOPT_HSTSWRITEFUNCTION in libcurl. Requires
HSTS_CTRLto be set toCURLHSTS_ENABLE.entry is an
HstsEntrynamedtuple withhost,expireandinclude_subdomainsfields:HstsEntry = namedtuple('HstsEntry', ('host', 'expire', 'include_subdomains'))
host is
bytes. expire is a tz-awaredatetimein UTC, orNonewhen the entry never expires. include_subdomains isbool.index is an
HstsIndexnamedtuple indicating progress through the cache:HstsIndex = namedtuple('HstsIndex', ('index', 'total'))
The callback should return
CURLSTS_OK(entry accepted),CURLSTS_DONE(stop iterating) orCURLSTS_FAIL(error). ReturningNoneis equivalent toCURLSTS_OK.libcurl invokes the callback during
perform()and during handle cleanup; exceptions raised inside the callback while the handle is being cleaned up are written tosys.unraisablehookand do not propagate.The callback may be unset by calling setopt with
Noneas the value or by calling unsetopt.
HSTSREADFUNCTION¶
- HSTSREADFUNCTION() HstsEntry or None¶
Callback for preloading the in-memory HSTS cache. Corresponds to CURLOPT_HSTSREADFUNCTION in libcurl. Requires
HSTS_CTRLto be set toCURLHSTS_ENABLE.The callback takes no arguments. It is invoked repeatedly until it returns
None.Return
Noneto signal there are no more entries (CURLSTS_DONE), or anHstsEntry(or any 3-tuple) where:host is
bytesor an ASCIIstr.expire is a
datetimeorNone.Nonemeans the entry never expires. Aware datetimes are converted to UTC; naive datetimes are interpreted as UTC.include_subdomains is any truthy/falsy value.
Raising an exception or returning anything else causes the transfer to fail with
CURLSTS_FAIL.The callback may be unset by calling setopt with
Noneas the value or by calling unsetopt.
WebSocket callback receive (libcurl 7.86.0 or later)¶
libcurl supports two WebSocket usage models: detached mode
(CONNECT_ONLY=2 plus ws_send / ws_recv, documented on the
Curl object) and callback-receive mode, where
libcurl drives the transfer and delivers each received frame chunk
through the ordinary WRITEFUNCTION callback. No separate callback
registration is required — set WRITEFUNCTION on a ws:// /
wss:// URL, leave CONNECT_ONLY unset, and call
perform as you would for any other transfer.
perform() blocks for the full lifetime of the WebSocket connection
and returns when the peer closes (or the transfer otherwise terminates),
so the server side must have a predictable end condition.
Inside the callback, pycurl.Curl.ws_meta() returns a WsFrame
namedtuple (age, flags, offset, bytesleft, len)
describing the chunk currently being delivered. The flags field is
a bitmask of WS_TEXT, WS_BINARY, WS_CONT, WS_PING,
WS_PONG, WS_CLOSE, and WS_OFFSET.
libcurl’s curl_ws_meta() returns NULL outside the valid
callback context; PycURL maps that to Python None. The same
ws_meta() method is safe to call after perform() has returned
(it simply returns None) and in detached mode (likewise).
Calling ws_send() or ws_close() from inside the
WRITEFUNCTION is allowed: libcurl treats the call as a blocking send
and returns only once the frame has been fully written (or an error
occurs). CURLE_AGAIN / BlockingIOError semantics do not apply in
this context. That relaxation applies only inside the callback itself;
calls from another thread while perform() is running are still
rejected. ws_recv() and ws_recv_into() remain
detached-only and still raise pycurl.error while perform() is
running.
Example:
import pycurl
c = pycurl.Curl()
def on_ws_chunk(data):
meta = c.ws_meta() # valid only inside this callback
if meta is not None and meta.flags & pycurl.WS_TEXT:
print("text chunk:", data)
c.ws_send(b"ack", pycurl.WS_BINARY) # blocking send
return len(data)
c.setopt(c.URL, "wss://example.com/socket")
c.setopt(c.WRITEFUNCTION, on_ws_chunk)
c.perform() # blocks until peer closes
c.close()
ws_callback.py example is a complete runnable version.