drb 2.4.2__tar.gz → 2.4.4__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.4.2/drb.egg-info → drb-2.4.4}/PKG-INFO +1 -3
- {drb-2.4.2 → drb-2.4.4}/README.md +0 -2
- {drb-2.4.2 → drb-2.4.4}/drb/core/_version.py +3 -3
- {drb-2.4.2 → drb-2.4.4}/drb/signatures/core.py +42 -5
- {drb-2.4.2 → drb-2.4.4}/drb/topics/resolver.py +72 -32
- {drb-2.4.2 → drb-2.4.4/drb.egg-info}/PKG-INFO +1 -3
- {drb-2.4.2 → drb-2.4.4}/drb.egg-info/SOURCES.txt +4 -0
- drb-2.4.4/tests/test_finest_multiparent.py +71 -0
- drb-2.4.4/tests/test_resolver_recursion_guard.py +65 -0
- drb-2.4.4/tests/test_scheme_force_url.py +81 -0
- drb-2.4.4/tests/test_signature_inline_flags.py +18 -0
- {drb-2.4.2 → drb-2.4.4}/LICENCE.txt +0 -0
- {drb-2.4.2 → drb-2.4.4}/MANIFEST.in +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/addons/addon.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/core/__init__.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/core/context.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/core/cortex_validator.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/core/events.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/core/factory.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/core/impls.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/core/item.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/core/node.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/core/path.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/core/predicate.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/core/signature.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/drivers/file/__init__.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/drivers/file/cortex.ttl +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/drivers/file/file.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/drivers/xml/__init__.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/drivers/xml/cortex.ttl +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/drivers/xml/factory.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/drivers/xml/node.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/exceptions/core.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/exceptions/file.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/keyring/bitwarden/__init__.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/keyring/bitwarden/bitwarden.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/nodes/abstract_node.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/nodes/logical_node.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/nodes/mutable_node.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/nodes/url_node.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/signatures/hierarchy.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/signatures/xquery.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/topics/dao/__init__.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/topics/dao/manager_dao.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/topics/dao/rdf_dao.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/topics/dao/topic_dao.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/topics/dao/topic_schema.yml +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/topics/dao/xml_dao.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/topics/file/__init__.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/topics/file/cortex.ttl +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/topics/topic.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/topics/xml/__init__.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/topics/xml/cortex.ttl +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/utils/__init__.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/utils/drb_python_script.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/utils/plugins.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/utils/url_security.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/utils/utils.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/xquery/XQueryLexer.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/xquery/XQueryParser.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/xquery/XQueryParserListener.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/xquery/XQueryParserVisitor.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/xquery/__init__.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/xquery/drb_xquery.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/xquery/drb_xquery_cmd.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/xquery/drb_xquery_context.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/xquery/drb_xquery_func.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/xquery/drb_xquery_item.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/xquery/drb_xquery_res_to_string.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/xquery/drb_xquery_utils.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/xquery/drb_xquery_variable.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/xquery/drb_xquery_visitor.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb/xquery/exceptions.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb.egg-info/dependency_links.txt +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb.egg-info/entry_points.txt +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb.egg-info/not-zip-safe +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb.egg-info/requires.txt +0 -0
- {drb-2.4.2 → drb-2.4.4}/drb.egg-info/top_level.txt +0 -0
- {drb-2.4.2 → drb-2.4.4}/pyproject.toml +0 -0
- {drb-2.4.2 → drb-2.4.4}/requirements.txt +0 -0
- {drb-2.4.2 → drb-2.4.4}/setup.cfg +0 -0
- {drb-2.4.2 → drb-2.4.4}/setup.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/tests/test_abstract_node.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/tests/test_addon.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/tests/test_children_unresolved.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/tests/test_core_topic_ttl.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/tests/test_cortex_validator.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/tests/test_drb_node_list.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/tests/test_drb_python_script.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/tests/test_event.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/tests/test_factory.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/tests/test_factory_resolver.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/tests/test_implementation_coupling.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/tests/test_implementation_manager.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/tests/test_keyring_bitwarden.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/tests/test_logical_node.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/tests/test_manager_dao.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/tests/test_manager_get_dao.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/tests/test_node_context.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/tests/test_node_descendant.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/tests/test_path.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/tests/test_protocol_dispatch.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/tests/test_rdf_dao.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/tests/test_rdf_dao_multi_url.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/tests/test_rdf_dao_scheme.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/tests/test_rdf_dao_signatures.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/tests/test_scheme_force_impl.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/tests/test_signature_exact.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/tests/test_signature_hierarchy.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/tests/test_signatures.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/tests/test_topic.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/tests/test_topic_descriptor_discovery.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/tests/test_url_node.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/tests/test_url_security.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/tests/test_xml_dao.py +0 -0
- {drb-2.4.2 → drb-2.4.4}/versioneer.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: drb
|
|
3
|
-
Version: 2.4.
|
|
3
|
+
Version: 2.4.4
|
|
4
4
|
Summary: Data Request Broker
|
|
5
5
|
Author: GAEL Systems
|
|
6
6
|
Author-email: drb-python@gael.fr
|
|
@@ -65,8 +65,6 @@ You can also join the moderators team. Please contact me.
|
|
|
65
65
|
|
|
66
66
|
Contribution process is based on gitlab best practice and processes the following schema:
|
|
67
67
|
|
|
68
|
-

|
|
69
|
-
|
|
70
68
|
### How to contribute
|
|
71
69
|
Contributor shall work in its own fork of the project and provides its contributions via the merge request process.
|
|
72
70
|
The merge request shall be approved and merged by a maintainers. So the step-by-step procedure to contribute is:
|
|
@@ -36,8 +36,6 @@ You can also join the moderators team. Please contact me.
|
|
|
36
36
|
|
|
37
37
|
Contribution process is based on gitlab best practice and processes the following schema:
|
|
38
38
|
|
|
39
|
-

|
|
40
|
-
|
|
41
39
|
### How to contribute
|
|
42
40
|
Contributor shall work in its own fork of the project and provides its contributions via the merge request process.
|
|
43
41
|
The merge request shall be approved and merged by a maintainers. So the step-by-step procedure to contribute is:
|
|
@@ -8,11 +8,11 @@ import json
|
|
|
8
8
|
|
|
9
9
|
version_json = '''
|
|
10
10
|
{
|
|
11
|
-
"date": "2026-06-
|
|
11
|
+
"date": "2026-06-30T02:14:50+0000",
|
|
12
12
|
"dirty": false,
|
|
13
13
|
"error": null,
|
|
14
|
-
"full-revisionid": "
|
|
15
|
-
"version": "2.4.
|
|
14
|
+
"full-revisionid": "6de9fd1d1e819008ecdd4ddbd56a71bd2e99c4ab",
|
|
15
|
+
"version": "2.4.4"
|
|
16
16
|
}
|
|
17
17
|
''' # END VERSION_JSON
|
|
18
18
|
|
|
@@ -1,10 +1,44 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import logging
|
|
3
|
+
|
|
1
4
|
from drb.core import DrbNode
|
|
2
5
|
from drb.core.predicate import Predicate
|
|
3
6
|
from drb.utils.drb_python_script import exec_with_return
|
|
4
7
|
from drb.exceptions.core import DrbException
|
|
5
8
|
from drb.core.signature import Signature, SignatureAggregator
|
|
6
|
-
|
|
7
|
-
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
_INLINE_FLAG_GROUP = re.compile(r'\(\?([aiLmsux]+)\)')
|
|
12
|
+
_FLAG_CHARS = {'a': re.A, 'i': re.I, 'L': re.L, 'm': re.M,
|
|
13
|
+
's': re.S, 'u': re.U, 'x': re.X}
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _compile_regex(pattern: str):
|
|
17
|
+
"""Compile a regex, tolerating global inline flags (e.g. ``(?i)``)
|
|
18
|
+
placed anywhere in the pattern. Python 3.11+ rejects a global inline
|
|
19
|
+
flag that is not at position 0, but drb-java / legacy descriptors
|
|
20
|
+
author them after an anchor (``^(?i)...``). A bare ``(?i)`` group is
|
|
21
|
+
already global, so its flags are moved to the compile-time ``flags``
|
|
22
|
+
argument and the group is stripped from the pattern. Scoped groups
|
|
23
|
+
like ``(?i:...)`` (with a colon) are untouched.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
pattern: The raw regex pattern string, possibly containing
|
|
27
|
+
global inline flag groups.
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
A compiled :class:`re.Pattern` with any extracted flags applied.
|
|
31
|
+
"""
|
|
32
|
+
chars = set()
|
|
33
|
+
for m in _INLINE_FLAG_GROUP.finditer(pattern):
|
|
34
|
+
chars.update(m.group(1))
|
|
35
|
+
if not chars:
|
|
36
|
+
return re.compile(pattern)
|
|
37
|
+
cleaned = _INLINE_FLAG_GROUP.sub('', pattern)
|
|
38
|
+
flags = 0
|
|
39
|
+
for ch in chars:
|
|
40
|
+
flags |= _FLAG_CHARS[ch]
|
|
41
|
+
return re.compile(cleaned, flags)
|
|
8
42
|
|
|
9
43
|
|
|
10
44
|
class NameSignature(Signature):
|
|
@@ -16,9 +50,10 @@ class NameSignature(Signature):
|
|
|
16
50
|
|
|
17
51
|
def __init__(self, regex: str):
|
|
18
52
|
self.__regex = regex
|
|
53
|
+
self.__pattern = _compile_regex(regex)
|
|
19
54
|
|
|
20
55
|
def matches(self, node: DrbNode) -> bool:
|
|
21
|
-
return
|
|
56
|
+
return self.__pattern.match(node.name) is not None
|
|
22
57
|
|
|
23
58
|
def to_dict(self) -> dict:
|
|
24
59
|
return {self.get_name(): self.__regex}
|
|
@@ -37,9 +72,10 @@ class NamespaceSignature(Signature):
|
|
|
37
72
|
|
|
38
73
|
def __init__(self, regex: str):
|
|
39
74
|
self.__regex = regex
|
|
75
|
+
self.__pattern = _compile_regex(regex)
|
|
40
76
|
|
|
41
77
|
def matches(self, node: DrbNode) -> bool:
|
|
42
|
-
return
|
|
78
|
+
return self.__pattern.match(node.namespace_uri) is not None
|
|
43
79
|
|
|
44
80
|
def to_dict(self) -> dict:
|
|
45
81
|
return {self.get_name(): self.__regex}
|
|
@@ -92,9 +128,10 @@ class PathSignature(Signature):
|
|
|
92
128
|
|
|
93
129
|
def __init__(self, regex: str):
|
|
94
130
|
self.__regex = regex
|
|
131
|
+
self.__pattern = _compile_regex(regex)
|
|
95
132
|
|
|
96
133
|
def matches(self, node: DrbNode) -> bool:
|
|
97
|
-
return
|
|
134
|
+
return self.__pattern.match(node.path.name) is not None
|
|
98
135
|
|
|
99
136
|
def to_dict(self) -> dict:
|
|
100
137
|
return {self.get_name(): self.__regex}
|
|
@@ -4,6 +4,7 @@ import logging
|
|
|
4
4
|
import pathlib
|
|
5
5
|
import sys
|
|
6
6
|
import re
|
|
7
|
+
import threading
|
|
7
8
|
|
|
8
9
|
from urllib.parse import urlparse
|
|
9
10
|
from typing import List, Optional, Tuple, Union
|
|
@@ -19,6 +20,8 @@ from drb.addons.addon import AddonManager
|
|
|
19
20
|
|
|
20
21
|
logger = logging.getLogger('DrbResolver')
|
|
21
22
|
|
|
23
|
+
_resolution_guard = threading.local()
|
|
24
|
+
|
|
22
25
|
|
|
23
26
|
def _is_remote_url(parsed_path):
|
|
24
27
|
"""
|
|
@@ -227,7 +230,7 @@ class _DrbFactoryResolver(DrbFactory):
|
|
|
227
230
|
DrbTopic: the finest topic matching the given node
|
|
228
231
|
"""
|
|
229
232
|
topics = [x for x in ManagerDao().get_all_drb_topics()
|
|
230
|
-
if x.subClassOf is not None if
|
|
233
|
+
if x.subClassOf is not None if finest.id in x.subClassOf]
|
|
231
234
|
for topic in topics:
|
|
232
235
|
node = forced_factory(topic, node)
|
|
233
236
|
if topic.matches(node):
|
|
@@ -283,6 +286,24 @@ class _DrbFactoryResolver(DrbFactory):
|
|
|
283
286
|
pp = urlparse(url)
|
|
284
287
|
|
|
285
288
|
if curl is None and path is None:
|
|
289
|
+
# Chained scheme "<base>+<forced>://": resolve the base URL
|
|
290
|
+
# normally -- walking the path through any container (e.g. a zip
|
|
291
|
+
# inside an S3 object) -- then apply each forced driver to the
|
|
292
|
+
# descended leaf. Without this, the forced token is crammed onto
|
|
293
|
+
# the whole-path base resource (e.g. an S3 object key that
|
|
294
|
+
# includes the in-archive path), which cannot be opened.
|
|
295
|
+
if pp.scheme and '+' in pp.scheme:
|
|
296
|
+
base, *tokens = pp.scheme.split('+')
|
|
297
|
+
if base == '' or any(t == '' for t in tokens):
|
|
298
|
+
raise DrbFactoryException(
|
|
299
|
+
f'Invalid scheme "{pp.scheme}": empty segment')
|
|
300
|
+
clean_url = base + url[len(pp.scheme):]
|
|
301
|
+
node = self.__create_from_url(clean_url)
|
|
302
|
+
for token in tokens:
|
|
303
|
+
_, factory = _resolve_token(token)
|
|
304
|
+
node = factory.create(node)
|
|
305
|
+
return node
|
|
306
|
+
|
|
286
307
|
try:
|
|
287
308
|
return self.create(UrlNode(url))
|
|
288
309
|
except (DrbFactoryException, DrbException,
|
|
@@ -338,38 +359,57 @@ class _DrbFactoryResolver(DrbFactory):
|
|
|
338
359
|
node = UrlNode(source)
|
|
339
360
|
else:
|
|
340
361
|
node = source
|
|
341
|
-
|
|
342
|
-
if
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
362
|
+
active = getattr(_resolution_guard, 'active', None)
|
|
363
|
+
if active is None:
|
|
364
|
+
active = set()
|
|
365
|
+
_resolution_guard.active = active
|
|
366
|
+
try:
|
|
367
|
+
key = node.path.path
|
|
368
|
+
except Exception: # noqa: BLE001
|
|
369
|
+
key = None
|
|
370
|
+
if key is not None and key in active:
|
|
371
|
+
raise DrbFactoryException(
|
|
372
|
+
f'Cyclic resolution detected for node path: {key}')
|
|
373
|
+
if key is not None:
|
|
374
|
+
active.add(key)
|
|
375
|
+
try:
|
|
376
|
+
protocol = None
|
|
377
|
+
if node.parent is None:
|
|
378
|
+
dispatched = self.__dispatch_scheme(node)
|
|
379
|
+
if dispatched is not None:
|
|
380
|
+
protocol, node = dispatched
|
|
381
|
+
else:
|
|
382
|
+
protocol = self.__retrieve_protocol(node)
|
|
383
|
+
if protocol is None:
|
|
384
|
+
raise DrbFactoryException(
|
|
385
|
+
f'Cannot resolve: {source}')
|
|
386
|
+
node = _create_via_factory(protocol, node)
|
|
387
|
+
|
|
388
|
+
container, node = self.__retrieve_container(node)
|
|
389
|
+
if container is not None and container.factory is not None:
|
|
390
|
+
node = _create_via_factory(container, node)
|
|
391
|
+
|
|
392
|
+
formatting = self.__retrieve_formatting(node)
|
|
393
|
+
if formatting is not None:
|
|
394
|
+
if formatting.factory is not None:
|
|
395
|
+
node = _create_via_factory(formatting, node)
|
|
396
|
+
t, n = self.__finest_drb_topic(node, formatting)
|
|
397
|
+
elif container is not None:
|
|
398
|
+
t, n = container, node
|
|
399
|
+
elif protocol is not None:
|
|
400
|
+
t, n = protocol, node
|
|
346
401
|
else:
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
if formatting.factory is not None:
|
|
359
|
-
node = _create_via_factory(formatting, node)
|
|
360
|
-
t, n = self.__finest_drb_topic(node, formatting)
|
|
361
|
-
elif container is not None:
|
|
362
|
-
t, n = container, node
|
|
363
|
-
elif protocol is not None:
|
|
364
|
-
t, n = protocol, node
|
|
365
|
-
else:
|
|
366
|
-
raise DrbFactoryException(f'Cannot resolve: {source}')
|
|
367
|
-
|
|
368
|
-
addons = filter(lambda x: x.can_apply(t),
|
|
369
|
-
AddonManager().get_all_addons())
|
|
370
|
-
for addon in addons:
|
|
371
|
-
node.add_impl(addon.return_type(), addon.apply, addon.identifier())
|
|
372
|
-
return t, n
|
|
402
|
+
raise DrbFactoryException(f'Cannot resolve: {source}')
|
|
403
|
+
|
|
404
|
+
addons = filter(lambda x: x.can_apply(t),
|
|
405
|
+
AddonManager().get_all_addons())
|
|
406
|
+
for addon in addons:
|
|
407
|
+
node.add_impl(
|
|
408
|
+
addon.return_type(), addon.apply, addon.identifier())
|
|
409
|
+
return t, n
|
|
410
|
+
finally:
|
|
411
|
+
if key is not None:
|
|
412
|
+
active.discard(key)
|
|
373
413
|
|
|
374
414
|
def resolve(self, source: Union[str, DrbNode]) \
|
|
375
415
|
-> Tuple[DrbTopic, Optional[DrbNode]]:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: drb
|
|
3
|
-
Version: 2.4.
|
|
3
|
+
Version: 2.4.4
|
|
4
4
|
Summary: Data Request Broker
|
|
5
5
|
Author: GAEL Systems
|
|
6
6
|
Author-email: drb-python@gael.fr
|
|
@@ -65,8 +65,6 @@ You can also join the moderators team. Please contact me.
|
|
|
65
65
|
|
|
66
66
|
Contribution process is based on gitlab best practice and processes the following schema:
|
|
67
67
|
|
|
68
|
-

|
|
69
|
-
|
|
70
68
|
### How to contribute
|
|
71
69
|
Contributor shall work in its own fork of the project and provides its contributions via the merge request process.
|
|
72
70
|
The merge request shall be approved and merged by a maintainers. So the step-by-step procedure to contribute is:
|
|
@@ -86,6 +86,7 @@ tests/test_drb_python_script.py
|
|
|
86
86
|
tests/test_event.py
|
|
87
87
|
tests/test_factory.py
|
|
88
88
|
tests/test_factory_resolver.py
|
|
89
|
+
tests/test_finest_multiparent.py
|
|
89
90
|
tests/test_implementation_coupling.py
|
|
90
91
|
tests/test_implementation_manager.py
|
|
91
92
|
tests/test_keyring_bitwarden.py
|
|
@@ -100,9 +101,12 @@ tests/test_rdf_dao.py
|
|
|
100
101
|
tests/test_rdf_dao_multi_url.py
|
|
101
102
|
tests/test_rdf_dao_scheme.py
|
|
102
103
|
tests/test_rdf_dao_signatures.py
|
|
104
|
+
tests/test_resolver_recursion_guard.py
|
|
103
105
|
tests/test_scheme_force_impl.py
|
|
106
|
+
tests/test_scheme_force_url.py
|
|
104
107
|
tests/test_signature_exact.py
|
|
105
108
|
tests/test_signature_hierarchy.py
|
|
109
|
+
tests/test_signature_inline_flags.py
|
|
106
110
|
tests/test_signatures.py
|
|
107
111
|
tests/test_topic.py
|
|
108
112
|
tests/test_topic_descriptor_discovery.py
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import tempfile
|
|
3
|
+
import unittest
|
|
4
|
+
import uuid
|
|
5
|
+
|
|
6
|
+
from drb.nodes.logical_node import DrbLogicalNode
|
|
7
|
+
from drb.topics.dao import ManagerDao
|
|
8
|
+
from drb.topics.dao.rdf_dao import RDFDao
|
|
9
|
+
import drb.topics.resolver as resolver
|
|
10
|
+
|
|
11
|
+
_IDS = ("a1a1a1a1-0000-0000-0000-00000000000a",
|
|
12
|
+
"b2b2b2b2-0000-0000-0000-00000000000b",
|
|
13
|
+
"c3c3c3c3-0000-0000-0000-00000000000c")
|
|
14
|
+
|
|
15
|
+
_TTL = """\
|
|
16
|
+
@prefix owl: <http://www.w3.org/2002/07/owl#> .
|
|
17
|
+
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
|
|
18
|
+
@prefix drb: <http://www.gael.fr/drb#> .
|
|
19
|
+
@prefix t: <http://example.org/mp/> .
|
|
20
|
+
|
|
21
|
+
t:base_a a owl:Class ; rdfs:label "mp_base_a" ;
|
|
22
|
+
drb:id "a1a1a1a1-0000-0000-0000-00000000000a" ;
|
|
23
|
+
drb:category "CONTAINER" ;
|
|
24
|
+
rdfs:subClassOf <http://www.gael.fr/drb#item> ;
|
|
25
|
+
drb:signature [ drb:nameMatch "mpnode" ] .
|
|
26
|
+
t:base_b a owl:Class ; rdfs:label "mp_base_b" ;
|
|
27
|
+
drb:id "b2b2b2b2-0000-0000-0000-00000000000b" ;
|
|
28
|
+
drb:category "CONTAINER" ;
|
|
29
|
+
rdfs:subClassOf <http://www.gael.fr/drb#item> ;
|
|
30
|
+
drb:signature [ drb:nameMatch "mpnode" ] .
|
|
31
|
+
t:child a owl:Class ; rdfs:label "mp_child" ;
|
|
32
|
+
drb:id "c3c3c3c3-0000-0000-0000-00000000000c" ;
|
|
33
|
+
drb:category "CONTAINER" ;
|
|
34
|
+
rdfs:subClassOf t:base_a, t:base_b ;
|
|
35
|
+
drb:signature [ drb:nameMatch "mpnode" ] .
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class TestFinestMultiParent(unittest.TestCase):
|
|
40
|
+
def setUp(self):
|
|
41
|
+
fd, self._ttl = tempfile.mkstemp(suffix=".ttl")
|
|
42
|
+
with os.fdopen(fd, "w") as f:
|
|
43
|
+
f.write(_TTL)
|
|
44
|
+
ManagerDao().add_dao_instance(RDFDao(self._ttl))
|
|
45
|
+
self.addCleanup(self._clean)
|
|
46
|
+
|
|
47
|
+
def _clean(self):
|
|
48
|
+
for u in _IDS:
|
|
49
|
+
try:
|
|
50
|
+
ManagerDao().delete(uuid.UUID(u))
|
|
51
|
+
except Exception:
|
|
52
|
+
pass
|
|
53
|
+
try:
|
|
54
|
+
os.unlink(self._ttl)
|
|
55
|
+
except OSError:
|
|
56
|
+
pass
|
|
57
|
+
|
|
58
|
+
def test_descends_via_non_first_parent(self):
|
|
59
|
+
topics = ManagerDao().get_all_drb_topics()
|
|
60
|
+
child = [t for t in topics
|
|
61
|
+
if t.uri and t.uri.endswith('/mp/child')][0]
|
|
62
|
+
# child has two parents; pick one that is NOT subClassOf[0]
|
|
63
|
+
self.assertEqual(2, len(child.subClassOf))
|
|
64
|
+
non_first = child.subClassOf[1]
|
|
65
|
+
parent = [t for t in topics if t.id == non_first][0]
|
|
66
|
+
res = resolver._DrbFactoryResolver()
|
|
67
|
+
node = DrbLogicalNode("mpnode")
|
|
68
|
+
topic, _ = res._DrbFactoryResolver__finest_drb_topic(node, parent)
|
|
69
|
+
self.assertTrue(
|
|
70
|
+
str(topic.uri).endswith('/mp/child'),
|
|
71
|
+
f"descent stopped at {topic.uri}, expected .../mp/child")
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import tempfile
|
|
3
|
+
import unittest
|
|
4
|
+
|
|
5
|
+
import drb.topics.resolver as resolver
|
|
6
|
+
from drb.nodes.logical_node import DrbLogicalNode
|
|
7
|
+
from drb.topics.dao import ManagerDao
|
|
8
|
+
from drb.topics.dao.rdf_dao import RDFDao
|
|
9
|
+
from drb.topics.resolver import DrbNodeList
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# A node whose resolved children list contains itself -> a resolution cycle.
|
|
13
|
+
class _CyclicNode(DrbLogicalNode):
|
|
14
|
+
@property
|
|
15
|
+
def children(self):
|
|
16
|
+
return DrbNodeList([self])
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
_LOOP_TTL = """\
|
|
20
|
+
@prefix owl: <http://www.w3.org/2002/07/owl#> .
|
|
21
|
+
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
|
|
22
|
+
@prefix drb: <http://www.gael.fr/drb#> .
|
|
23
|
+
@prefix t: <http://example.org/test/> .
|
|
24
|
+
|
|
25
|
+
t:loop a owl:Class ;
|
|
26
|
+
rdfs:label "loop" ;
|
|
27
|
+
drb:id "aaaaaaaa-0000-0000-0000-000000000001" ;
|
|
28
|
+
drb:category "CONTAINER" ;
|
|
29
|
+
rdfs:subClassOf <http://www.gael.fr/drb#item> ;
|
|
30
|
+
drb:signature [ drb:nameMatch "loop" ;
|
|
31
|
+
drb:xqueryTest "*[fn:matches(fn:name(),'loop')]" ] .
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class TestResolverRecursionGuard(unittest.TestCase):
|
|
36
|
+
def setUp(self):
|
|
37
|
+
fd, self._ttl = tempfile.mkstemp(suffix=".ttl")
|
|
38
|
+
with os.fdopen(fd, "w") as f:
|
|
39
|
+
f.write(_LOOP_TTL)
|
|
40
|
+
self._dao = ManagerDao().add_dao_instance(RDFDao(self._ttl))
|
|
41
|
+
self.addCleanup(self._cleanup)
|
|
42
|
+
|
|
43
|
+
def _cleanup(self):
|
|
44
|
+
try:
|
|
45
|
+
import uuid
|
|
46
|
+
ManagerDao().delete(
|
|
47
|
+
uuid.UUID("aaaaaaaa-0000-0000-0000-000000000001"))
|
|
48
|
+
except Exception:
|
|
49
|
+
pass
|
|
50
|
+
try:
|
|
51
|
+
os.unlink(self._ttl)
|
|
52
|
+
except OSError:
|
|
53
|
+
pass
|
|
54
|
+
|
|
55
|
+
def test_cyclic_resolution_terminates(self):
|
|
56
|
+
# Without the guard this recurses without bound (RecursionError, then
|
|
57
|
+
# swallowed -> DrbFactoryException); with the guard it terminates and
|
|
58
|
+
# classifies the node as the 'loop' container.
|
|
59
|
+
# The node must have a parent so the resolver skips the protocol/root
|
|
60
|
+
# dispatch path and goes directly to container signature matching,
|
|
61
|
+
# where the xquery '*' iterates children via DrbNodeList -> create().
|
|
62
|
+
parent = DrbLogicalNode("root")
|
|
63
|
+
node = _CyclicNode("loop", parent=parent)
|
|
64
|
+
topic, _ = resolver.resolve(node)
|
|
65
|
+
self.assertEqual("loop", topic.label)
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from unittest.mock import patch, MagicMock
|
|
3
|
+
|
|
4
|
+
import drb.topics.resolver as resolver_mod
|
|
5
|
+
from drb.core.factory import DrbFactory
|
|
6
|
+
from drb.core.node import DrbNode
|
|
7
|
+
from drb.nodes.logical_node import DrbLogicalNode
|
|
8
|
+
from drb.nodes.url_node import UrlNode
|
|
9
|
+
from drb.exceptions.core import DrbFactoryException
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class _WrapFactory(DrbFactory):
|
|
13
|
+
"""Wraps the received node and records the call order on a shared list."""
|
|
14
|
+
def __init__(self, tag, order):
|
|
15
|
+
self.tag = tag
|
|
16
|
+
self.order = order
|
|
17
|
+
|
|
18
|
+
def _create(self, node: DrbNode) -> DrbNode:
|
|
19
|
+
self.order.append(self.tag)
|
|
20
|
+
wrapped = DrbLogicalNode(self.tag)
|
|
21
|
+
wrapped.append_child(node)
|
|
22
|
+
return wrapped
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class TestForcedSchemeUrl(unittest.TestCase):
|
|
26
|
+
"""``__create_from_url`` on a chained ``<base>+<forced>://`` scheme must
|
|
27
|
+
resolve the *base* URL (token stripped, so the existing path-walking can
|
|
28
|
+
descend any container) and then apply each forced driver to the resulting
|
|
29
|
+
leaf -- not cram the forced driver onto the whole-path base resource.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
def _from_url(self, url, base_leaf, token_factories):
|
|
33
|
+
r = resolver_mod._DrbFactoryResolver()
|
|
34
|
+
with patch.object(r, 'create', return_value=base_leaf) as create, \
|
|
35
|
+
patch.object(resolver_mod, '_resolve_token',
|
|
36
|
+
side_effect=lambda tok: (MagicMock(),
|
|
37
|
+
token_factories[tok])):
|
|
38
|
+
result = r._DrbFactoryResolver__create_from_url(url)
|
|
39
|
+
return result, create
|
|
40
|
+
|
|
41
|
+
def test_base_url_resolved_with_token_stripped(self):
|
|
42
|
+
leaf = DrbLogicalNode('base-leaf')
|
|
43
|
+
order = []
|
|
44
|
+
result, create = self._from_url(
|
|
45
|
+
's3+xml://bucket/a.zip/inner', leaf,
|
|
46
|
+
{'xml': _WrapFactory('xml', order)})
|
|
47
|
+
# The base resolution must receive the clean s3:// URL.
|
|
48
|
+
self.assertEqual(1, create.call_count)
|
|
49
|
+
received = create.call_args[0][0]
|
|
50
|
+
self.assertIsInstance(received, UrlNode)
|
|
51
|
+
self.assertEqual('s3', received.path.scheme)
|
|
52
|
+
self.assertEqual('s3://bucket/a.zip/inner', received.path.name)
|
|
53
|
+
|
|
54
|
+
def test_forced_driver_applied_to_base_leaf(self):
|
|
55
|
+
leaf = DrbLogicalNode('base-leaf')
|
|
56
|
+
order = []
|
|
57
|
+
result, _ = self._from_url(
|
|
58
|
+
's3+xml://bucket/a.zip/inner', leaf,
|
|
59
|
+
{'xml': _WrapFactory('xml', order)})
|
|
60
|
+
self.assertEqual('xml', result.name) # forced driver on top
|
|
61
|
+
self.assertEqual('base-leaf', result[0].name) # wrapping the leaf
|
|
62
|
+
self.assertEqual(['xml'], order)
|
|
63
|
+
|
|
64
|
+
def test_multiple_tokens_applied_in_order(self):
|
|
65
|
+
leaf = DrbLogicalNode('base-leaf')
|
|
66
|
+
order = []
|
|
67
|
+
result, _ = self._from_url(
|
|
68
|
+
'https+odata+xml://host/x', leaf,
|
|
69
|
+
{'odata': _WrapFactory('odata', order),
|
|
70
|
+
'xml': _WrapFactory('xml', order)})
|
|
71
|
+
self.assertEqual(['odata', 'xml'], order)
|
|
72
|
+
self.assertEqual('xml', result.name)
|
|
73
|
+
|
|
74
|
+
def test_empty_scheme_segment_raises(self):
|
|
75
|
+
r = resolver_mod._DrbFactoryResolver()
|
|
76
|
+
with self.assertRaises(DrbFactoryException):
|
|
77
|
+
r._DrbFactoryResolver__create_from_url('s3++xml://host/x')
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
if __name__ == '__main__':
|
|
81
|
+
unittest.main()
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
|
|
3
|
+
from drb.nodes.logical_node import DrbLogicalNode
|
|
4
|
+
from drb.signatures.core import NameSignature
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class TestNameSignatureInlineFlags(unittest.TestCase):
|
|
8
|
+
def test_misplaced_inline_flag_compiles_and_matches(self):
|
|
9
|
+
# (?i) after the ^ anchor: invalid as a raw py3.11 regex.
|
|
10
|
+
sig = NameSignature(r'^(?i)S2[AB]_.*\.SAFE$')
|
|
11
|
+
self.assertTrue(sig.matches(DrbLogicalNode('S2B_x.SAFE')))
|
|
12
|
+
# global case-insensitivity preserved
|
|
13
|
+
self.assertTrue(sig.matches(DrbLogicalNode('s2b_x.safe')))
|
|
14
|
+
self.assertFalse(sig.matches(DrbLogicalNode('X9_x.SAFE')))
|
|
15
|
+
|
|
16
|
+
def test_to_dict_keeps_the_original_pattern(self):
|
|
17
|
+
pat = r'^(?i)S2[AB]_.*\.SAFE$'
|
|
18
|
+
self.assertEqual({'name': pat}, NameSignature(pat).to_dict())
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{drb-2.4.2 → drb-2.4.4}/setup.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|