Browse Source

Add basic namespace module (not fully integrated).

Stefano Cossu 3 years ago
parent
commit
88011455c2
8 changed files with 276 additions and 9 deletions
  1. 2 2
      TODO.md
  2. 1 1
      cpython/lsup_rdf/__init__.py
  3. 1 1
      cpython/py_graph.h
  4. 28 0
      cpython/py_lsup_rdf.c
  5. 189 0
      cpython/py_namespace.h
  6. 14 2
      include/namespace.h
  7. 14 3
      include/store_mdb.h
  8. 27 0
      src/namespace.c

+ 2 - 2
TODO.md

@@ -12,8 +12,8 @@
     - *D* Basic module framework
     - *D* term, triple, graph modules
     - *D* Codec integration
-    - *W* Graph remove and lookup ops
-    - *P* Namespace module
+    - *D* Graph remove and lookup ops
+    - *W* Namespace module
     - *P* Tests
 - *P* Environment
 - *P* Turtle serialization / deserialization

+ 1 - 1
cpython/lsup_rdf/__init__.py

@@ -25,5 +25,5 @@ def _load_module(mod_name, path):
     return module
 
 
-for mod_name in ('term', 'triple', 'graph'):
+for mod_name in ('term', 'triple', 'namespace', 'graph'):
     locals()[mod_name] = _load_module(mod_name, pkg_path)

+ 1 - 1
cpython/py_graph.h

@@ -442,7 +442,7 @@ finally:
 static PyObject *
 Graph_encode (PyObject *self, PyObject *args)
 {
-    char *type;
+    const char *type;
 
     if (! PyArg_ParseTuple (args, "s", &type)) return NULL;
 

+ 28 - 0
cpython/py_lsup_rdf.c

@@ -12,6 +12,7 @@
 #include <Python.h>
 
 #include "py_graph.h"
+#include "py_namespace.h"
 
 
 static PyModuleDef _lsup_rdf_pkg = {
@@ -87,6 +88,33 @@ PyInit_triple()
 }
 
 
+static PyModuleDef namespace_mod = {
+    PyModuleDef_HEAD_INIT,
+    .m_name = "namespace",
+    .m_doc = "RDF namespace module.",
+    .m_size = -1,
+};
+
+
+PyMODINIT_FUNC
+PyInit_namespace()
+{
+    if (PyType_Ready (&NSMapType) < 0) return NULL;
+
+    PyObject *m = PyModule_Create(&namespace_mod);
+    if (m == NULL) return NULL;
+
+    Py_INCREF(&NSMapType);
+    if (PyModule_AddObject(m, "NSMap", (PyObject *) &NSMapType) < 0) {
+        Py_DECREF(&NSMapType);
+        Py_DECREF(m);
+        return NULL;
+    }
+
+    return m;
+}
+
+
 static PyModuleDef graph_mod = {
     PyModuleDef_HEAD_INIT,
     .m_name = "graph",

+ 189 - 0
cpython/py_namespace.h

@@ -0,0 +1,189 @@
+#ifndef _PY_NAMESPACE_OBJ_H
+#define _PY_NAMESPACE_OBJ_H
+
+#define PY_SSIZE_T_CLEAN
+
+#include <Python.h>
+#include <structmember.h>
+
+#include "namespace.h"
+
+
+typedef struct {
+    PyObject_HEAD
+    LSUP_NSMap      *ob_struct;
+} NSMapObject;
+
+
+static int
+NSMap_init (NSMapObject *self, PyObject *args)
+{
+    self->ob_struct = LSUP_nsmap_new();
+    if (!self->ob_struct) {
+        PyErr_SetString (PyExc_SystemError, "Could not create namespace.");
+        return -1;
+    }
+
+    return 0;
+}
+
+
+static void
+NSMap_dealloc (NSMapObject *self)
+{
+    LSUP_nsmap_free (self->ob_struct);
+    Py_TYPE (self)->tp_free ((PyObject *) self);
+}
+
+
+static PyObject *
+NSMap_add (PyObject *self, PyObject *args)
+{
+    const char *pfx, *ns;
+    if (! PyArg_ParseTuple (args, "ss", &pfx, &ns)) return NULL;
+
+    if (LSUP_nsmap_add (((NSMapObject *)self)->ob_struct, pfx, ns) < 0) {
+        PyErr_SetString (PyExc_ValueError, "Error adding namespace.");
+        return NULL;
+    }
+
+    Py_RETURN_NONE;
+}
+
+
+static PyObject *
+NSMap_remove (PyObject *self, PyObject *pfx_obj)
+{
+    if (PyUnicode_READY (pfx_obj) < 0) return NULL;
+    const char *pfx = PyUnicode_AsUTF8 (pfx_obj);
+
+    if (LSUP_nsmap_remove (((NSMapObject *)self)->ob_struct, pfx) < 0) {
+        PyErr_SetString (PyExc_ValueError, "Error removing namespace.");
+        return NULL;
+    }
+
+    Py_RETURN_NONE;
+}
+
+
+static PyObject *
+NSMap_get (PyObject *self, PyObject *pfx_obj)
+{
+    if (PyUnicode_READY (pfx_obj) < 0) return NULL;
+    const char *pfx = PyUnicode_AsUTF8 (pfx_obj);
+
+    const char *ns = LSUP_nsmap_get (((NSMapObject *)self)->ob_struct, pfx);
+    if (!ns) Py_RETURN_NONE;
+
+    PyObject *ns_obj = PyUnicode_FromString (ns);
+
+    Py_INCREF (ns_obj);
+    return (ns_obj);
+}
+
+
+static PyObject *
+NSMap_normalize_uri (PyObject *self, PyObject *fq_uri_obj)
+{
+    if (PyUnicode_READY (fq_uri_obj) < 0) return NULL;
+    const char *fq_uri = PyUnicode_AsUTF8 (fq_uri_obj);
+
+    char *pfx_uri;
+    LSUP_rc rc = LSUP_nsmap_normalize_uri (
+            ((NSMapObject *)self)->ob_struct, fq_uri, &pfx_uri);
+    if (rc < 0)  {
+        PyErr_SetString (PyExc_ValueError, "Error normalizing URI.");
+        return NULL;
+    }
+
+    PyObject *uri_obj = PyUnicode_FromString (pfx_uri);
+
+    Py_INCREF (uri_obj);
+    return (uri_obj);
+}
+
+
+static PyObject *
+NSMap_denormalize_uri (PyObject *self, PyObject *pfx_uri_obj)
+{
+    if (PyUnicode_READY (pfx_uri_obj) < 0) return NULL;
+    const char *pfx_uri = PyUnicode_AsUTF8 (pfx_uri_obj);
+
+    char *fq_uri;
+    LSUP_rc rc = LSUP_nsmap_denormalize_uri (
+            ((NSMapObject *)self)->ob_struct, pfx_uri, &fq_uri);
+    if (rc < 0)  {
+        PyErr_SetString (PyExc_ValueError, "Error denormalizing URI.");
+        return NULL;
+    }
+
+    PyObject *uri_obj = PyUnicode_FromString (fq_uri);
+
+    Py_INCREF (uri_obj);
+    return (uri_obj);
+}
+
+
+static PyObject *
+NSMap_as_dict (PyObject *self)
+{
+    const char ***data = LSUP_nsmap_dump (((NSMapObject *)self)->ob_struct);
+
+    PyObject *data_obj = PyDict_New();
+    // TODO PyDictProxy_New(PyObject *mapping)
+
+    for (size_t i = 0; data[i] != NULL; i++) {
+        PyObject *val_obj = PyUnicode_FromString(data[i][1]);
+        if (PyDict_SetItemString (data_obj, data[i][0], val_obj) < 0)
+            return NULL;
+    }
+
+    Py_INCREF (data_obj);
+    return data_obj;
+}
+
+
+static PyMethodDef NSMap_methods[] = {
+    {
+        "add", (PyCFunction) NSMap_add, METH_VARARGS,
+        "Add a namespace prefix."
+    },
+    {
+        "remove", (PyCFunction) NSMap_remove, METH_O,
+        "Remove a namespace prefix."
+    },
+    {
+        "get", (PyCFunction) NSMap_get, METH_O,
+        "Get the fully qualified namespace for a prefix."
+    },
+    {
+        "normalize_uri", (PyCFunction) NSMap_normalize_uri, METH_O,
+        "Normalize a URI (i.e. convert from fully qualified to prefixed)."
+    },
+    {
+        "denormalize_uri", (PyCFunction) NSMap_denormalize_uri, METH_O,
+        "Denormalize a URI (i.e. convert from prefixed to fully qualified)."
+    },
+    {
+        "as_dict", (PyCFunction) NSMap_as_dict, METH_NOARGS,
+        "Namespace map as a dictionary of prefixes and namespaces.",
+    },
+    {NULL},
+};
+
+
+PyTypeObject NSMapType = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    .tp_name            = "namespace.NSMap",
+    .tp_doc             = "Namespace prefix map.",
+    .tp_basicsize       = sizeof(NSMapObject),
+    .tp_itemsize        = 0,
+    .tp_flags           = Py_TPFLAGS_DEFAULT,
+    .tp_new             = PyType_GenericNew,
+    .tp_init            = (initproc) NSMap_init,
+    .tp_dealloc         = (destructor) NSMap_dealloc,
+    .tp_methods         = NSMap_methods,
+};
+
+
+#endif  /* _PY_NAMESPACE_OBJ */

+ 14 - 2
include/namespace.h

@@ -97,7 +97,7 @@ LSUP_nsmap_get (const LSUP_NSMap *map, const ns_pfx pfx);
  */
 LSUP_rc
 LSUP_nsmap_normalize_uri (
-        const LSUP_NSMap *map, const char *uri, char **pfx_uri);
+        const LSUP_NSMap *map, const char *fq_uri, char **pfx_uri);
 
 
 /** @brief Convert a namespace-prefixed string to a FQ URI sring if mapped.
@@ -115,6 +115,18 @@ LSUP_nsmap_normalize_uri (
  */
 LSUP_rc
 LSUP_nsmap_denormalize_uri (
-        const LSUP_NSMap *map, const char *pfx_uri, char **uri);
+        const LSUP_NSMap *map, const char *pfx_uri, char **fq_uri);
+
+
+/** @brief Dump all entries of a namespace map.
+ *
+ * @param[in] map Map to dump.
+ *
+ * @return 2-dimensional array of strings, with as many rows as namespace
+ *  entries, and two columns: the first for the namespace prefix, the second
+ *  for the namespace.
+ */
+const char ***
+LSUP_nsmap_dump (const LSUP_NSMap *map);
 
 #endif

+ 14 - 3
include/store_mdb.h

@@ -198,6 +198,12 @@ LSUP_rc LSUP_mdbstore_add(
         const LSUP_SerTriple strp[], const size_t ct, size_t *inserted);
 
 
+/** @brief Delete triples by pattern matching.
+ *
+ * The ss, sp, so, sc terms act as a matching pattern as documented in
+ * #LSUP_mdbstore_lookup. if not NULL, ct yields the number of triples actually
+ * deleted.
+ */
 LSUP_rc
 LSUP_mdbstore_remove(
         LSUP_MDBStore *store, const LSUP_Buffer *ss, const LSUP_Buffer *sp,
@@ -209,11 +215,16 @@ LSUP_mdbstore_remove(
  * This function may return a count of matches and/or an iterator of results as
  * serialized triples.
  *
+ * Any and all of the terms may be NULL, which indicates an unbound query
+ * term. Stores with context not set will always ignore the fourth term.
+ *
  * @param store[in] The store to be queried.
  *
- * @param sspo Serialized triple representing the s, p, o
- * terms. Any and all of these may be NULL, which indicates an unbound query
- * term. Stores with context not set will always ignore the fourth term.
+ * @param ss Buffer representing the serialized s term.
+ *
+ * @param sp Buffer representing the serialized p term.
+ *
+ * @param so Buffer representing the serialized o term.
  *
  * @param sc Serialized context to limit search to. It may be NULL, in which
  *  case search is done in all contexts. Note that triples inserted without

+ 27 - 0
src/namespace.c

@@ -188,3 +188,30 @@ LSUP_nsmap_denormalize_uri (
 
     return LSUP_OK;
 }
+
+
+const char ***
+LSUP_nsmap_dump (const NSMap *map)
+{
+    size_t i = 0;
+
+    Namespace *cur;
+    for (cur = map->pn; cur != NULL; cur = cur->hh.next) i++;
+
+    const char ***data = malloc (2 * i + 1 * sizeof (char *));
+    if (UNLIKELY (!data)) return NULL;
+
+    for (size_t j = 0; j < i; j++) {
+        data[j] = malloc (2 * sizeof (char *));
+        if (UNLIKELY (!data[j])) return NULL;
+    }
+
+    i = 0;
+    for (cur = map->pn; cur != NULL; cur = cur->hh.next) {
+        data[i][0] = (const char *)cur->pfx;
+        data[i++][1] = (const char *)cur->ns;
+    }
+    data[i] = NULL; // Sentinel
+
+    return data;
+}