From 267226c3655ae2bdffd3cfd350be675501a35a33 Mon Sep 17 00:00:00 2001 From: Iwan Aucamp Date: Sun, 10 Oct 2021 23:07:43 +0200 Subject: [PATCH 1/2] Export DCMITYPE Not sure what went wrong here but for some reason master branch tries to export DCMITYPE without also importing it. --- rdflib/__init__.py | 1 + rdflib/namespace/__init__.py | 1 + 2 files changed, 2 insertions(+) diff --git a/rdflib/__init__.py b/rdflib/__init__.py index 386d14eb8..9b8daeb0e 100644 --- a/rdflib/__init__.py +++ b/rdflib/__init__.py @@ -167,6 +167,7 @@ CSVW, DC, DCAT, + DCMITYPE, DCTERMS, DOAP, FOAF, diff --git a/rdflib/namespace/__init__.py b/rdflib/namespace/__init__.py index 86dbdba4d..0487a55da 100644 --- a/rdflib/namespace/__init__.py +++ b/rdflib/namespace/__init__.py @@ -701,6 +701,7 @@ def get_longest_namespace(trie, value): from rdflib.namespace._CSVW import CSVW from rdflib.namespace._DC import DC from rdflib.namespace._DCAT import DCAT +from rdflib.namespace._DCMITYPE import DCMITYPE from rdflib.namespace._DCTERMS import DCTERMS from rdflib.namespace._DOAP import DOAP from rdflib.namespace._FOAF import FOAF From ffff25d3afcb48a9ac23aa52cb367ebb670d4035 Mon Sep 17 00:00:00 2001 From: Iwan Aucamp Date: Sun, 10 Oct 2021 22:31:33 +0200 Subject: [PATCH 2/2] Make Store.namespaces an empty generator 90f6fe5 changed ```python class Store: def namespaces(self): """ """ if False: yield None ``` to ```python class Store: def namespaces(self): """ """ ``` This resulted in `Store.namespaces` no longer being an emtpy generator by default (see https://stackoverflow.com/q/13243766). The reason it was removed is because it is unreachable code, however I did not consider that it would make the function an empty generator. The reason why `if False: yield None` made it an empty generator is because the presence of the yield keyword informs CPython that a function is a generator: https://www.python.org/dev/peps/pep-0255/#why-a-new-keyword-for-yield-why-not-a-builtin-function-instead There are several ways of making an empty generator: - `if False: yield` - `return` and `yield` - `yield from ()` - ... more Both `if False: yield` and `return`; `yield` results in same bytecode however, which is that of an empty function, and they are both equally confusing I would say. `yield from ()` is somewhat clearer, but the bytecode of the funciton looks very different from an empty function (see https://stackoverflow.com/a/61496399) So the best option I see is to just put back what was there and add a comment and a test to prevent the issue from re-occuring. --- rdflib/store.py | 5 +++++ test/test_store.py | 25 +++++++++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 test/test_store.py diff --git a/rdflib/store.py b/rdflib/store.py index 915ca57ad..a7aa8d0b0 100644 --- a/rdflib/store.py +++ b/rdflib/store.py @@ -364,6 +364,11 @@ def namespace(self, prefix): def namespaces(self): """ """ + # This is here so that the function becomes an empty generator. + # See https://stackoverflow.com/q/13243766 and + # https://www.python.org/dev/peps/pep-0255/#why-a-new-keyword-for-yield-why-not-a-builtin-function-instead + if False: + yield None # type: ignore[unreachable] # Optional Transactional methods diff --git a/test/test_store.py b/test/test_store.py new file mode 100644 index 000000000..d4d8d0a8a --- /dev/null +++ b/test/test_store.py @@ -0,0 +1,25 @@ +import unittest +from rdflib import Graph +from rdflib.store import Store +from rdflib.namespace import NamespaceManager + + +class TestAbstractStore(unittest.TestCase): + def test_namespaces(self): + """ + This tests that Store.namespaces is an empty generator. + """ + store = Store() + self.assertEqual(list(store.namespaces()), []) + + def test_namespaces_via_manager(self): + """ + This tests that NamespaceManager.namespaces works correctly with an + abstract Store. + """ + namespace_manager = NamespaceManager(Graph(store=Store())) + self.assertEqual(list(namespace_manager.namespaces()), []) + + +if __name__ == "__main__": + unittest.main()