Sfoglia il codice sorgente

Temporary (?) solution to get a file stream from Python file object.

Stefano Cossu 4 anni fa
parent
commit
fe2ae54a75
1 ha cambiato i file con 46 aggiunte e 21 eliminazioni
  1. 46 21
      cpython/py_graph.h

+ 46 - 21
cpython/py_graph.h

@@ -19,7 +19,7 @@
 typedef struct {
     PyObject_HEAD
     LSUP_CodecIterator *it;
-    char *line;
+    unsigned char *line;
 } StringIteratorObject;
 
 
@@ -31,8 +31,8 @@ StringIterator_dealloc (StringIteratorObject *it_obj)
 static PyObject *
 StringIterator_next (StringIteratorObject *it_obj)
 {
-    unsigned char *rdf_str;
-    LSUP_rc rc = it_obj->it->codec->encode_graph_iter (it_obj->it, &rdf_str);
+    LSUP_rc rc = it_obj->it->codec->encode_graph_iter (
+            it_obj->it, &it_obj->line);
     if (rc != LSUP_OK) {
         if (rc != LSUP_END)
             PyErr_SetString (PyExc_ValueError, "Error encoding graph.");
@@ -41,7 +41,7 @@ StringIterator_next (StringIteratorObject *it_obj)
         return NULL;
     }
 
-    PyObject *rdf_obj = PyUnicode_FromString ((char*)rdf_str);
+    PyObject *rdf_obj = PyUnicode_FromString ((char*)it_obj->line);
     if (UNLIKELY (!rdf_obj)) return NULL;
 
     Py_INCREF (rdf_obj);
@@ -152,38 +152,60 @@ Graph_copy (PyTypeObject *cls, PyObject *src)
 static PyObject *
 Graph_new_from_rdf (PyTypeObject *cls, PyObject *args)
 {
-
-    Py_buffer *buf;
+    PyObject *buf, *fileno_fn, *fileno_obj;
     const char *type;
-    if (! PyArg_ParseTuple (args, "s*s", &buf, &type)) return NULL;
+    if (! PyArg_ParseTuple (args, "Os", &buf, &type)) return NULL;
+
+    // Get the file descriptor from the Python BufferedIO object.
+    // FIXME This is not sure to be reliable. See
+    // https://docs.python.org/3/library/io.html?highlight=io%20bufferedreader#io.IOBase.fileno
+    if (! (fileno_fn = PyObject_GetAttrString (buf, "fileno"))) {
+        PyErr_SetString (PyExc_TypeError, "Object has no fileno function.");
+        return NULL;
+    }
+    PyObject* fileno_args = PyTuple_New(0);
+    if (! (fileno_obj = PyObject_CallObject (fileno_fn, fileno_args))) {
+        PyErr_SetString (PyExc_SystemError, "Error calling fileno function.");
+        return NULL;
+    }
+    int fd = PyLong_AsSize_t (fileno_obj);
+
+    /*
+     * From the Linux man page:
+     *
+     * > The file descriptor is not dup'ed, and will be closed when the stream
+     * > created by fdopen() is closed. The result of applying fdopen() to a
+     * > shared memory object is undefined.
+     *
+     * This handle must not be closed. Leave open for the Python caller to
+     * handle it.
+     */
+    FILE *fh = fdopen (fd, "r");
 
     GraphObject *res = (GraphObject *) cls->tp_alloc(cls, 0);
-    if (!res) goto fail;
+    if (!res) return PyErr_NoMemory();
 
     const LSUP_Codec *codec;
     if (strcmp(type, "nt") == 0) codec = &nt_codec;
     // TODO other codecs here.
     else {
             PyErr_SetString (PyExc_ValueError, "Unsupported codec.");
-            goto fail;
+            return NULL;
     }
 
     size_t ct;
     char *err;
-    codec->decode_graph (buf->buf, &res->ob_struct, &ct, &err);
+    codec->decode_graph (fh, &res->ob_struct, &ct, &err);
 
     TRACE ("Decoded %lu triples.\n", ct);
-    if (!UNLIKELY (err)) goto fail;
+    if (UNLIKELY (err)) {
+        PyErr_SetString (PyExc_IOError, err);
+        return NULL;
+    }
 
-    PyBuffer_Release (buf);
     Py_INCREF(res);
 
     return (PyObject *) res;
-
-fail:
-    PyBuffer_Release (buf);
-
-    return NULL;
 }
 
 
@@ -306,12 +328,12 @@ Graph_encode (PyObject *self, PyObject *args)
             ((GraphObject *)self)->ob_struct);
 
     // Initialize the generator object.
-    //StringIteratorType *it_type;
     StringIteratorObject *it_obj = PyObject_New (
             StringIteratorObject, &StringIteratorType);
     if (!it_obj) return NULL;
 
     it_obj->it = it;
+    it_obj->line = NULL;
 
     Py_INCREF (it_obj);
     return (PyObject *)it_obj;
@@ -319,10 +341,13 @@ Graph_encode (PyObject *self, PyObject *args)
 
 
 static PyMethodDef Graph_methods[] = {
-    {"copy", (PyCFunction) Graph_copy, METH_CLASS, "Copy a graph."},
     {
-        "from_rdf", (PyCFunction) Graph_new_from_rdf, METH_CLASS,
-        "Create a graph from a RDF file."
+        "copy", (PyCFunction) Graph_copy,
+        METH_CLASS | METH_VARARGS, "Copy a graph."
+    },
+    {
+        "from_rdf", (PyCFunction) Graph_new_from_rdf,
+        METH_CLASS | METH_VARARGS, "Create a graph from a RDF file."
     },
     {"add", (PyCFunction) Graph_add, METH_O, "Add triples to a graph."},
     {