);
}
-FILE* PyObject_AsFileHandle(PyObject* object, const char* mode) {
- // Get a file descriptor
- int fd = PyObject_AsFileDescriptor(object);
- if (fd < 0)
- return NULL;
+static ssize_t PyObject_AsFileHandle_read(void* cookie, char* buffer, size_t size) {
+ PyObject* object = (PyObject*)cookie;
+ PyObject* pybuffer = NULL;
+ const char* bytes = NULL;
+ Py_ssize_t bytes_read = -1;
+
+ // Make sure we acquired the GIL
+ PyGILState_STATE state = PyGILState_Ensure();
+
+ // Read data from the object
+ pybuffer = PyObject_CallMethod(object, "read", "i", size);
+ if (!pybuffer)
+ goto ERROR;
+
+ // Handle strings
+ if (PyUnicode_Check(pybuffer)) {
+ bytes = PyUnicode_AsUTF8AndSize(pybuffer, &bytes_read);
+ if (!bytes)
+ goto ERROR;
- // Duplicate the file handle
- fd = dup(fd);
+ // Handle bytes
+ } else if (PyBytes_Check(pybuffer)) {
+ bytes = PyBytes_AsString(pybuffer);
+ if (!bytes)
+ goto ERROR;
+
+ bytes_read = PyBytes_Size(pybuffer);
+
+ // Fail on anything else
+ } else {
+ PyErr_SetString(PyExc_TypeError, "read() returned an unexpected data type\n");
+ goto ERROR;
+ }
- // Convert to file handle
- FILE* f = fdopen(fd, mode);
- if (!f) {
- PyErr_SetFromErrno(PyExc_OSError);
+ // Copy the data into the target buffer
+ memcpy(buffer, bytes, bytes_read);
+
+ERROR:
+ Py_DECREF(pybuffer);
+
+ // Release the GIL
+ PyGILState_Release(state);
+
+ return bytes_read;
+}
+
+static ssize_t PyObject_AsFileHandle_write(void* cookie, const char* buffer, size_t size) {
+ PyObject* object = (PyObject*)cookie;
+ ssize_t r = 0;
+
+ // Make sure we acquired the GIL
+ PyGILState_STATE state = PyGILState_Ensure();
+
+ PyObject* pybuffer = PyBytes_FromStringAndSize(buffer, size);
+ if (!pybuffer)
+ goto ERROR;
+
+ PyObject* result = PyObject_CallMethod(object, "write", "b", pybuffer);
+ if (!result)
+ goto ERROR;
+
+ // It looks like we successfully wrote the data
+ r = size;
+
+ERROR:
+ Py_XDECREF(pybuffer);
+
+ // Release the GIL
+ PyGILState_Release(state);
+
+ return r;
+}
+
+static int PyObject_AsFileHandle_close(void* cookie) {
+ PyObject* object = (PyObject*)cookie;
+
+ // Make sure we acquired the GIL
+ PyGILState_STATE state = PyGILState_Ensure();
+
+ // Decrement the reference counter for the object
+ Py_DECREF(object);
+
+ // Release the GIL
+ PyGILState_Release(state);
+
+ return 0;
+}
+
+static cookie_io_functions_t PyFileHandleMethods = {
+ .read = PyObject_AsFileHandle_read,
+ .write = PyObject_AsFileHandle_write,
+ .close = PyObject_AsFileHandle_close,
+};
+
+FILE* PyObject_AsFileHandle(PyObject* object, const char* mode) {
+ // We only support reading files
+ if (!mode || *mode != 'r') {
+ errno = ENOTSUP;
return NULL;
}
- return f;
+ // Increment the reference counter for the object
+ Py_INCREF(object);
+
+ return fopencookie(object, mode, PyFileHandleMethods);
}