drb 2.3.8__tar.gz → 2.3.9__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.
Files changed (112) hide show
  1. {drb-2.3.8/drb.egg-info → drb-2.3.9}/PKG-INFO +1 -1
  2. {drb-2.3.8 → drb-2.3.9}/drb/core/_version.py +3 -3
  3. {drb-2.3.8 → drb-2.3.9}/drb/topics/dao/rdf_dao.py +7 -0
  4. {drb-2.3.8 → drb-2.3.9}/drb/topics/resolver.py +18 -8
  5. {drb-2.3.8 → drb-2.3.9}/drb/topics/topic.py +1 -0
  6. {drb-2.3.8 → drb-2.3.9/drb.egg-info}/PKG-INFO +1 -1
  7. {drb-2.3.8 → drb-2.3.9}/drb.egg-info/SOURCES.txt +1 -2
  8. {drb-2.3.8 → drb-2.3.9}/setup.cfg +0 -1
  9. drb-2.3.9/tests/test_protocol_dispatch.py +102 -0
  10. drb-2.3.9/tests/test_rdf_dao_scheme.py +34 -0
  11. drb-2.3.8/drb/core/protocol.py +0 -58
  12. drb-2.3.8/tests/test_protocol_dispatch.py +0 -115
  13. drb-2.3.8/tests/test_protocol_loader.py +0 -29
  14. {drb-2.3.8 → drb-2.3.9}/LICENCE.txt +0 -0
  15. {drb-2.3.8 → drb-2.3.9}/MANIFEST.in +0 -0
  16. {drb-2.3.8 → drb-2.3.9}/README.md +0 -0
  17. {drb-2.3.8 → drb-2.3.9}/drb/addons/addon.py +0 -0
  18. {drb-2.3.8 → drb-2.3.9}/drb/core/__init__.py +0 -0
  19. {drb-2.3.8 → drb-2.3.9}/drb/core/context.py +0 -0
  20. {drb-2.3.8 → drb-2.3.9}/drb/core/cortex_validator.py +0 -0
  21. {drb-2.3.8 → drb-2.3.9}/drb/core/events.py +0 -0
  22. {drb-2.3.8 → drb-2.3.9}/drb/core/factory.py +0 -0
  23. {drb-2.3.8 → drb-2.3.9}/drb/core/impls.py +0 -0
  24. {drb-2.3.8 → drb-2.3.9}/drb/core/item.py +0 -0
  25. {drb-2.3.8 → drb-2.3.9}/drb/core/node.py +0 -0
  26. {drb-2.3.8 → drb-2.3.9}/drb/core/path.py +0 -0
  27. {drb-2.3.8 → drb-2.3.9}/drb/core/predicate.py +0 -0
  28. {drb-2.3.8 → drb-2.3.9}/drb/core/signature.py +0 -0
  29. {drb-2.3.8 → drb-2.3.9}/drb/drivers/file/__init__.py +0 -0
  30. {drb-2.3.8 → drb-2.3.9}/drb/drivers/file/file.py +0 -0
  31. {drb-2.3.8 → drb-2.3.9}/drb/drivers/xml/__init__.py +0 -0
  32. {drb-2.3.8 → drb-2.3.9}/drb/drivers/xml/factory.py +0 -0
  33. {drb-2.3.8 → drb-2.3.9}/drb/drivers/xml/node.py +0 -0
  34. {drb-2.3.8 → drb-2.3.9}/drb/exceptions/core.py +0 -0
  35. {drb-2.3.8 → drb-2.3.9}/drb/exceptions/file.py +0 -0
  36. {drb-2.3.8 → drb-2.3.9}/drb/keyring/bitwarden/__init__.py +0 -0
  37. {drb-2.3.8 → drb-2.3.9}/drb/keyring/bitwarden/bitwarden.py +0 -0
  38. {drb-2.3.8 → drb-2.3.9}/drb/nodes/abstract_node.py +0 -0
  39. {drb-2.3.8 → drb-2.3.9}/drb/nodes/logical_node.py +0 -0
  40. {drb-2.3.8 → drb-2.3.9}/drb/nodes/mutable_node.py +0 -0
  41. {drb-2.3.8 → drb-2.3.9}/drb/nodes/url_node.py +0 -0
  42. {drb-2.3.8 → drb-2.3.9}/drb/signatures/core.py +0 -0
  43. {drb-2.3.8 → drb-2.3.9}/drb/signatures/hierarchy.py +0 -0
  44. {drb-2.3.8 → drb-2.3.9}/drb/signatures/xquery.py +0 -0
  45. {drb-2.3.8 → drb-2.3.9}/drb/topics/dao/__init__.py +0 -0
  46. {drb-2.3.8 → drb-2.3.9}/drb/topics/dao/manager_dao.py +0 -0
  47. {drb-2.3.8 → drb-2.3.9}/drb/topics/dao/topic_dao.py +0 -0
  48. {drb-2.3.8 → drb-2.3.9}/drb/topics/dao/topic_schema.yml +0 -0
  49. {drb-2.3.8 → drb-2.3.9}/drb/topics/dao/xml_dao.py +0 -0
  50. {drb-2.3.8 → drb-2.3.9}/drb/topics/dao/yaml_dao.py +0 -0
  51. {drb-2.3.8 → drb-2.3.9}/drb/topics/file/__init__.py +0 -0
  52. {drb-2.3.8 → drb-2.3.9}/drb/topics/file/cortex.yml +0 -0
  53. {drb-2.3.8 → drb-2.3.9}/drb/topics/xml/__init__.py +0 -0
  54. {drb-2.3.8 → drb-2.3.9}/drb/topics/xml/cortex.yml +0 -0
  55. {drb-2.3.8 → drb-2.3.9}/drb/utils/__init__.py +0 -0
  56. {drb-2.3.8 → drb-2.3.9}/drb/utils/drb_python_script.py +0 -0
  57. {drb-2.3.8 → drb-2.3.9}/drb/utils/plugins.py +0 -0
  58. {drb-2.3.8 → drb-2.3.9}/drb/utils/url_security.py +0 -0
  59. {drb-2.3.8 → drb-2.3.9}/drb/utils/utils.py +0 -0
  60. {drb-2.3.8 → drb-2.3.9}/drb/xquery/XQueryLexer.py +0 -0
  61. {drb-2.3.8 → drb-2.3.9}/drb/xquery/XQueryParser.py +0 -0
  62. {drb-2.3.8 → drb-2.3.9}/drb/xquery/XQueryParserListener.py +0 -0
  63. {drb-2.3.8 → drb-2.3.9}/drb/xquery/XQueryParserVisitor.py +0 -0
  64. {drb-2.3.8 → drb-2.3.9}/drb/xquery/__init__.py +0 -0
  65. {drb-2.3.8 → drb-2.3.9}/drb/xquery/drb_xquery.py +0 -0
  66. {drb-2.3.8 → drb-2.3.9}/drb/xquery/drb_xquery_cmd.py +0 -0
  67. {drb-2.3.8 → drb-2.3.9}/drb/xquery/drb_xquery_context.py +0 -0
  68. {drb-2.3.8 → drb-2.3.9}/drb/xquery/drb_xquery_func.py +0 -0
  69. {drb-2.3.8 → drb-2.3.9}/drb/xquery/drb_xquery_item.py +0 -0
  70. {drb-2.3.8 → drb-2.3.9}/drb/xquery/drb_xquery_res_to_string.py +0 -0
  71. {drb-2.3.8 → drb-2.3.9}/drb/xquery/drb_xquery_utils.py +0 -0
  72. {drb-2.3.8 → drb-2.3.9}/drb/xquery/drb_xquery_variable.py +0 -0
  73. {drb-2.3.8 → drb-2.3.9}/drb/xquery/drb_xquery_visitor.py +0 -0
  74. {drb-2.3.8 → drb-2.3.9}/drb/xquery/exceptions.py +0 -0
  75. {drb-2.3.8 → drb-2.3.9}/drb.egg-info/dependency_links.txt +0 -0
  76. {drb-2.3.8 → drb-2.3.9}/drb.egg-info/entry_points.txt +0 -0
  77. {drb-2.3.8 → drb-2.3.9}/drb.egg-info/not-zip-safe +0 -0
  78. {drb-2.3.8 → drb-2.3.9}/drb.egg-info/requires.txt +0 -0
  79. {drb-2.3.8 → drb-2.3.9}/drb.egg-info/top_level.txt +0 -0
  80. {drb-2.3.8 → drb-2.3.9}/pyproject.toml +0 -0
  81. {drb-2.3.8 → drb-2.3.9}/requirements.txt +0 -0
  82. {drb-2.3.8 → drb-2.3.9}/setup.py +0 -0
  83. {drb-2.3.8 → drb-2.3.9}/tests/test_abstract_node.py +0 -0
  84. {drb-2.3.8 → drb-2.3.9}/tests/test_addon.py +0 -0
  85. {drb-2.3.8 → drb-2.3.9}/tests/test_children_unresolved.py +0 -0
  86. {drb-2.3.8 → drb-2.3.9}/tests/test_drb_node_list.py +0 -0
  87. {drb-2.3.8 → drb-2.3.9}/tests/test_drb_python_script.py +0 -0
  88. {drb-2.3.8 → drb-2.3.9}/tests/test_event.py +0 -0
  89. {drb-2.3.8 → drb-2.3.9}/tests/test_factory.py +0 -0
  90. {drb-2.3.8 → drb-2.3.9}/tests/test_factory_resolver.py +0 -0
  91. {drb-2.3.8 → drb-2.3.9}/tests/test_implementation_coupling.py +0 -0
  92. {drb-2.3.8 → drb-2.3.9}/tests/test_implementation_manager.py +0 -0
  93. {drb-2.3.8 → drb-2.3.9}/tests/test_keyring_bitwarden.py +0 -0
  94. {drb-2.3.8 → drb-2.3.9}/tests/test_logical_node.py +0 -0
  95. {drb-2.3.8 → drb-2.3.9}/tests/test_manager_dao.py +0 -0
  96. {drb-2.3.8 → drb-2.3.9}/tests/test_manager_get_dao.py +0 -0
  97. {drb-2.3.8 → drb-2.3.9}/tests/test_node_context.py +0 -0
  98. {drb-2.3.8 → drb-2.3.9}/tests/test_node_descendant.py +0 -0
  99. {drb-2.3.8 → drb-2.3.9}/tests/test_path.py +0 -0
  100. {drb-2.3.8 → drb-2.3.9}/tests/test_rdf_dao.py +0 -0
  101. {drb-2.3.8 → drb-2.3.9}/tests/test_rdf_dao_multi_url.py +0 -0
  102. {drb-2.3.8 → drb-2.3.9}/tests/test_rdf_dao_signatures.py +0 -0
  103. {drb-2.3.8 → drb-2.3.9}/tests/test_signature_exact.py +0 -0
  104. {drb-2.3.8 → drb-2.3.9}/tests/test_signature_hierarchy.py +0 -0
  105. {drb-2.3.8 → drb-2.3.9}/tests/test_signatures.py +0 -0
  106. {drb-2.3.8 → drb-2.3.9}/tests/test_topic.py +0 -0
  107. {drb-2.3.8 → drb-2.3.9}/tests/test_topic_descriptor_discovery.py +0 -0
  108. {drb-2.3.8 → drb-2.3.9}/tests/test_url_node.py +0 -0
  109. {drb-2.3.8 → drb-2.3.9}/tests/test_url_security.py +0 -0
  110. {drb-2.3.8 → drb-2.3.9}/tests/test_xml_dao.py +0 -0
  111. {drb-2.3.8 → drb-2.3.9}/tests/test_yaml_dao.py +0 -0
  112. {drb-2.3.8 → drb-2.3.9}/versioneer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: drb
3
- Version: 2.3.8
3
+ Version: 2.3.9
4
4
  Summary: Data Request Broker
5
5
  Author: GAEL Systems
6
6
  Author-email: drb-python@gael.fr
@@ -8,11 +8,11 @@ import json
8
8
 
9
9
  version_json = '''
10
10
  {
11
- "date": "2026-06-25T20:48:59+0000",
11
+ "date": "2026-06-25T22:03:16+0000",
12
12
  "dirty": false,
13
13
  "error": null,
14
- "full-revisionid": "15fc9c3ff4c5c9f27c0ec6e7bba7d7c3e3885305",
15
- "version": "2.3.8"
14
+ "full-revisionid": "e77eb7f024e245d4ecab611276e4c247ccdd516a",
15
+ "version": "2.3.9"
16
16
  }
17
17
  ''' # END VERSION_JSON
18
18
 
@@ -91,6 +91,9 @@ class RDFDao(DrbTopicDao):
91
91
  (GROUP_CONCAT(DISTINCT IF(BOUND(?parentClassId),
92
92
  STR(?parentClassId),
93
93
  ?parentClass); separator="§ ") AS ?parentClasses)
94
+ (GROUP_CONCAT(DISTINCT IF(BOUND(?scheme),
95
+ STR(?scheme), ""); separator=" ")
96
+ AS ?schemes)
94
97
  WHERE {
95
98
  {
96
99
  ?Class rdfs:subClassOf+ <http://www.gael.fr/drb#item> .
@@ -101,6 +104,7 @@ class RDFDao(DrbTopicDao):
101
104
  OPTIONAL { ?Class drb:id ?id .}
102
105
  OPTIONAL { ?Class drb:category ?category .}
103
106
  OPTIONAL { ?Class drb:implementationIdentifier ?factory .}
107
+ OPTIONAL { ?Class drb:scheme ?scheme .}
104
108
  OPTIONAL {
105
109
  ?Class rdfs:subClassOf ?parentClass .
106
110
  OPTIONAL { ?parentClass drb:id ?parentClassId .}
@@ -190,6 +194,9 @@ class RDFDao(DrbTopicDao):
190
194
  if row.factory is not None:
191
195
  data['factory'] = row.factory.toPython()
192
196
 
197
+ schemes = str(row.schemes) if row.schemes is not None else ''
198
+ data['scheme'] = schemes.split() if schemes else []
199
+
193
200
  data['subClassOf'] = None
194
201
 
195
202
  if uri_parents is not None:
@@ -10,7 +10,6 @@ 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
14
13
  from drb.topics.topic import DrbTopic, TopicCategory
15
14
  from drb.exceptions.core import DrbFactoryException, DrbException
16
15
  from drb.nodes.url_node import UrlNode
@@ -55,7 +54,6 @@ def _create_via_factory(topic: DrbTopic, node: DrbNode) -> DrbNode:
55
54
 
56
55
  def _topic_by_factory(name: str):
57
56
  """The PROTOCOL topic whose implementationIdentifier == name, or None."""
58
- from drb.topics.dao import ManagerDao
59
57
  for topic in ManagerDao().get_drb_topics_by_category(
60
58
  TopicCategory.PROTOCOL):
61
59
  if topic.factory == name:
@@ -63,6 +61,15 @@ def _topic_by_factory(name: str):
63
61
  return None
64
62
 
65
63
 
64
+ def _topic_by_scheme(scheme: str):
65
+ """The PROTOCOL topic declaring this scheme (drb:scheme), or None."""
66
+ for topic in ManagerDao().get_drb_topics_by_category(
67
+ TopicCategory.PROTOCOL):
68
+ if scheme in (topic.scheme or []):
69
+ return topic
70
+ return None
71
+
72
+
66
73
  class _DrbFactoryResolver(DrbFactory):
67
74
  """ The factory resolver
68
75
 
@@ -100,22 +107,25 @@ class _DrbFactoryResolver(DrbFactory):
100
107
  return None
101
108
 
102
109
  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)."""
110
+ """Dispatch a root URL by drb:scheme, then apply a +<name> forced
111
+ driver if present. Returns (topic, node), or None when no PROTOCOL
112
+ topic declares the base scheme or its factory is not installed
113
+ (caller falls back to signature matching)."""
107
114
  scheme = node.path.scheme
108
115
  if not scheme:
109
116
  return None
110
117
  parts = scheme.split('+', 1)
111
118
  base = parts[0]
112
119
  forced = parts[1] if len(parts) > 1 else None
113
- base_factory = ProtocolLoader().get_factory(base)
120
+ base_topic = _topic_by_scheme(base)
121
+ if base_topic is None:
122
+ return None
123
+ base_factory = FactoryLoader().get_factory(base_topic.factory)
114
124
  if base_factory is None:
115
125
  return None
116
126
  created = base_factory.create(node)
117
127
  if forced is None:
118
- return _topic_by_factory(base), created
128
+ return base_topic, created
119
129
  forced_factory = FactoryLoader().get_factory(forced)
120
130
  if forced_factory is None:
121
131
  raise DrbFactoryException(
@@ -31,6 +31,7 @@ class DrbTopic:
31
31
  factory: str = field(default=None, repr=False)
32
32
  description: str = field(default=None, repr=False)
33
33
  forced: bool = field(default=False, repr=False)
34
+ scheme: List[str] = field(default_factory=list, repr=False)
34
35
  signatures: List[Signature] = field(default=None, repr=False)
35
36
  override: bool = field(default=False, repr=False)
36
37
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: drb
3
- Version: 2.3.8
3
+ Version: 2.3.9
4
4
  Summary: Data Request Broker
5
5
  Author: GAEL Systems
6
6
  Author-email: drb-python@gael.fr
@@ -25,7 +25,6 @@ drb/core/item.py
25
25
  drb/core/node.py
26
26
  drb/core/path.py
27
27
  drb/core/predicate.py
28
- drb/core/protocol.py
29
28
  drb/core/signature.py
30
29
  drb/drivers/file/__init__.py
31
30
  drb/drivers/file/file.py
@@ -94,9 +93,9 @@ tests/test_node_context.py
94
93
  tests/test_node_descendant.py
95
94
  tests/test_path.py
96
95
  tests/test_protocol_dispatch.py
97
- tests/test_protocol_loader.py
98
96
  tests/test_rdf_dao.py
99
97
  tests/test_rdf_dao_multi_url.py
98
+ tests/test_rdf_dao_scheme.py
100
99
  tests/test_rdf_dao_signatures.py
101
100
  tests/test_signature_exact.py
102
101
  tests/test_signature_hierarchy.py
@@ -25,7 +25,6 @@ console_scripts =
25
25
  drb.driver =
26
26
  xml = drb.drivers.xml:XmlNodeFactory
27
27
  file = drb.drivers.file.file:DrbFileFactory
28
- drb.protocol =
29
28
  drb.topic =
30
29
  xml = drb.topics.xml
31
30
  file = drb.topics.file
@@ -0,0 +1,102 @@
1
+ import unittest
2
+ from unittest.mock import patch, MagicMock
3
+ from drb.core.factory import DrbFactory
4
+ from drb.core.node import DrbNode
5
+ from drb.nodes.logical_node import DrbLogicalNode
6
+ import drb.topics.resolver as resolver_mod
7
+
8
+
9
+ class _Node(DrbLogicalNode):
10
+ pass
11
+
12
+
13
+ class _BaseFactory(DrbFactory):
14
+ def _create(self, node: DrbNode) -> DrbNode:
15
+ return _Node('dispatched')
16
+
17
+
18
+ class _ForcedNode(DrbLogicalNode):
19
+ pass
20
+
21
+
22
+ class _ForcedFactory(DrbFactory):
23
+ received = None
24
+
25
+ def _create(self, node: DrbNode) -> DrbNode:
26
+ _ForcedFactory.received = node
27
+ return _ForcedNode('forced')
28
+
29
+
30
+ def _topic(factory, scheme):
31
+ t = MagicMock()
32
+ t.factory = factory
33
+ t.scheme = scheme
34
+ return t
35
+
36
+
37
+ class TestSchemeDispatch(unittest.TestCase):
38
+ def _resolve(self, url, protocol_topics, factories):
39
+ r = resolver_mod._DrbFactoryResolver()
40
+ with patch.object(resolver_mod, 'ManagerDao') as md, \
41
+ patch.object(resolver_mod.FactoryLoader, 'get_factory') as gf:
42
+ md.return_value.get_drb_topics_by_category.return_value = \
43
+ protocol_topics
44
+ gf.side_effect = lambda name: factories.get(name)
45
+ return r._DrbFactoryResolver__dispatch_scheme(DrbLogicalNode(url))
46
+
47
+ def test_scheme_decoupled_from_factory(self):
48
+ topic = _topic('aws3', ['s3'])
49
+ result = self._resolve('s3://bucket/key', [topic],
50
+ {'aws3': _BaseFactory()})
51
+ self.assertIsNotNone(result)
52
+ t, n = result
53
+ self.assertIs(t, topic)
54
+ self.assertIsInstance(n, _Node)
55
+
56
+ def test_two_topics_one_factory_pick_right(self):
57
+ http = _topic('http', ['http'])
58
+ https = _topic('http', ['https'])
59
+ result = self._resolve('https://host', [http, https],
60
+ {'http': _BaseFactory()})
61
+ t, _ = result
62
+ self.assertIs(t, https)
63
+
64
+ def test_unknown_scheme_returns_none(self):
65
+ topic = _topic('aws3', ['s3'])
66
+ self.assertIsNone(
67
+ self._resolve('ftp://h', [topic], {'aws3': _BaseFactory()}))
68
+
69
+ def test_factory_absent_returns_none(self):
70
+ topic = _topic('aws3', ['s3'])
71
+ self.assertIsNone(
72
+ self._resolve('s3://b', [topic], {})) # aws3 not installed
73
+
74
+ def test_force_by_name(self):
75
+ base = _topic('http', ['https'])
76
+ forced = _topic('odata', [])
77
+ r = resolver_mod._DrbFactoryResolver()
78
+ with patch.object(resolver_mod, 'ManagerDao') as md, \
79
+ patch.object(resolver_mod.FactoryLoader, 'get_factory') as gf, \
80
+ patch.object(resolver_mod, '_topic_by_factory',
81
+ return_value=forced):
82
+ md.return_value.get_drb_topics_by_category.return_value = [base]
83
+ facs = {'http': _BaseFactory(), 'odata': _ForcedFactory()}
84
+ gf.side_effect = lambda name: facs.get(name)
85
+ t, n = r._DrbFactoryResolver__dispatch_scheme(
86
+ DrbLogicalNode('https+odata://host'))
87
+ self.assertIs(t, forced)
88
+ self.assertIsInstance(n, _ForcedNode)
89
+ self.assertIsInstance(_ForcedFactory.received, _Node)
90
+
91
+ def test_unknown_forced_raises(self):
92
+ base = _topic('http', ['https'])
93
+ from drb.exceptions.core import DrbFactoryException
94
+ r = resolver_mod._DrbFactoryResolver()
95
+ with patch.object(resolver_mod, 'ManagerDao') as md, \
96
+ patch.object(resolver_mod.FactoryLoader, 'get_factory') as gf:
97
+ md.return_value.get_drb_topics_by_category.return_value = [base]
98
+ gf.side_effect = lambda name: (
99
+ {'http': _BaseFactory()}).get(name)
100
+ with self.assertRaises(DrbFactoryException):
101
+ r._DrbFactoryResolver__dispatch_scheme(
102
+ DrbLogicalNode('https+nosuch://host'))
@@ -0,0 +1,34 @@
1
+ import os
2
+ import tempfile
3
+ import unittest
4
+ from drb.topics.dao.rdf_dao import RDFDao
5
+
6
+ _TTL = '''
7
+ @prefix owl: <http://www.w3.org/2002/07/owl#> .
8
+ @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
9
+ @prefix drb: <http://www.gael.fr/drb#> .
10
+ <http://www.gael.fr/drb#> a owl:Ontology .
11
+
12
+ drb:s3demo a owl:Class ;
13
+ drb:id "66666666-6666-6666-6666-666666666666" ;
14
+ drb:category "PROTOCOL" ;
15
+ rdfs:subClassOf drb:item ;
16
+ rdfs:label "s3demo" ;
17
+ drb:scheme "s3" ;
18
+ drb:implementationIdentifier "aws3" .
19
+ '''
20
+
21
+
22
+ class TestRdfDaoScheme(unittest.TestCase):
23
+ def test_scheme_parsed_into_topic(self):
24
+ fd, path = tempfile.mkstemp(suffix='.ttl')
25
+ os.close(fd)
26
+ with open(path, 'w') as f:
27
+ f.write(_TTL)
28
+ try:
29
+ dao = RDFDao(path)
30
+ topic = next(t for t in dao.read_all() if t.label == 's3demo')
31
+ self.assertEqual(['s3'], topic.scheme)
32
+ self.assertEqual('aws3', topic.factory)
33
+ finally:
34
+ os.remove(path)
@@ -1,58 +0,0 @@
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)
@@ -1,115 +0,0 @@
1
- import unittest
2
- from unittest.mock import patch, MagicMock
3
- import drb.core.protocol as protocol_mod
4
- from drb.core.factory import DrbFactory
5
- from drb.core.node import DrbNode
6
- from drb.nodes.logical_node import DrbLogicalNode
7
- import drb.topics.resolver as resolver_mod
8
-
9
-
10
- class _SchemeNode(DrbLogicalNode):
11
- pass
12
-
13
-
14
- class _BaseFactory(DrbFactory):
15
- def _create(self, node: DrbNode) -> DrbNode:
16
- n = _SchemeNode('dispatched')
17
- return n
18
-
19
-
20
- class TestProtocolDispatch(unittest.TestCase):
21
- def setUp(self):
22
- protocol_mod.ProtocolLoader._ProtocolLoader__instance = None
23
- protocol_mod.ProtocolLoader._ProtocolLoader__factories = None
24
-
25
- def _loader_with(self, scheme, factory):
26
- ep = MagicMock()
27
- ep.name = scheme
28
- ep.value = f'{__name__}:{type(factory).__name__}'
29
- with patch.object(protocol_mod.drb.utils.plugins, 'get_entry_points',
30
- return_value=[ep]):
31
- return protocol_mod.ProtocolLoader()
32
-
33
- def test_base_scheme_dispatch_creates_node(self):
34
- loader = self._loader_with('foo', _BaseFactory())
35
- topic = MagicMock()
36
- topic.factory = 'foo'
37
- with patch.object(
38
- resolver_mod, 'ProtocolLoader', return_value=loader), \
39
- patch.object(
40
- resolver_mod, '_topic_by_factory', return_value=topic):
41
- r = resolver_mod._DrbFactoryResolver()
42
- result = r._DrbFactoryResolver__dispatch_scheme(
43
- DrbLogicalNode('foo://host/x'))
44
- self.assertIsNotNone(result)
45
- t, n = result
46
- self.assertIs(t, topic)
47
- self.assertIsInstance(n, _SchemeNode)
48
-
49
- def test_unknown_scheme_returns_none(self):
50
- loader = self._loader_with('foo', _BaseFactory())
51
- with patch.object(resolver_mod, 'ProtocolLoader',
52
- return_value=loader):
53
- r = resolver_mod._DrbFactoryResolver()
54
- result = r._DrbFactoryResolver__dispatch_scheme(
55
- DrbLogicalNode('bar://host/x'))
56
- self.assertIsNone(result)
57
-
58
-
59
- class _ForcedNode(DrbLogicalNode):
60
- pass
61
-
62
-
63
- class _ForcedFactory(DrbFactory):
64
- received = None
65
-
66
- def _create(self, node: DrbNode) -> DrbNode:
67
- _ForcedFactory.received = node
68
- return _ForcedNode('forced')
69
-
70
-
71
- class TestForceByName(unittest.TestCase):
72
- def setUp(self):
73
- protocol_mod.ProtocolLoader._ProtocolLoader__instance = None
74
- protocol_mod.ProtocolLoader._ProtocolLoader__factories = None
75
- _ForcedFactory.received = None
76
-
77
- def test_force_applies_named_factory(self):
78
- ep = MagicMock()
79
- ep.name = 'foo'
80
- ep.value = f'{__name__}:_BaseFactory'
81
- with patch.object(protocol_mod.drb.utils.plugins, 'get_entry_points',
82
- return_value=[ep]):
83
- loader = protocol_mod.ProtocolLoader()
84
- forced = _ForcedFactory()
85
- forced_topic = MagicMock()
86
- forced_topic.factory = 'bar'
87
- with patch.object(
88
- resolver_mod, 'ProtocolLoader', return_value=loader), \
89
- patch.object(resolver_mod.FactoryLoader, 'get_factory',
90
- return_value=forced), \
91
- patch.object(resolver_mod, '_topic_by_factory',
92
- return_value=forced_topic):
93
- r = resolver_mod._DrbFactoryResolver()
94
- t, n = r._DrbFactoryResolver__dispatch_scheme(
95
- DrbLogicalNode('foo+bar://host/x'))
96
- self.assertIs(t, forced_topic)
97
- self.assertIsInstance(n, _ForcedNode)
98
- self.assertIsInstance(_ForcedFactory.received, _SchemeNode)
99
-
100
- def test_unknown_forced_name_raises(self):
101
- ep = MagicMock()
102
- ep.name = 'foo'
103
- ep.value = f'{__name__}:_BaseFactory'
104
- with patch.object(protocol_mod.drb.utils.plugins, 'get_entry_points',
105
- return_value=[ep]):
106
- loader = protocol_mod.ProtocolLoader()
107
- from drb.exceptions.core import DrbFactoryException
108
- with patch.object(
109
- resolver_mod, 'ProtocolLoader', return_value=loader), \
110
- patch.object(resolver_mod.FactoryLoader, 'get_factory',
111
- return_value=None):
112
- r = resolver_mod._DrbFactoryResolver()
113
- with self.assertRaises(DrbFactoryException):
114
- r._DrbFactoryResolver__dispatch_scheme(
115
- DrbLogicalNode('foo+nosuch://host/x'))
@@ -1,29 +0,0 @@
1
- import unittest
2
- from unittest.mock import patch, MagicMock
3
- import drb.core.protocol as protocol_mod
4
- from drb.core.factory import DrbFactory
5
- from drb.core.node import DrbNode
6
-
7
-
8
- class _FakeProtoFactory(DrbFactory):
9
- def _create(self, node: DrbNode) -> DrbNode:
10
- return node
11
-
12
-
13
- class TestProtocolLoader(unittest.TestCase):
14
- def setUp(self):
15
- # reset the singleton so each test loads its own entry points
16
- protocol_mod.ProtocolLoader._ProtocolLoader__instance = None
17
- protocol_mod.ProtocolLoader._ProtocolLoader__factories = None
18
-
19
- def test_loads_scheme_to_factory(self):
20
- ep = MagicMock()
21
- ep.name = 'testscheme'
22
- ep.value = f'{__name__}:_FakeProtoFactory'
23
- with patch.object(protocol_mod.drb.utils.plugins, 'get_entry_points',
24
- return_value=[ep]):
25
- loader = protocol_mod.ProtocolLoader()
26
- self.assertIn('testscheme', loader.get_schemes())
27
- self.assertIsInstance(loader.get_factory('testscheme'),
28
- _FakeProtoFactory)
29
- self.assertIsNone(loader.get_factory('absent'))
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
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