Python: Optimizes copy on basic model classes

Immutable values do not need to be actually copied
pull/430/head
Tpt 2 years ago committed by Thomas Tanon
parent d4e964ac47
commit c40c81447e
  1. 15
      python/generate_stubs.py
  2. 91
      python/src/model.rs
  3. 25
      python/tests/test_model.py

@ -153,12 +153,13 @@ def class_stubs(
)
doc = inspect.getdoc(cls_def)
doc_comment = build_doc_comment(doc) if doc else None
return ast.ClassDef(
cls_name,
bases=[],
keywords=[],
body=(
([build_doc_comment(doc)] if doc else [])
([doc_comment] if doc_comment else [])
+ attributes
+ methods
+ magic_methods
@ -193,7 +194,8 @@ def data_descriptor_stub(
annotation=annotation or AST_TYPING_ANY,
simple=1,
)
return (assign, build_doc_comment(doc_comment)) if doc_comment else (assign,)
doc_comment = build_doc_comment(doc_comment) if doc_comment else None
return (assign, doc_comment) if doc_comment else (assign,)
def function_stub(
@ -207,7 +209,9 @@ def function_stub(
body: List[ast.AST] = []
doc = inspect.getdoc(fn_def)
if doc is not None:
body.append(build_doc_comment(doc))
doc_comment = build_doc_comment(doc)
if doc_comment is not None:
body.append(doc_comment)
decorator_list = []
if in_class and hasattr(fn_def, "__self__"):
@ -439,14 +443,15 @@ def parse_type_to_ast(
return parse_sequence(stack[0])
def build_doc_comment(doc: str) -> ast.Expr:
def build_doc_comment(doc: str) -> Optional[ast.Expr]:
lines = [line.strip() for line in doc.split("\n")]
clean_lines = []
for line in lines:
if line.startswith((":type", ":rtype")):
continue
clean_lines.append(line)
return ast.Expr(value=ast.Constant("\n".join(clean_lines).strip()))
text = "\n".join(clean_lines).strip()
return ast.Expr(value=ast.Constant(text)) if text else None
def format_with_black(code: str) -> str:

@ -111,9 +111,22 @@ impl PyNamedNode {
}
}
/// :rtype: typing.Any
fn __getnewargs__(&self) -> (&str,) {
(self.value(),)
}
/// :rtype: NamedNode
fn __copy__(slf: PyRef<'_, Self>) -> PyRef<Self> {
slf
}
/// :type memo: typing.Any
/// :rtype: NamedNode
#[allow(unused_variables)]
fn __deepcopy__<'a>(slf: PyRef<'a, Self>, memo: &'_ PyAny) -> PyRef<'a, Self> {
slf
}
}
/// An RDF `blank node <https://www.w3.org/TR/rdf11-concepts/#dfn-blank-node>`_.
@ -221,9 +234,22 @@ impl PyBlankNode {
}
}
/// :rtype: typing.Any
fn __getnewargs__(&self) -> (&str,) {
(self.value(),)
}
/// :rtype: BlankNode
fn __copy__(slf: PyRef<'_, Self>) -> PyRef<Self> {
slf
}
/// :type memo: typing.Any
/// :rtype: BlankNode
#[allow(unused_variables)]
fn __deepcopy__<'a>(slf: PyRef<'a, Self>, memo: &'_ PyAny) -> PyRef<'a, Self> {
slf
}
}
/// An RDF `literal <https://www.w3.org/TR/rdf11-concepts/#dfn-literal>`_.
@ -361,6 +387,7 @@ impl PyLiteral {
}
}
/// :rtype: typing.Any
fn __getnewargs_ex__<'a>(&'a self, py: Python<'a>) -> PyResult<((&'a str,), &'a PyDict)> {
let kwargs = PyDict::new(py);
if let Some(language) = self.language() {
@ -370,6 +397,18 @@ impl PyLiteral {
}
Ok(((self.value(),), kwargs))
}
/// :rtype: Literal
fn __copy__(slf: PyRef<'_, Self>) -> PyRef<Self> {
slf
}
/// :type memo: typing.Any
/// :rtype: Literal
#[allow(unused_variables)]
fn __deepcopy__<'a>(slf: PyRef<'a, Self>, memo: &'_ PyAny) -> PyRef<'a, Self> {
slf
}
}
/// The RDF `default graph name <https://www.w3.org/TR/rdf11-concepts/#dfn-default-graph>`_.
@ -425,9 +464,22 @@ impl PyDefaultGraph {
}
}
/// :rtype: typing.Any
fn __getnewargs__<'a>(&self, py: Python<'a>) -> &'a PyTuple {
PyTuple::empty(py)
}
/// :rtype: DefaultGraph
fn __copy__(slf: PyRef<'_, Self>) -> PyRef<Self> {
slf
}
/// :type memo: typing.Any
/// :rtype: DefaultGraph
#[allow(unused_variables)]
fn __deepcopy__<'a>(slf: PyRef<'a, Self>, memo: &'_ PyAny) -> PyRef<'a, Self> {
slf
}
}
#[derive(FromPyObject)]
@ -674,9 +726,22 @@ impl PyTriple {
}
}
/// :rtype: typing.Any
fn __getnewargs__(&self) -> (PySubject, PyNamedNode, PyTerm) {
(self.subject(), self.predicate(), self.object())
}
/// :rtype: Triple
fn __copy__(slf: PyRef<'_, Self>) -> PyRef<Self> {
slf
}
/// :type memo: typing.Any
/// :rtype: Triple
#[allow(unused_variables)]
fn __deepcopy__<'a>(slf: PyRef<'a, Self>, memo: &'_ PyAny) -> PyRef<'a, Self> {
slf
}
}
#[derive(FromPyObject)]
@ -889,6 +954,7 @@ impl PyQuad {
}
}
/// :rtype: typing.Any
fn __getnewargs__(&self) -> (PySubject, PyNamedNode, PyTerm, PyGraphName) {
(
self.subject(),
@ -897,6 +963,18 @@ impl PyQuad {
self.graph_name(),
)
}
/// :rtype: Quad
fn __copy__(slf: PyRef<'_, Self>) -> PyRef<Self> {
slf
}
/// :type memo: typing.Any
/// :rtype: Quad
#[allow(unused_variables)]
fn __deepcopy__<'a>(slf: PyRef<'a, Self>, memo: &'_ PyAny) -> PyRef<'a, Self> {
slf
}
}
/// A SPARQL query variable.
@ -969,9 +1047,22 @@ impl PyVariable {
eq_compare(self, other, op)
}
/// :rtype: typing.Any
fn __getnewargs__(&self) -> (&str,) {
(self.value(),)
}
/// :rtype: Variable
fn __copy__(slf: PyRef<'_, Self>) -> PyRef<Self> {
slf
}
/// :type memo: typing.Any
/// :rtype: Variable
#[allow(unused_variables)]
fn __deepcopy__<'a>(slf: PyRef<'a, Self>, memo: &'_ PyAny) -> PyRef<'a, Self> {
slf
}
}
pub struct PyNamedNodeRef<'a>(PyRef<'a, PyNamedNode>);

@ -1,3 +1,4 @@
import copy
import pickle
import unittest
@ -29,8 +30,9 @@ class TestNamedNode(unittest.TestCase):
def test_pickle(self) -> None:
node = NamedNode("http://foo")
self.assertEqual(NamedNode(*node.__getnewargs__()), node)
self.assertEqual(pickle.loads(pickle.dumps(node)), node)
self.assertEqual(copy.copy(node), node)
self.assertEqual(copy.deepcopy(node), node)
class TestBlankNode(unittest.TestCase):
@ -50,8 +52,13 @@ class TestBlankNode(unittest.TestCase):
def test_pickle(self) -> None:
node = BlankNode("foo")
self.assertEqual(pickle.loads(pickle.dumps(node)), node)
self.assertEqual(copy.copy(node), node)
self.assertEqual(copy.deepcopy(node), node)
auto = BlankNode()
self.assertEqual(pickle.loads(pickle.dumps(auto)), auto)
self.assertEqual(copy.copy(auto), auto)
self.assertEqual(copy.deepcopy(auto), auto)
class TestLiteral(unittest.TestCase):
@ -88,10 +95,18 @@ class TestLiteral(unittest.TestCase):
def test_pickle(self) -> None:
simple = Literal("foo")
self.assertEqual(pickle.loads(pickle.dumps(simple)), simple)
self.assertEqual(copy.copy(simple), simple)
self.assertEqual(copy.deepcopy(simple), simple)
lang_tagged = Literal("foo", language="en")
self.assertEqual(pickle.loads(pickle.dumps(lang_tagged)), lang_tagged)
self.assertEqual(copy.copy(lang_tagged), lang_tagged)
self.assertEqual(copy.deepcopy(lang_tagged), lang_tagged)
number = Literal("1", datatype=XSD_INTEGER)
self.assertEqual(pickle.loads(pickle.dumps(number)), number)
self.assertEqual(copy.copy(number), number)
self.assertEqual(copy.deepcopy(number), number)
class TestTriple(unittest.TestCase):
@ -176,6 +191,8 @@ class TestTriple(unittest.TestCase):
NamedNode("http://example.com/o"),
)
self.assertEqual(pickle.loads(pickle.dumps(triple)), triple)
self.assertEqual(copy.copy(triple), triple)
self.assertEqual(copy.deepcopy(triple), triple)
class TestDefaultGraph(unittest.TestCase):
@ -185,6 +202,8 @@ class TestDefaultGraph(unittest.TestCase):
def test_pickle(self) -> None:
self.assertEqual(pickle.loads(pickle.dumps(DefaultGraph())), DefaultGraph())
self.assertEqual(copy.copy(DefaultGraph()), DefaultGraph())
self.assertEqual(copy.deepcopy(DefaultGraph()), DefaultGraph())
class TestQuad(unittest.TestCase):
@ -265,6 +284,8 @@ class TestQuad(unittest.TestCase):
NamedNode("http://example.com/g"),
)
self.assertEqual(pickle.loads(pickle.dumps(quad)), quad)
self.assertEqual(copy.copy(quad), quad)
self.assertEqual(copy.deepcopy(quad), quad)
class TestVariable(unittest.TestCase):
@ -281,6 +302,8 @@ class TestVariable(unittest.TestCase):
def test_pickle(self) -> None:
v = Variable("foo")
self.assertEqual(pickle.loads(pickle.dumps(v)), v)
self.assertEqual(copy.copy(v), v)
self.assertEqual(copy.deepcopy(v), v)
if __name__ == "__main__":

Loading…
Cancel
Save