diff --git a/.github/workflows/test-report.yml b/.github/workflows/test-report.yml index ad6a82883..386183ad1 100644 --- a/.github/workflows/test-report.yml +++ b/.github/workflows/test-report.yml @@ -11,14 +11,14 @@ jobs: checks: write statuses: write steps: - - uses: dorny/test-reporter@v1 + - uses: dorny/test-reporter@v2 with: artifact: /(.*)-mypy-junit-xml$/ name: mypy report path: "*.xml" reporter: java-junit fail-on-error: "false" - - uses: dorny/test-reporter@v1 + - uses: dorny/test-reporter@v2 with: artifact: /(.*)-pytest-junit-xml$/ name: pytest report diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1f3965bab..3df05af4a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -8,7 +8,7 @@ ci: repos: - repo: https://github.com/astral-sh/ruff-pre-commit # WARNING: Ruff version should be the same as in `pyproject.toml` - rev: v0.8.6 + rev: v0.11.0 hooks: - id: ruff args: ["--fix"] @@ -21,9 +21,8 @@ repos: require_serial: true args: ["."] - repo: https://github.com/python-poetry/poetry - rev: 2.0.0 + rev: 2.1.1 hooks: - id: poetry-check - - id: poetry-lock # sadly `--no-update` does not work on pre-commit.ci - + args: ["--lock"] diff --git a/CHANGELOG.md b/CHANGELOG.md index 301e6a3e8..5c459b75f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,46 @@ +## 2025-03-29 RELEASE 7.1.4 + +A tidy-up release with no major updates over 7.1.3. This may be the last 7.x +release as we move to a version 8 with breaking changes to Dataset and a few +APIs. + +Interesting PRs merged: + +* 2025-03-24 - remove old hacks against 2to3 + [PR #3095](https://github.com/RDFLib/rdflib/pull/3095) +* 2025-03-24 - Allow multi subjects & objects in graph funcs + [PR #3086](https://github.com/RDFLib/rdflib/pull/3086) +* 2025-03-24 - Reduce test warnings + [PR #3085](https://github.com/RDFLib/rdflib/pull/3085) +* 2025-03-22 - Downgrade log message about plugin + [PR #3063](https://github.com/RDFLib/rdflib/pull/3063) +* 2025-03-22 - remove old hacks against 2to3 + [PR #3076](https://github.com/RDFLib/rdflib/pull/3076) +* 2025-03-22 - Cope with Namespace annotations in Python 3.14 + [PR #3084](https://github.com/RDFLib/rdflib/pull/3084) +* 2025-01-18 - small docco update + [PR #3053](https://github.com/RDFLib/rdflib/pull/3053) + +... and lots of boring dependency bump PRs merged! + +## 2025-01-17 RELEASE 7.1.3 + +A fix-up release that re-adds support for Python 3.8 after it was accidentally +removed in Release 7.1.2. + +This release cherrypicks many additions to 7.1.2 added to 7.1.1 but leaves out +typing changes that are not compatible +with Python 3.8. + +Also not carried over from 7.1.2 is the change from Poetry 1.x to 2.0. + +Included are PRs such as _Defined Namespace warnings fix_, _sort longturtle +blank nodes_, _deterministic longturtle serialisation_ and _Dataset documentation +improvements_. + +For the full list of included PRs, see the preparatory PR: +. + ## 2025-01-10 RELEASE 7.1.2 A minor release that bumped up a few dev dependencies and achieved a few small but notable improvements, particularly with longturtle sorting: @@ -54,7 +97,6 @@ Merged PRs: * 2024-10-23 - build(deps-dev): bump ruff from 0.6.9 to 0.7.0 [PR #2942](https://github.com/RDFLib/rdflib/pull/2942) - ## 2024-10-17 RELEASE 7.1.0 This minor release incorporates just over 100 substantive PRs - interesting diff --git a/CITATION.cff b/CITATION.cff index 88ae8274d..dd440adfb 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -72,7 +72,7 @@ authors: - family-names: "Stuart" given-names: "Veyndan" title: "RDFLib" -version: 7.1.2 -date-released: 2025-01-10 +version: 7.1.4 +date-released: 2025-03-29 url: "https://github.com/RDFLib/rdflib" doi: 10.5281/zenodo.6845245 diff --git a/README.md b/README.md index 1948e8e02..9c8e0c9f3 100644 --- a/README.md +++ b/README.md @@ -18,9 +18,10 @@ RDFLib is a pure Python package for working with [RDF](http://www.w3.org/RDF/). RDFLib contains most things you need to work with RDF, including: -* parsers and serializers for RDF/XML, N3, NTriples, N-Quads, Turtle, TriX, Trig and JSON-LD +* parsers and serializers for RDF/XML, N3, NTriples, N-Quads, Turtle, TriX, Trig, JSON-LD and even HexTuples * a Graph interface which can be backed by any one of a number of Store implementations -* store implementations for in-memory, persistent on disk (Berkeley DB) and remote SPARQL endpoints +* Store implementations for in-memory, persistent on disk (Berkeley DB) and remote SPARQL endpoints + * additional Stores can be supplied via plugins * a SPARQL 1.1 implementation - supporting SPARQL 1.1 Queries and Update statements * SPARQL function extension mechanisms @@ -45,9 +46,12 @@ Help with maintenance of all of the RDFLib family of packages is always welcome * `main` branch in this repository is the current unstable release - version 8 alpha * `7.1.4` tidy-up release, possibly last 7.x release -* `7.1.2` current stable release, small improvements on to 7.1.1 +* `7.1.3` current stable release, small improvements to 7.1.1 +* `7.1.2` previously deleted release * `7.1.1` previous stable release * see +* `7.0.0` previous stable release, supports Python 3.8.1+ only. + * see [Releases](https://github.com/RDFLib/rdflib/releases) * `6.x.y` supports Python 3.7+ only. Many improvements over 5.0.0 * see * `5.x.y` supports Python 2.7 and 3.4+ and is [mostly backwards compatible with 4.2.2](https://rdflib.readthedocs.io/en/stable/upgrade4to5.html). @@ -70,8 +74,6 @@ Some features of RDFLib require optional dependencies which may be installed usi Alternatively manually download the package from the Python Package Index (PyPI) at https://pypi.python.org/pypi/rdflib -The current version of RDFLib is 7.1.2, see the ``CHANGELOG.md`` file for what's new in this release. - ### Installation of the current main branch (for developers) With *pip* you can also install rdflib from the git repository with one of the following options: diff --git a/admin/get_merged_prs.py b/admin/get_merged_prs.py index 53294bda1..4b049b425 100644 --- a/admin/get_merged_prs.py +++ b/admin/get_merged_prs.py @@ -5,7 +5,7 @@ import urllib.request # https://api.github.com/search/issues?q=repo:rdflib/rdflib+is:pr+merged:%3E=2023-08-02&per_page=300&page=1 -LAST_RELEASE_DATE = "2024-10-29" +LAST_RELEASE_DATE = "2025-01-18" ISSUES_URL = "https://api.github.com/search/issues" ITEMS = [] PAGE = 1 diff --git a/examples/datasets.py b/examples/datasets.py index 7dfc4e48b..4ae9d5c2c 100644 --- a/examples/datasets.py +++ b/examples/datasets.py @@ -1,5 +1,5 @@ """ -This file contains a number of common tasks using the RDFLib Dataset class. +This module contains a number of common tasks using the RDFLib Dataset class. An RDFLib Dataset is an object that stores multiple Named Graphs - instances of RDFLib Graph identified by IRI - within it and allows whole-of-dataset or single Graph use. @@ -10,11 +10,11 @@ There is an older implementation of a Dataset-like class in RDFLib < 7.x called ConjunctiveGraph that is now deprecated. -Sections in this file: +Sections in this module: -1. Creating & Adding -2. Looping & Counting -3. Manipulating Graphs +1. Creating & Growing Datasets +2. Looping & Counting triples/quads in Datasets +3. Manipulating Graphs with Datasets """ from rdflib import Dataset, Graph, Literal, URIRef diff --git a/examples/jsonld_serialization.py b/examples/jsonld_serialization.py index 5bee1a614..dd83d6a5d 100644 --- a/examples/jsonld_serialization.py +++ b/examples/jsonld_serialization.py @@ -1,24 +1,35 @@ """ -JSON-LD is "A JSON-based Serialization for Linked Data" (https://www.w3.org/TR/json-ld/) that RDFLib implements for RDF serialization. +JSON-LD is "A JSON-based Serialization for Linked Data" (https://www.w3.org/TR/json-ld/) +that RDFLib implements for RDF serialization. -This file demonstrated some of the JSON-LD things you can do with RDFLib. Parsing & serializing so far. More to be added later. +This file demonstrated some of the JSON-LD things you can do with RDFLib. Parsing & +serializing so far. More to be added later. Parsing ------- -There are a number of "flavours" of JSON-LD - compact and verbose etc. RDFLib can parse all of these in a normal RDFLib way. + +There are a number of "flavours" of JSON-LD - compact and verbose etc. RDFLib can parse +all of these in a normal RDFLib way. Serialization ------------- -JSON-LD has a number of options for serialization - more than other RDF formats. For example, IRIs within JSON-LD can be compacted down to CURIES when a "context" statment is added to the JSON-LD data that maps identifiers - short codes - to IRIs and namespace IRIs like this: -# here the short code "dcterms" is mapped to the IRI http://purl.org/dc/terms/ and "schema" to https://schema.org/, as per RDFLib's in-build namespace prefixes +JSON-LD has a number of options for serialization - more than other RDF formats. For +example, IRIs within JSON-LD can be compacted down to CURIES when a "context" statement +is added to the JSON-LD data that maps identifiers - short codes - to IRIs and namespace +IRIs like this: -"@context": { - "dct": "http://purl.org/dc/terms/", - "schema": "https://schema.org/" -} +.. code-block:: json + + "@context": { + "dcterms": "http://purl.org/dc/terms/", + "schema": "https://schema.org/" + } + +Here the short code "dcterms" is mapped to the IRI http://purl.org/dc/terms/ and +"schema" to https://schema.org/, as per RDFLib's in-build namespace prefixes. """ # import RDFLib and other things diff --git a/rdflib/__init__.py b/rdflib/__init__.py index 290214c6c..77d30996c 100644 --- a/rdflib/__init__.py +++ b/rdflib/__init__.py @@ -54,7 +54,7 @@ __docformat__ = "restructuredtext en" __version__: str = _DISTRIBUTION_METADATA["Version"] -__date__ = "2025-01-10" +__date__ = "2025-03-29" __all__ = [ "URIRef", diff --git a/rdflib/extras/shacl.py b/rdflib/extras/shacl.py index 1330a16ac..926202a34 100644 --- a/rdflib/extras/shacl.py +++ b/rdflib/extras/shacl.py @@ -15,6 +15,9 @@ from rdflib.graph import _ObjectType from rdflib.term import IdentifiedNode +if TYPE_CHECKING: + from rdflib.term import IdentifiedNode + class SHACLPathError(Exception): pass diff --git a/rdflib/graph.py b/rdflib/graph.py index 7a4fb910a..55177b1a7 100644 --- a/rdflib/graph.py +++ b/rdflib/graph.py @@ -380,7 +380,11 @@ Optional["_ContextType"], ] _TripleOrQuadSelectorType: te.TypeAlias = Union[_TripleSelectorType, _QuadSelectorType] - +_TripleChoiceType: te.TypeAlias = Union[ + tuple[list[_SubjectType], Optional[_PredicateType], Optional[_ObjectType]], + tuple[Optional[_SubjectType], list[_PredicateType], Optional[_ObjectType]], + tuple[Optional[_SubjectType], Optional[_PredicateType], list[_ObjectType]], +] _GraphT = TypeVar("_GraphT", bound="Graph") _ConjunctiveGraphT = TypeVar("_ConjunctiveGraphT", bound="ConjunctiveGraph") @@ -919,7 +923,7 @@ def set( def subjects( self, predicate: Path | _PredicateType | None = None, - object: _ObjectType | None = None, + object: _ObjectType | list[_ObjectType] | None = None, unique: bool = False, ) -> Generator[_SubjectType, None, None]: """Generate subjects with the given predicate and object. @@ -932,21 +936,27 @@ def subjects( Yields: Subjects matching the given predicate and object. """ - if not unique: - for s, p, o in self.triples((None, predicate, object)): - yield s + # if the object is a list of Nodes, yield results from subject() call for each + if isinstance(object, list): + for obj in object: + for s in self.subjects(predicate, obj, unique): + yield s else: - subs = set() - for s, p, o in self.triples((None, predicate, object)): - if s not in subs: + if not unique: + for s, p, o in self.triples((None, predicate, object)): yield s - try: - subs.add(s) - except MemoryError as e: - logger.error( - f"{e}. Consider not setting parameter 'unique' to True" - ) - raise + else: + subs = set() + for s, p, o in self.triples((None, predicate, object)): + if s not in subs: + yield s + try: + subs.add(s) + except MemoryError as e: + logger.error( + f"{e}. Consider not setting parameter 'unique' to True" + ) + raise def predicates( self, @@ -982,7 +992,7 @@ def predicates( def objects( self, - subject: _SubjectType | None = None, + subject: _SubjectType | list[_SubjectType] | None = None, predicate: Path | _PredicateType | None = None, unique: bool = False, ) -> Generator[_ObjectType, None, None]: @@ -996,21 +1006,26 @@ def objects( Yields: Objects matching the given subject and predicate. """ - if not unique: - for s, p, o in self.triples((subject, predicate, None)): - yield o + if isinstance(subject, list): + for subj in subject: + for o in self.objects(subj, predicate, unique): + yield o else: - objs = set() - for s, p, o in self.triples((subject, predicate, None)): - if o not in objs: + if not unique: + for s, p, o in self.triples((subject, predicate, None)): yield o - try: - objs.add(o) - except MemoryError as e: - logger.error( - f"{e}. Consider not setting parameter 'unique' to True" - ) - raise + else: + objs = set() + for s, p, o in self.triples((subject, predicate, None)): + if o not in objs: + yield o + try: + objs.add(o) + except MemoryError as e: + logger.error( + f"{e}. Consider not setting parameter 'unique' to True" + ) + raise def subject_predicates( self, object: _ObjectType | None = None, unique: bool = False @@ -1079,23 +1094,7 @@ def predicate_objects( def triples_choices( self, - triple: ( - tuple[ - list[_SubjectType] | tuple[_SubjectType, ...], - _PredicateType, - _ObjectType | None, - ] - | tuple[ - _SubjectType | None, - list[_PredicateType] | tuple[_PredicateType, ...], - _ObjectType | None, - ] - | tuple[ - _SubjectType | None, - _PredicateType, - list[_ObjectType] | tuple[_ObjectType, ...], - ] - ), + triple: _TripleChoiceType, context: _ContextType | None = None, ) -> Generator[_TripleType, None, None]: subject, predicate, object_ = triple @@ -2362,24 +2361,8 @@ def quads( def triples_choices( self, - triple: ( - tuple[ - list[_SubjectType] | tuple[_SubjectType, ...], - _PredicateType, - _ObjectType | None, - ] - | tuple[ - _SubjectType | None, - list[_PredicateType] | tuple[_PredicateType, ...], - _ObjectType | None, - ] - | tuple[ - _SubjectType | None, - _PredicateType, - list[_ObjectType] | tuple[_ObjectType, ...], - ] - ), - context: _ContextType | None = None, + triple: _TripleChoiceType, + context: Optional[_ContextType] = None, ) -> Generator[_TripleType, None, None]: """Iterate over all the triples in the entire conjunctive graph""" s, p, o = triple @@ -3156,23 +3139,7 @@ def __isub__(self: _GraphT, other: Iterable[_TripleType]) -> NoReturn: def triples_choices( self, - triple: ( - tuple[ - list[_SubjectType] | tuple[_SubjectType, ...], - _PredicateType, - _ObjectType | None, - ] - | tuple[ - _SubjectType | None, - list[_PredicateType] | tuple[_PredicateType, ...], - _ObjectType | None, - ] - | tuple[ - _SubjectType | None, - _PredicateType, - list[_ObjectType] | tuple[_ObjectType, ...], - ] - ), + triple: _TripleChoiceType, context: _ContextType | None = None, ) -> Generator[_TripleType, None, None]: subject, predicate, object_ = triple diff --git a/rdflib/namespace/__init__.py b/rdflib/namespace/__init__.py index 871ba24a3..ef8aa3241 100644 --- a/rdflib/namespace/__init__.py +++ b/rdflib/namespace/__init__.py @@ -743,7 +743,7 @@ def _store_bind(self, prefix: str, namespace: URIRef, override: bool) -> None: return self.store.bind(prefix, namespace, override=override) except TypeError as error: if "override" in str(error): - logger.warning( + logger.debug( "caught a TypeError, " "retrying call to %s.bind without override, " "see https://github.com/RDFLib/rdflib/issues/1880 for more info", diff --git a/rdflib/plugins/parsers/notation3.py b/rdflib/plugins/parsers/notation3.py index 56ce90462..dedbfcc87 100755 --- a/rdflib/plugins/parsers/notation3.py +++ b/rdflib/plugins/parsers/notation3.py @@ -49,7 +49,7 @@ from rdflib.compat import long_type from rdflib.exceptions import ParserError -from rdflib.graph import ConjunctiveGraph, Graph, QuotedGraph +from rdflib.graph import Dataset, Graph, QuotedGraph from rdflib.term import ( _XSD_PFX, BNode, @@ -2064,7 +2064,7 @@ def parse( # type: ignore[override] elif not fa: raise ParserError("Cannot parse N3 into non-formula-aware store.") - conj_graph = ConjunctiveGraph(store=graph.store) + conj_graph = Dataset(store=graph.store) conj_graph.default_context = graph # TODO: CG __init__ should have a # default_context arg # TODO: update N3Processor so that it can use conj_graph as the sink diff --git a/rdflib/plugins/parsers/rdfxml.py b/rdflib/plugins/parsers/rdfxml.py index 9ea1fceca..14a9bc69d 100644 --- a/rdflib/plugins/parsers/rdfxml.py +++ b/rdflib/plugins/parsers/rdfxml.py @@ -298,7 +298,8 @@ def document_element_start( self, name: tuple[str, str], qname, attrs: AttributesImpl ) -> None: if name[0] and URIRef("".join(name)) == RDFVOC.RDF: - next = self.next + # Cheap hack so 2to3 doesn't turn it into __next__ + next = getattr(self, "next") next.start = self.node_element_start next.end = self.node_element_end else: @@ -315,7 +316,8 @@ def node_element_start( current = self.current absolutize = self.absolutize - next = self.next + # Cheap hack so 2to3 doesn't turn it into __next__ + next = getattr(self, "next") next.start = self.property_element_start next.end = self.property_element_end @@ -408,7 +410,8 @@ def property_element_start( current = self.current absolutize = self.absolutize - next = self.next + # Cheap hack so 2to3 doesn't turn it into __next__ + next = getattr(self, "next") object: _ObjectType | None = None current.data = None current.list = None diff --git a/rdflib/plugins/stores/berkeleydb.py b/rdflib/plugins/stores/berkeleydb.py index 6a034d1e6..cd8c095fc 100644 --- a/rdflib/plugins/stores/berkeleydb.py +++ b/rdflib/plugins/stores/berkeleydb.py @@ -427,7 +427,8 @@ def remove( # type: ignore[override] cursor = index.cursor(txn=txn) try: cursor.set_range(key) - current = cursor.next + # Hack to stop 2to3 converting this to next(cursor) + current = getattr(cursor, "next")() except db.DBNotFoundError: current = None cursor.close() @@ -504,7 +505,8 @@ def triples( cursor = index.cursor(txn=txn) try: cursor.set_range(key) - current = cursor.next + # Cheap hack so 2to3 doesn't convert to next(cursor) + current = getattr(cursor, "next")() except db.DBNotFoundError: current = None cursor.close() @@ -536,7 +538,8 @@ def __len__(self, context: _ContextType | None = None) -> int: key, value = current if key.startswith(prefix): count += 1 - current = cursor.next + # Hack to stop 2to3 converting this to next(cursor) + current = getattr(cursor, "next")() else: break cursor.close() @@ -589,7 +592,8 @@ def namespaces(self) -> Generator[tuple[str, URIRef], None, None]: while current: prefix, namespace = current results.append((prefix.decode("utf-8"), namespace.decode("utf-8"))) - current = cursor.next + # Hack to stop 2to3 converting this to next(cursor) + current = getattr(cursor, "next")() cursor.close() for prefix, namespace in results: yield prefix, URIRef(namespace) @@ -628,7 +632,8 @@ def contexts( cursor = index.cursor() try: cursor.set_range(key) - current = cursor.next + # Hack to stop 2to3 converting this to next(cursor) + current = getattr(cursor, "next")() except db.DBNotFoundError: current = None cursor.close() diff --git a/rdflib/plugins/stores/sparqlstore.py b/rdflib/plugins/stores/sparqlstore.py index b544c88a2..2cbae5d51 100644 --- a/rdflib/plugins/stores/sparqlstore.py +++ b/rdflib/plugins/stores/sparqlstore.py @@ -36,6 +36,7 @@ _TripleType, _ContextType, _QuadType, + _TripleChoiceType, _TriplePatternType, _SubjectType, _PredicateType, @@ -79,15 +80,15 @@ class SPARQLStore(SPARQLConnector, Store): >>> from rdflib import Dataset >>> from rdflib.plugins.stores.sparqlstore import SPARQLStore >>> - >>> g = Dataset( + >>> g = Dataset( # doctest: +SKIP ... SPARQLStore("https://query.wikidata.org/sparql", returnFormat="xml"), ... default_union=True ... ) >>> - >>> res = g.query("SELECT ?s ?p ?o WHERE { ?s ?p ?o } LIMIT 5") + >>> res = g.query("SELECT ?s ?p ?o WHERE { ?s ?p ?o } LIMIT 5") # doctest: +SKIP >>> >>> # Iterate the results - >>> for row in res: + >>> for row in res: # doctest: +SKIP ... pass # but really you'd do something like: print(row) >>> >>> # Or serialize the results @@ -440,23 +441,7 @@ def triples( # type: ignore[override] def triples_choices( self, - _: ( - tuple[ - list[_SubjectType] | tuple[_SubjectType, ...], - _PredicateType, - _ObjectType | None, - ] - | tuple[ - _SubjectType | None, - list[_PredicateType] | tuple[_PredicateType, ...], - _ObjectType | None, - ] - | tuple[ - _SubjectType | None, - _PredicateType, - list[_ObjectType] | tuple[_ObjectType, ...], - ] - ), + _: _TripleChoiceType, context: _ContextType | None = None, ) -> Generator[ tuple[ diff --git a/rdflib/store.py b/rdflib/store.py index 1565517d7..d7ce6f2cb 100644 --- a/rdflib/store.py +++ b/rdflib/store.py @@ -31,10 +31,8 @@ from rdflib.graph import ( Graph, _ContextType, - _ObjectType, - _PredicateType, _QuadType, - _SubjectType, + _TripleChoiceType, _TriplePatternType, _TripleType, ) @@ -273,23 +271,7 @@ def remove( def triples_choices( self, - triple: ( - tuple[ - list[_SubjectType] | tuple[_SubjectType, ...], - _PredicateType, - _ObjectType | None, - ] - | tuple[ - _SubjectType | None, - list[_PredicateType] | tuple[_PredicateType, ...], - _ObjectType | None, - ] - | tuple[ - _SubjectType | None, - _PredicateType, - list[_ObjectType] | tuple[_ObjectType, ...], - ] - ), + triple: _TripleChoiceType, context: _ContextType | None = None, ) -> Generator[ tuple[ @@ -305,9 +287,6 @@ def triples_choices( time from the default 'fallback' implementation, which will iterate over each term in the list and dispatch to triples """ - subject: _SubjectType | list[_SubjectType] | tuple[_SubjectType, ...] | None - predicate: _PredicateType | list[_PredicateType] | tuple[_PredicateType, ...] - object_: _ObjectType | list[_ObjectType] | tuple[_ObjectType, ...] | None subject, predicate, object_ = triple if isinstance(object_, (list, tuple)): # MyPy thinks these are unreachable due to the triple pattern signature. diff --git a/test/test_graph/test_aggregate_graphs.py b/test/test_graph/test_aggregate_graphs.py index fd45c4a14..6ede92fda 100644 --- a/test/test_graph/test_aggregate_graphs.py +++ b/test/test_graph/test_aggregate_graphs.py @@ -1,7 +1,7 @@ from io import StringIO -from rdflib import logger, plugin -from rdflib.graph import ConjunctiveGraph, Graph, ReadOnlyGraphAggregate +from rdflib import Dataset, Graph, logger, plugin +from rdflib.graph import ReadOnlyGraphAggregate from rdflib.namespace import RDF, RDFS from rdflib.store import Store from rdflib.term import URIRef @@ -108,10 +108,10 @@ def test_aggregate2(): graph4 = Graph(mem_store, RDFS) graph4.parse(data=TEST_GRAPH_1N3, format="n3") - g = ConjunctiveGraph(mem_store) + g = Dataset(store=mem_store, default_union=True) assert g is not None assert len(list(g.quads((None, None, None, None)))) == 11 - assert len(list(g.contexts())) == 4 + assert len(list(g.contexts())) == 5 logger.debug(list(g.contexts())) assert ( len(list(g.quads((None, None, None, URIRef("http://example.com/graph2"))))) == 4 diff --git a/test/test_graph/test_diff.py b/test/test_graph/test_diff.py index d9893058e..24a5828a1 100644 --- a/test/test_graph/test_diff.py +++ b/test/test_graph/test_diff.py @@ -10,7 +10,7 @@ import rdflib from rdflib import Graph from rdflib.compare import graph_diff -from rdflib.graph import ConjunctiveGraph, Dataset +from rdflib.graph import Dataset from rdflib.namespace import FOAF, RDF from rdflib.term import BNode, Literal from test.utils import ( @@ -112,7 +112,7 @@ def as_element_set(self, value: _ElementSetTypeOrStr) -> _ElementSetType: if isinstance(value, str): graph = self.graph_type() graph.parse(data=value, format=self.format) - if isinstance(graph, ConjunctiveGraph): + if isinstance(graph, Dataset): return GraphHelper.quad_set(graph, BNodeHandling.COLLAPSE) else: return GraphHelper.triple_set(graph, BNodeHandling.COLLAPSE) diff --git a/test/test_graph/test_graph.py b/test/test_graph/test_graph.py index 1c95b05fa..50da73228 100644 --- a/test/test_graph/test_graph.py +++ b/test/test_graph/test_graph.py @@ -399,7 +399,7 @@ def test_guess_format_for_parse_http_text_plain(): assert len(graph) > 0 # A url that returns content-type text/html. - url = "https://github.com/RDFLib/rdflib/issues/2734" + url = "https://www.w3.org/TR/REC-rdf-syntax/" with pytest.raises(PluginException): graph = Graph().parse(url) diff --git a/test/test_graph/test_graph_context.py b/test/test_graph/test_graph_context.py index b6c597c90..a3cb83390 100644 --- a/test/test_graph/test_graph_context.py +++ b/test/test_graph/test_graph_context.py @@ -8,7 +8,7 @@ import pytest -from rdflib import BNode, ConjunctiveGraph, Graph, URIRef, plugin +from rdflib import BNode, Dataset, Graph, URIRef, plugin from rdflib.store import Store @@ -19,7 +19,7 @@ class ContextTestCase(unittest.TestCase): def setUp(self): try: - self.graph = ConjunctiveGraph(store=self.store) + self.graph = Dataset(store=self.store, default_union=True) except ImportError: pytest.skip("Dependencies for store '%s' not available!" % self.store) if self.store == "SQLite": @@ -290,71 +290,66 @@ def test_triples(self): for c in [graph, self.graph.get_context(c1)]: # unbound subjects - asserte(set(c.subjects(likes, pizza)), set((michel, tarek))) - asserte(set(c.subjects(hates, pizza)), set((bob,))) - asserte(set(c.subjects(likes, cheese)), set([tarek, bob, michel])) + asserte(set(c.subjects(likes, pizza)), {michel, tarek}) + asserte(set(c.subjects(hates, pizza)), {bob}) + asserte(set(c.subjects(likes, cheese)), {tarek, bob, michel}) asserte(set(c.subjects(hates, cheese)), set()) # unbound objects - asserte(set(c.objects(michel, likes)), set([cheese, pizza])) - asserte(set(c.objects(tarek, likes)), set([cheese, pizza])) - asserte(set(c.objects(bob, hates)), set([michel, pizza])) - asserte(set(c.objects(bob, likes)), set([cheese])) + asserte(set(c.objects(michel, likes)), {cheese, pizza}) + asserte(set(c.objects(tarek, likes)), {cheese, pizza}) + asserte(set(c.objects(bob, hates)), {michel, pizza}) + asserte(set(c.objects(bob, likes)), {cheese}) # unbound predicates - asserte(set(c.predicates(michel, cheese)), set([likes])) - asserte(set(c.predicates(tarek, cheese)), set([likes])) - asserte(set(c.predicates(bob, pizza)), set([hates])) - asserte(set(c.predicates(bob, michel)), set([hates])) + asserte(set(c.predicates(michel, cheese)), {likes}) + asserte(set(c.predicates(tarek, cheese)), {likes}) + asserte(set(c.predicates(bob, pizza)), {hates}) + asserte(set(c.predicates(bob, michel)), {hates}) - asserte(set(c.subject_objects(hates)), set([(bob, pizza), (bob, michel)])) + asserte(set(c.subject_objects(hates)), {(bob, pizza), (bob, michel)}) asserte( set(c.subject_objects(likes)), - set( - [ - (tarek, cheese), - (michel, cheese), - (michel, pizza), - (bob, cheese), - (tarek, pizza), - ] - ), + { + (tarek, cheese), + (michel, cheese), + (michel, pizza), + (bob, cheese), + (tarek, pizza), + }, ) - asserte( - set(c.predicate_objects(michel)), set([(likes, cheese), (likes, pizza)]) - ) + asserte(set(c.predicate_objects(michel)), {(likes, cheese), (likes, pizza)}) asserte( set(c.predicate_objects(bob)), - set([(likes, cheese), (hates, pizza), (hates, michel)]), - ) - asserte( - set(c.predicate_objects(tarek)), set([(likes, cheese), (likes, pizza)]) + {(likes, cheese), (hates, pizza), (hates, michel)}, ) + asserte(set(c.predicate_objects(tarek)), {(likes, cheese), (likes, pizza)}) asserte( set(c.subject_predicates(pizza)), - set([(bob, hates), (tarek, likes), (michel, likes)]), + {(bob, hates), (tarek, likes), (michel, likes)}, ) asserte( set(c.subject_predicates(cheese)), - set([(bob, likes), (tarek, likes), (michel, likes)]), + {(bob, likes), (tarek, likes), (michel, likes)}, ) - asserte(set(c.subject_predicates(michel)), set([(bob, hates)])) + asserte(set(c.subject_predicates(michel)), {(bob, hates)}) + d = set() + for x in c: + d.add(x[0:3]) asserte( - set(c), - set( - [ - (bob, hates, michel), - (bob, likes, cheese), - (tarek, likes, pizza), - (michel, likes, pizza), - (michel, likes, cheese), - (bob, hates, pizza), - (tarek, likes, cheese), - ] - ), + set(d), + { + (bob, hates, michel), + (bob, likes, cheese), + (tarek, likes, pizza), + (michel, likes, pizza), + (michel, likes, cheese), + (bob, hates, pizza), + (tarek, likes, cheese), + }, ) # remove stuff and make sure the graph is empty again diff --git a/test/test_graph/test_graph_formula.py b/test/test_graph/test_graph_formula.py index 0f77dc294..eebc8385a 100644 --- a/test/test_graph/test_graph_formula.py +++ b/test/test_graph/test_graph_formula.py @@ -4,7 +4,7 @@ import pytest from rdflib import RDF, RDFS, BNode, URIRef, Variable, plugin -from rdflib.graph import ConjunctiveGraph, QuotedGraph +from rdflib.graph import Dataset, QuotedGraph implies = URIRef("http://www.w3.org/2000/10/swap/log#implies") testN3 = """ @@ -21,7 +21,7 @@ def checkFormulaStore(store="default", configString=None): # noqa: N802, N803 try: - g = ConjunctiveGraph(store=store) + g = Dataset(store=store) except ImportError: pytest.skip("Dependencies for store '%s' not available!" % store) @@ -48,7 +48,7 @@ def checkFormulaStore(store="default", configString=None): # noqa: N802, N803 d = URIRef("http://test/d") v = Variable("y") - universe = ConjunctiveGraph(g.store) + universe = Dataset(g.store) # test formula as terms assert len(list(universe.triples((formulaA, implies, formulaB)))) == 1 diff --git a/test/test_graph/test_graph_generators.py b/test/test_graph/test_graph_generators.py index 0d89c9b7f..bec7ccb4c 100644 --- a/test/test_graph/test_graph_generators.py +++ b/test/test_graph/test_graph_generators.py @@ -75,3 +75,19 @@ def test_parse_berners_lee_card_into_graph(): assert len(list(graph.subjects(unique=True))) == no_of_unique_subjects assert len(list(graph.predicates(unique=True))) == no_of_unique_predicates assert len(list(graph.objects(unique=True))) == no_of_unique_objects + + +def test_subjects_multi(): + graph = Graph() + add_stuff(graph) + assert len([subj for subj in graph.subjects(LIKES, [CHEESE, PIZZA])]) == 5 + assert len([subj for subj in graph.subjects(LIKES, [])]) == 0 + assert len([subj for subj in graph.subjects(LIKES | HATES, [CHEESE, PIZZA])]) == 6 + + +def test_objects_multi(): + graph = Graph() + add_stuff(graph) + assert len([obj for obj in graph.objects([TAREK, BOB], LIKES)]) == 6 + assert len([obj for obj in graph.objects([], LIKES)]) == 0 + assert len([obj for obj in graph.objects([TAREK, BOB], LIKES | HATES)]) == 8 diff --git a/test/test_graph/test_namespace_rebinding.py b/test/test_graph/test_namespace_rebinding.py index a46cadd43..9aaf07251 100644 --- a/test/test_graph/test_namespace_rebinding.py +++ b/test/test_graph/test_namespace_rebinding.py @@ -1,6 +1,6 @@ import pytest -from rdflib import ConjunctiveGraph, Graph, Literal +from rdflib import Dataset, Graph, Literal from rdflib.namespace import OWL, Namespace, NamespaceManager from rdflib.plugins.stores.memory import Memory from rdflib.term import URIRef @@ -290,12 +290,11 @@ def test_multigraph_bindings(): assert list(g1.namespaces()) == [("friend-of-a-friend", foaf1_uri)] # Including newly-created objects that use the store - cg = ConjunctiveGraph(store=store) + cg = Dataset(store=store, default_union=True) cg.namespace_manager = NamespaceManager(cg, bind_namespaces="core") assert ("foaf", foaf1_uri) not in list(cg.namespaces()) assert ("friend-of-a-friend", foaf1_uri) in list(cg.namespaces()) - assert len(list(g1.namespaces())) == 6 assert len(list(g2.namespaces())) == 6 assert len(list(cg.namespaces())) == 6 diff --git a/test/test_issues/test_issue535.py b/test/test_issues/test_issue535.py index 03f75d601..80a639694 100644 --- a/test/test_issues/test_issue535.py +++ b/test/test_issues/test_issue535.py @@ -1,8 +1,8 @@ -from rdflib import ConjunctiveGraph, URIRef +from rdflib import Dataset, URIRef def test_nquads_default_graph(): - ds = ConjunctiveGraph() + ds = Dataset(default_union=True) data = """ . diff --git a/test/test_misc/test_plugins.py b/test/test_misc/test_plugins.py index ce450da37..98d6e4236 100644 --- a/test/test_misc/test_plugins.py +++ b/test/test_misc/test_plugins.py @@ -11,6 +11,8 @@ from pathlib import Path from typing import Any, cast +import pytest + import rdflib.plugin import rdflib.plugins.sparql import rdflib.plugins.sparql.evaluate @@ -31,6 +33,25 @@ def ctx_plugin(tmp_path: Path, plugin_src: Path) -> Generator[None, None, None]: base = tmp_path / f"{hash(plugin_src)}" pypath = (base / "pypath").absolute() plugpath = (base / "plugin").absolute() + wheel_cache = (base / "wheel_cache").absolute() + wheel_cache.mkdir(parents=True) + + # Create a local wheel cache with setuptools and wheel + subprocess.run( + [ + sys.executable, + "-m", + "pip", + "download", + "--no-deps", + "--dest", + f"{wheel_cache}", + "setuptools", + "wheel", + ], + check=True, + ) + shutil.copytree(plugin_src, plugpath) logging.debug("Installing %s into %s", plugin_src, pypath) subprocess.run( @@ -43,6 +64,8 @@ def ctx_plugin(tmp_path: Path, plugin_src: Path) -> Generator[None, None, None]: "--no-input", "--no-clean", "--no-index", + "--find-links", + f"{wheel_cache}", "--disable-pip-version-check", "--target", f"{pypath}", @@ -68,6 +91,7 @@ def ctx_cleaners() -> Generator[list[Callable[[], None]], None, None]: # Using no_cover as coverage freaks out and crashes because of what is happening here. +@pytest.mark.webtest def test_sparqleval(tmp_path: Path, no_cover: None) -> None: with ExitStack() as stack: stack.enter_context(ctx_plugin(tmp_path, TEST_PLUGINS_DIR / "sparqleval")) @@ -105,6 +129,7 @@ def test_sparqleval(tmp_path: Path, no_cover: None) -> None: # Using no_cover as coverage freaks out and crashes because of what is happening here. +@pytest.mark.webtest def test_parser(tmp_path: Path, no_cover: None) -> None: with ExitStack() as stack: stack.enter_context(ctx_plugin(tmp_path, TEST_PLUGINS_DIR / "parser")) diff --git a/test/test_n3.py b/test/test_n3.py index f3d7eeb07..d2c362a39 100644 --- a/test/test_n3.py +++ b/test/test_n3.py @@ -4,7 +4,7 @@ import pytest -from rdflib.graph import ConjunctiveGraph, Graph +from rdflib.graph import Dataset, Graph from rdflib.plugins.parsers.notation3 import BadSyntax, exponent_syntax from rdflib.term import Literal, URIRef from test import TEST_DIR @@ -44,7 +44,7 @@ n3:predicate :p; n3:object :y ] a log:Truth}. -# Needs more thought ... ideally, we have the implcit AND rules of +# Needs more thought ... ideally, we have the implicit AND rules of # juxtaposition (introduction and elimination) { @@ -125,9 +125,9 @@ def test_base_serialize(self): ) s = g.serialize(base="http://example.com/", format="n3", encoding="latin-1") assert b"" in s - g2 = ConjunctiveGraph() - g2.parse(data=s, publicID="http://example.com/", format="n3") - assert list(g) == list(g2) + g2 = Dataset() + g2.parse(data=s, format="n3") + assert list(g) == list(g2.triples((None, None, None))) def test_issue23(self): input = """ "this word is in \\u201Cquotes\\u201D".""" @@ -195,24 +195,24 @@ def test_dot_in_prefix(self): ) def test_model(self): - g = ConjunctiveGraph() + g = Dataset() g.parse(data=test_data, format="n3") i = 0 - for s, p, o in g: + for s, p, o, c in g: if isinstance(s, Graph): i += 1 assert i == 3 - assert len(list(g.contexts())) == 13 + assert len(list(g.graphs())) == 13 g.close() def test_quoted_serialization(self): - g = ConjunctiveGraph() + g = Dataset() g.parse(data=test_data, format="n3") g.serialize(format="n3") def test_parse(self): - g = ConjunctiveGraph() + g = Dataset() try: g.parse( "http://groups.csail.mit.edu/dig/2005/09/rein/examples/troop42-policy.n3", @@ -229,14 +229,14 @@ def test_single_quoted_literals(self): for data in test_data: # N3 doesn't accept single quotes around string literals - g = ConjunctiveGraph() + g = Dataset() with pytest.raises(BadSyntax): g.parse(data=data, format="n3") - g = ConjunctiveGraph() + g = Dataset() g.parse(data=data, format="turtle") assert len(g) == 1 - for _, _, o in g: + for _, _, o, c in g: assert o == Literal("o") def test_empty_prefix(self): diff --git a/test/test_namespace/test_namespace.py b/test/test_namespace/test_namespace.py index 36c04486b..22c727c26 100644 --- a/test/test_namespace/test_namespace.py +++ b/test/test_namespace/test_namespace.py @@ -232,7 +232,7 @@ def add_not_in_namespace(s): # a property name within the FOAF namespace assert FOAF.givenName == URIRef("http://xmlns.com/foaf/0.1/givenName") - # namescape can be used as str + # namespace can be used as str assert FOAF.givenName.startswith(FOAF) def test_contains_method(self): diff --git a/test/test_parsers/test_broken_parse_data_from_jena.py b/test/test_parsers/test_broken_parse_data_from_jena.py index 353593837..da25154a7 100644 --- a/test/test_parsers/test_broken_parse_data_from_jena.py +++ b/test/test_parsers/test_broken_parse_data_from_jena.py @@ -33,6 +33,6 @@ def xfail_broken_parse_data(request): @pytest.mark.parametrize("testfile", os.listdir(broken_parse_data)) @pytest.mark.usefixtures("xfail_broken_parse_data") def test_n3_serializer_roundtrip(testfile) -> None: - g1 = rdflib.ConjunctiveGraph() + g1 = rdflib.Dataset(default_union=True) g1.parse(os.path.join(broken_parse_data, testfile), format="n3") diff --git a/test/test_parsers/test_empty_xml_base.py b/test/test_parsers/test_empty_xml_base.py index 0f3f18694..0225c4c63 100644 --- a/test/test_parsers/test_empty_xml_base.py +++ b/test/test_parsers/test_empty_xml_base.py @@ -5,7 +5,7 @@ and RDF/XML dependence on it """ -from rdflib.graph import ConjunctiveGraph +from rdflib.graph import Dataset from rdflib.namespace import FOAF, RDF from rdflib.term import URIRef @@ -36,7 +36,7 @@ class TestEmptyBase: def test_empty_base_ref(self): - self.graph = ConjunctiveGraph() + self.graph = Dataset() self.graph.parse(data=test_data, publicID=baseUri, format="xml") assert ( len(list(self.graph)) > 0 @@ -50,7 +50,7 @@ def test_empty_base_ref(self): class TestRelativeBase: def test_relative_base_ref(self): - self.graph = ConjunctiveGraph() + self.graph = Dataset() self.graph.parse(data=test_data2, publicID=baseUri2, format="xml") assert ( len(self.graph) > 0 diff --git a/test/test_parsers/test_nquads.py b/test/test_parsers/test_nquads.py index ad17b5aee..ee14856ab 100644 --- a/test/test_parsers/test_nquads.py +++ b/test/test_parsers/test_nquads.py @@ -1,6 +1,7 @@ import os -from rdflib import ConjunctiveGraph, Namespace, URIRef +from rdflib import Dataset, Literal, URIRef +from rdflib.namespace import FOAF from test.data import TEST_DATA_DIR TEST_BASE = os.path.join(TEST_DATA_DIR, "nquads.rdflib") @@ -8,7 +9,7 @@ class TestNQuadsParser: def _load_example(self): - g = ConjunctiveGraph() + g = Dataset() nq_path = os.path.relpath( os.path.join(TEST_DATA_DIR, "nquads.rdflib/example.nquads"), os.curdir ) @@ -21,9 +22,9 @@ def test_01_simple_open(self): assert len(g.store) == 449 def test_02_contexts(self): - # There should be 16 separate contexts + # There should be 17 separate contexts - 16 Named + default g = self._load_example() - assert len([x for x in g.store.contexts()]) == 16 + assert len([x for x in g.store.contexts()]) == 17 def test_03_get_value(self): # is the name of entity E10009 "Arco Publications"? @@ -36,11 +37,11 @@ def test_03_get_value(self): g = self._load_example() s = URIRef("http://bibliographica.org/entity/E10009") - FOAF = Namespace("http://xmlns.com/foaf/0.1/") # noqa: N806 - assert g.value(s, FOAF.name).eq("Arco Publications") + for s, p, o, c in list(g.quads((s, FOAF.name, None, None))): + assert o == Literal("Arco Publications") def test_context_is_optional(self): - g = ConjunctiveGraph() + g = Dataset() nq_path = os.path.relpath( os.path.join(TEST_DATA_DIR, "nquads.rdflib/test6.nq"), os.curdir ) @@ -49,7 +50,7 @@ def test_context_is_optional(self): assert len(g) > 0 def test_serialize(self): - g = ConjunctiveGraph() + g = Dataset() uri1 = URIRef("http://example.org/mygraph1") uri2 = URIRef("http://example.org/mygraph2") @@ -63,7 +64,7 @@ def test_serialize(self): s = g.serialize(format="nquads", encoding="utf-8") assert len([x for x in s.split(b"\n") if x.strip()]) == 2 - g2 = ConjunctiveGraph() + g2 = Dataset() g2.parse(data=s, format="nquads") assert len(g) == len(g2) @@ -89,8 +90,8 @@ def teardown_method(self, method): def test_parse_shared_bnode_context(self): bnode_ctx = dict() - g = ConjunctiveGraph() - h = ConjunctiveGraph() + g = Dataset() + h = Dataset() g.parse(self.data, format="nquads", bnode_context=bnode_ctx) self.data.seek(0) h.parse(self.data, format="nquads", bnode_context=bnode_ctx) @@ -98,7 +99,7 @@ def test_parse_shared_bnode_context(self): def test_parse_shared_bnode_context_same_graph(self): bnode_ctx = dict() - g = ConjunctiveGraph() + g = Dataset() g.parse(self.data_obnodes, format="nquads", bnode_context=bnode_ctx) o1 = set(g.objects()) self.data_obnodes.seek(0) @@ -107,27 +108,27 @@ def test_parse_shared_bnode_context_same_graph(self): assert o1 == o2 def test_parse_distinct_bnode_context(self): - g = ConjunctiveGraph() + g = Dataset() g.parse(self.data, format="nquads", bnode_context=dict()) - s1 = set(g.subjects()) + s1 = set([x for x, p, o, c in list(g.quads((None, None, None, None)))]) self.data.seek(0) g.parse(self.data, format="nquads", bnode_context=dict()) - s2 = set(g.subjects()) + s2 = set([x for x, p, o, c in list(g.quads((None, None, None, None)))]) assert set() != (s2 - s1) def test_parse_distinct_bnode_contexts_between_graphs(self): - g = ConjunctiveGraph() - h = ConjunctiveGraph() + g = Dataset() + h = Dataset() g.parse(self.data, format="nquads") - s1 = set(g.subjects()) + s1 = sorted(set([x for x, p, o, c in list(g.quads((None, None, None, None)))])) self.data.seek(0) h.parse(self.data, format="nquads") - s2 = set(h.subjects()) + s2 = sorted(set([x for x, p, o, c in list(h.quads((None, None, None, None)))])) assert s1 != s2 def test_parse_distinct_bnode_contexts_named_graphs(self): - g = ConjunctiveGraph() - h = ConjunctiveGraph() + g = Dataset() + h = Dataset() g.parse(self.data, format="nquads") self.data.seek(0) h.parse(self.data, format="nquads") @@ -135,9 +136,17 @@ def test_parse_distinct_bnode_contexts_named_graphs(self): def test_parse_shared_bnode_contexts_named_graphs(self): bnode_ctx = dict() - g = ConjunctiveGraph() - h = ConjunctiveGraph() - g.parse(self.data, format="nquads", bnode_context=bnode_ctx) + g = Dataset() + h = Dataset() + g.parse( + TEST_DATA_DIR / "nquads.rdflib/bnode_context.nquads", + format="nquads", + bnode_context=bnode_ctx, + ) self.data.seek(0) - h.parse(self.data, format="nquads", bnode_context=bnode_ctx) + h.parse( + TEST_DATA_DIR / "nquads.rdflib/bnode_context.nquads", + format="nquads", + bnode_context=bnode_ctx, + ) assert set(h.contexts()) == set(g.contexts()) diff --git a/test/test_parsers/test_parser_hext.py b/test/test_parsers/test_parser_hext.py index c71bd1a49..17d19fa26 100644 --- a/test/test_parsers/test_parser_hext.py +++ b/test/test_parsers/test_parser_hext.py @@ -1,6 +1,6 @@ from pathlib import Path -from rdflib import BNode, ConjunctiveGraph, Dataset, Literal, URIRef +from rdflib import BNode, Dataset, Literal, URIRef from rdflib.compare import isomorphic from rdflib.graph import DATASET_DEFAULT_GRAPH_ID from rdflib.namespace import XSD @@ -97,7 +97,7 @@ def test_small_string_cg(): ["http://example.com/s01", "http://example.com/op1", "http://example.com/o2", "globalId", "", ""] ["http://example.com/s01", "http://example.com/op2", "http://example.com/o3", "globalId", "", ""] """ - d = ConjunctiveGraph(identifier=DATASET_DEFAULT_GRAPH_ID) + d = Dataset() d.parse(data=s, format="hext") expected_graph_names = ( @@ -140,7 +140,7 @@ def test_small_file_multigraph(): def test_small_file_multigraph_cg(): - d = ConjunctiveGraph() + d = Dataset() assert len(d) == 0 d.parse( Path(__file__).parent.parent / "data/test_parser_hext_multigraph.ndjson", @@ -185,14 +185,14 @@ def test_roundtrip(): print(f"Test {tests}: {f}") if f.name not in files_to_skip.keys(): try: - cg = ConjunctiveGraph().parse(f, format="nt") + cg = Dataset().parse(f, format="nt") # print(cg.serialize(format="n3")) except Exception: print("Skipping: could not NT parse") skipped += 1 skip = True if not skip: - cg2 = ConjunctiveGraph() + cg2 = Dataset() cg2.parse( data=cg.serialize(format="hext"), format="hext", diff --git a/test/test_parsers/test_trix_parse.py b/test/test_parsers/test_trix_parse.py index e6f2ae91b..e48cf9b07 100644 --- a/test/test_parsers/test_trix_parse.py +++ b/test/test_parsers/test_trix_parse.py @@ -1,6 +1,6 @@ import os -from rdflib.graph import ConjunctiveGraph +from rdflib.graph import Dataset from test.data import TEST_DATA_DIR @@ -12,7 +12,7 @@ def teardown_method(self): pass def testAperture(self): # noqa: N802 - g = ConjunctiveGraph() + g = Dataset() trix_path = os.path.relpath( os.path.join(TEST_DATA_DIR, "suites", "trix/trix-aperture.trix"), os.curdir @@ -24,12 +24,12 @@ def testAperture(self): # noqa: N802 t = sum(map(len, g.contexts())) assert t == 24 - assert len(c) == 4 + assert len(c) == 5 # print "Parsed %d triples"%t def testSpec(self): # noqa: N802 - g = ConjunctiveGraph() + g = Dataset() trix_path = os.path.relpath( os.path.join(TEST_DATA_DIR, "suites", "trix/trix-nokia-example.trix"), @@ -40,7 +40,7 @@ def testSpec(self): # noqa: N802 # print "Parsed %d triples"%len(g) def testNG4j(self): # noqa: N802 - g = ConjunctiveGraph() + g = Dataset() trix_path = os.path.relpath( os.path.join(TEST_DATA_DIR, "suites", "trix/trix-ng4j-test-01.trix"), diff --git a/test/test_serializers/test_prettyxml.py b/test/test_serializers/test_prettyxml.py index 645606d0d..295d4e991 100644 --- a/test/test_serializers/test_prettyxml.py +++ b/test/test_serializers/test_prettyxml.py @@ -1,6 +1,6 @@ from io import BytesIO -from rdflib.graph import ConjunctiveGraph +from rdflib.graph import Dataset from rdflib.namespace import RDF, RDFS from rdflib.plugins.serializers.rdfxml import PrettyXMLSerializer from rdflib.term import BNode, Literal, URIRef @@ -10,7 +10,7 @@ class SerializerTestBase: repeats = 8 def setup_method(self): - graph = ConjunctiveGraph() + graph = Dataset() graph.parse(data=self.test_content, format=self.test_content_format) self.source_graph = graph @@ -40,13 +40,13 @@ def _assert_equal_graphs(g1, g2): def _mangled_copy(g): - "Makes a copy of the graph, replacing all bnodes with the bnode `_blank`." - gcopy = ConjunctiveGraph() + "Makes a copy of the graph, replacing all bnodes with the bnode ``_blank``." + gcopy = Dataset() def isbnode(v): return isinstance(v, BNode) - for s, p, o in g: + for s, p, o, c in g: if isbnode(s): s = _blank if isbnode(p): @@ -67,7 +67,7 @@ def serialize(source_graph, make_serializer, get_value=True, extra_args={}): def serialize_and_load(source_graph, make_serializer): stream = serialize(source_graph, make_serializer, False) stream.seek(0) - reparsed_graph = ConjunctiveGraph() + reparsed_graph = Dataset() reparsed_graph.parse(stream, format="xml") return reparsed_graph @@ -170,7 +170,7 @@ def test_subclass_of_objects(self): def test_pretty_xmlliteral(self): # given: - g = ConjunctiveGraph() + g = Dataset() g.add( ( BNode(), @@ -191,7 +191,7 @@ def test_pretty_xmlliteral(self): def test_pretty_broken_xmlliteral(self): # given: - g = ConjunctiveGraph() + g = Dataset() g.add((BNode(), RDF.value, Literal("""

None: NS = Namespace("example:") # noqa: N806 - graph = ConjunctiveGraph() + graph = Dataset(default_union=True) graph.bind("eg", NS) nodes = [NS.subj, NS.pred, NS.obj, NS.graph] nodes[tuple_index] = RDF.type @@ -62,7 +62,7 @@ def test_rdf_type(format: str, tuple_index: int, is_keyword: bool) -> None: assert str(RDF) not in data else: assert str(RDF) in data - parsed_graph = ConjunctiveGraph() + parsed_graph = Dataset(default_union=True) parsed_graph.parse(data=data, format=format) GraphHelper.assert_triple_sets_equals(graph, parsed_graph) diff --git a/test/test_serializers/test_serializer_hext.py b/test/test_serializers/test_serializer_hext.py index 2b0577bc1..de39e37dc 100644 --- a/test/test_serializers/test_serializer_hext.py +++ b/test/test_serializers/test_serializer_hext.py @@ -1,7 +1,7 @@ import json from pathlib import Path -from rdflib import ConjunctiveGraph, Dataset, Graph +from rdflib import Dataset, Graph def test_hext_graph(): @@ -90,7 +90,7 @@ def test_hext_graph(): def test_hext_cg(): """Tests ConjunctiveGraph data""" - d = ConjunctiveGraph() + d = Dataset() trig_data = """ PREFIX ex: PREFIX owl: diff --git a/test/test_serializers/test_serializer_trix.py b/test/test_serializers/test_serializer_trix.py index bdfc91c81..37bc5912f 100644 --- a/test/test_serializers/test_serializer_trix.py +++ b/test/test_serializers/test_serializer_trix.py @@ -1,6 +1,6 @@ from io import BytesIO -from rdflib.graph import ConjunctiveGraph, Graph +from rdflib.graph import Dataset, Graph from rdflib.term import Literal, URIRef @@ -19,7 +19,7 @@ def test_serialize(): g2 = Graph(identifier=s2) g2.add((r2, label, Literal("label 3"))) - g = ConjunctiveGraph() + g = Dataset() for s, p, o in g1.triples((None, None, None)): g.addN([(s, p, o, g1)]) for s, p, o in g2.triples((None, None, None)): @@ -28,14 +28,14 @@ def test_serialize(): g.add((r3, label, Literal(4))) r = g.serialize(format="trix", encoding="utf-8") - g3 = ConjunctiveGraph() + g3 = Dataset() g3.parse(BytesIO(r), format="trix") for q in g3.quads((None, None, None)): # TODO: Fix once getGraph/getContext is in conjunctive graph - if isinstance(q[3].identifier, URIRef): - tg = Graph(store=g.store, identifier=q[3].identifier) + if isinstance(q[3], URIRef): + tg = Graph(store=g.store, identifier=q[3]) else: # BNode, this is a bit ugly # we cannot match the bnode to the right graph automagically @@ -74,7 +74,7 @@ def test_issue_250(): """ - graph = ConjunctiveGraph() + graph = Dataset() graph.bind(None, "http://defaultnamespace") sg = graph.serialize(format="trix") assert 'xmlns="http://defaultnamespace"' not in sg, sg diff --git a/test/test_serializers/test_serializer_xml.py b/test/test_serializers/test_serializer_xml.py index f14945523..1e2c7920b 100644 --- a/test/test_serializers/test_serializer_xml.py +++ b/test/test_serializers/test_serializer_xml.py @@ -1,6 +1,6 @@ from io import BytesIO -from rdflib.graph import ConjunctiveGraph +from rdflib.graph import Dataset from rdflib.namespace import RDFS from rdflib.plugins.serializers.rdfxml import XMLSerializer from rdflib.term import BNode, URIRef @@ -10,7 +10,7 @@ class SerializerTestBase: repeats = 8 def setup_method(self): - graph = ConjunctiveGraph() + graph = Dataset(default_union=True) graph.parse(data=self.test_content, format=self.test_content_format) self.source_graph = graph @@ -40,13 +40,13 @@ def _assert_equal_graphs(g1, g2): def _mangled_copy(g): - "Makes a copy of the graph, replacing all bnodes with the bnode `_blank`." - gcopy = ConjunctiveGraph() + """Makes a copy of the graph, replacing all bnodes with the bnode ``_blank``.""" + gcopy = Dataset() def isbnode(v): return isinstance(v, BNode) - for s, p, o in g: + for s, p, o, c in g: if isbnode(s): s = _blank if isbnode(p): @@ -67,8 +67,8 @@ def serialize(source_graph, make_serializer, get_value=True, extra_args={}): def serialize_and_load(source_graph, make_serializer): stream = serialize(source_graph, make_serializer, False) stream.seek(0) - reparsed_graph = ConjunctiveGraph() - reparsed_graph.parse(stream, publicID=None, format="xml") + reparsed_graph = Dataset(default_union=True) + reparsed_graph.parse(stream, format="xml") return reparsed_graph @@ -173,7 +173,7 @@ def test_result_fragments_with_base(self): '' term. g.add(TRIPLE + (rdflib.URIRef("http://example.com/foo."),)) @@ -81,7 +81,7 @@ def test_graph_uri_syntax(): def test_blank_graph_identifier(): - g = rdflib.ConjunctiveGraph() + g = rdflib.Dataset() g.add(TRIPLE + (rdflib.BNode(),)) out = g.serialize(format="trig", encoding="latin-1") graph_label_line = out.splitlines()[-4] @@ -94,7 +94,7 @@ def test_graph_parsing(): data = """ . """ - g = rdflib.ConjunctiveGraph() + g = rdflib.Dataset() g.parse(data=data, format="trig") assert len(list(g.contexts())) == 1 @@ -104,7 +104,7 @@ def test_graph_parsing(): { . } """ - g = rdflib.ConjunctiveGraph() + g = rdflib.Dataset() g.parse(data=data, format="trig") assert len(list(g.contexts())) == 1 @@ -118,7 +118,7 @@ def test_graph_parsing(): . } """ - g = rdflib.ConjunctiveGraph() + g = rdflib.Dataset() g.parse(data=data, format="trig") assert len(list(g.contexts())) == 2 @@ -133,7 +133,7 @@ def test_round_trips(): . } """ - g = rdflib.ConjunctiveGraph() + g = rdflib.Dataset() for i in range(5): g.parse(data=data, format="trig") data = g.serialize(format="trig") @@ -154,7 +154,7 @@ def test_default_graph_serializes_without_name(): { . } """ - g = rdflib.ConjunctiveGraph() + g = rdflib.Dataset() g.parse(data=data, format="trig") data = g.serialize(format="trig", encoding="latin-1") @@ -174,7 +174,7 @@ def test_prefixes(): } """ - cg = rdflib.ConjunctiveGraph() + cg = rdflib.Dataset() cg.parse(data=data, format="trig") data = cg.serialize(format="trig", encoding="latin-1") diff --git a/test/test_turtle_quoting.py b/test/test_turtle_quoting.py index 512e705d1..e53a0c85e 100644 --- a/test/test_turtle_quoting.py +++ b/test/test_turtle_quoting.py @@ -13,7 +13,7 @@ import pytest -from rdflib.graph import ConjunctiveGraph, Graph +from rdflib.graph import Dataset, Graph from rdflib.plugins.parsers import ntriples from rdflib.term import Literal, URIRef from test.utils.namespace import EGDC @@ -148,7 +148,7 @@ def test_parse_correctness( data = f' "{quoted}" .' else: data = f' "{quoted}".' - graph = ConjunctiveGraph() + graph = Dataset(default_union=True) graph.parse(data=data, format=format) objs = list(graph.objects()) assert len(objs) == 1 diff --git a/test/test_util.py b/test/test_util.py index 1c03e3560..0e9d5eecb 100644 --- a/test/test_util.py +++ b/test/test_util.py @@ -9,7 +9,7 @@ import pytest from rdflib import XSD, util -from rdflib.graph import ConjunctiveGraph, Graph, QuotedGraph +from rdflib.graph import Dataset, Graph, QuotedGraph from rdflib.namespace import RDF, RDFS from rdflib.term import BNode, IdentifiedNode, Literal, URIRef from rdflib.util import _coalesce, _iri2uri, find_roots, get_tree @@ -271,7 +271,7 @@ def parse_n3(term_n3): "@prefix xsd: .\n" " %s.\n" % term_n3 ) - g = ConjunctiveGraph() + g = Dataset(default_union=True) g.parse(data=prepstr, format="n3") return [t for t in g.triples((None, None, None))][0][2] diff --git a/test_reports/rdflib_w3c_sparql10-HEAD.ttl b/test_reports/rdflib_w3c_sparql10-HEAD.ttl index f3ac4255d..b8369a94d 100644 --- a/test_reports/rdflib_w3c_sparql10-HEAD.ttl +++ b/test_reports/rdflib_w3c_sparql10-HEAD.ttl @@ -1603,7 +1603,7 @@ earl:assertedBy ; earl:mode earl:automatic ; earl:result [ a earl:TestResult ; - earl:outcome earl:failed ] ; + earl:outcome earl:passed ] ; earl:subject ; earl:test . diff --git a/test_reports/rdflib_w3c_sparql11-HEAD.ttl b/test_reports/rdflib_w3c_sparql11-HEAD.ttl index 6d498df8b..6140fa914 100644 --- a/test_reports/rdflib_w3c_sparql11-HEAD.ttl +++ b/test_reports/rdflib_w3c_sparql11-HEAD.ttl @@ -691,7 +691,7 @@ earl:assertedBy ; earl:mode earl:automatic ; earl:result [ a earl:TestResult ; - earl:outcome earl:failed ] ; + earl:outcome earl:passed ] ; earl:subject ; earl:test . @@ -699,7 +699,7 @@ earl:assertedBy ; earl:mode earl:automatic ; earl:result [ a earl:TestResult ; - earl:outcome earl:failed ] ; + earl:outcome earl:passed ] ; earl:subject ; earl:test . @@ -707,7 +707,7 @@ earl:assertedBy ; earl:mode earl:automatic ; earl:result [ a earl:TestResult ; - earl:outcome earl:failed ] ; + earl:outcome earl:passed ] ; earl:subject ; earl:test . @@ -715,7 +715,7 @@ earl:assertedBy ; earl:mode earl:automatic ; earl:result [ a earl:TestResult ; - earl:outcome earl:failed ] ; + earl:outcome earl:passed ] ; earl:subject ; earl:test . @@ -723,7 +723,7 @@ earl:assertedBy ; earl:mode earl:automatic ; earl:result [ a earl:TestResult ; - earl:outcome earl:failed ] ; + earl:outcome earl:passed ] ; earl:subject ; earl:test . @@ -731,7 +731,7 @@ earl:assertedBy ; earl:mode earl:automatic ; earl:result [ a earl:TestResult ; - earl:outcome earl:failed ] ; + earl:outcome earl:passed ] ; earl:subject ; earl:test . @@ -1939,7 +1939,7 @@ earl:assertedBy ; earl:mode earl:automatic ; earl:result [ a earl:TestResult ; - earl:outcome earl:failed ] ; + earl:outcome earl:passed ] ; earl:subject ; earl:test . @@ -1947,7 +1947,7 @@ earl:assertedBy ; earl:mode earl:automatic ; earl:result [ a earl:TestResult ; - earl:outcome earl:failed ] ; + earl:outcome earl:passed ] ; earl:subject ; earl:test . @@ -2251,7 +2251,7 @@ earl:assertedBy ; earl:mode earl:automatic ; earl:result [ a earl:TestResult ; - earl:outcome earl:failed ] ; + earl:outcome earl:passed ] ; earl:subject ; earl:test . @@ -2259,7 +2259,7 @@ earl:assertedBy ; earl:mode earl:automatic ; earl:result [ a earl:TestResult ; - earl:outcome earl:failed ] ; + earl:outcome earl:passed ] ; earl:subject ; earl:test . diff --git a/tox.ini b/tox.ini index f8fe83bbd..ad8784132 100644 --- a/tox.ini +++ b/tox.ini @@ -7,6 +7,7 @@ envlist = toxworkdir={env:TOX_WORK_DIR:{tox_root}/.tox} [testenv] +deps = setuptools>=68,<72 passenv = DBUS_SESSION_BUS_ADDRESS # This is needed for keyring acccess on Linux. allowlist_externals = poetry @@ -69,6 +70,7 @@ base = void deps = pytest==7.* pytest-cov==4.* + setuptools>=68,<72 setenv = BERKELEYDB_DIR = /usr COVERAGE_FILE = {env:COVERAGE_FILE:{toxinidir}/.coverage.{envname}}