diff --git a/examples/berkeleydb_example.py b/examples/berkeleydb_example.py index 120370be92..6430315a69 100644 --- a/examples/berkeleydb_example.py +++ b/examples/berkeleydb_example.py @@ -16,10 +16,12 @@ * does not delete the DB at the end so you can see it on disk """ import os -from rdflib import ConjunctiveGraph, Namespace, Literal -from rdflib.store import NO_STORE, VALID_STORE import tempfile +from rdflib import ConjunctiveGraph, Literal, Namespace +from rdflib.plugins.stores.berkeleydb import has_bsddb +from rdflib.store import NO_STORE, VALID_STORE + def example_1(): """Creates a ConjunctiveGraph and performs some BerkeleyDB tasks with it""" @@ -98,10 +100,10 @@ def example_2(): 9719 ... """ - from urllib.request import urlopen, Request - from urllib.error import HTTPError - import json import base64 + import json + from urllib.error import HTTPError + from urllib.request import Request, urlopen g = ConjunctiveGraph("BerkeleyDB") g.open("gsg_vocabs", create=True) @@ -129,5 +131,7 @@ def example_2(): if __name__ == "__main__": - example_1() - example_2() + if has_bsddb: + # Only run the examples if BerkeleyDB is available + example_1() + example_2() diff --git a/examples/conjunctive_graphs.py b/examples/conjunctive_graphs.py index b2e6aacfcf..433a843f49 100644 --- a/examples/conjunctive_graphs.py +++ b/examples/conjunctive_graphs.py @@ -8,12 +8,11 @@ conjunction (union) of all the graphs. """ -from rdflib import Namespace, Literal, URIRef -from rdflib.graph import Graph, ConjunctiveGraph +from rdflib import Literal, Namespace, URIRef +from rdflib.graph import ConjunctiveGraph, Graph from rdflib.plugins.stores.memory import Memory if __name__ == "__main__": - LOVE = Namespace("http://love.com#") LOVERS = Namespace("http://love.com/lovers/") @@ -59,7 +58,7 @@ print("Query the conjunction of all graphs:") xx = None - for x in g[mary : LOVE.loves / LOVE.hasCuteName]: + for x in g[mary : LOVE.loves / LOVE.hasCuteName]: # type: ignore[misc] xx = x print("Q: Who does Mary love?") print("A: Mary loves {}".format(xx)) diff --git a/examples/custom_datatype.py b/examples/custom_datatype.py index c3f4382060..4d402793f1 100644 --- a/examples/custom_datatype.py +++ b/examples/custom_datatype.py @@ -9,11 +9,9 @@ """ -from rdflib import Graph, Literal, Namespace, XSD -from rdflib import term +from rdflib import XSD, Graph, Literal, Namespace, term if __name__ == "__main__": - # Complex numbers are not registered by default # No custom constructor/serializer needed since # complex('(2+3j)') works fine @@ -46,4 +44,5 @@ # Compare with the original python complex object (should be True) # l2[2] is the object of the triple + assert isinstance(l2[2], Literal) print(l2[2].value == c) diff --git a/examples/custom_eval.py b/examples/custom_eval.py index 36998087d9..f8dfd39027 100644 --- a/examples/custom_eval.py +++ b/examples/custom_eval.py @@ -16,12 +16,19 @@ } """ -import rdflib -from rdflib.plugins.sparql.evaluate import evalBGP +from pathlib import Path + +import rdflib from rdflib.namespace import FOAF, RDF, RDFS +from rdflib.plugins.sparql.evaluate import evalBGP -inferredSubClass = RDFS.subClassOf * "*" # any number of rdfs.subClassOf +EXAMPLES_DIR = Path(__file__).parent + + +inferred_sub_class = ( + RDFS.subClassOf * "*" # type: ignore[operator] +) # any number of rdfs.subClassOf def customEval(ctx, part): @@ -36,7 +43,7 @@ def customEval(ctx, part): if t[1] == RDF.type: bnode = rdflib.BNode() triples.append((t[0], t[1], bnode)) - triples.append((bnode, inferredSubClass, t[2])) + triples.append((bnode, inferred_sub_class, t[2])) else: triples.append(t) @@ -47,12 +54,11 @@ def customEval(ctx, part): if __name__ == "__main__": - # add function directly, normally we would use setuptools and entry_points rdflib.plugins.sparql.CUSTOM_EVALS["exampleEval"] = customEval g = rdflib.Graph() - g.parse("foaf.n3") + g.parse(f"{EXAMPLES_DIR / 'foaf.n3'}") # Add the subClassStmt so that we can query for it! g.add((FOAF.Person, RDFS.subClassOf, FOAF.Agent)) @@ -60,11 +66,11 @@ def customEval(ctx, part): # Find all FOAF Agents for x in g.query( f""" - PREFIX foaf: <{FOAF}> - - SELECT * + PREFIX foaf: <{FOAF}> + + SELECT * WHERE {{ - ?s a foaf:Agent . + ?s a foaf:Agent . }} """ ): diff --git a/examples/datasets.py b/examples/datasets.py index e5d14aa17f..8bf3c9d3cd 100644 --- a/examples/datasets.py +++ b/examples/datasets.py @@ -7,7 +7,18 @@ and remove things from it. """ -from rdflib import Dataset, URIRef, Literal, Namespace +from rdflib import Dataset, Literal, Namespace, URIRef + +# Note regarding `mypy: ignore_errors=true`: +# +# This example is using URIRef values as context identifiers. This is contrary +# to the type hints, but it does work. Most likely the type hints are wrong. +# Ideally we should just use `# type: ignore` comments for the lines that are +# causing problems, but for some reason the error occurs on different lines with +# different python versions, so the only option is to ignore errors for the +# whole file. + +# mypy: ignore_errors=true # # Create & Add @@ -99,7 +110,7 @@ """ print("Printing all triple from one Graph in the Dataset:") print("---") -for triple in d.triples((None, None, None, graph_1)): +for triple in d.triples((None, None, None, graph_1)): # type: ignore[arg-type] print(triple) print("---") print() diff --git a/examples/foafpaths.py b/examples/foafpaths.py index 6bb21efd52..db34fb3162 100644 --- a/examples/foafpaths.py +++ b/examples/foafpaths.py @@ -26,13 +26,16 @@ This example shows how to get the name of friends (i.e values two steps away x knows y, y name z) with a single query. """ -from rdflib import URIRef, Graph +from pathlib import Path + +from rdflib import Graph, URIRef from rdflib.namespace import FOAF -if __name__ == "__main__": +EXAMPLES_DIR = Path(__file__).parent +if __name__ == "__main__": g = Graph() - g.parse("foaf.n3") + g.parse(f"{EXAMPLES_DIR / 'foaf.n3'}") tim = URIRef("http://www.w3.org/People/Berners-Lee/card#i") diff --git a/examples/prepared_query.py b/examples/prepared_query.py index 828ed052a1..035c6137d0 100644 --- a/examples/prepared_query.py +++ b/examples/prepared_query.py @@ -8,22 +8,27 @@ ``initBindings`` keyword parameter. """ +from pathlib import Path + import rdflib -from rdflib.plugins.sparql import prepareQuery from rdflib.namespace import FOAF +from rdflib.plugins.sparql import prepareQuery +EXAMPLES_DIR = Path(__file__).parent if __name__ == "__main__": - q = prepareQuery( "SELECT ?name WHERE { ?person foaf:knows/foaf:name ?name . }", initNs={"foaf": FOAF}, ) g = rdflib.Graph() - g.parse("foaf.n3") + g.parse(f"{EXAMPLES_DIR / 'foaf.n3'}") tim = rdflib.URIRef("http://www.w3.org/People/Berners-Lee/card#i") for row in g.query(q, initBindings={"person": tim}): + # For select queries, the Result object is an iterable of ResultRow + # objects. + assert isinstance(row, rdflib.query.ResultRow) print(row.name) diff --git a/examples/resource_example.py b/examples/resource_example.py index 9085c32c6b..da93042fa5 100644 --- a/examples/resource_example.py +++ b/examples/resource_example.py @@ -7,7 +7,7 @@ This example shows g.resource() in action. """ -from rdflib import Graph, RDF, RDFS, Literal +from rdflib import RDF, RDFS, Graph, Literal from rdflib.namespace import FOAF if __name__ == "__main__": diff --git a/examples/simple_example.py b/examples/simple_example.py index 49f08408aa..c27f3181d3 100644 --- a/examples/simple_example.py +++ b/examples/simple_example.py @@ -1,8 +1,10 @@ -from rdflib import Graph, Literal, BNode, RDF -from rdflib.namespace import FOAF, DC +import os.path +from tempfile import TemporaryDirectory -if __name__ == "__main__": +from rdflib import RDF, BNode, Graph, Literal +from rdflib.namespace import DC, FOAF +if __name__ == "__main__": store = Graph() # Bind a few prefix, namespace pairs for pretty output @@ -29,9 +31,11 @@ for mbox in store.objects(person, FOAF["mbox"]): print(mbox) - print("--- saving RDF to a file (donna_foaf.rdf) ---") + tmp_dir = TemporaryDirectory() + output_file = os.path.join(tmp_dir.name, "donna_foaf.rdf") + print(f"--- saving RDF to a file ({output_file}) ---") # Serialize the store as RDF/XML to the file donna_foaf.rdf. - store.serialize("donna_foaf.rdf", format="pretty-xml", max_depth=3) + store.serialize(f"{output_file}", format="pretty-xml", max_depth=3) # Let's show off the serializers print() diff --git a/examples/slice.py b/examples/slice.py index 47c77fad6c..6994613e6f 100644 --- a/examples/slice.py +++ b/examples/slice.py @@ -9,16 +9,20 @@ See :meth:`rdflib.graph.Graph.__getitem__` for details """ -from rdflib import Graph, RDF +from pathlib import Path + +from rdflib import RDF, Graph from rdflib.namespace import FOAF -if __name__ == "__main__": +EXAMPLES_DIR = Path(__file__).parent + +if __name__ == "__main__": graph = Graph() - graph.parse("foaf.n3", format="n3") + graph.parse(f"{EXAMPLES_DIR / 'foaf.n3'}", format="n3") - for person in graph[: RDF.type : FOAF.Person]: - friends = list(graph[person : FOAF.knows * "+" / FOAF.name]) + for person in graph[: RDF.type : FOAF.Person]: # type: ignore[misc] + friends = list(graph[person : FOAF.knows * "+" / FOAF.name]) # type: ignore[operator] if friends: print(f"{graph.value(person, FOAF.name)}'s circle of friends:") for name in friends: diff --git a/examples/smushing.py b/examples/smushing.py index 8cdb13b414..7c367a25ff 100644 --- a/examples/smushing.py +++ b/examples/smushing.py @@ -21,17 +21,25 @@ with my existing data. """ +from pathlib import Path + from rdflib import Graph, Namespace from rdflib.namespace import FOAF STABLE = Namespace("http://example.com/person/mbox_sha1sum/") +EXAMPLES_DIR = Path(__file__).parent + if __name__ == "__main__": g = Graph() - g.parse("smushingdemo.n3", format="n3") + g.parse(f"{EXAMPLES_DIR / 'smushingdemo.n3'}", format="n3") newURI = {} # old subject : stable uri for s, p, o in g.triples((None, FOAF["mbox_sha1sum"], None)): + # For this graph, all objects are Identifiers, which is a subclass of + # string. `n3` does allow for objects which are not Identifiers, like + # subgraphs. + assert isinstance(o, str) newURI[s] = STABLE[o] out = Graph() diff --git a/examples/sparql_query_example.py b/examples/sparql_query_example.py index 8379a634c9..0e9fc225cf 100644 --- a/examples/sparql_query_example.py +++ b/examples/sparql_query_example.py @@ -11,16 +11,26 @@ :attr:`~rdflib.query.Result.vars` contains the variables """ +import logging +import sys +from pathlib import Path + import rdflib +EXAMPLES_DIR = Path(__file__).parent + if __name__ == "__main__": + logging.basicConfig(level=logging.DEBUG, stream=sys.stderr) g = rdflib.Graph() - g.parse("foaf.n3", format="n3") + g.parse(f"{EXAMPLES_DIR / 'foaf.n3'}", format="n3") # The QueryProcessor knows the FOAF prefix from the graph # which in turn knows it from reading the N3 RDF file for row in g.query("SELECT ?s WHERE { [] foaf:knows ?s .}"): + # For select queries, the Result object is an iterable of ResultRow + # objects. + assert isinstance(row, rdflib.query.ResultRow) print(row.s) # or row["s"] # or row[rdflib.Variable("s")] diff --git a/examples/sparql_update_example.py b/examples/sparql_update_example.py index 4236ce37dc..a99749962b 100644 --- a/examples/sparql_update_example.py +++ b/examples/sparql_update_example.py @@ -2,12 +2,16 @@ SPARQL Update statements can be applied with :meth:`rdflib.graph.Graph.update` """ +from pathlib import Path + import rdflib -if __name__ == "__main__": +EXAMPLES_DIR = Path(__file__).parent + +if __name__ == "__main__": g = rdflib.Graph() - g.parse("foaf.n3", format="n3") + g.parse(f"{EXAMPLES_DIR / 'foaf.n3'}", format="n3") print(f"Initially there are {len(g)} triples in the graph") @@ -15,8 +19,8 @@ """ PREFIX foaf: PREFIX dbpedia: - INSERT { - ?s a dbpedia:Human . + INSERT { + ?s a dbpedia:Human . } WHERE { ?s a foaf:Person . diff --git a/examples/sparqlstore_example.py b/examples/sparqlstore_example.py index 0fe23314c7..12fe43301a 100644 --- a/examples/sparqlstore_example.py +++ b/examples/sparqlstore_example.py @@ -2,11 +2,11 @@ Simple examples showing how to use the SPARQLStore """ -from rdflib import Graph, URIRef, Namespace +from rdflib import Graph, Namespace, URIRef from rdflib.plugins.stores.sparqlstore import SPARQLStore +from rdflib.term import Identifier if __name__ == "__main__": - dbo = Namespace("http://dbpedia.org/ontology/") # EXAMPLE 1: using a Graph with the Store type string set to "SPARQLStore" @@ -14,6 +14,7 @@ graph.open("http://dbpedia.org/sparql") pop = graph.value(URIRef("http://dbpedia.org/resource/Berlin"), dbo.populationTotal) + assert isinstance(pop, Identifier) print( "According to DBPedia, Berlin has a population of {0:,}".format( @@ -28,9 +29,9 @@ for p in st.objects( URIRef("http://dbpedia.org/resource/Brisbane"), dbo.populationTotal ): + assert isinstance(p, Identifier) print( - "According to DBPedia, Brisbane has a population of " - "{0:,}".format(int(p), ",d") + "According to DBPedia, Brisbane has a population of " "{0}".format(int(p)) ) print() @@ -50,7 +51,7 @@ # EXAMPLE 4: using a SPARQL endpoint that requires Basic HTTP authentication # NOTE: this example won't run since the endpoint isn't live (or real) - s = SPARQLStore( + sparql_store = SPARQLStore( query_endpoint="http://fake-sparql-endpoint.com/repository/x", auth=("my_username", "my_password"), ) diff --git a/examples/swap_primer.py b/examples/swap_primer.py index dad762118b..fbcc52c3c0 100644 --- a/examples/swap_primer.py +++ b/examples/swap_primer.py @@ -5,11 +5,10 @@ http://www.w3.org/2000/10/swap/Primer """ -from rdflib import ConjunctiveGraph, Namespace, Literal -from rdflib.namespace import OWL, DC +from rdflib import ConjunctiveGraph, Literal, Namespace +from rdflib.namespace import DC, OWL if __name__ == "__main__": - # Firstly, it doesn't have to be so complex. # Here we create a "Graph" of our work. # Think of it as a blank piece of graph paper! diff --git a/pyproject.toml b/pyproject.toml index 8e9348f342..24be1293f6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -130,7 +130,6 @@ exclude = ''' | _build | htmlcov | benchmarks - | examples # No need to Black examples | test_reports | rdflib.egg-info | buck-out @@ -168,7 +167,7 @@ log_cli_date_format = "%Y-%m-%dT%H:%M:%S" profile = "black" py_version = 37 line_length = 88 -src_paths= ["rdflib", "test"] +src_paths= ["rdflib", "test", "devtools", "examples"] supported_extensions = ["pyw", "pyi", "py"] skip = [ '.eggs', # exclude a few common directories in the @@ -183,7 +182,6 @@ skip = [ '_build', 'htmlcov', 'benchmarks', - 'examples', # No need to isort examples 'test_reports', 'rdflib.egg-info', 'buck-out', @@ -193,7 +191,7 @@ skip = [ ] [tool.mypy] -files = ['rdflib', 'test', 'devtools'] +files = ['rdflib', 'test', 'devtools', 'examples'] python_version = "3.7" warn_unused_configs = true ignore_missing_imports = true diff --git a/test/test_examples.py b/test/test_examples.py new file mode 100644 index 0000000000..3bdd73894f --- /dev/null +++ b/test/test_examples.py @@ -0,0 +1,28 @@ +import subprocess +import sys +from pathlib import Path +from typing import Iterable + +import pytest +from _pytest.mark.structures import ParameterSet + +FILE_PATH = Path(__file__) + +EXAMPLES_DIR = FILE_PATH.parent.parent / "examples" + + +def generate_example_cases() -> Iterable[ParameterSet]: + for example_file in EXAMPLES_DIR.glob("*.py"): + if example_file.name == "__init__.py": + # this is not an example ... + continue + yield pytest.param(example_file, id=f"{example_file.relative_to(EXAMPLES_DIR)}") + + +@pytest.mark.parametrize(["example_file"], generate_example_cases()) +def test_example(example_file: Path) -> None: + """ + The example runs without errors. + """ + + subprocess.run([sys.executable, f"{example_file}"], check=True)