py_graph.h 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693
  1. #ifndef _PY_GRAPH_MOD_H
  2. #define _PY_GRAPH_MOD_H
  3. #define PY_SSIZE_T_CLEAN
  4. #include <Python.h>
  5. #include <structmember.h>
  6. #include "graph.h"
  7. #include "codec/codec_nt.h"
  8. #include "codec/codec_ttl.h"
  9. #include "py_triple.h"
  10. /*
  11. * Iterator helpers.
  12. */
  13. /*
  14. * String iterator for encoder output.
  15. *
  16. * Yields one string (one or more lines) at a time.
  17. */
  18. typedef struct {
  19. PyObject_HEAD
  20. LSUP_CodecIterator *it;
  21. unsigned char *line;
  22. } StringIteratorObject;
  23. static void
  24. StringIterator_dealloc (StringIteratorObject *it_obj)
  25. { it_obj->it->codec->encode_graph_done (it_obj->it); }
  26. static PyObject *
  27. StringIterator_next (StringIteratorObject *it_obj)
  28. {
  29. LSUP_rc rc = it_obj->it->codec->encode_graph_iter (
  30. it_obj->it, &it_obj->line);
  31. if (rc != LSUP_OK) {
  32. if (rc != LSUP_END)
  33. PyErr_SetString (PyExc_ValueError, "Error encoding graph.");
  34. // If not an error, this raises StopIteration.
  35. return NULL;
  36. }
  37. return PyUnicode_FromString ((char *) it_obj->line);
  38. }
  39. /*
  40. * String iterator type.
  41. *
  42. * Objects of this type are never generated from Python code, rather from
  43. * Graph_encode, hence the type has no special new or init function.
  44. */
  45. PyTypeObject StringIteratorType = {
  46. PyVarObject_HEAD_INIT(&PyType_Type, 0)
  47. .tp_name = "graph.StringIterator",
  48. .tp_basicsize = sizeof (StringIteratorObject),
  49. .tp_itemsize = 0,
  50. .tp_flags = Py_TPFLAGS_DEFAULT,
  51. .tp_dealloc = (destructor) StringIterator_dealloc,
  52. .tp_iter = PyObject_SelfIter,
  53. .tp_iternext = (iternextfunc)StringIterator_next,
  54. };
  55. /*
  56. * Graph iterator.
  57. *
  58. * Yields one triple at a time.
  59. */
  60. typedef struct {
  61. PyObject_HEAD
  62. LSUP_GraphIterator *it;
  63. LSUP_Triple *spo;
  64. } GraphIteratorObject;
  65. static void
  66. GraphIterator_dealloc (GraphIteratorObject *it_obj)
  67. {
  68. LSUP_graph_iter_free (it_obj->it);
  69. free (it_obj->spo);
  70. }
  71. static PyObject *
  72. GraphIterator_next (GraphIteratorObject *it_obj)
  73. {
  74. LSUP_rc rc = LSUP_graph_iter_next (it_obj->it, it_obj->spo);
  75. if (rc != LSUP_OK) {
  76. if (rc != LSUP_END)
  77. PyErr_SetString (PyExc_ValueError, "Error encoding graph.");
  78. // If not an error, this raises StopIteration.
  79. return NULL;
  80. }
  81. return build_triple (it_obj->spo);
  82. }
  83. /*
  84. * Graph iterator type.
  85. */
  86. PyTypeObject GraphIteratorType = {
  87. PyVarObject_HEAD_INIT(&PyType_Type, 0)
  88. .tp_name = "graph.GraphIterator",
  89. .tp_basicsize = sizeof (GraphIteratorObject),
  90. .tp_itemsize = 0,
  91. .tp_flags = Py_TPFLAGS_DEFAULT,
  92. .tp_dealloc = (destructor) GraphIterator_dealloc,
  93. .tp_iter = PyObject_SelfIter,
  94. .tp_iternext = (iternextfunc) GraphIterator_next,
  95. };
  96. /*
  97. * Graph stuff.
  98. */
  99. typedef struct {
  100. PyObject_HEAD
  101. LSUP_Graph *ob_struct;
  102. } GraphObject;
  103. static int
  104. Graph_init (GraphObject *self, PyObject *args, PyObject *kwargs)
  105. {
  106. unsigned char store_type;
  107. PyObject *uri_obj = NULL;
  108. LSUP_Term *uri = NULL, *src_uri = NULL;
  109. static char *kwlist[] = {"", "uri_obj", NULL};
  110. if (!PyArg_ParseTupleAndKeywords (
  111. args, kwargs, "b|O", kwlist, &store_type, &uri_obj))
  112. return -1;
  113. if (uri_obj) {
  114. if (!PyObject_TypeCheck (uri_obj, &TermType)) {
  115. PyErr_SetString (PyExc_TypeError, "uri is not a Term type.");
  116. return -1;
  117. }
  118. src_uri = ((TermObject *) uri_obj)->ob_struct;
  119. uri = LSUP_iriref_new (src_uri->data, LSUP_iriref_nsm (src_uri));
  120. if (! LSUP_IS_IRI (uri)) {
  121. PyErr_SetString (PyExc_TypeError, "uri is not a IRIREF type.");
  122. return -1;
  123. }
  124. } else uri = LSUP_iriref_new (NULL, NULL);
  125. // Set up the store if a function for that is defined.
  126. const LSUP_StoreInt *sif = LSUP_store_int (store_type);
  127. if (UNLIKELY (!sif)) {
  128. PyErr_SetString (
  129. PyExc_TypeError,
  130. "No interface defined for given store type.");
  131. return -1;
  132. }
  133. if (sif->setup_fn) {
  134. if (sif->setup_fn(NULL, false) < LSUP_OK) {
  135. PyErr_SetString (
  136. PyExc_IOError, "Error initializing back end store.");
  137. return -1;
  138. }
  139. }
  140. // TODO Make store ID, nsm and initial size accessible.
  141. self->ob_struct = LSUP_graph_new (
  142. uri, (LSUP_StoreType) store_type, NULL, NULL, 0);
  143. if (!self->ob_struct) {
  144. PyErr_SetString (PyExc_ValueError, "Could not create graph.");
  145. return -1;
  146. }
  147. LSUP_Term *uri2 = LSUP_graph_uri (self->ob_struct);
  148. log_debug("Graph URI (%p): %s", uri2, uri2->data);
  149. return 0;
  150. }
  151. static void
  152. Graph_dealloc (GraphObject *self)
  153. {
  154. LSUP_graph_free (self->ob_struct);
  155. Py_TYPE (self)->tp_free ((PyObject *) self);
  156. }
  157. static PyObject *
  158. Graph_get_uri (GraphObject *self, void *closure)
  159. {
  160. LSUP_Term *uri = LSUP_graph_uri (self->ob_struct);
  161. log_debug("Graph URI address: %p", uri);
  162. log_debug("Graph URI: %s", uri->data);
  163. return PyUnicode_FromString (uri->data);
  164. }
  165. static int
  166. Graph_set_uri (GraphObject *self, PyObject *value, void *closure)
  167. {
  168. if (!PyObject_TypeCheck (value, &TermType)) {
  169. PyErr_SetString (PyExc_TypeError, "URI is not a Term type.");
  170. return -1;
  171. }
  172. LSUP_Term *gr_uri = ((TermObject*)value)->ob_struct;
  173. log_debug ("New graph URI: %s", (gr_uri->data));
  174. LSUP_rc rc = LSUP_graph_set_uri (self->ob_struct, LSUP_term_copy (gr_uri));
  175. return rc == LSUP_OK ? 0 : -1;
  176. }
  177. static PyGetSetDef Graph_getsetters[] = {
  178. {
  179. "uri", (getter) Graph_get_uri, (setter) Graph_set_uri,
  180. "Graph URI.", NULL
  181. },
  182. {NULL}
  183. };
  184. static int
  185. Graph_copy_contents (GraphObject *self, GraphObject *dest)
  186. {
  187. if (LSUP_graph_copy_contents (self->ob_struct, dest->ob_struct) < LSUP_OK)
  188. {
  189. PyErr_SetString (PyExc_ValueError, "Error copying graph contents.");
  190. return -1;
  191. }
  192. return 0;
  193. };
  194. #if 0
  195. static PyObject *
  196. Graph_store (PyObject *self)
  197. {
  198. GraphObject *dest_obj = (GraphObject *) Py_TYPE (self)->tp_alloc(
  199. Py_TYPE (self), 0);
  200. if (!dest_obj) return PyErr_NoMemory();
  201. // TODO Make store ID, nsm and initial size accessible.
  202. LSUP_Graph *dest = LSUP_graph_new (
  203. uri, LSUP_STORE_HTABLE, NULL, NULL, 0);
  204. if (!dest) {
  205. PyErr_SetString (PyExc_ValueError, "Could not create graph.");
  206. return -1;
  207. }
  208. LSUP_rc rc = LSUP_GraphStore (
  209. ((GraphObject *) self)->ob_struct, &((dest_obj)->ob_struct), NULL);
  210. if (rc != LSUP_OK) {
  211. log_error (LSUP_strerror (rc));
  212. PyErr_SetString (PyExc_SystemError, "Error storing graph.");
  213. return NULL;
  214. };
  215. Py_INCREF (dest_obj);
  216. return (PyObject *)dest_obj;
  217. }
  218. #endif
  219. static PyObject *
  220. Graph_new_from_rdf (PyTypeObject *cls, PyObject *args)
  221. {
  222. PyObject *buf, *fileno_fn, *fileno_obj;
  223. const char *type;
  224. if (! PyArg_ParseTuple (args, "Os", &buf, &type)) return NULL;
  225. // Get the file descriptor from the Python BufferedIO object.
  226. // FIXME This is not sure to be reliable. See
  227. // https://docs.python.org/3/library/io.html?highlight=io%20bufferedreader#io.IOBase.fileno
  228. if (! (fileno_fn = PyObject_GetAttrString (buf, "fileno"))) {
  229. PyErr_SetString (PyExc_TypeError, "Object has no fileno function.");
  230. return NULL;
  231. }
  232. PyObject* fileno_args = PyTuple_New(0);
  233. if (! (fileno_obj = PyObject_CallObject (fileno_fn, fileno_args))) {
  234. PyErr_SetString (PyExc_SystemError, "Error calling fileno function.");
  235. return NULL;
  236. }
  237. int fd = PyLong_AsSize_t (fileno_obj);
  238. /*
  239. * From the Linux man page:
  240. *
  241. * > The file descriptor is not dup'ed, and will be closed when the stream
  242. * > created by fdopen() is closed. The result of applying fdopen() to a
  243. * > shared memory object is undefined.
  244. *
  245. * Hence the `dup()`.
  246. */
  247. fd = dup (fd);
  248. FILE *fh = fdopen (fd, "r");
  249. GraphObject *res = (GraphObject *) cls->tp_alloc(cls, 0);
  250. if (!res) return PyErr_NoMemory();
  251. const LSUP_Codec *codec;
  252. if (strcmp(type, "nt") == 0) codec = &nt_codec;
  253. // TODO other codecs here.
  254. else {
  255. PyErr_SetString (PyExc_ValueError, "Unsupported codec.");
  256. return NULL;
  257. }
  258. size_t ct;
  259. char *err;
  260. codec->decode_graph (fh, &res->ob_struct, &ct, &err);
  261. fclose (fh);
  262. log_debug ("Decoded %lu triples.", ct);
  263. if (UNLIKELY (err)) {
  264. PyErr_SetString (PyExc_IOError, err);
  265. return NULL;
  266. }
  267. Py_INCREF (res);
  268. return (PyObject *) res;
  269. }
  270. /** @brief Build a triple pattern for lookup purposes.
  271. */
  272. inline static int build_trp_pattern (PyObject *args, LSUP_Term *spo[])
  273. {
  274. PyObject *s_obj, *p_obj, *o_obj;
  275. if (! (PyArg_ParseTuple (args, "OOO", &s_obj, &p_obj, &o_obj)))
  276. return -1;
  277. if (s_obj != Py_None && !PyObject_TypeCheck (s_obj, &TermType)) {
  278. PyErr_SetString (PyExc_TypeError, "Subject must be a term or None.");
  279. return -1;
  280. }
  281. if (p_obj != Py_None && !PyObject_TypeCheck (p_obj, &TermType)) {
  282. PyErr_SetString (PyExc_TypeError, "Predicate must be a term or None.");
  283. return -1;
  284. }
  285. if (o_obj != Py_None && !PyObject_TypeCheck (o_obj, &TermType)) {
  286. PyErr_SetString (PyExc_TypeError, "Object must be a term or None.");
  287. return -1;
  288. }
  289. spo[0] = s_obj != Py_None ? ((TermObject *)s_obj)->ob_struct : NULL;
  290. spo[1] = p_obj != Py_None ? ((TermObject *)p_obj)->ob_struct : NULL;
  291. spo[2] = o_obj != Py_None ? ((TermObject *)o_obj)->ob_struct : NULL;
  292. return 0;
  293. }
  294. /*
  295. static PyObject *
  296. Graph_new_set_from_store_lookup (PyTypeObject *cls, PyObject *args)
  297. {
  298. LSUP_Term *spo[3];
  299. if (UNLIKELY ((build_trp_pattern (args, spo)) < 0)) return NULL;
  300. LSUP_Graph **gr_a = LSUP_graph_new_lookup (spo[0], spo[1], spo[2]);
  301. if (UNLIKELY (!gr_a)) {
  302. // TODO implement LSUP_strerror for more details.
  303. PyErr_SetString (PyExc_SystemError, "Error looking up triples.");
  304. return NULL;
  305. }
  306. PyObject *ret = PySet_New (NULL);
  307. size_t i;
  308. for (i = 0; gr_a[i] != NULL; i++) {
  309. PyObject *gr_obj = cls->tp_alloc(cls, 0);
  310. if (!gr_obj) return NULL;
  311. ((GraphObject *) gr_obj)->ob_struct = gr_a[i];
  312. Py_INCREF (gr_obj);
  313. PySet_Add (ret, gr_obj);
  314. }
  315. log_debug ("Found %lu graphs for pattern.", i + 1);
  316. Py_INCREF (ret);
  317. return ret;
  318. }
  319. */
  320. static PyObject *
  321. Graph_richcmp (PyObject *self, PyObject *other, int op)
  322. {
  323. // Only equality and non-equality are supported.
  324. if (op != Py_EQ && op != Py_NE) Py_RETURN_NOTIMPLEMENTED;
  325. LSUP_Graph *t1 = ((GraphObject *) self)->ob_struct;
  326. LSUP_Graph *t2 = ((GraphObject *) other)->ob_struct;
  327. if (LSUP_graph_equals (t1, t2) ^ (op == Py_NE)) Py_RETURN_TRUE;
  328. Py_RETURN_FALSE;
  329. }
  330. static inline PyObject *
  331. Graph_bool_op (
  332. PyTypeObject *cls, LSUP_bool_op op, PyObject *gr1, PyObject *gr2)
  333. {
  334. if (! PyObject_TypeCheck (gr1, cls) || ! PyObject_TypeCheck (gr2, cls))
  335. return NULL;
  336. GraphObject *res = (GraphObject *) cls->tp_alloc (cls, 0);
  337. if (!res) return NULL;
  338. LSUP_Graph *dest = LSUP_graph_new (
  339. NULL, LSUP_STORE_HTABLE, NULL, NULL, 0);
  340. if (!dest) {
  341. PyErr_SetString (PyExc_Exception, "Could not create destination graph.");
  342. return NULL;
  343. }
  344. LSUP_rc rc = LSUP_graph_bool_op (
  345. op, ((GraphObject *) gr1)->ob_struct,
  346. ((GraphObject *) gr2)->ob_struct, res->ob_struct);
  347. if (rc < LSUP_OK) {
  348. PyErr_SetString (PyExc_Exception, "Error performing boolean operation.");
  349. return NULL;
  350. }
  351. Py_INCREF(res);
  352. return (PyObject *) res;
  353. }
  354. static PyObject *
  355. Graph_add (PyObject *self, PyObject *triples)
  356. {
  357. // Triple may be any iterable.
  358. PyObject *iter = PyObject_GetIter (triples);
  359. if (! iter) {
  360. PyErr_SetString (
  361. PyExc_ValueError, "Triples object cannot be iterated.");
  362. return NULL;
  363. }
  364. PyObject *trp_obj;
  365. int rc = 0;
  366. size_t ct = 0;
  367. LSUP_GraphIterator *it = LSUP_graph_add_init (
  368. ((GraphObject *)self)->ob_struct);
  369. while ((trp_obj = PyIter_Next (iter))) {
  370. if (!PyObject_TypeCheck (trp_obj, &TripleType)) {
  371. PyErr_SetString (
  372. PyExc_ValueError, "Object is not a triple.");
  373. rc = -1;
  374. goto finally;
  375. }
  376. log_trace ("Inserting triple #%lu", ct);
  377. LSUP_rc db_rc = LSUP_graph_add_iter (
  378. it, ((TripleObject *) trp_obj)->ob_struct);
  379. if (db_rc == LSUP_OK) {
  380. rc = LSUP_OK;
  381. ct++;
  382. } else if (UNLIKELY (db_rc < 0)) {
  383. PyErr_SetString (PyExc_ValueError, "Error while adding triples.");
  384. rc = -1;
  385. goto finally;
  386. }
  387. // If db_rc > 0, it's a no-op and the counter is not increased.
  388. }
  389. finally:
  390. LSUP_graph_add_done (it);
  391. if (rc == LSUP_OK) return PyLong_FromSize_t (ct);
  392. return NULL;
  393. }
  394. static PyObject *Graph_remove (PyObject *self, PyObject *args)
  395. {
  396. LSUP_rc rc;
  397. LSUP_Term *spo[3];
  398. rc = build_trp_pattern (args, spo);
  399. if (rc < 0) goto finally;
  400. size_t ct;
  401. rc = LSUP_graph_remove (
  402. ((GraphObject *)self)->ob_struct, spo[0], spo[1], spo[2], &ct);
  403. if (rc < 0) {
  404. // TODO implement strerror for more details.
  405. PyErr_SetString (PyExc_SystemError, "Error removing triples.");
  406. goto finally;
  407. }
  408. log_debug ("Removed %lu triples.", ct);
  409. finally:
  410. if (rc < 0) return NULL;
  411. Py_RETURN_NONE;
  412. }
  413. static PyObject *Graph_lookup (PyObject *self, PyObject *args)
  414. {
  415. LSUP_rc rc;
  416. GraphIteratorObject *it_obj = NULL;
  417. LSUP_Term *spo[3];
  418. rc = build_trp_pattern (args, spo);
  419. if (UNLIKELY (rc < 0)) goto finally;
  420. size_t ct;
  421. LSUP_GraphIterator *it = LSUP_graph_lookup (
  422. ((GraphObject *)self)->ob_struct, spo[0], spo[1], spo[2], &ct);
  423. if (UNLIKELY (!it)) {
  424. // TODO implement LSUP_strerror for more details.
  425. PyErr_SetString (PyExc_SystemError, "Error looking up triples.");
  426. rc = -1;
  427. goto finally;
  428. }
  429. log_debug ("Found %lu triples.", ct);
  430. // Initialize the generator object.
  431. it_obj = PyObject_New (
  432. GraphIteratorObject, &GraphIteratorType);
  433. if (UNLIKELY (!it_obj)) return PyErr_NoMemory();
  434. it_obj->it = it;
  435. it_obj->spo = TRP_DUMMY;
  436. Py_INCREF (it_obj);
  437. finally:
  438. return (PyObject *)it_obj;
  439. }
  440. static PyObject *
  441. Graph_encode (PyObject *self, PyObject *args)
  442. {
  443. const char *type;
  444. if (! PyArg_ParseTuple (args, "s", &type)) return NULL;
  445. const LSUP_Codec *codec;
  446. if (strcmp(type, "nt") == 0) codec = &nt_codec;
  447. // TODO other codecs here.
  448. else {
  449. PyErr_SetString (PyExc_ValueError, "Unsupported codec.");
  450. return NULL;
  451. }
  452. LSUP_CodecIterator *it = codec->encode_graph_init (
  453. ((GraphObject *)self)->ob_struct);
  454. // Initialize the generator object.
  455. StringIteratorObject *it_obj = PyObject_New (
  456. StringIteratorObject, &StringIteratorType);
  457. if (!it_obj) return NULL;
  458. it_obj->it = it;
  459. it_obj->line = NULL;
  460. Py_INCREF (it_obj);
  461. return (PyObject *)it_obj;
  462. }
  463. static PyMethodDef Graph_methods[] = {
  464. {
  465. "copy", (PyCFunction) Graph_copy_contents, METH_CLASS | METH_VARARGS,
  466. "Copy the contents of a graph into another."
  467. },
  468. {
  469. "from_rdf", (PyCFunction) Graph_new_from_rdf,
  470. METH_CLASS | METH_VARARGS,
  471. "Create a graph from a RDF file."
  472. },
  473. /*
  474. {
  475. "from_lookup", (PyCFunction) Graph_new_set_from_store_lookup,
  476. METH_CLASS | METH_VARARGS,
  477. "Create a set of graphs from a store SPO lookup."
  478. },
  479. */
  480. /*
  481. {
  482. "store", (PyCFunction) Graph_store, METH_NOARGS,
  483. "Store a graph into the permanent back end."
  484. },
  485. */
  486. {"add", (PyCFunction) Graph_add, METH_O, "Add triples to a graph."},
  487. {
  488. "remove", (PyCFunction) Graph_remove, METH_VARARGS,
  489. "Remove triples from a graph by matching a pattern."
  490. },
  491. {
  492. "lookup", (PyCFunction) Graph_lookup, METH_VARARGS,
  493. "Look triples in a graph by matching a pattern."
  494. },
  495. {
  496. "to_rdf", (PyCFunction) Graph_encode, METH_VARARGS,
  497. "Encode a graph into a RDF byte buffer."
  498. },
  499. {NULL},
  500. };
  501. static inline PyObject *Graph_bool_and (
  502. PyTypeObject *cls, PyObject *gr1, PyObject *gr2)
  503. { return Graph_bool_op (cls, LSUP_BOOL_INTERSECTION, gr1, gr2); }
  504. static inline PyObject *Graph_bool_or (
  505. PyTypeObject *cls, PyObject *gr1, PyObject *gr2)
  506. { return Graph_bool_op (cls, LSUP_BOOL_UNION, gr1, gr2); }
  507. static inline PyObject *Graph_bool_subtract (
  508. PyTypeObject *cls, PyObject *gr1, PyObject *gr2)
  509. { return Graph_bool_op (cls, LSUP_BOOL_SUBTRACTION, gr1, gr2); }
  510. static inline PyObject *Graph_bool_xor (
  511. PyTypeObject *cls, PyObject *gr1, PyObject *gr2)
  512. { return Graph_bool_op (cls, LSUP_BOOL_XOR, gr1, gr2); }
  513. static PyNumberMethods Graph_number_methods = {
  514. .nb_and = (binaryfunc) Graph_bool_and,
  515. .nb_or = (binaryfunc) Graph_bool_or,
  516. .nb_subtract = (binaryfunc) Graph_bool_subtract,
  517. .nb_xor = (binaryfunc) Graph_bool_xor,
  518. };
  519. static int
  520. Graph_contains (PyObject *self, PyObject *value)
  521. {
  522. if (!PyObject_TypeCheck (value, &TripleType)) {
  523. PyErr_SetString (PyExc_ValueError, "Error parsing input value.");
  524. return -1;
  525. }
  526. int rc = LSUP_graph_contains (
  527. ((GraphObject *) self)->ob_struct,
  528. ((TripleObject *) value)->ob_struct);
  529. return rc;
  530. }
  531. static Py_ssize_t
  532. Graph_get_size (PyObject *self)
  533. { return LSUP_graph_size (((GraphObject *) self)->ob_struct); }
  534. static PySequenceMethods Graph_seq_methods = {
  535. .sq_length = (lenfunc) Graph_get_size,
  536. .sq_contains = (objobjproc) Graph_contains,
  537. };
  538. PyTypeObject GraphType = {
  539. PyVarObject_HEAD_INIT(NULL, 0)
  540. .tp_name = "graph.Graph",
  541. .tp_doc = "RDF graph",
  542. .tp_basicsize = sizeof (GraphObject),
  543. .tp_itemsize = 0,
  544. .tp_flags = Py_TPFLAGS_DEFAULT,
  545. .tp_new = PyType_GenericNew,
  546. .tp_init = (initproc) Graph_init,
  547. .tp_dealloc = (destructor) Graph_dealloc,
  548. .tp_getset = Graph_getsetters,
  549. .tp_methods = Graph_methods,
  550. .tp_richcompare = (richcmpfunc) Graph_richcmp,
  551. .tp_as_number = &Graph_number_methods,
  552. .tp_as_sequence = &Graph_seq_methods,
  553. };
  554. #endif