drb 2.3.4__tar.gz → 2.3.8__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {drb-2.3.4/drb.egg-info → drb-2.3.8}/PKG-INFO +1 -17
- {drb-2.3.4 → drb-2.3.8}/README.md +0 -16
- {drb-2.3.4 → drb-2.3.8}/drb/core/_version.py +3 -3
- drb-2.3.8/drb/core/context.py +24 -0
- {drb-2.3.4 → drb-2.3.8}/drb/core/node.py +7 -0
- drb-2.3.8/drb/core/protocol.py +58 -0
- {drb-2.3.4 → drb-2.3.8}/drb/signatures/core.py +34 -0
- drb-2.3.8/drb/signatures/hierarchy.py +124 -0
- {drb-2.3.4 → drb-2.3.8}/drb/topics/dao/manager_dao.py +77 -1
- {drb-2.3.4 → drb-2.3.8}/drb/topics/dao/rdf_dao.py +30 -3
- {drb-2.3.4 → drb-2.3.8}/drb/topics/resolver.py +62 -15
- {drb-2.3.4 → drb-2.3.8/drb.egg-info}/PKG-INFO +1 -17
- {drb-2.3.4 → drb-2.3.8}/drb.egg-info/SOURCES.txt +12 -0
- {drb-2.3.4 → drb-2.3.8}/drb.egg-info/entry_points.txt +6 -0
- {drb-2.3.4 → drb-2.3.8}/setup.cfg +7 -0
- drb-2.3.8/tests/test_implementation_coupling.py +115 -0
- drb-2.3.8/tests/test_manager_get_dao.py +27 -0
- drb-2.3.8/tests/test_node_context.py +42 -0
- drb-2.3.8/tests/test_protocol_dispatch.py +115 -0
- drb-2.3.8/tests/test_protocol_loader.py +29 -0
- drb-2.3.8/tests/test_rdf_dao_signatures.py +122 -0
- drb-2.3.8/tests/test_signature_exact.py +20 -0
- drb-2.3.8/tests/test_signature_hierarchy.py +105 -0
- drb-2.3.8/tests/test_topic_descriptor_discovery.py +55 -0
- {drb-2.3.4 → drb-2.3.8}/LICENCE.txt +0 -0
- {drb-2.3.4 → drb-2.3.8}/MANIFEST.in +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/addons/addon.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/core/__init__.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/core/cortex_validator.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/core/events.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/core/factory.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/core/impls.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/core/item.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/core/path.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/core/predicate.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/core/signature.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/drivers/file/__init__.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/drivers/file/file.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/drivers/xml/__init__.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/drivers/xml/factory.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/drivers/xml/node.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/exceptions/core.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/exceptions/file.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/keyring/bitwarden/__init__.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/keyring/bitwarden/bitwarden.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/nodes/abstract_node.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/nodes/logical_node.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/nodes/mutable_node.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/nodes/url_node.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/signatures/xquery.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/topics/dao/__init__.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/topics/dao/topic_dao.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/topics/dao/topic_schema.yml +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/topics/dao/xml_dao.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/topics/dao/yaml_dao.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/topics/file/__init__.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/topics/file/cortex.yml +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/topics/topic.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/topics/xml/__init__.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/topics/xml/cortex.yml +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/utils/__init__.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/utils/drb_python_script.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/utils/plugins.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/utils/url_security.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/utils/utils.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/xquery/XQueryLexer.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/xquery/XQueryParser.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/xquery/XQueryParserListener.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/xquery/XQueryParserVisitor.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/xquery/__init__.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/xquery/drb_xquery.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/xquery/drb_xquery_cmd.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/xquery/drb_xquery_context.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/xquery/drb_xquery_func.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/xquery/drb_xquery_item.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/xquery/drb_xquery_res_to_string.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/xquery/drb_xquery_utils.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/xquery/drb_xquery_variable.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/xquery/drb_xquery_visitor.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb/xquery/exceptions.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb.egg-info/dependency_links.txt +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb.egg-info/not-zip-safe +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb.egg-info/requires.txt +0 -0
- {drb-2.3.4 → drb-2.3.8}/drb.egg-info/top_level.txt +0 -0
- {drb-2.3.4 → drb-2.3.8}/pyproject.toml +0 -0
- {drb-2.3.4 → drb-2.3.8}/requirements.txt +0 -0
- {drb-2.3.4 → drb-2.3.8}/setup.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/tests/test_abstract_node.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/tests/test_addon.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/tests/test_children_unresolved.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/tests/test_drb_node_list.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/tests/test_drb_python_script.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/tests/test_event.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/tests/test_factory.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/tests/test_factory_resolver.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/tests/test_implementation_manager.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/tests/test_keyring_bitwarden.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/tests/test_logical_node.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/tests/test_manager_dao.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/tests/test_node_descendant.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/tests/test_path.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/tests/test_rdf_dao.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/tests/test_rdf_dao_multi_url.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/tests/test_signatures.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/tests/test_topic.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/tests/test_url_node.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/tests/test_url_security.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/tests/test_xml_dao.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/tests/test_yaml_dao.py +0 -0
- {drb-2.3.4 → drb-2.3.8}/versioneer.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: drb
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.8
|
|
4
4
|
Summary: Data Request Broker
|
|
5
5
|
Author: GAEL Systems
|
|
6
6
|
Author-email: drb-python@gael.fr
|
|
@@ -54,22 +54,6 @@ Once successfully run, the command line prompt is updated with the virtual envir
|
|
|
54
54
|
Once the virtual environment installed, the dependencies shall be downloaded:
|
|
55
55
|
```commandline
|
|
56
56
|
(venv) $~> pip install -r requirements.txt --no-cache-dir
|
|
57
|
-
Collecting pip==21.1.2
|
|
58
|
-
Downloading pip-21.1.2-py3-none-any.whl (1.5 MB)
|
|
59
|
-
|████████████████████████████████| 1.5 MB 54 kB/s
|
|
60
|
-
Collecting setuptools==57.0.0
|
|
61
|
-
Downloading setuptools-57.0.0-py3-none-any.whl (821 kB)
|
|
62
|
-
|████████████████████████████████| 821 kB 73 kB/s
|
|
63
|
-
Installing collected packages: pip, setuptools
|
|
64
|
-
Attempting uninstall: pip
|
|
65
|
-
Found existing installation: pip 20.0.2
|
|
66
|
-
Uninstalling pip-20.0.2:
|
|
67
|
-
Successfully uninstalled pip-20.0.2
|
|
68
|
-
Attempting uninstall: setuptools
|
|
69
|
-
Found existing installation: setuptools 44.0.0
|
|
70
|
-
Uninstalling setuptools-44.0.0:
|
|
71
|
-
Successfully uninstalled setuptools-44.0.0
|
|
72
|
-
Successfully installed pip-21.1.2 setuptools-57.0.0
|
|
73
57
|
```
|
|
74
58
|
At this stage the environment is installed.
|
|
75
59
|
|
|
@@ -25,22 +25,6 @@ Once successfully run, the command line prompt is updated with the virtual envir
|
|
|
25
25
|
Once the virtual environment installed, the dependencies shall be downloaded:
|
|
26
26
|
```commandline
|
|
27
27
|
(venv) $~> pip install -r requirements.txt --no-cache-dir
|
|
28
|
-
Collecting pip==21.1.2
|
|
29
|
-
Downloading pip-21.1.2-py3-none-any.whl (1.5 MB)
|
|
30
|
-
|████████████████████████████████| 1.5 MB 54 kB/s
|
|
31
|
-
Collecting setuptools==57.0.0
|
|
32
|
-
Downloading setuptools-57.0.0-py3-none-any.whl (821 kB)
|
|
33
|
-
|████████████████████████████████| 821 kB 73 kB/s
|
|
34
|
-
Installing collected packages: pip, setuptools
|
|
35
|
-
Attempting uninstall: pip
|
|
36
|
-
Found existing installation: pip 20.0.2
|
|
37
|
-
Uninstalling pip-20.0.2:
|
|
38
|
-
Successfully uninstalled pip-20.0.2
|
|
39
|
-
Attempting uninstall: setuptools
|
|
40
|
-
Found existing installation: setuptools 44.0.0
|
|
41
|
-
Uninstalling setuptools-44.0.0:
|
|
42
|
-
Successfully uninstalled setuptools-44.0.0
|
|
43
|
-
Successfully installed pip-21.1.2 setuptools-57.0.0
|
|
44
28
|
```
|
|
45
29
|
At this stage the environment is installed.
|
|
46
30
|
|
|
@@ -8,11 +8,11 @@ import json
|
|
|
8
8
|
|
|
9
9
|
version_json = '''
|
|
10
10
|
{
|
|
11
|
-
"date": "2026-06-
|
|
11
|
+
"date": "2026-06-25T20:48:59+0000",
|
|
12
12
|
"dirty": false,
|
|
13
13
|
"error": null,
|
|
14
|
-
"full-revisionid": "
|
|
15
|
-
"version": "2.3.
|
|
14
|
+
"full-revisionid": "15fc9c3ff4c5c9f27c0ec6e7bba7d7c3e3885305",
|
|
15
|
+
"version": "2.3.8"
|
|
16
16
|
}
|
|
17
17
|
''' # END VERSION_JSON
|
|
18
18
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from typing import Dict
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Context:
|
|
5
|
+
"""Per-node store of named context buckets.
|
|
6
|
+
|
|
7
|
+
A bucket is a plain ``dict`` keyed by a name (e.g. ``'resolver'`` for
|
|
8
|
+
resolution state, ``'metadata'`` for an add-on's extracted values).
|
|
9
|
+
Buckets are created lazily on first access, so any consumer
|
|
10
|
+
(resolver, add-on) can stash per-node state without the node knowing
|
|
11
|
+
about it:
|
|
12
|
+
|
|
13
|
+
node.context['resolver']['topic'] = topic
|
|
14
|
+
node.context['metadata']['platform'] = 'S2A'
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def __init__(self):
|
|
18
|
+
self._buckets: Dict[str, dict] = {}
|
|
19
|
+
|
|
20
|
+
def __getitem__(self, name: str) -> dict:
|
|
21
|
+
return self._buckets.setdefault(name, {})
|
|
22
|
+
|
|
23
|
+
def __contains__(self, name: str) -> bool:
|
|
24
|
+
return name in self._buckets
|
|
@@ -9,6 +9,7 @@ from deprecated import deprecated
|
|
|
9
9
|
|
|
10
10
|
from .item import DrbItem
|
|
11
11
|
from .path import ParsedPath, parse_path
|
|
12
|
+
from .context import Context
|
|
12
13
|
from .impls import ImplementationManager
|
|
13
14
|
from ..exceptions.core import DrbException, DrbNotImplementationException
|
|
14
15
|
from ..utils.utils import get_name_namespace_index, get_name_namespace_value
|
|
@@ -42,6 +43,7 @@ class DrbNode(DrbItem, abc.ABC):
|
|
|
42
43
|
self.__namespace_aware = None
|
|
43
44
|
self._attrs = {}
|
|
44
45
|
self._parent = None
|
|
46
|
+
self._context = Context()
|
|
45
47
|
self._impl_mng = ImplementationManager()
|
|
46
48
|
for event_name in self.__event_type_node:
|
|
47
49
|
self._event_manager.append_event_type(event_name)
|
|
@@ -464,5 +466,10 @@ class DrbNode(DrbItem, abc.ABC):
|
|
|
464
466
|
return self.has_child(*item)
|
|
465
467
|
return False
|
|
466
468
|
|
|
469
|
+
@property
|
|
470
|
+
def context(self) -> Context:
|
|
471
|
+
"""Per-node store of named context buckets (resolver, add-ons...)."""
|
|
472
|
+
return self._context
|
|
473
|
+
|
|
467
474
|
def __eq__(self, other):
|
|
468
475
|
return self.path == other.path
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import importlib
|
|
4
|
+
import inspect
|
|
5
|
+
import logging
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
import drb.utils.plugins
|
|
9
|
+
from drb.core.factory import DrbFactory
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ProtocolLoader:
|
|
13
|
+
"""Loads ``drb.protocol`` entry points into a scheme -> factory map.
|
|
14
|
+
|
|
15
|
+
Each entry point's name is a URL scheme (e.g. ``s3``, ``https``) and its
|
|
16
|
+
value is ``module:FactoryClass`` (a ``DrbFactory`` subclass). Mirrors
|
|
17
|
+
:class:`drb.core.factory.FactoryLoader`.
|
|
18
|
+
"""
|
|
19
|
+
__instance = None
|
|
20
|
+
__factories = None
|
|
21
|
+
|
|
22
|
+
def __new__(cls, *args, **kwargs):
|
|
23
|
+
if cls.__instance is None:
|
|
24
|
+
cls.__instance = super(ProtocolLoader, cls).__new__(cls)
|
|
25
|
+
cls.__factories = cls.__load()
|
|
26
|
+
return cls.__instance
|
|
27
|
+
|
|
28
|
+
@classmethod
|
|
29
|
+
def __is_class(cls, class_name: str):
|
|
30
|
+
return lambda obj: inspect.isclass(obj) and class_name == obj.__name__
|
|
31
|
+
|
|
32
|
+
@classmethod
|
|
33
|
+
def __load(cls) -> dict:
|
|
34
|
+
factories = {}
|
|
35
|
+
for ep in drb.utils.plugins.get_entry_points('drb.protocol'):
|
|
36
|
+
if ep.name in factories:
|
|
37
|
+
continue
|
|
38
|
+
m, c = ep.value.split(':')
|
|
39
|
+
try:
|
|
40
|
+
module = importlib.import_module(m)
|
|
41
|
+
for _, obj in inspect.getmembers(module, cls.__is_class(c)):
|
|
42
|
+
if obj != DrbFactory and issubclass(obj, DrbFactory):
|
|
43
|
+
factories[ep.name] = obj()
|
|
44
|
+
else:
|
|
45
|
+
logging.getLogger().warning(
|
|
46
|
+
f'Failed to load protocol "{ep.name}": '
|
|
47
|
+
f'invalid factory: {ep.value}')
|
|
48
|
+
except ModuleNotFoundError:
|
|
49
|
+
logging.getLogger().warning(
|
|
50
|
+
f'Failed to load protocol "{ep.name}": module {m} '
|
|
51
|
+
f'not found')
|
|
52
|
+
return factories
|
|
53
|
+
|
|
54
|
+
def get_schemes(self) -> set:
|
|
55
|
+
return set(self.__factories.keys())
|
|
56
|
+
|
|
57
|
+
def get_factory(self, scheme: str) -> Optional[DrbFactory]:
|
|
58
|
+
return self.__factories.get(scheme, None)
|
|
@@ -49,6 +49,40 @@ class NamespaceSignature(Signature):
|
|
|
49
49
|
return 'namespace'
|
|
50
50
|
|
|
51
51
|
|
|
52
|
+
class NameTestSignature(Signature):
|
|
53
|
+
"""Allowing to check if a DRB Node name equals a specific string."""
|
|
54
|
+
|
|
55
|
+
def __init__(self, value: str):
|
|
56
|
+
self.__value = value
|
|
57
|
+
|
|
58
|
+
def matches(self, node: DrbNode) -> bool:
|
|
59
|
+
return node.name == self.__value
|
|
60
|
+
|
|
61
|
+
def to_dict(self) -> dict:
|
|
62
|
+
return {self.get_name(): self.__value}
|
|
63
|
+
|
|
64
|
+
@staticmethod
|
|
65
|
+
def get_name():
|
|
66
|
+
return 'nameTest'
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class NamespaceTestSignature(Signature):
|
|
70
|
+
"""Allowing to check if a DRB Node namespace equals a specific string."""
|
|
71
|
+
|
|
72
|
+
def __init__(self, value: str):
|
|
73
|
+
self.__value = value
|
|
74
|
+
|
|
75
|
+
def matches(self, node: DrbNode) -> bool:
|
|
76
|
+
return node.namespace_uri == self.__value
|
|
77
|
+
|
|
78
|
+
def to_dict(self) -> dict:
|
|
79
|
+
return {self.get_name(): self.__value}
|
|
80
|
+
|
|
81
|
+
@staticmethod
|
|
82
|
+
def get_name():
|
|
83
|
+
return 'namespaceTest'
|
|
84
|
+
|
|
85
|
+
|
|
52
86
|
class PathSignature(Signature):
|
|
53
87
|
"""
|
|
54
88
|
Allowing to check if a DRB Node path match a specific regex.
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
from drb.core.node import DrbNode
|
|
4
|
+
from drb.core.signature import Signature
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def _parent_topic(node: DrbNode):
|
|
8
|
+
"""Resolve the parent node to its finest topic, or None."""
|
|
9
|
+
import drb.topics.resolver as resolver
|
|
10
|
+
parent = node.parent
|
|
11
|
+
if parent is None:
|
|
12
|
+
return None
|
|
13
|
+
try:
|
|
14
|
+
topic, _ = resolver.resolve(parent)
|
|
15
|
+
except Exception:
|
|
16
|
+
return None
|
|
17
|
+
return topic
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _topic_by_uri(uri: str, dao=None):
|
|
21
|
+
if dao is None:
|
|
22
|
+
from drb.topics.dao import ManagerDao
|
|
23
|
+
dao = ManagerDao()
|
|
24
|
+
for t in dao.get_all_drb_topics():
|
|
25
|
+
if t.uri == uri:
|
|
26
|
+
return t
|
|
27
|
+
return None
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class ParentClassTestSignature(Signature):
|
|
31
|
+
"""Matches when the node's parent resolves exactly to ``class_uri``."""
|
|
32
|
+
|
|
33
|
+
def __init__(self, class_uri: str):
|
|
34
|
+
self.__uri = class_uri
|
|
35
|
+
|
|
36
|
+
def matches(self, node: DrbNode) -> bool:
|
|
37
|
+
topic = _parent_topic(node)
|
|
38
|
+
return topic is not None and topic.uri == self.__uri
|
|
39
|
+
|
|
40
|
+
def to_dict(self) -> dict:
|
|
41
|
+
return {self.get_name(): self.__uri}
|
|
42
|
+
|
|
43
|
+
@staticmethod
|
|
44
|
+
def get_name():
|
|
45
|
+
return 'parentClassTest'
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class ParentIsSubClassTestSignature(Signature):
|
|
49
|
+
"""Matches when the parent's topic is a sub-class of ``class_uri``."""
|
|
50
|
+
|
|
51
|
+
def __init__(self, class_uri: str):
|
|
52
|
+
self.__uri = class_uri
|
|
53
|
+
|
|
54
|
+
def matches(self, node: DrbNode) -> bool:
|
|
55
|
+
from drb.topics.dao import ManagerDao
|
|
56
|
+
topic = _parent_topic(node)
|
|
57
|
+
if topic is None:
|
|
58
|
+
return False
|
|
59
|
+
dao = ManagerDao()
|
|
60
|
+
expected = _topic_by_uri(self.__uri, dao)
|
|
61
|
+
if expected is None:
|
|
62
|
+
return False
|
|
63
|
+
return dao.is_subclass(topic, expected)
|
|
64
|
+
|
|
65
|
+
def to_dict(self) -> dict:
|
|
66
|
+
return {self.get_name(): self.__uri}
|
|
67
|
+
|
|
68
|
+
@staticmethod
|
|
69
|
+
def get_name():
|
|
70
|
+
return 'parentIsSubClassTest'
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class ParentClassMatchSignature(Signature):
|
|
74
|
+
"""Matches when the parent's topic class URI matches ``regex``.
|
|
75
|
+
|
|
76
|
+
Parity with Java ``DrbCortexItemSignature``: the regex is tested against
|
|
77
|
+
the parent topic's **class URI**, not its label.
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
def __init__(self, regex: str):
|
|
81
|
+
self.__regex = regex
|
|
82
|
+
|
|
83
|
+
def matches(self, node: DrbNode) -> bool:
|
|
84
|
+
topic = _parent_topic(node)
|
|
85
|
+
return topic is not None and re.match(self.__regex, topic.uri) \
|
|
86
|
+
is not None
|
|
87
|
+
|
|
88
|
+
def to_dict(self) -> dict:
|
|
89
|
+
return {self.get_name(): self.__regex}
|
|
90
|
+
|
|
91
|
+
@staticmethod
|
|
92
|
+
def get_name():
|
|
93
|
+
return 'parentClassMatch'
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class ParentIsSubClassMatchSignature(Signature):
|
|
97
|
+
"""Matches when a topic whose **class URI** matches ``regex`` is a
|
|
98
|
+
super-class of the parent's topic.
|
|
99
|
+
|
|
100
|
+
Parity with Java ``DrbCortexItemSignature``: the regex is tested against
|
|
101
|
+
the candidate topic's **class URI**, not its label.
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
def __init__(self, regex: str):
|
|
105
|
+
self.__regex = regex
|
|
106
|
+
|
|
107
|
+
def matches(self, node: DrbNode) -> bool:
|
|
108
|
+
topic = _parent_topic(node)
|
|
109
|
+
if topic is None:
|
|
110
|
+
return False
|
|
111
|
+
from drb.topics.dao import ManagerDao
|
|
112
|
+
dao = ManagerDao()
|
|
113
|
+
for candidate in dao.get_all_drb_topics():
|
|
114
|
+
if re.match(self.__regex, candidate.uri) is not None \
|
|
115
|
+
and dao.is_subclass(topic, candidate):
|
|
116
|
+
return True
|
|
117
|
+
return False
|
|
118
|
+
|
|
119
|
+
def to_dict(self) -> dict:
|
|
120
|
+
return {self.get_name(): self.__regex}
|
|
121
|
+
|
|
122
|
+
@staticmethod
|
|
123
|
+
def get_name():
|
|
124
|
+
return 'parentIsSubClassMatch'
|
|
@@ -24,6 +24,38 @@ logger = logging.getLogger('DrbTopic')
|
|
|
24
24
|
_HTTP_TIMEOUT = (10, 60)
|
|
25
25
|
|
|
26
26
|
|
|
27
|
+
# Topic descriptor file names looked up in a topic package, in priority
|
|
28
|
+
# order: ontology descriptors (TTL/OWL) first, legacy YAML cortex last. This
|
|
29
|
+
# lets a package ship its topic as TTL (the knowledge-base format) while YAML
|
|
30
|
+
# keeps working during the migration.
|
|
31
|
+
_DESCRIPTOR_NAMES = ('cortex.ttl', 'cortex.owl', 'cortex.yml', 'cortex.yaml')
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _find_topic_descriptor(directory: str):
|
|
35
|
+
"""Return the topic descriptor path in ``directory`` (TTL/OWL preferred
|
|
36
|
+
over YAML), or None if none is present."""
|
|
37
|
+
for name in _DESCRIPTOR_NAMES:
|
|
38
|
+
path = os.path.join(directory, name)
|
|
39
|
+
if os.path.exists(path):
|
|
40
|
+
return path
|
|
41
|
+
return None
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _implementation_available(topic: DrbTopic) -> bool:
|
|
45
|
+
"""Whether the topic's driver implementation is installed.
|
|
46
|
+
|
|
47
|
+
A topic with no ``factory`` needs no driver (abstract/container topic).
|
|
48
|
+
Otherwise its ``implementationIdentifier`` (``topic.factory``) must match
|
|
49
|
+
an installed ``drb.driver`` entry point: a descriptor (TTL/OWL/YAML) is not
|
|
50
|
+
enough — the implementation is required.
|
|
51
|
+
"""
|
|
52
|
+
if topic.factory is None:
|
|
53
|
+
return True
|
|
54
|
+
# Lazy import to avoid any import cycle with drb.core.factory.
|
|
55
|
+
from drb.core.factory import FactoryLoader
|
|
56
|
+
return topic.factory in FactoryLoader().get_factories()
|
|
57
|
+
|
|
58
|
+
|
|
27
59
|
def _load_all_drb_topic_dao() -> Dict[uuid.UUID, DrbTopicDao]:
|
|
28
60
|
"""
|
|
29
61
|
Loads topics defined in the Python context
|
|
@@ -39,11 +71,23 @@ def _load_all_drb_topic_dao() -> Dict[uuid.UUID, DrbTopicDao]:
|
|
|
39
71
|
logger.warning(f'Invalid entry point {ep.name}: {ex.msg}')
|
|
40
72
|
continue
|
|
41
73
|
|
|
74
|
+
path = _find_topic_descriptor(module.__path__[0])
|
|
75
|
+
if path is None:
|
|
76
|
+
logger.warning(
|
|
77
|
+
f'No topic descriptor (cortex.ttl/.owl/.yml) for {ep.name}')
|
|
78
|
+
continue
|
|
79
|
+
|
|
42
80
|
try:
|
|
43
|
-
path = os.path.join(module.__path__[0], 'cortex.yml')
|
|
44
81
|
dao = DaoFactory().create(path)
|
|
45
82
|
topics = dao.read_all()
|
|
46
83
|
for topic in topics:
|
|
84
|
+
if not _implementation_available(topic):
|
|
85
|
+
logger.warning(
|
|
86
|
+
"Topic %s (%s) skipped: no installed driver "
|
|
87
|
+
"implementation '%s' (drb.driver). Descriptor present "
|
|
88
|
+
"but implementation required.",
|
|
89
|
+
topic.label, topic.uri, topic.factory)
|
|
90
|
+
continue
|
|
47
91
|
if topic.id in result:
|
|
48
92
|
logger.warning(
|
|
49
93
|
f'Topic definition conflict: id ({topic.id}) defined '
|
|
@@ -165,6 +209,24 @@ class ManagerDao:
|
|
|
165
209
|
|
|
166
210
|
raise DrbException('No DrbTopic found')
|
|
167
211
|
|
|
212
|
+
def get_dao(self, identifier: uuid.UUID) -> DrbTopicDao:
|
|
213
|
+
"""
|
|
214
|
+
Retrieves the DAO that loaded a topic.
|
|
215
|
+
|
|
216
|
+
Lets callers (e.g. add-ons) reach the source of a topic without the
|
|
217
|
+
manager knowing about their concern: for an RDF topic the returned
|
|
218
|
+
``RDFDao`` exposes ``graph``, the ontology graph it was read from.
|
|
219
|
+
|
|
220
|
+
Parameters:
|
|
221
|
+
identifier (UUID): topic UUID
|
|
222
|
+
Returns:
|
|
223
|
+
DrbTopicDao: the DAO that loaded the topic identified by the
|
|
224
|
+
given UUID.
|
|
225
|
+
"""
|
|
226
|
+
if identifier in self.__topic_dao.keys():
|
|
227
|
+
return self.__topic_dao[identifier]
|
|
228
|
+
raise DrbException('No DrbTopic found')
|
|
229
|
+
|
|
168
230
|
def is_subclass(self, actual: DrbTopic, expected: DrbTopic) -> bool:
|
|
169
231
|
"""
|
|
170
232
|
Check if a topic is subclass of another.
|
|
@@ -264,6 +326,13 @@ class ManagerDao:
|
|
|
264
326
|
self.__added_dao.append(dao)
|
|
265
327
|
topics = dao.read_all()
|
|
266
328
|
for topic in topics:
|
|
329
|
+
if not _implementation_available(topic):
|
|
330
|
+
logger.warning(
|
|
331
|
+
"Topic %s (%s) skipped: no installed driver "
|
|
332
|
+
"implementation '%s' (drb.driver). Descriptor present "
|
|
333
|
+
"but implementation required.",
|
|
334
|
+
topic.label, topic.uri, topic.factory)
|
|
335
|
+
continue
|
|
267
336
|
self.__topic_dao[topic.id] = dao
|
|
268
337
|
self.__update_topics()
|
|
269
338
|
|
|
@@ -280,6 +349,13 @@ class ManagerDao:
|
|
|
280
349
|
"""
|
|
281
350
|
self.__added_dao.append(dao)
|
|
282
351
|
for topic in dao.read_all():
|
|
352
|
+
if not _implementation_available(topic):
|
|
353
|
+
logger.warning(
|
|
354
|
+
"Topic %s (%s) skipped: no installed driver "
|
|
355
|
+
"implementation '%s' (drb.driver). Descriptor present "
|
|
356
|
+
"but implementation required.",
|
|
357
|
+
topic.label, topic.uri, topic.factory)
|
|
358
|
+
continue
|
|
283
359
|
self.__topic_dao[topic.id] = dao
|
|
284
360
|
self.__update_topics()
|
|
285
361
|
return dao
|
|
@@ -115,13 +115,31 @@ class RDFDao(DrbTopicDao):
|
|
|
115
115
|
|
|
116
116
|
result = self.graph.query(f'''
|
|
117
117
|
PREFIX drb: <http://www.gael.fr/drb#>
|
|
118
|
-
SELECT ?
|
|
118
|
+
SELECT ?name ?nameTest ?namespace ?namespaceTest
|
|
119
|
+
?xquery ?python ?pythonScript
|
|
120
|
+
?parentClassMatch ?parentClassTest
|
|
121
|
+
?parentIsSubClassMatch ?parentIsSubClassTest
|
|
119
122
|
WHERE {{
|
|
120
123
|
?Class a owl:Class .
|
|
121
124
|
FILTER(?Class = <{topic.uri}>)
|
|
122
125
|
?Class drb:signature ?signature .
|
|
123
|
-
OPTIONAL {{ ?signature drb:xqueryTest ?xquery . }}
|
|
124
126
|
OPTIONAL {{ ?signature drb:nameMatch ?name . }}
|
|
127
|
+
OPTIONAL {{ ?signature drb:nameTest ?nameTest . }}
|
|
128
|
+
OPTIONAL {{ ?signature drb:namespaceMatch ?namespace . }}
|
|
129
|
+
OPTIONAL {{ ?signature drb:namespaceTest ?namespaceTest . }}
|
|
130
|
+
OPTIONAL {{ ?signature drb:xqueryTest ?xquery . }}
|
|
131
|
+
OPTIONAL {{ ?signature drb:pythonTest ?python . }}
|
|
132
|
+
OPTIONAL {{ ?signature drb:pythonScript ?pythonScript . }}
|
|
133
|
+
OPTIONAL {{
|
|
134
|
+
?signature drb:parentClassMatch ?parentClassMatch . }}
|
|
135
|
+
OPTIONAL {{
|
|
136
|
+
?signature drb:parentClassTest ?parentClassTest . }}
|
|
137
|
+
OPTIONAL {{
|
|
138
|
+
?signature drb:parentIsSubClassMatch
|
|
139
|
+
?parentIsSubClassMatch . }}
|
|
140
|
+
OPTIONAL {{
|
|
141
|
+
?signature drb:parentIsSubClassTest
|
|
142
|
+
?parentIsSubClassTest . }}
|
|
125
143
|
}}
|
|
126
144
|
GROUP BY ?signature
|
|
127
145
|
''')
|
|
@@ -131,7 +149,16 @@ class RDFDao(DrbTopicDao):
|
|
|
131
149
|
for k in row.asdict().keys():
|
|
132
150
|
v = row[k]
|
|
133
151
|
if v is not None:
|
|
134
|
-
|
|
152
|
+
if k == 'pythonScript':
|
|
153
|
+
# drb:pythonScript is the deprecated spelling of
|
|
154
|
+
# drb:pythonTest (kept for backward-compat with KB
|
|
155
|
+
# descriptors); both map to the python PythonSignature.
|
|
156
|
+
logging.getLogger('drb.topics.dao.rdf_dao').warning(
|
|
157
|
+
'drb:pythonScript is deprecated; '
|
|
158
|
+
'use drb:pythonTest')
|
|
159
|
+
signature['python'] = v.toPython()
|
|
160
|
+
else:
|
|
161
|
+
signature[k] = v.toPython()
|
|
135
162
|
if signature:
|
|
136
163
|
signatures.append(parse_signature(signature))
|
|
137
164
|
|
|
@@ -10,6 +10,7 @@ from typing import List, Optional, Tuple, Union
|
|
|
10
10
|
|
|
11
11
|
from drb.core.node import DrbNode
|
|
12
12
|
from drb.core.factory import DrbFactory, FactoryLoader
|
|
13
|
+
from drb.core.protocol import ProtocolLoader
|
|
13
14
|
from drb.topics.topic import DrbTopic, TopicCategory
|
|
14
15
|
from drb.exceptions.core import DrbFactoryException, DrbException
|
|
15
16
|
from drb.nodes.url_node import UrlNode
|
|
@@ -30,14 +31,38 @@ def _is_remote_url(parsed_path):
|
|
|
30
31
|
def forced_factory(topic: DrbTopic, node: DrbNode) -> Optional[DrbNode]:
|
|
31
32
|
if topic.forced and topic.factory is not None:
|
|
32
33
|
try:
|
|
33
|
-
new_node =
|
|
34
|
-
topic.factory).create(node)
|
|
34
|
+
new_node = _create_via_factory(topic, node)
|
|
35
35
|
except Exception as e:
|
|
36
36
|
return node
|
|
37
37
|
return new_node
|
|
38
38
|
return node
|
|
39
39
|
|
|
40
40
|
|
|
41
|
+
def _create_via_factory(topic: DrbTopic, node: DrbNode) -> DrbNode:
|
|
42
|
+
"""Create the child node via the topic's driver factory.
|
|
43
|
+
|
|
44
|
+
Raises a clear :class:`DrbException` when no ``drb.driver`` implementation
|
|
45
|
+
is installed for ``topic.factory`` — replacing the opaque ``None.create``
|
|
46
|
+
``AttributeError``.
|
|
47
|
+
"""
|
|
48
|
+
factory = FactoryLoader().get_factory(topic.factory)
|
|
49
|
+
if factory is None:
|
|
50
|
+
raise DrbException(
|
|
51
|
+
f"No driver implementation '{topic.factory}' installed for "
|
|
52
|
+
f"topic '{topic.label}' ({topic.uri})")
|
|
53
|
+
return factory.create(node)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _topic_by_factory(name: str):
|
|
57
|
+
"""The PROTOCOL topic whose implementationIdentifier == name, or None."""
|
|
58
|
+
from drb.topics.dao import ManagerDao
|
|
59
|
+
for topic in ManagerDao().get_drb_topics_by_category(
|
|
60
|
+
TopicCategory.PROTOCOL):
|
|
61
|
+
if topic.factory == name:
|
|
62
|
+
return topic
|
|
63
|
+
return None
|
|
64
|
+
|
|
65
|
+
|
|
41
66
|
class _DrbFactoryResolver(DrbFactory):
|
|
42
67
|
""" The factory resolver
|
|
43
68
|
|
|
@@ -74,6 +99,29 @@ class _DrbFactoryResolver(DrbFactory):
|
|
|
74
99
|
return protocol
|
|
75
100
|
return None
|
|
76
101
|
|
|
102
|
+
def __dispatch_scheme(self, node):
|
|
103
|
+
"""Dispatch a root URL by its scheme via drb.protocol, then apply a
|
|
104
|
+
``+<name>`` forced driver if present. Returns (topic, node) or None
|
|
105
|
+
when the base scheme is not registered (caller falls back to
|
|
106
|
+
signature matching)."""
|
|
107
|
+
scheme = node.path.scheme
|
|
108
|
+
if not scheme:
|
|
109
|
+
return None
|
|
110
|
+
parts = scheme.split('+', 1)
|
|
111
|
+
base = parts[0]
|
|
112
|
+
forced = parts[1] if len(parts) > 1 else None
|
|
113
|
+
base_factory = ProtocolLoader().get_factory(base)
|
|
114
|
+
if base_factory is None:
|
|
115
|
+
return None
|
|
116
|
+
created = base_factory.create(node)
|
|
117
|
+
if forced is None:
|
|
118
|
+
return _topic_by_factory(base), created
|
|
119
|
+
forced_factory = FactoryLoader().get_factory(forced)
|
|
120
|
+
if forced_factory is None:
|
|
121
|
+
raise DrbFactoryException(
|
|
122
|
+
f'No driver registered for forced scheme "+{forced}"')
|
|
123
|
+
return _topic_by_factory(forced), forced_factory.create(created)
|
|
124
|
+
|
|
77
125
|
def __retrieve_container(
|
|
78
126
|
self, node: DrbNode) -> Tuple[Optional[DrbTopic], DrbNode]:
|
|
79
127
|
"""
|
|
@@ -122,8 +170,7 @@ class _DrbFactoryResolver(DrbFactory):
|
|
|
122
170
|
if topic.matches(node):
|
|
123
171
|
n = node
|
|
124
172
|
if topic.factory is not None:
|
|
125
|
-
n =
|
|
126
|
-
topic.factory).create(node)
|
|
173
|
+
n = _create_via_factory(topic, node)
|
|
127
174
|
return self.__finest_drb_topic(n, topic)
|
|
128
175
|
return finest, node
|
|
129
176
|
|
|
@@ -230,23 +277,23 @@ class _DrbFactoryResolver(DrbFactory):
|
|
|
230
277
|
node = source
|
|
231
278
|
protocol = None
|
|
232
279
|
if node.parent is None:
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
protocol
|
|
280
|
+
dispatched = self.__dispatch_scheme(node)
|
|
281
|
+
if dispatched is not None:
|
|
282
|
+
protocol, node = dispatched
|
|
283
|
+
else:
|
|
284
|
+
protocol = self.__retrieve_protocol(node)
|
|
285
|
+
if protocol is None:
|
|
286
|
+
raise DrbFactoryException(f'Cannot resolve: {source}')
|
|
287
|
+
node = _create_via_factory(protocol, node)
|
|
239
288
|
|
|
240
289
|
container, node = self.__retrieve_container(node)
|
|
241
290
|
if container is not None and container.factory is not None:
|
|
242
|
-
node =
|
|
243
|
-
container.factory).create(node)
|
|
291
|
+
node = _create_via_factory(container, node)
|
|
244
292
|
|
|
245
293
|
formatting = self.__retrieve_formatting(node)
|
|
246
294
|
if formatting is not None:
|
|
247
295
|
if formatting.factory is not None:
|
|
248
|
-
node =
|
|
249
|
-
node)
|
|
296
|
+
node = _create_via_factory(formatting, node)
|
|
250
297
|
t, n = self.__finest_drb_topic(node, formatting)
|
|
251
298
|
elif container is not None:
|
|
252
299
|
t, n = container, node
|
|
@@ -284,7 +331,7 @@ class _DrbFactoryResolver(DrbFactory):
|
|
|
284
331
|
if result is not None:
|
|
285
332
|
topic, node = result
|
|
286
333
|
if topic.factory is not None:
|
|
287
|
-
node =
|
|
334
|
+
node = _create_via_factory(topic, node)
|
|
288
335
|
return topic, node
|
|
289
336
|
return self._basic_resolve(node)
|
|
290
337
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: drb
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.8
|
|
4
4
|
Summary: Data Request Broker
|
|
5
5
|
Author: GAEL Systems
|
|
6
6
|
Author-email: drb-python@gael.fr
|
|
@@ -54,22 +54,6 @@ Once successfully run, the command line prompt is updated with the virtual envir
|
|
|
54
54
|
Once the virtual environment installed, the dependencies shall be downloaded:
|
|
55
55
|
```commandline
|
|
56
56
|
(venv) $~> pip install -r requirements.txt --no-cache-dir
|
|
57
|
-
Collecting pip==21.1.2
|
|
58
|
-
Downloading pip-21.1.2-py3-none-any.whl (1.5 MB)
|
|
59
|
-
|████████████████████████████████| 1.5 MB 54 kB/s
|
|
60
|
-
Collecting setuptools==57.0.0
|
|
61
|
-
Downloading setuptools-57.0.0-py3-none-any.whl (821 kB)
|
|
62
|
-
|████████████████████████████████| 821 kB 73 kB/s
|
|
63
|
-
Installing collected packages: pip, setuptools
|
|
64
|
-
Attempting uninstall: pip
|
|
65
|
-
Found existing installation: pip 20.0.2
|
|
66
|
-
Uninstalling pip-20.0.2:
|
|
67
|
-
Successfully uninstalled pip-20.0.2
|
|
68
|
-
Attempting uninstall: setuptools
|
|
69
|
-
Found existing installation: setuptools 44.0.0
|
|
70
|
-
Uninstalling setuptools-44.0.0:
|
|
71
|
-
Successfully uninstalled setuptools-44.0.0
|
|
72
|
-
Successfully installed pip-21.1.2 setuptools-57.0.0
|
|
73
57
|
```
|
|
74
58
|
At this stage the environment is installed.
|
|
75
59
|
|