In PycURL 18.104.22.168 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 read method can be passed to CURLOPT_READDATA, and any Python file-like object implementing a write method can be passed to 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 read or write method, as appropriate, 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 reading or writing in binary mode. Files opened in text mode (without "b" flag to open()) expect string objects and reading from or 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.