From d2c1894c4544a04c1240763f7477d78442bae051 Mon Sep 17 00:00:00 2001 From: Alex Nelson Date: Thu, 16 Dec 2021 18:59:46 -0500 Subject: [PATCH 01/25] Have term .toPython() methods pass with mypy-strict-reviewed consumers This patch is believed to be part of what would be necessary to resolve Issue 1447. This patch addresses type errors raised by `mypy` pertaining to use of untyped methods within typed contexts. These signatures are sufficient to pass a test application (coming in a following patch) that exercises: * creation of a graph; * use of `Graph.add()` to supply triples containing `URIRef`, `BNode`, and `Literal` objects; * use of `Graph.triples()`; * use of `Graph.query()` with a string argument; * use of `Graph.query()` with a `Query` argument; * use of `Graph.query()` to return query results with different bound-variable lengths (i.e. different tuple lengths); and * use of several `.toPython()` methods, including `Literal.toPython()`. To address issues with the various `Node` subclasses appearing to be untyped on initialization, this patch introduces `__init__` methods to satisfy `mypy` needing a type signature. To support any future initialization logic, `super()` is used to support the Method Resolution Order chain. The alternative approach of trying to assign return types to `__new__` didn't seem to work for two reasons that would require more significant design revision: * `BNode.__new__()` returns an `Identifier`, and `mypy` complained that this is an incompatible return type. Attempting `typing.cast()` didn't seem to resolve this problem. * `Literal.__new__()` was raising this `mypy --strict` error: ``` "Literal" has no attribute "_value"; maybe "value"? ``` I can't think of a practice aside from exposing `_value` as a slot that would convince `mypy` that the field exists. Hence, the classes under `rdflib/term.py` got `__init__()` methods. References: * https://github.com/RDFLib/rdflib/issues/1447 * https://stackoverflow.com/a/33533514 describes PEPs 484 and 563 relative to this patch. Signed-off-by: Alex Nelson --- rdflib/term.py | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/rdflib/term.py b/rdflib/term.py index ab54657a4..44299a1eb 100644 --- a/rdflib/term.py +++ b/rdflib/term.py @@ -21,6 +21,7 @@ """ import re +import typing from fractions import Fraction @@ -116,6 +117,9 @@ class Node(object): __slots__ = () + def __init__(self, *args, **kwargs) -> None: + super().__init__() + class Identifier(Node, str): # allow Identifiers to be Nodes in the Graph """ @@ -125,6 +129,9 @@ class Identifier(Node, str): # allow Identifiers to be Nodes in the Graph __slots__ = () + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + def __new__(cls, value): return str.__new__(cls, value) @@ -250,7 +257,7 @@ def __new__(cls, value: str, base: Optional[str] = None): rt = str.__new__(cls, value, "utf-8") # type: ignore[call-overload] return rt - def toPython(self): + def toPython(self) -> str: return str(self) def n3(self, namespace_manager=None) -> str: @@ -394,6 +401,9 @@ class BNode(Identifier): __slots__ = () + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + def __new__( cls, value=None, _sn_gen=_serial_number_generator(), _prefix=_unique_id() ): @@ -415,7 +425,7 @@ def __new__( # http://www.w3.org/TR/2004/REC-rdf-testcases-20040210/#nodeID return Identifier.__new__(cls, value) - def toPython(self): + def toPython(self) -> str: return str(self) def n3(self, namespace_manager=None): @@ -534,6 +544,9 @@ class Literal(Identifier): __slots__ = ("_language", "_datatype", "_value") + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + def __new__(cls, lexical_or_value, lang=None, datatype=None, normalize=None): if lang == "": @@ -1219,7 +1232,7 @@ def n3(self, namespace_manager=None): else: return self._literal_n3() - def _literal_n3(self, use_plain=False, qname_callback=None): + def _literal_n3(self, use_plain=False, qname_callback=None) -> str: """ Using plain literal (shorthand) output:: >>> from rdflib.namespace import XSD @@ -1361,7 +1374,7 @@ def __repr__(self): clsName = self.__class__.__name__ return """%s(%s)""" % (clsName, ", ".join(args)) - def toPython(self): + def toPython(self) -> typing.Any: """ Returns an appropriate python datatype derived from this RDF Literal """ @@ -1675,7 +1688,7 @@ def _strip_and_collapse_whitespace(lexical_or_value): def bind( datatype, pythontype, constructor=None, lexicalizer=None, datatype_specific=False -): +) -> None: """ register a new datatype<->pythontype binding @@ -1728,7 +1741,7 @@ def __repr__(self): return """%s(%s)""" % (clsName, super(Variable, self).__repr__()) - def toPython(self): + def toPython(self) -> str: return "?%s" % self def n3(self, namespace_manager=None): From d63bc916b6b14837a505f663e1bbacfb33becad1 Mon Sep 17 00:00:00 2001 From: Alex Nelson Date: Thu, 16 Dec 2021 19:00:53 -0500 Subject: [PATCH 02/25] Have prepareQuery() pass with mypy-strict-reviewed consumers Signed-off-by: Alex Nelson --- rdflib/plugins/sparql/processor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rdflib/plugins/sparql/processor.py b/rdflib/plugins/sparql/processor.py index 84e8c8231..9f463a779 100644 --- a/rdflib/plugins/sparql/processor.py +++ b/rdflib/plugins/sparql/processor.py @@ -17,7 +17,7 @@ from rdflib.plugins.sparql.update import evalUpdate -def prepareQuery(queryString, initNs={}, base=None): +def prepareQuery(queryString, initNs={}, base=None) -> Query: """ Parse and translate a SPARQL Query """ From 3ce1c4caa864e79ed953c0c533addeea495b2958 Mon Sep 17 00:00:00 2001 From: Alex Nelson Date: Thu, 16 Dec 2021 19:40:38 -0500 Subject: [PATCH 03/25] Add consumer application to exercise methods with multiple run-time types This test is anchored off of Issue 1447, trying to identify "common" functions that would be used in a script exercising multiple SPARQL queries that return Python values. References: * https://github.com/RDFLib/rdflib/issues/1447 * https://github.com/python/typing/discussions/911 Signed-off-by: Alex Nelson --- test/test_issue1447.py | 140 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 test/test_issue1447.py diff --git a/test/test_issue1447.py b/test/test_issue1447.py new file mode 100644 index 000000000..a5692b773 --- /dev/null +++ b/test/test_issue1447.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python3 + +# This software was developed at the National Institute of Standards +# and Technology by employees of the Federal Government in the course +# of their official duties. Pursuant to title 17 Section 105 of the +# United States Code this software is not subject to copyright +# protection and is in the public domain. NIST assumes no +# responsibility whatsoever for its use by other parties, and makes +# no guarantees, expressed or implied, about its quality, +# reliability, or any other characteristic. +# +# We would appreciate acknowledgement if the software is used. + +import typing + +# TODO Bug - rdflib.plugins.sparql.prepareQuery() will run fine if this +# test is run, but mypy can't tell the symbol is exposed. +import rdflib.plugins.sparql.processor + + +# TODO Question - is there a usable type name or class name for +# 'typing.Union[rdflib.BNode, rdflib.URIRef]'? +example_BlankNodeOrIRI = typing.Union[rdflib.BNode, rdflib.URIRef] + + +def test_rdflib_query_exercise() -> None: + graph = rdflib.Graph() + + literal_one = rdflib.Literal("1", datatype=rdflib.XSD.integer) + literal_two = rdflib.Literal(2) + literal_true = rdflib.Literal(True) + + # Set up predicate nodes, using hash IRI pattern. + predicate_p = rdflib.URIRef("http://example.org/predicates#p") + predicate_q = rdflib.URIRef("http://example.org/predicates#q") + predicate_r = rdflib.URIRef("http://example.org/predicates#r") + + # Set up knowledge base nodes, using URN namespace, slash IRI pattern, and/or blank node. + kb_bnode = rdflib.BNode() + kb_http_uriref = rdflib.URIRef("http://example.org/kb/x") + kb_https_uriref = rdflib.URIRef("https://example.org/kb/y") + kb_urn_uriref = rdflib.URIRef("urn:example:kb:z") + + graph.add((kb_urn_uriref, predicate_p, literal_one)) + graph.add((kb_http_uriref, predicate_p, literal_one)) + graph.add((kb_https_uriref, predicate_p, literal_one)) + graph.add((kb_https_uriref, predicate_p, literal_two)) + graph.add((kb_https_uriref, predicate_q, literal_two)) + graph.add((kb_bnode, predicate_p, literal_one)) + + expected_nodes_using_predicate_q: typing.Set[ + example_BlankNodeOrIRI + ] = {kb_https_uriref} + computed_nodes_using_predicate_q: typing.Set[ + example_BlankNodeOrIRI + ] = set() + for triple in graph.triples((None, predicate_q, None)): + computed_nodes_using_predicate_q.add(triple[0]) + assert expected_nodes_using_predicate_q == computed_nodes_using_predicate_q + + one_usage_query = """\ +SELECT ?resource +WHERE { + ?resource 1 . +} +""" + + expected_one_usage: typing.Set[example_BlankNodeOrIRI] = { + kb_bnode, + kb_http_uriref, + kb_https_uriref, + kb_urn_uriref, + } + computed_one_usage: typing.Set[example_BlankNodeOrIRI] = set() + for one_usage_result in graph.query(one_usage_query): + computed_one_usage.add(one_usage_result[0]) + assert expected_one_usage == computed_one_usage + + # The purpose of this query is confirming the believed return + # signature of graph.query() is determined at *each* call of + # graph.query(), rather than the first. I.e. there should not be a + # type error when the first call returns a length-one result tuple, + # and the second a length-two result tuple. + two_usage_query = """\ +SELECT ?resource ?predicate +WHERE { + ?resource ?predicate "2"^^xsd:integer . +} +""" + + expected_two_usage: typing.Set[ + typing.Tuple[ + example_BlankNodeOrIRI, + example_BlankNodeOrIRI, + ] + ] = {(kb_https_uriref, predicate_p), (kb_https_uriref, predicate_q)} + computed_two_usage: typing.Set[ + typing.Tuple[ + example_BlankNodeOrIRI, + example_BlankNodeOrIRI, + ] + ] = set() + for two_usage_result in graph.query(two_usage_query): + computed_two_usage.add(two_usage_result) + assert expected_two_usage == computed_two_usage + + nsdict = {k: v for (k, v) in graph.namespace_manager.namespaces()} + + prepared_one_usage_query = rdflib.plugins.sparql.processor.prepareQuery( + one_usage_query, initNs=nsdict + ) + computed_one_usage_from_prepared_query: typing.Set[ + example_BlankNodeOrIRI + ] = set() + for prepared_one_usage_result in graph.query(prepared_one_usage_query): + computed_one_usage_from_prepared_query.add(prepared_one_usage_result[0]) + assert expected_one_usage == computed_one_usage_from_prepared_query + + for node_using_one in sorted(computed_one_usage): + graph.add((node_using_one, predicate_r, literal_true)) + + python_one: int = literal_one.toPython() + assert python_one == 1 + + python_two: int = literal_two.toPython() + assert python_two == 2 + + python_true: bool = literal_true.toPython() + assert python_true == True + + python_iri: str = kb_https_uriref.toPython() + assert python_iri == "https://example.org/kb/y" + + +def main() -> None: + test_rdflib_query_exercise() + + +if __name__ == "__main__": + main() From 2d1d53c0e9109536c56f1a7ebc9fe24e15ad86b5 Mon Sep 17 00:00:00 2001 From: Alex Nelson Date: Thu, 16 Dec 2021 19:44:26 -0500 Subject: [PATCH 04/25] Test Literals and SPARQL .prepareQuery() with mypy-strict-reviewed consumer Signed-off-by: Alex Nelson --- .github/workflows/validate.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/validate.yaml b/.github/workflows/validate.yaml index b36db7652..f6a780aeb 100644 --- a/.github/workflows/validate.yaml +++ b/.github/workflows/validate.yaml @@ -74,6 +74,7 @@ jobs: black --config black.toml --check ./rdflib || true flake8 --exit-zero rdflib mypy --show-error-context --show-error-codes rdflib + mypy --show-error-context --show-error-codes --strict test/test_issue1447.py if [ "${{ matrix.os }}" == "windows-latest" ] then pytest -ra --cov From 3f4d1d0bf66e5aebf4e11975aa7c1fedd42673eb Mon Sep 17 00:00:00 2001 From: Alex Nelson Date: Thu, 16 Dec 2021 19:45:15 -0500 Subject: [PATCH 05/25] Avoid use of built-in keyword as variable name Signed-off-by: Alex Nelson --- test/test_literal.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_literal.py b/test/test_literal.py index 48b80cade..9c4a06ccf 100644 --- a/test/test_literal.py +++ b/test/test_literal.py @@ -258,9 +258,9 @@ def test_make_literals_ki(self): def check_make_literals(self, inputs): for literal_pair in inputs: - (lexical, type, value_cls) = literal_pair + (lexical, _type, value_cls) = literal_pair with self.subTest(f"testing {literal_pair}"): - literal = Literal(lexical, datatype=type) + literal = Literal(lexical, datatype=_type) if value_cls is not None: self.assertIsInstance(literal.value, value_cls) else: From 15b17105e7eba27bb970fe9bf4b9bd93c5e9fac6 Mon Sep 17 00:00:00 2001 From: Alex Nelson Date: Thu, 16 Dec 2021 19:46:54 -0500 Subject: [PATCH 06/25] Add boolean sample to round-trip test Signed-off-by: Alex Nelson --- test/test_literal.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test_literal.py b/test/test_literal.py index 9c4a06ccf..45f2d9606 100644 --- a/test/test_literal.py +++ b/test/test_literal.py @@ -227,6 +227,7 @@ def test_make_literals(self): ("2002", XSD.gYear, datetime.date), ("1921-05-01T00:00:00+00:30", XSD.dateTime, datetime.datetime), ("1921-05-01T00:00:00-00:30", XSD.dateTime, datetime.datetime), + ("true", XSD.boolean, bool), ("abcdef0123", XSD.hexBinary, bytes), ("", XSD.hexBinary, bytes), ("UkRGTGli", XSD.base64Binary, bytes), From 59b1a3720d93e168017191f3954b916bf705505b Mon Sep 17 00:00:00 2001 From: Alex Nelson Date: Thu, 16 Dec 2021 19:47:56 -0500 Subject: [PATCH 07/25] Avoid implicit re-export that raises mypy --strict error Signed-off-by: Alex Nelson --- test/test_literal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_literal.py b/test/test_literal.py index 45f2d9606..7df121843 100644 --- a/test/test_literal.py +++ b/test/test_literal.py @@ -4,7 +4,7 @@ import rdflib # needed for eval(repr(...)) below from rdflib.term import Literal, URIRef, _XSD_DOUBLE, bind, _XSD_BOOLEAN -from rdflib.namespace import XSD +from rdflib import XSD class TestLiteral(unittest.TestCase): From a210552b817b92ae948414915223d4d59f4c4ee6 Mon Sep 17 00:00:00 2001 From: Alex Nelson Date: Thu, 16 Dec 2021 19:49:15 -0500 Subject: [PATCH 08/25] Test Literals further with mypy-strict-reviewed consumer Signed-off-by: Alex Nelson --- .github/workflows/validate.yaml | 2 +- test/test_literal.py | 57 +++++++++++++++++---------------- 2 files changed, 30 insertions(+), 29 deletions(-) diff --git a/.github/workflows/validate.yaml b/.github/workflows/validate.yaml index f6a780aeb..79d1335fe 100644 --- a/.github/workflows/validate.yaml +++ b/.github/workflows/validate.yaml @@ -74,7 +74,7 @@ jobs: black --config black.toml --check ./rdflib || true flake8 --exit-zero rdflib mypy --show-error-context --show-error-codes rdflib - mypy --show-error-context --show-error-codes --strict test/test_issue1447.py + mypy --show-error-context --show-error-codes --strict test/test_issue1447.py test/test_literal.py if [ "${{ matrix.os }}" == "windows-latest" ] then pytest -ra --cov diff --git a/test/test_literal.py b/test/test_literal.py index 7df121843..681a53f36 100644 --- a/test/test_literal.py +++ b/test/test_literal.py @@ -1,6 +1,7 @@ from decimal import Decimal import unittest import datetime +import typing import rdflib # needed for eval(repr(...)) below from rdflib.term import Literal, URIRef, _XSD_DOUBLE, bind, _XSD_BOOLEAN @@ -8,20 +9,20 @@ class TestLiteral(unittest.TestCase): - def setUp(self): + def setUp(self) -> None: pass - def test_repr_apostrophe(self): + def test_repr_apostrophe(self) -> None: a = rdflib.Literal("'") b = eval(repr(a)) self.assertEqual(a, b) - def test_repr_quote(self): + def test_repr_quote(self) -> None: a = rdflib.Literal('"') b = eval(repr(a)) self.assertEqual(a, b) - def test_backslash(self): + def test_backslash(self) -> None: d = r""" None: l = rdflib.Literal(True) self.assertEqual(l.datatype, rdflib.XSD["boolean"]) class TestNew(unittest.TestCase): - def testCantPassLangAndDatatype(self): + def testCantPassLangAndDatatype(self) -> None: self.assertRaises( TypeError, Literal, "foo", lang="en", datatype=URIRef("http://example.com/") ) - def testCantPassInvalidLang(self): + def testCantPassInvalidLang(self) -> None: self.assertRaises( ValueError, Literal, "foo", lang="999" ) - def testFromOtherLiteral(self): + def testFromOtherLiteral(self) -> None: l = Literal(1) l2 = Literal(l) self.assertTrue(isinstance(l.value, int)) @@ -64,7 +65,7 @@ def testFromOtherLiteral(self): l2 = Literal(l, datatype=rdflib.XSD.integer) self.assertTrue(isinstance(l2.value, int)) - def testDatatypeGetsAutoURIRefConversion(self): + def testDatatypeGetsAutoURIRefConversion(self) -> None: # drewp disapproves of this behavior, but it should be # represented in the tests x = Literal("foo", datatype="http://example.com/") @@ -75,22 +76,22 @@ def testDatatypeGetsAutoURIRefConversion(self): class TestRepr(unittest.TestCase): - def testOmitsMissingDatatypeAndLang(self): + def testOmitsMissingDatatypeAndLang(self) -> None: self.assertEqual(repr(Literal("foo")), "rdflib.term.Literal('foo')") - def testOmitsMissingDatatype(self): + def testOmitsMissingDatatype(self) -> None: self.assertEqual( repr(Literal("foo", lang="en")), "rdflib.term.Literal('foo', lang='en')", ) - def testOmitsMissingLang(self): + def testOmitsMissingLang(self) -> None: self.assertEqual( repr(Literal("foo", datatype=URIRef("http://example.com/"))), "rdflib.term.Literal('foo', datatype=rdflib.term.URIRef('http://example.com/'))", ) - def testSubclassNameAppearsInRepr(self): + def testSubclassNameAppearsInRepr(self) -> None: class MyLiteral(Literal): pass @@ -99,7 +100,7 @@ class MyLiteral(Literal): class TestDoubleOutput(unittest.TestCase): - def testNoDanglingPoint(self): + def testNoDanglingPoint(self) -> None: """confirms the fix for https://github.com/RDFLib/rdflib/issues/237""" vv = Literal("0.88", datatype=_XSD_DOUBLE) out = vv._literal_n3(use_plain=True) @@ -109,19 +110,19 @@ def testNoDanglingPoint(self): class TestParseBoolean(unittest.TestCase): """confirms the fix for https://github.com/RDFLib/rdflib/issues/913""" - def testTrueBoolean(self): + def testTrueBoolean(self) -> None: test_value = Literal("tRue", datatype=_XSD_BOOLEAN) self.assertTrue(test_value.value) test_value = Literal("1", datatype=_XSD_BOOLEAN) self.assertTrue(test_value.value) - def testFalseBoolean(self): + def testFalseBoolean(self) -> None: test_value = Literal("falsE", datatype=_XSD_BOOLEAN) self.assertFalse(test_value.value) test_value = Literal("0", datatype=_XSD_BOOLEAN) self.assertFalse(test_value.value) - def testNonFalseBoolean(self): + def testNonFalseBoolean(self) -> None: test_value = Literal("abcd", datatype=_XSD_BOOLEAN) self.assertRaises(UserWarning) self.assertFalse(test_value.value) @@ -131,12 +132,12 @@ def testNonFalseBoolean(self): class TestBindings(unittest.TestCase): - def testBinding(self): + def testBinding(self) -> None: class a: - def __init__(self, v): + def __init__(self, v: str) -> None: self.v = v[3:-3] - def __str__(self): + def __str__(self) -> str: return "<<<%s>>>" % self.v dtA = rdflib.URIRef("urn:dt:a") @@ -152,10 +153,10 @@ def __str__(self): self.assertEqual(la2.value.v, va.v) class b: - def __init__(self, v): + def __init__(self, v: str) -> None: self.v = v[3:-3] - def __str__(self): + def __str__(self) -> str: return "B%s" % self.v dtB = rdflib.URIRef("urn:dt:b") @@ -166,11 +167,11 @@ def __str__(self): self.assertEqual(lb.value, vb) self.assertEqual(lb.datatype, dtB) - def testSpecificBinding(self): - def lexify(s): + def testSpecificBinding(self) -> None: + def lexify(s: str) -> str: return "--%s--" % s - def unlexify(s): + def unlexify(s: str) -> str: return s[2:-2] datatype = rdflib.URIRef("urn:dt:mystring") @@ -191,7 +192,7 @@ def unlexify(s): class TestXsdLiterals(unittest.TestCase): - def test_make_literals(self): + def test_make_literals(self) -> None: """ Tests literal construction. """ @@ -239,7 +240,7 @@ def test_make_literals(self): self.check_make_literals(inputs) @unittest.expectedFailure - def test_make_literals_ki(self): + def test_make_literals_ki(self) -> None: """ Known issues with literal construction. """ @@ -257,7 +258,7 @@ def test_make_literals_ki(self): ] self.check_make_literals(inputs) - def check_make_literals(self, inputs): + def check_make_literals(self, inputs: typing.Sequence[typing.Tuple[str, URIRef, typing.Optional[type]]]) -> None: for literal_pair in inputs: (lexical, _type, value_cls) = literal_pair with self.subTest(f"testing {literal_pair}"): From 41fc12eb5fe7e930b50112d8e9243d64276de272 Mon Sep 17 00:00:00 2001 From: Alex Nelson Date: Thu, 16 Dec 2021 19:50:50 -0500 Subject: [PATCH 09/25] Make Graph.bind() pass with mypy-strict-reviewed consumers Signed-off-by: Alex Nelson --- rdflib/graph.py | 2 +- rdflib/namespace/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rdflib/graph.py b/rdflib/graph.py index 8f5b05442..75291f1d2 100644 --- a/rdflib/graph.py +++ b/rdflib/graph.py @@ -1002,7 +1002,7 @@ def qname(self, uri): def compute_qname(self, uri, generate=True): return self.namespace_manager.compute_qname(uri, generate) - def bind(self, prefix, namespace, override=True, replace=False): + def bind(self, prefix, namespace, override=True, replace=False) -> None: """Bind prefix to namespace If override is True will bind namespace to given prefix even diff --git a/rdflib/namespace/__init__.py b/rdflib/namespace/__init__.py index 8ce4de79b..dbc22f0be 100644 --- a/rdflib/namespace/__init__.py +++ b/rdflib/namespace/__init__.py @@ -521,7 +521,7 @@ def compute_qname_strict(self, uri, generate=True): return self.__cache_strict[uri] - def bind(self, prefix, namespace, override=True, replace=False): + def bind(self, prefix, namespace, override=True, replace=False) -> None: """bind a given namespace to the prefix if override, rebind, even if the given namespace is already From b6aa255d3cb32abe2421f9be504cd7a94de573b2 Mon Sep 17 00:00:00 2001 From: Alex Nelson Date: Thu, 16 Dec 2021 19:52:28 -0500 Subject: [PATCH 10/25] Make Namespace() pass with mypy-strict-reviewed consumers Signed-off-by: Alex Nelson --- rdflib/namespace/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rdflib/namespace/__init__.py b/rdflib/namespace/__init__.py index dbc22f0be..168bd2bd7 100644 --- a/rdflib/namespace/__init__.py +++ b/rdflib/namespace/__init__.py @@ -101,6 +101,9 @@ class Namespace(str): False """ + def __init__(self, *args, **kwargs) -> None: + super().__init__() + def __new__(cls, value): try: rt = str.__new__(cls, value) From d7803dc0aa41d2010307497046e3d2cf32b88877 Mon Sep 17 00:00:00 2001 From: Alex Nelson Date: Thu, 16 Dec 2021 19:54:32 -0500 Subject: [PATCH 11/25] Test Graph.bind(), Namespace() with mypy-strict-reviewed consumer Signed-off-by: Alex Nelson --- .github/workflows/validate.yaml | 2 +- test/test_issue_git_336.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/validate.yaml b/.github/workflows/validate.yaml index 79d1335fe..8bbef4004 100644 --- a/.github/workflows/validate.yaml +++ b/.github/workflows/validate.yaml @@ -74,7 +74,7 @@ jobs: black --config black.toml --check ./rdflib || true flake8 --exit-zero rdflib mypy --show-error-context --show-error-codes rdflib - mypy --show-error-context --show-error-codes --strict test/test_issue1447.py test/test_literal.py + mypy --show-error-context --show-error-codes --strict test/test_issue1447.py test/test_issue_git_336.py test/test_literal.py if [ "${{ matrix.os }}" == "windows-latest" ] then pytest -ra --cov diff --git a/test/test_issue_git_336.py b/test/test_issue_git_336.py index 1f312fbdd..958626cdf 100644 --- a/test/test_issue_git_336.py +++ b/test/test_issue_git_336.py @@ -18,7 +18,7 @@ """ -def test_ns_localname_roundtrip(): +def test_ns_localname_roundtrip() -> None: XNS = rdflib.Namespace("http://example.net/fs") From cbe47453ab4f20414cb3c20dbacc929af2601373 Mon Sep 17 00:00:00 2001 From: Alex Nelson Date: Thu, 16 Dec 2021 19:56:09 -0500 Subject: [PATCH 12/25] Make util.guess_format() pass with mypy-strict-reviewed consumers Signed-off-by: Alex Nelson --- rdflib/util.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rdflib/util.py b/rdflib/util.py index 182c1fe3e..7318dcc36 100644 --- a/rdflib/util.py +++ b/rdflib/util.py @@ -31,6 +31,7 @@ from calendar import timegm from time import altzone +from typing import Optional # from time import daylight from time import gmtime @@ -370,7 +371,7 @@ def parse_date_time(val): } -def guess_format(fpath, fmap=None): +def guess_format(fpath, fmap=None) -> Optional[str]: """ Guess RDF serialization based on file suffix. Uses ``SUFFIX_FORMAT_MAP`` unless ``fmap`` is provided. Examples: From fe028e1205c9f679f3f255c6f63181590a0816d3 Mon Sep 17 00:00:00 2001 From: Alex Nelson Date: Thu, 16 Dec 2021 19:57:46 -0500 Subject: [PATCH 13/25] Add direct test of util.guess_format() Signed-off-by: Alex Nelson --- test/test_parse_file_guess_format.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/test_parse_file_guess_format.py b/test/test_parse_file_guess_format.py index 081255e84..16affd257 100644 --- a/test/test_parse_file_guess_format.py +++ b/test/test_parse_file_guess_format.py @@ -7,9 +7,13 @@ from rdflib.exceptions import ParserError from rdflib import Graph +from rdflib.util import guess_format class FileParserGuessFormatTest(unittest.TestCase): + def test_guess_format(self): + self.assertEqual(guess_format("example.trix"), "trix") + def test_jsonld(self): g = Graph() self.assertIsInstance(g.parse("test/jsonld/1.1/manifest.jsonld"), Graph) From 53ca0844db118c84cf002e6dd0514f01318bd03d Mon Sep 17 00:00:00 2001 From: Alex Nelson Date: Thu, 16 Dec 2021 20:00:14 -0500 Subject: [PATCH 14/25] Test util.guess_format() with mypy-strict-reviewed consumer Signed-off-by: Alex Nelson --- .github/workflows/validate.yaml | 2 +- test/test_parse_file_guess_format.py | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/validate.yaml b/.github/workflows/validate.yaml index 8bbef4004..d7a70d3ba 100644 --- a/.github/workflows/validate.yaml +++ b/.github/workflows/validate.yaml @@ -74,7 +74,7 @@ jobs: black --config black.toml --check ./rdflib || true flake8 --exit-zero rdflib mypy --show-error-context --show-error-codes rdflib - mypy --show-error-context --show-error-codes --strict test/test_issue1447.py test/test_issue_git_336.py test/test_literal.py + mypy --show-error-context --show-error-codes --strict test/test_issue1447.py test/test_issue_git_336.py test/test_literal.py test/test_parse_file_guess_format.py if [ "${{ matrix.os }}" == "windows-latest" ] then pytest -ra --cov diff --git a/test/test_parse_file_guess_format.py b/test/test_parse_file_guess_format.py index 16affd257..bd9014c07 100644 --- a/test/test_parse_file_guess_format.py +++ b/test/test_parse_file_guess_format.py @@ -11,25 +11,25 @@ class FileParserGuessFormatTest(unittest.TestCase): - def test_guess_format(self): + def test_guess_format(self) -> None: self.assertEqual(guess_format("example.trix"), "trix") - def test_jsonld(self): + def test_jsonld(self) -> None: g = Graph() self.assertIsInstance(g.parse("test/jsonld/1.1/manifest.jsonld"), Graph) self.assertIsInstance(g.parse("test/jsonld/file_ending_test_01.json"), Graph) self.assertIsInstance(g.parse("test/jsonld/file_ending_test_01.json-ld"), Graph) self.assertIsInstance(g.parse("test/jsonld/file_ending_test_01.jsonld"), Graph) - def test_ttl(self): + def test_ttl(self) -> None: g = Graph() self.assertIsInstance(g.parse("test/w3c/turtle/IRI_subject.ttl"), Graph) - def test_n3(self): + def test_n3(self) -> None: g = Graph() self.assertIsInstance(g.parse("test/n3/example-lots_of_graphs.n3"), Graph) - def test_warning(self): + def test_warning(self) -> None: g = Graph() graph_logger = logging.getLogger("rdflib") From d58ac2d542d4000c7f8f7b52b0be0ee9be122fbb Mon Sep 17 00:00:00 2001 From: Iwan Aucamp Date: Sun, 19 Dec 2021 19:24:45 +0100 Subject: [PATCH 15/25] Fix mypy settings to run select test files in strict mode - Changed all mypy invocation to not include paths and moved the paths to the mypy section in setup.cfg. This removes redudancy in directory specification and reduces the potential that directories are changed in one invocation but not others. - Ignore mypy errors in test directory. - Enable strict mypy config with in-file config comments in the following files: - test/test_issue1447.py - test/test_issue_git_336.py - test/test_literal.py - test/test_parse_file_guess_format.py --- .drone.yml | 2 +- .github/workflows/validate.yaml | 3 +-- docker-compose.tests.yml | 2 +- requirements.dev.txt | 1 + setup.cfg | 9 +++++++++ test/test_issue1447.py | 23 ++++++++++++++--------- test/test_issue_git_336.py | 9 +++++++++ test/test_literal.py | 17 +++++++++++++---- test/test_parse_file_guess_format.py | 9 +++++++++ tox.ini | 2 +- 10 files changed, 59 insertions(+), 18 deletions(-) diff --git a/.drone.yml b/.drone.yml index d1c95d08a..bb01af7d9 100644 --- a/.drone.yml +++ b/.drone.yml @@ -28,7 +28,7 @@ steps: - python setup.py install - black --config black.toml --check ./rdflib || true - flake8 --exit-zero rdflib - - mypy --show-error-context --show-error-codes rdflib + - mypy --show-error-context --show-error-codes - ./with-fuseki.sh pytest -ra --cov - coveralls diff --git a/.github/workflows/validate.yaml b/.github/workflows/validate.yaml index d7a70d3ba..5e231f642 100644 --- a/.github/workflows/validate.yaml +++ b/.github/workflows/validate.yaml @@ -73,8 +73,7 @@ jobs: run: | black --config black.toml --check ./rdflib || true flake8 --exit-zero rdflib - mypy --show-error-context --show-error-codes rdflib - mypy --show-error-context --show-error-codes --strict test/test_issue1447.py test/test_issue_git_336.py test/test_literal.py test/test_parse_file_guess_format.py + mypy --show-error-context --show-error-codes if [ "${{ matrix.os }}" == "windows-latest" ] then pytest -ra --cov diff --git a/docker-compose.tests.yml b/docker-compose.tests.yml index f433c70f4..b8c24a78e 100644 --- a/docker-compose.tests.yml +++ b/docker-compose.tests.yml @@ -24,4 +24,4 @@ services: volumes: - .:/rdflib working_dir: /rdflib - command: ["python", "-m", "mypy", "--show-error-context", "--show-error-codes" ,"rdflib"] + command: ["python", "-m", "mypy", "--show-error-context", "--show-error-codes"] diff --git a/requirements.dev.txt b/requirements.dev.txt index a42f20e33..a0d867468 100644 --- a/requirements.dev.txt +++ b/requirements.dev.txt @@ -12,3 +12,4 @@ pytest-subtests sphinx sphinxcontrib-apidoc types-setuptools +types-tabulate diff --git a/setup.cfg b/setup.cfg index 5a0defdae..3647d7d0e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -22,12 +22,21 @@ exclude_lines = if __name__==.__main__.: [mypy] +files = rdflib,test +# NOTE: There are some errrors coming from test/translate_algerba directory and +# should be fixed so this exclude can be removed. +exclude = test/translate_algebra/ python_version = 3.7 warn_unused_configs = True ignore_missing_imports = True disallow_subclassing_any = False warn_unreachable = True +[mypy-test.*] +# NOTE: Currently there are many type errors in tests, these should be fixed but +# until they are fixed errors are ignored. +ignore_errors = True + [tool:pytest] addopts = --doctest-modules diff --git a/test/test_issue1447.py b/test/test_issue1447.py index a5692b773..62605a608 100644 --- a/test/test_issue1447.py +++ b/test/test_issue1447.py @@ -11,6 +11,15 @@ # # We would appreciate acknowledgement if the software is used. +# NOTE: The config below enables strict mode for mypy. +# mypy: no_ignore_errors +# mypy: warn_unused_configs, disallow_any_generics +# mypy: disallow_subclassing_any, disallow_untyped_calls +# mypy: disallow_untyped_defs, disallow_incomplete_defs +# mypy: check_untyped_defs, disallow_untyped_decorators +# mypy: no_implicit_optional, warn_redundant_casts, warn_unused_ignores +# mypy: warn_return_any, no_implicit_reexport, strict_equality + import typing # TODO Bug - rdflib.plugins.sparql.prepareQuery() will run fine if this @@ -48,12 +57,10 @@ def test_rdflib_query_exercise() -> None: graph.add((kb_https_uriref, predicate_q, literal_two)) graph.add((kb_bnode, predicate_p, literal_one)) - expected_nodes_using_predicate_q: typing.Set[ - example_BlankNodeOrIRI - ] = {kb_https_uriref} - computed_nodes_using_predicate_q: typing.Set[ - example_BlankNodeOrIRI - ] = set() + expected_nodes_using_predicate_q: typing.Set[example_BlankNodeOrIRI] = { + kb_https_uriref + } + computed_nodes_using_predicate_q: typing.Set[example_BlankNodeOrIRI] = set() for triple in graph.triples((None, predicate_q, None)): computed_nodes_using_predicate_q.add(triple[0]) assert expected_nodes_using_predicate_q == computed_nodes_using_predicate_q @@ -109,9 +116,7 @@ def test_rdflib_query_exercise() -> None: prepared_one_usage_query = rdflib.plugins.sparql.processor.prepareQuery( one_usage_query, initNs=nsdict ) - computed_one_usage_from_prepared_query: typing.Set[ - example_BlankNodeOrIRI - ] = set() + computed_one_usage_from_prepared_query: typing.Set[example_BlankNodeOrIRI] = set() for prepared_one_usage_result in graph.query(prepared_one_usage_query): computed_one_usage_from_prepared_query.add(prepared_one_usage_result[0]) assert expected_one_usage == computed_one_usage_from_prepared_query diff --git a/test/test_issue_git_336.py b/test/test_issue_git_336.py index 958626cdf..e0301e581 100644 --- a/test/test_issue_git_336.py +++ b/test/test_issue_git_336.py @@ -1,3 +1,12 @@ +# NOTE: The config below enables strict mode for mypy. +# mypy: no_ignore_errors +# mypy: warn_unused_configs, disallow_any_generics +# mypy: disallow_subclassing_any, disallow_untyped_calls +# mypy: disallow_untyped_defs, disallow_incomplete_defs +# mypy: check_untyped_defs, disallow_untyped_decorators +# mypy: no_implicit_optional, warn_redundant_casts, warn_unused_ignores +# mypy: warn_return_any, no_implicit_reexport, strict_equality + import rdflib from rdflib.plugins.parsers.notation3 import BadSyntax diff --git a/test/test_literal.py b/test/test_literal.py index 681a53f36..f8bbb12ab 100644 --- a/test/test_literal.py +++ b/test/test_literal.py @@ -1,3 +1,12 @@ +# NOTE: The config below enables strict mode for mypy. +# mypy: no_ignore_errors +# mypy: warn_unused_configs, disallow_any_generics +# mypy: disallow_subclassing_any, disallow_untyped_calls +# mypy: disallow_untyped_defs, disallow_incomplete_defs +# mypy: check_untyped_defs, disallow_untyped_decorators +# mypy: no_implicit_optional, warn_redundant_casts, warn_unused_ignores +# mypy: warn_return_any, no_implicit_reexport, strict_equality + from decimal import Decimal import unittest import datetime @@ -50,9 +59,7 @@ def testCantPassLangAndDatatype(self) -> None: ) def testCantPassInvalidLang(self) -> None: - self.assertRaises( - ValueError, Literal, "foo", lang="999" - ) + self.assertRaises(ValueError, Literal, "foo", lang="999") def testFromOtherLiteral(self) -> None: l = Literal(1) @@ -258,7 +265,9 @@ def test_make_literals_ki(self) -> None: ] self.check_make_literals(inputs) - def check_make_literals(self, inputs: typing.Sequence[typing.Tuple[str, URIRef, typing.Optional[type]]]) -> None: + def check_make_literals( + self, inputs: typing.Sequence[typing.Tuple[str, URIRef, typing.Optional[type]]] + ) -> None: for literal_pair in inputs: (lexical, _type, value_cls) = literal_pair with self.subTest(f"testing {literal_pair}"): diff --git a/test/test_parse_file_guess_format.py b/test/test_parse_file_guess_format.py index bd9014c07..f08bf14c0 100644 --- a/test/test_parse_file_guess_format.py +++ b/test/test_parse_file_guess_format.py @@ -1,3 +1,12 @@ +# NOTE: The config below enables strict mode for mypy. +# mypy: no_ignore_errors +# mypy: warn_unused_configs, disallow_any_generics +# mypy: disallow_subclassing_any, disallow_untyped_calls +# mypy: disallow_untyped_defs, disallow_incomplete_defs +# mypy: check_untyped_defs, disallow_untyped_decorators +# mypy: no_implicit_optional, warn_redundant_casts, warn_unused_ignores +# mypy: warn_return_any, no_implicit_reexport, strict_equality + import unittest import logging from pathlib import Path diff --git a/tox.ini b/tox.ini index 81482217a..20595d4eb 100644 --- a/tox.ini +++ b/tox.ini @@ -30,7 +30,7 @@ deps = basepython = python3.7 commands = - {envpython} -m mypy rdflib --show-error-context --show-error-codes + {envpython} -m mypy --show-error-context --show-error-codes deps = -rrequirements.txt -rrequirements.dev.txt From 03d097140cf51712de11b26a3c83c836beebdfb0 Mon Sep 17 00:00:00 2001 From: Iwan Aucamp Date: Tue, 21 Dec 2021 17:47:30 +0100 Subject: [PATCH 16/25] Type __new__ instead of adding __init__ --- rdflib/namespace/__init__.py | 9 +++------ rdflib/term.py | 21 +++++++-------------- test/test_literal.py | 2 +- 3 files changed, 11 insertions(+), 21 deletions(-) diff --git a/rdflib/namespace/__init__.py b/rdflib/namespace/__init__.py index bf019e248..80ede21e6 100644 --- a/rdflib/namespace/__init__.py +++ b/rdflib/namespace/__init__.py @@ -1,6 +1,6 @@ import logging import warnings -from typing import List +from typing import TYPE_CHECKING, List, Union from unicodedata import category from pathlib import Path @@ -101,14 +101,11 @@ class Namespace(str): False """ - def __init__(self, *args, **kwargs) -> None: - super().__init__() - - def __new__(cls, value): + def __new__(cls, value: Union[str, bytes]) -> "Namespace": try: rt = str.__new__(cls, value) except UnicodeDecodeError: - rt = str.__new__(cls, value, "utf-8") + rt = str.__new__(cls, value, "utf-8") # type: ignore[arg-type] return rt @property diff --git a/rdflib/term.py b/rdflib/term.py index 70f3a0135..38ad5c5ae 100644 --- a/rdflib/term.py +++ b/rdflib/term.py @@ -129,10 +129,7 @@ class Identifier(Node, str): # allow Identifiers to be Nodes in the Graph __slots__ = () - def __init__(self, *args, **kwargs) -> None: - super().__init__(*args, **kwargs) - - def __new__(cls, value): + def __new__(cls, value: str) -> "Identifier": return str.__new__(cls, value) def eq(self, other): @@ -401,12 +398,12 @@ class BNode(Identifier): __slots__ = () - def __init__(self, *args, **kwargs) -> None: - super().__init__(*args, **kwargs) - def __new__( - cls, value=None, _sn_gen=_serial_number_generator(), _prefix=_unique_id() - ): + cls, + value: Optional[str] = None, + _sn_gen: Callable[[], str] = _serial_number_generator(), + _prefix: str = _unique_id(), + ) -> "BNode": """ # only store implementations should pass in a value """ @@ -423,7 +420,7 @@ def __new__( pass # assert is_ncname(str(value)), "BNode identifiers # must be valid NCNames" _:[A-Za-z][A-Za-z0-9]* # http://www.w3.org/TR/2004/REC-rdf-testcases-20040210/#nodeID - return Identifier.__new__(cls, value) + return Identifier.__new__(cls, value) # type: ignore[return-value] def toPython(self) -> str: return str(self) @@ -545,10 +542,6 @@ class Literal(Identifier): _value: Any __slots__ = ("_language", "_datatype", "_value") - - def __init__(self, *args, **kwargs) -> None: - super().__init__(*args, **kwargs) - def __new__( cls, lexical_or_value: Any, diff --git a/test/test_literal.py b/test/test_literal.py index ae77825c9..3fa29aa00 100644 --- a/test/test_literal.py +++ b/test/test_literal.py @@ -74,7 +74,7 @@ def test_cant_pass_invalid_lang( self, lang: Any, exception_type: Type[Exception], - ): + ) -> None: """ Construction of Literal fails if the language tag is invalid. """ From 2c6821c65cb8b0307a01b6b50f136329ed381355 Mon Sep 17 00:00:00 2001 From: Alex Nelson Date: Tue, 21 Dec 2021 12:00:50 -0500 Subject: [PATCH 17/25] Revert addition of Node.__init__ This follows through with Iwan's suggestion to type `__new__` instead of adding `__init__`. `Node.__init__` becomes a vestigial addition with his patch. Reported-by: Iwan Aucamp Signed-off-by: Alex Nelson --- rdflib/term.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/rdflib/term.py b/rdflib/term.py index 38ad5c5ae..2002727bc 100644 --- a/rdflib/term.py +++ b/rdflib/term.py @@ -117,9 +117,6 @@ class Node(object): __slots__ = () - def __init__(self, *args, **kwargs) -> None: - super().__init__() - class Identifier(Node, str): # allow Identifiers to be Nodes in the Graph """ From 14405147f2c9f0b65c88403cbc3cd50b08d2364c Mon Sep 17 00:00:00 2001 From: Alex Nelson Date: Tue, 21 Dec 2021 12:02:20 -0500 Subject: [PATCH 18/25] Reformat per configured 'black' Signed-off-by: Alex Nelson --- rdflib/term.py | 1 + 1 file changed, 1 insertion(+) diff --git a/rdflib/term.py b/rdflib/term.py index 2002727bc..918d9141e 100644 --- a/rdflib/term.py +++ b/rdflib/term.py @@ -539,6 +539,7 @@ class Literal(Identifier): _value: Any __slots__ = ("_language", "_datatype", "_value") + def __new__( cls, lexical_or_value: Any, From 480bdf61649ca380a6b908e08f214967ea64bd93 Mon Sep 17 00:00:00 2001 From: Alex Nelson Date: Tue, 21 Dec 2021 12:04:28 -0500 Subject: [PATCH 19/25] Use imported symbol Reported-by: Iwan Aucamp Signed-off-by: Alex Nelson --- rdflib/term.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rdflib/term.py b/rdflib/term.py index 918d9141e..1182ecb8f 100644 --- a/rdflib/term.py +++ b/rdflib/term.py @@ -21,7 +21,6 @@ """ import re -import typing from fractions import Fraction @@ -1373,7 +1372,7 @@ def __repr__(self): clsName = self.__class__.__name__ return """%s(%s)""" % (clsName, ", ".join(args)) - def toPython(self) -> typing.Any: + def toPython(self) -> Any: """ Returns an appropriate python datatype derived from this RDF Literal """ From 7bf70e4dab159291a291cb84fccc406d2bbfe429 Mon Sep 17 00:00:00 2001 From: Alex Nelson Date: Tue, 21 Dec 2021 12:07:02 -0500 Subject: [PATCH 20/25] Rely on pytest instead of "standalone" test-call mode Reported-by: Iwan Aucamp Signed-off-by: Alex Nelson --- test/test_issue1447.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/test/test_issue1447.py b/test/test_issue1447.py index 62605a608..ac1429b3d 100644 --- a/test/test_issue1447.py +++ b/test/test_issue1447.py @@ -135,11 +135,3 @@ def test_rdflib_query_exercise() -> None: python_iri: str = kb_https_uriref.toPython() assert python_iri == "https://example.org/kb/y" - - -def main() -> None: - test_rdflib_query_exercise() - - -if __name__ == "__main__": - main() From bc3535260a970b16a645f3d5db1e5eeb94c615da Mon Sep 17 00:00:00 2001 From: Alex Nelson Date: Tue, 21 Dec 2021 12:24:15 -0500 Subject: [PATCH 21/25] Rename test program and inline documentation on purpose Reported-by: Iwan Aucamp Signed-off-by: Alex Nelson --- test/{test_issue1447.py => test_typing.py} | 4 ++++ 1 file changed, 4 insertions(+) rename test/{test_issue1447.py => test_typing.py} (97%) diff --git a/test/test_issue1447.py b/test/test_typing.py similarity index 97% rename from test/test_issue1447.py rename to test/test_typing.py index ac1429b3d..e2cbfc1cd 100644 --- a/test/test_issue1447.py +++ b/test/test_typing.py @@ -33,6 +33,10 @@ def test_rdflib_query_exercise() -> None: + """ + The purpose of this test is to exercise a selection of rdflib features under "mypy --strict" review. + """ + graph = rdflib.Graph() literal_one = rdflib.Literal("1", datatype=rdflib.XSD.integer) From fb100cf2de216041d80bb9708df0d238a274f262 Mon Sep 17 00:00:00 2001 From: Alex Nelson Date: Tue, 21 Dec 2021 12:27:17 -0500 Subject: [PATCH 22/25] Fix typo --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 3647d7d0e..9cba6e1ca 100644 --- a/setup.cfg +++ b/setup.cfg @@ -23,7 +23,7 @@ exclude_lines = [mypy] files = rdflib,test -# NOTE: There are some errrors coming from test/translate_algerba directory and +# NOTE: There are some errrors coming from test/translate_algebra directory and # should be fixed so this exclude can be removed. exclude = test/translate_algebra/ python_version = 3.7 From 7c0665f10b1a23f90a278d1bdd8f0cd4fd2020fe Mon Sep 17 00:00:00 2001 From: Alex Nelson Date: Tue, 21 Dec 2021 12:30:08 -0500 Subject: [PATCH 23/25] Align symbol import style Signed-off-by: Alex Nelson --- test/test_literal.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/test_literal.py b/test/test_literal.py index 3fa29aa00..990e84645 100644 --- a/test/test_literal.py +++ b/test/test_literal.py @@ -8,10 +8,9 @@ # mypy: warn_return_any, no_implicit_reexport, strict_equality from decimal import Decimal -from typing import Any, Optional, Type +from typing import Any, Optional, Sequence, Tuple, Type import unittest import datetime -import typing import rdflib # needed for eval(repr(...)) below from rdflib.term import Literal, URIRef, _XSD_DOUBLE, bind, _XSD_BOOLEAN @@ -298,7 +297,7 @@ def test_make_literals_ki(self) -> None: self.check_make_literals(inputs) def check_make_literals( - self, inputs: typing.Sequence[typing.Tuple[str, URIRef, typing.Optional[type]]] + self, inputs: Sequence[Tuple[str, URIRef, Optional[type]]] ) -> None: for literal_pair in inputs: (lexical, _type, value_cls) = literal_pair From b82a5049b6096a014935787b6b1ff4e6aa43d376 Mon Sep 17 00:00:00 2001 From: Alex Nelson Date: Tue, 21 Dec 2021 12:34:33 -0500 Subject: [PATCH 24/25] Use symbol-import style This is just to fit with practice used in other places in this repository. This patch has no impact on function. Signed-off-by: Alex Nelson --- test/test_typing.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/test/test_typing.py b/test/test_typing.py index e2cbfc1cd..2696e5937 100644 --- a/test/test_typing.py +++ b/test/test_typing.py @@ -20,7 +20,7 @@ # mypy: no_implicit_optional, warn_redundant_casts, warn_unused_ignores # mypy: warn_return_any, no_implicit_reexport, strict_equality -import typing +from typing import Set, Tuple, Union # TODO Bug - rdflib.plugins.sparql.prepareQuery() will run fine if this # test is run, but mypy can't tell the symbol is exposed. @@ -29,7 +29,9 @@ # TODO Question - is there a usable type name or class name for # 'typing.Union[rdflib.BNode, rdflib.URIRef]'? -example_BlankNodeOrIRI = typing.Union[rdflib.BNode, rdflib.URIRef] +# Conversation to resolve: +# https://github.com/RDFLib/rdflib/issues/1526 +example_BlankNodeOrIRI = Union[rdflib.BNode, rdflib.URIRef] def test_rdflib_query_exercise() -> None: @@ -61,10 +63,10 @@ def test_rdflib_query_exercise() -> None: graph.add((kb_https_uriref, predicate_q, literal_two)) graph.add((kb_bnode, predicate_p, literal_one)) - expected_nodes_using_predicate_q: typing.Set[example_BlankNodeOrIRI] = { + expected_nodes_using_predicate_q: Set[example_BlankNodeOrIRI] = { kb_https_uriref } - computed_nodes_using_predicate_q: typing.Set[example_BlankNodeOrIRI] = set() + computed_nodes_using_predicate_q: Set[example_BlankNodeOrIRI] = set() for triple in graph.triples((None, predicate_q, None)): computed_nodes_using_predicate_q.add(triple[0]) assert expected_nodes_using_predicate_q == computed_nodes_using_predicate_q @@ -76,13 +78,13 @@ def test_rdflib_query_exercise() -> None: } """ - expected_one_usage: typing.Set[example_BlankNodeOrIRI] = { + expected_one_usage: Set[example_BlankNodeOrIRI] = { kb_bnode, kb_http_uriref, kb_https_uriref, kb_urn_uriref, } - computed_one_usage: typing.Set[example_BlankNodeOrIRI] = set() + computed_one_usage: Set[example_BlankNodeOrIRI] = set() for one_usage_result in graph.query(one_usage_query): computed_one_usage.add(one_usage_result[0]) assert expected_one_usage == computed_one_usage @@ -99,14 +101,14 @@ def test_rdflib_query_exercise() -> None: } """ - expected_two_usage: typing.Set[ - typing.Tuple[ + expected_two_usage: Set[ + Tuple[ example_BlankNodeOrIRI, example_BlankNodeOrIRI, ] ] = {(kb_https_uriref, predicate_p), (kb_https_uriref, predicate_q)} - computed_two_usage: typing.Set[ - typing.Tuple[ + computed_two_usage: Set[ + Tuple[ example_BlankNodeOrIRI, example_BlankNodeOrIRI, ] @@ -120,7 +122,7 @@ def test_rdflib_query_exercise() -> None: prepared_one_usage_query = rdflib.plugins.sparql.processor.prepareQuery( one_usage_query, initNs=nsdict ) - computed_one_usage_from_prepared_query: typing.Set[example_BlankNodeOrIRI] = set() + computed_one_usage_from_prepared_query: Set[example_BlankNodeOrIRI] = set() for prepared_one_usage_result in graph.query(prepared_one_usage_query): computed_one_usage_from_prepared_query.add(prepared_one_usage_result[0]) assert expected_one_usage == computed_one_usage_from_prepared_query From 0fd66cebef524416a83beef074eebc066696f2cd Mon Sep 17 00:00:00 2001 From: Nicholas Car Date: Wed, 22 Dec 2021 14:35:12 +1000 Subject: [PATCH 25/25] more guess_format tests --- test/test_parse_file_guess_format.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/test_parse_file_guess_format.py b/test/test_parse_file_guess_format.py index f08bf14c0..1195aef8a 100644 --- a/test/test_parse_file_guess_format.py +++ b/test/test_parse_file_guess_format.py @@ -22,6 +22,10 @@ class FileParserGuessFormatTest(unittest.TestCase): def test_guess_format(self) -> None: self.assertEqual(guess_format("example.trix"), "trix") + self.assertEqual(guess_format("local-file.jsonld"), "json-ld") + self.assertEqual(guess_format("local-file.json-ld"), "json-ld") + self.assertEqual(guess_format("/some/place/on/disk/example.json"), "json-ld") + self.assertEqual(guess_format("../../relative/place/on/disk/example.json"), "json-ld") def test_jsonld(self) -> None: g = Graph()