Adds beginning of Python bindings

pull/10/head
Tpt 6 years ago
parent 23387ab76e
commit e1409cc067
  1. 3
      Cargo.toml
  2. 2
      README.md
  3. 19
      python/Cargo.toml
  4. 2
      python/MANIFEST.in
  5. 2
      python/pyproject.toml
  6. 1
      python/rudf/__init__.py
  7. 15
      python/setup.py
  8. 128
      python/src/lib.rs
  9. 33
      python/tests/test_model.py

@ -1,4 +1,5 @@
[workspace]
members = [
"lib"
"lib",
"python"
]

@ -4,7 +4,7 @@ This library is a work in progress of a [RDF](https://www.w3.org/RDF/) stack imp
Its goal is to provide a compliant, safe and fast implementation of W3C specifications in Rust.
The `lib` directory contains the Rust library code.
The `lib` directory contains the Rust library code and the `python` directory a beginning of Python bindings.
[![Build Status](https://travis-ci.org/Tpt/rudf.svg?branch=master)](https://travis-ci.org/Tpt/rudf)
[![dependency status](https://deps.rs/repo/github/Tpt/rudf/status.svg)](https://deps.rs/repo/github/Tpt/rudf)

@ -0,0 +1,19 @@
[package]
name = "rudf_python"
version = "0.1.0"
authors = ["Tpt <thomas@pellissier-tanon.fr>"]
license = "MIT/Apache-2.0"
readme = "../README.md"
keywords = ["RDF", "N-Triples", "Turtle", "RDF/XML", "SPARQL", "Python"]
repository = "https://github.com/Tpt/rudf"
description = """
Python bindings of the Rudf library
"""
[lib]
name = "rudf"
crate-type = ["cdylib"]
[dependencies]
rudf = {path = "../lib"}
cpython = { version = "0.2", features = ["extension-module"]}

@ -0,0 +1,2 @@
include Cargo.toml
recursive-include src *

@ -0,0 +1,2 @@
[build-system]
requires = ["setuptools", "wheel", "setuptools-rust"]

@ -0,0 +1 @@
from .rudf import *

@ -0,0 +1,15 @@
from setuptools import setup
try:
from setuptools_rust import Binding, RustExtension
except ImportError:
print('You should install the setuptool-rust package to be able to build rudf')
setup(
name="rudf",
version="0.1",
rust_extensions=[RustExtension("rudf.rudf", binding=Binding.RustCPython)],
packages=["rudf"],
zip_safe=False,
)

@ -0,0 +1,128 @@
#[macro_use]
extern crate cpython;
extern crate rudf;
use cpython::exc::ValueError;
use cpython::CompareOp;
use cpython::PyErr;
use cpython::PyResult;
use cpython::Python;
use cpython::PythonObject;
use cpython::ToPyObject;
use rudf::model;
use std::collections::hash_map::DefaultHasher;
use std::error::Error;
use std::hash::Hash;
use std::hash::Hasher;
use std::str::FromStr;
py_module_initializer!(rudf, initrudf, PyInit_rudf, |py, m| {
try!(m.add(py, "__doc__", "Rudf Python bindings"));
try!(m.add_class::<NamedNode>(py));
try!(m.add_class::<BlankNode>(py));
try!(m.add_class::<Literal>(py));
Ok(())
});
fn new_value_error(py: Python, error: impl Error) -> PyErr {
PyErr::new::<ValueError, _>(py, error.description())
}
fn eq_compare<T: Eq + Ord>(a: T, b: T, op: CompareOp) -> bool {
match op {
CompareOp::Lt => a < b,
CompareOp::Le => a <= b,
CompareOp::Eq => a == b,
CompareOp::Ne => a != b,
CompareOp::Gt => a > b,
CompareOp::Ge => a >= b,
}
}
fn hash(t: &impl Hash) -> u64 {
let mut s = DefaultHasher::new();
t.hash(&mut s);
s.finish()
}
py_class!(class NamedNode |py| {
data inner: model::NamedNode;
def __new__(_cls, value: &str) -> PyResult<NamedNode> {
NamedNode::create_instance(py, model::NamedNode::from_str(value).map_err(|error| new_value_error(py, error))?)
}
def value(&self) -> PyResult<String> {
Ok(self.inner(py).as_str().to_string())
}
def __str__(&self) -> PyResult<String> {
Ok(self.inner(py).to_string())
}
def __richcmp__(&self, other: &NamedNode, op: CompareOp) -> PyResult<bool> {
Ok(eq_compare(self.inner(py), other.inner(py), op))
}
def __hash__(&self) -> PyResult<u64> {
Ok(hash(self.inner(py)))
}
});
py_class!(class BlankNode |py| {
data inner: model::BlankNode;
def __new__(_cls) -> PyResult<BlankNode> {
BlankNode::create_instance(py, model::BlankNode::default())
}
def __str__(&self) -> PyResult<String> {
Ok(self.inner(py).to_string())
}
def __richcmp__(&self, other: &BlankNode, op: CompareOp) -> PyResult<bool> {
Ok(eq_compare(self.inner(py), other.inner(py), op))
}
def __hash__(&self) -> PyResult<u64> {
Ok(hash(self.inner(py)))
}
});
py_class!(class Literal |py| {
data inner: model::Literal;
def __new__(_cls, value: String, language: Option<String> = None, datatype: Option<NamedNode> = None) -> PyResult<Literal> {
Literal::create_instance(py, match language {
Some(language) => model::Literal::new_language_tagged_literal(value, language),
None => match datatype {
Some(datatype) => model::Literal::new_typed_literal(value, datatype.inner(py).clone()),
None => model::Literal::new_simple_literal(value)
}
})
}
def value(&self) -> PyResult<String> {
Ok(self.inner(py).value().to_string())
}
def language(&self) -> PyResult<Option<String>> {
Ok(self.inner(py).language().map(|l| l.to_string()))
}
def datatype(&self) -> PyResult<NamedNode> {
NamedNode::create_instance(py, self.inner(py).datatype().clone())
}
def __str__(&self) -> PyResult<String> {
Ok(self.inner(py).to_string())
}
def __richcmp__(&self, other: &Literal, op: CompareOp) -> PyResult<bool> {
Ok(eq_compare(self.inner(py), other.inner(py), op))
}
def __hash__(&self) -> PyResult<u64> {
Ok(hash(self.inner(py)))
}
});

@ -0,0 +1,33 @@
import unittest
from rudf import *
XSD_STRING = NamedNode('http://www.w3.org/2001/XMLSchema#string')
XSD_INTEGER = NamedNode('http://www.w3.org/2001/XMLSchema#integer')
RDF_LANG_STRING = NamedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#langString')
class TestNamedNode(unittest.TestCase):
def test_constructor(self):
self.assertEqual(NamedNode('http://foo').value(), 'http://foo/')
class TestBlankNode(unittest.TestCase):
def test_constructor(self):
self.assertNotEqual(BlankNode(), BlankNode())
class TestLiteral(unittest.TestCase):
def test_constructor(self):
self.assertEqual(Literal('foo').value(), 'foo')
self.assertEqual(Literal('foo').datatype(), XSD_STRING)
self.assertEqual(Literal('foo', 'en').value(), 'foo')
self.assertEqual(Literal('foo', 'en').language(), 'en')
self.assertEqual(Literal('foo', 'en').datatype(), RDF_LANG_STRING)
self.assertEqual(Literal('foo', datatype=XSD_INTEGER).value(), 'foo')
self.assertEqual(Literal('foo', datatype=XSD_INTEGER).datatype(), XSD_INTEGER)
if __name__ == '__main__':
unittest.main()
Loading…
Cancel
Save