You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucy.apache.org by ma...@apache.org on 2016/02/25 00:07:34 UTC

[07/20] lucy-clownfish git commit: Conversion routines from Python to Clownfish.

Conversion routines from Python to Clownfish.


Project: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/commit/99d6bc0f
Tree: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/tree/99d6bc0f
Diff: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/diff/99d6bc0f

Branch: refs/heads/master
Commit: 99d6bc0fe89382c8492d97825fc03ca82eac051f
Parents: 999b39f
Author: Marvin Humphrey <ma...@rectangular.com>
Authored: Wed Jan 27 15:35:08 2016 -0800
Committer: Marvin Humphrey <ma...@rectangular.com>
Committed: Tue Feb 23 18:22:03 2016 -0800

----------------------------------------------------------------------
 runtime/python/cfext/CFBind.c | 196 +++++++++++++++++++++++++++++++++++++
 runtime/python/cfext/CFBind.h |  34 +++++++
 2 files changed, 230 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/99d6bc0f/runtime/python/cfext/CFBind.c
----------------------------------------------------------------------
diff --git a/runtime/python/cfext/CFBind.c b/runtime/python/cfext/CFBind.c
index d3c39c2..501c342 100644
--- a/runtime/python/cfext/CFBind.c
+++ b/runtime/python/cfext/CFBind.c
@@ -37,6 +37,7 @@
 #include "Clownfish/String.h"
 #include "Clownfish/TestHarness/TestUtils.h"
 #include "Clownfish/Util/Memory.h"
+#include "Clownfish/Util/StringHelper.h"
 #include "Clownfish/Vector.h"
 
 static bool Err_initialized;
@@ -85,6 +86,201 @@ CFBind_reraise_pyerr(cfish_Class *err_klass, cfish_String *mess) {
     cfish_Err_throw_mess(err_klass, new_mess);
 }
 
+static cfish_Vector*
+S_py_list_to_vector(PyObject *list) {
+    Py_ssize_t size = PyList_GET_SIZE(list);
+    cfish_Vector *vec = cfish_Vec_new(size);
+    for (Py_ssize_t i = 0; i < size; i++) {
+        CFISH_Vec_Store(vec, i, CFBind_py_to_cfish(PyList_GET_ITEM(list, i), NULL));
+    }
+    return vec;
+}
+
+static cfish_Hash*
+S_py_dict_to_hash(PyObject *dict) {
+    Py_ssize_t pos = 0;
+    PyObject *key, *value;
+    cfish_Hash *hash = cfish_Hash_new(PyDict_Size(dict));
+    while (PyDict_Next(dict, &pos, &key, &value)) {
+        char *ptr;
+        Py_ssize_t size;
+        PyObject *stringified = key;
+        if (!PyUnicode_CheckExact(key)) {
+            stringified = PyObject_Str(key);
+        }
+        ptr = PyUnicode_AsUTF8AndSize(stringified, &size);
+        if (!ptr) {
+            cfish_String *mess
+                = CFISH_MAKE_MESS("Failed to stringify as UTF-8");
+            CFBind_reraise_pyerr(CFISH_ERR, mess);
+        }
+        CFISH_Hash_Store_Utf8(hash, ptr, size, CFBind_py_to_cfish(value, NULL));
+        if (stringified != key) {
+            Py_DECREF(stringified);
+        }
+    }
+    return hash;
+}
+
+static cfish_Obj*
+S_maybe_increment(void *vobj, bool increment) {
+    if (increment) {
+        return CFISH_INCREF((cfish_Obj*)vobj);
+    }
+    return (cfish_Obj*)vobj;
+}
+
+static bool
+S_maybe_py_to_cfish(PyObject *py_obj, cfish_Class *klass, bool increment,
+                    bool nullable, void *allocation, cfish_Obj **obj_ptr) {
+    CFISH_UNUSED_VAR(allocation); // FIXME implement stack strings
+
+    if (!py_obj || py_obj == Py_None) {
+        *obj_ptr = NULL;
+        return nullable;
+    }
+
+	// Default to accepting any type.
+	if (klass == NULL) {
+		klass = CFISH_OBJ;
+	}
+
+    if (S_py_obj_is_a(py_obj, klass)) {
+        *obj_ptr = S_maybe_increment(py_obj, increment);
+        return true;
+    }
+    else if (py_obj == Py_True) {
+        if (klass != CFISH_BOOLEAN && klass != CFISH_OBJ) {
+            return false;
+        }
+        *obj_ptr = S_maybe_increment(CFISH_TRUE, increment);
+        return true;
+    }
+    else if (py_obj == Py_False) {
+        if (klass != CFISH_BOOLEAN && klass != CFISH_OBJ) {
+            return false;
+        }
+        *obj_ptr = S_maybe_increment(CFISH_FALSE, increment);
+        return true;
+    }
+    else if (klass == CFISH_BOOLEAN) {
+        int truthiness = PyObject_IsTrue(py_obj);
+        if (truthiness == 1) {
+            *obj_ptr = S_maybe_increment(CFISH_TRUE, increment);
+        }
+        else if (truthiness == 0) {
+            *obj_ptr = S_maybe_increment(CFISH_FALSE, increment);
+        }
+        else {
+            return false;
+        }
+        return true;
+    }
+
+    // From here on out, we're going to return a new Clownfish object.  The
+    // caller has to take ownership of a refcount; if they don't want to, then
+    // fail rather than attempt to return an object with a refcount of 0.
+    if (!increment) {
+        return false;
+    }
+
+    if (PyUnicode_CheckExact(py_obj)) {
+        if (klass != CFISH_STRING && klass != CFISH_OBJ) {
+            return false;
+        }
+        // TODO: Allow Clownfish String to wrap buffer of Python str?
+        Py_ssize_t size;
+        char *ptr = PyUnicode_AsUTF8AndSize(py_obj, &size);
+        // TODO: Can we guarantee that Python will always supply valid UTF-8?
+        if (!ptr || !cfish_StrHelp_utf8_valid(ptr, size)) {
+            return false;
+        }
+        *obj_ptr = (cfish_Obj*)cfish_Str_new_from_trusted_utf8(ptr, size);
+        return true;
+    }
+    else if (PyBytes_CheckExact(py_obj)) {
+        if (klass != CFISH_BLOB && klass != CFISH_OBJ) {
+            return false;
+        }
+        char *ptr = PyBytes_AS_STRING(py_obj);
+        Py_ssize_t size = PyBytes_GET_SIZE(py_obj);
+        *obj_ptr = (cfish_Obj*)cfish_BB_new_bytes(ptr, size);
+        return true;
+    }
+    else if (PyList_CheckExact(py_obj)) {
+        if (klass != CFISH_VECTOR && klass != CFISH_OBJ) {
+            return false;
+        }
+        *obj_ptr = (cfish_Obj*)S_py_list_to_vector(py_obj);
+        return true;
+    }
+    else if (PyDict_CheckExact(py_obj)) {
+        if (klass != CFISH_HASH && klass != CFISH_OBJ) {
+            return false;
+        }
+        *obj_ptr = (cfish_Obj*)S_py_dict_to_hash(py_obj);
+        return true;
+    }
+    else if (PyLong_CheckExact(py_obj)) {
+        if (klass != CFISH_INTEGER && klass != CFISH_OBJ) {
+            return false;
+        }
+        // Raises ValueError on overflow.
+        int64_t value = PyLong_AsLongLong(py_obj);
+        if (PyErr_Occurred()) {
+            return false;
+        }
+        *obj_ptr = (cfish_Obj*)cfish_Int_new(value);
+        return true;
+    }
+    else if (PyFloat_CheckExact(py_obj)) {
+        if (klass != CFISH_FLOAT && klass != CFISH_OBJ) {
+            return false;
+        }
+        double value = PyFloat_AsDouble(py_obj);
+        if (PyErr_Occurred()) {
+            return false;
+        }
+        *obj_ptr = (cfish_Obj*)cfish_Float_new(value);
+        return true;
+    }
+
+    // The value did not meet the required spec, so return false to indicate
+    // failure.
+    return false;
+}
+
+cfish_Obj*
+CFBind_py_to_cfish_nullable(PyObject *py_obj, cfish_Class *klass) {
+    cfish_Obj *retval;
+    bool success = S_maybe_py_to_cfish(py_obj, klass, true, true, NULL, &retval);
+    if (!success) {
+        CFISH_THROW(CFISH_ERR, "Can't convert to %o", klass);
+    }
+    return retval;
+}
+
+cfish_Obj*
+CFBind_py_to_cfish(PyObject *py_obj, cfish_Class *klass) {
+    cfish_Obj *retval;
+    bool success = S_maybe_py_to_cfish(py_obj, klass, true, false, NULL, &retval);
+    if (!success) {
+        CFISH_THROW(CFISH_ERR, "Can't convert to %o", klass);
+    }
+    return retval;
+}
+
+cfish_Obj*
+CFBind_py_to_cfish_noinc(PyObject *py_obj, cfish_Class *klass,
+                         void *allocation) {
+    cfish_Obj *retval;
+    bool success = S_maybe_py_to_cfish(py_obj, klass, false, false, allocation, &retval);
+    if (!success) {
+        CFISH_THROW(CFISH_ERR, "Can't convert to %o", klass);
+    }
+    return retval;
+}
+
 static int
 S_convert_sint(PyObject *py_obj, void *ptr, bool nullable, unsigned width) {
     if (py_obj == Py_None) {

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/99d6bc0f/runtime/python/cfext/CFBind.h
----------------------------------------------------------------------
diff --git a/runtime/python/cfext/CFBind.h b/runtime/python/cfext/CFBind.h
index 6637fca..e4e0f23 100644
--- a/runtime/python/cfext/CFBind.h
+++ b/runtime/python/cfext/CFBind.h
@@ -66,6 +66,40 @@ CFBind_cfish_to_py_zeroref(struct cfish_Obj *obj) {
     }
 }
 
+/** Perform recursive conversion of Python objects to Clownfish, return an
+  * incremented Clownfish Obj.
+  *
+  *     string -> String
+  *     bytes  -> Blob
+  *     None   -> NULL
+  *     bool   -> Boolean
+  *     int    -> Integer
+  *     float  -> Float
+  *     list   -> Vector
+  *     dict   -> Hash
+  *
+  * Python dict keys will be stringified.  Other Clownfish objects will be
+  * left intact.  Anything else will be stringified.
+  */
+cfish_Obj*
+CFBind_py_to_cfish(PyObject *py_obj, cfish_Class *klass);
+
+/** As CFBind_py_to_cfish above, but returns NULL if the PyObject is None
+  * or NULL.
+  */
+cfish_Obj*
+CFBind_py_to_cfish_nullable(PyObject *py_obj, cfish_Class *klass);
+
+/** As CFBind_py_to_cfish above, but returns an object that can be used for a
+  * while with no need to decref.
+  *
+  * If `klass` is STRING or OBJ, `allocation` must point to stack-allocated
+  * memory that can hold a String. Otherwise, `allocation` should be NULL.
+  */
+cfish_Obj*
+CFBind_py_to_cfish_noinc(PyObject *py_obj, cfish_Class *klass,
+                         void *allocation);
+
 /* ParseTuple conversion routines for primitive numeric types.
  *
  * If the value of `input` is out of range for the an integer C type, an