pyRegRep 11__tar.gz → 13__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyRegRep
3
- Version: 11
3
+ Version: 13
4
4
  Summary: Your package description
5
5
  Author: Andrey Shapovalov
6
6
  Author-email: mt.andrey@gmail.com
@@ -28,8 +28,10 @@ Dynamic: license-file
28
28
  - ✅ Обробка всіх типів значень: Boolean, String, Integer, DateTime, Collection, InternationalString, AnyValue
29
29
  - ✅ Серіалізація даних у зручний Python-формат (`dict`)
30
30
  - ✅ Фабрика слотів `get_slot()` — програмне створення RIM-слотів за типом
31
+ - ✅ Builder-класи для елементів `RegistryObject`, `RepositoryItemRef`, `QueryResponse`, `Classification`
31
32
  - ✅ Серіалізація `AnyValueType` через `xmltodict` (параметр `any_type=True`)
32
33
  - ✅ Підтримка вкладених колекцій та складних структур
34
+ - ✅ Безпечний доступ до вкладених даних через `deep_get()`
33
35
 
34
36
  ## Вимоги
35
37
 
@@ -68,6 +70,12 @@ print(parser.serialize())
68
70
 
69
71
  # Серіалізація з обробкою AnyValueType в dict
70
72
  print(parser.serialize(any_type=True))
73
+
74
+ from pyRegRep4 import deep_get
75
+
76
+ # Безпечний доступ до вкладених ключів
77
+ spec_id = deep_get(parser.serialize(), "doc", "SpecificationIdentifier", default="unknown")
78
+ print(spec_id)
71
79
  ```
72
80
 
73
81
  ### Програмне створення RIM-слотів
@@ -191,6 +199,75 @@ except ValueError as e:
191
199
 
192
200
  ---
193
201
 
202
+ ### Builder-класи XML елементів (`pyRegRep4.RIMElement`)
203
+
204
+ Публічні класи для побудови типових елементів RIM/query:
205
+
206
+ - `RegistryObject`
207
+ - `RepositoryItemRef`
208
+ - `QueryResponse`
209
+ - `Classification`
210
+
211
+ Усі класи мають однаковий базовий контракт:
212
+ - `element` — повертає `etree._Element` (або `ValueError`, якщо елемент ще не створено)
213
+ - `text` — серіалізований XML (`bytes`)
214
+ - `create_element(...)` — будує XML елемент і повертає `self` (chain-style)
215
+
216
+ ```python
217
+ from pyRegRep4.RIMElement import RepositoryItemRef, QueryResponse
218
+
219
+ ref = RepositoryItemRef().create_element(
220
+ "https://example.org/document.xml",
221
+ "Document",
222
+ )
223
+ print(ref.text)
224
+
225
+ response = QueryResponse().create_element(
226
+ "urn:oasis:names:tc:ebxml-regrep:ResponseStatusType:Success",
227
+ "req-1",
228
+ )
229
+ print(response.element.tag)
230
+ ```
231
+
232
+ ---
233
+
234
+ ### Функція `deep_get()` (`pyRegRep4.utils`)
235
+
236
+ ```python
237
+ deep_get(data: dict, *keys, default=None)
238
+ ```
239
+
240
+ Безпечно дістає значення з вкладеного `dict` за послідовністю ключів.
241
+
242
+ | Параметр | Тип | Опис |
243
+ |----------|-----|------|
244
+ | `data` | `dict` | Джерело даних |
245
+ | `*keys` | `Any` | Шлях ключів для проходу по вкладеному словнику |
246
+ | `default` | `Any` | Значення за замовчуванням, якщо шлях не знайдено |
247
+
248
+ **Поведінка:**
249
+ - якщо на будь-якому кроці значення не є `dict` — повертається `default`
250
+ - якщо кінцеве значення `None` — повертається `default`
251
+ - якщо `keys` не передані — повертається `data`
252
+
253
+ ```python
254
+ from pyRegRep4 import deep_get
255
+
256
+ data = {
257
+ "doc": {
258
+ "Procedure": [{"lang": "en", "value": "GetBirthCertificate"}]
259
+ }
260
+ }
261
+
262
+ value = deep_get(data, "doc", "Procedure", 0, "value", default="n/a")
263
+ print(value) # n/a (бо deep_get працює тільки з dict-ланцюжком)
264
+
265
+ procedure = deep_get(data, "doc", "Procedure", default=[])
266
+ print(procedure) # [{"lang": "en", "value": "GetBirthCertificate"}]
267
+ ```
268
+
269
+ ---
270
+
194
271
  ### Клас `NS` (`pyRegRep4.NS`)
195
272
 
196
273
  Базовий клас для управління RIM namespace. Надає метод `_tname(prefix, localname)` для генерування кваліфікованих імен тегів у нотації Clark.
@@ -206,14 +283,16 @@ except ValueError as e:
206
283
  ```
207
284
  pyRegRep/
208
285
  ├── pyRegRep4/
209
- │ ├── __init__.py # Експорт: get_slot та типи слотів
286
+ │ ├── __init__.py # Експорт: get_slot, deep_get, Parsing, serialize_any_value_type
210
287
  │ ├── RIMElement.py # Класи слотів + фабрика get_slot()
211
288
  │ ├── RIMParsing.py # Парсер Parsing + serialize()
289
+ │ ├── utils.py # deep_get() для вкладених словників
212
290
  │ └── NS.py # Базовий клас для namespace
213
291
  ├── tests/
214
292
  │ ├── conftest.py # sys.path для CI
215
293
  │ ├── test_rim_parsing.py # Тести парсера
216
294
  │ ├── test_rim_element.py # Тести get_slot() та типів слотів
295
+ │ ├── test_utils.py # Тести deep_get()
217
296
  │ ├── EDM_Ferst_Request.xml
218
297
  │ ├── EDM_Ferst_Response.xml
219
298
  │ ├── EDM_Second_Request.xml
@@ -291,4 +370,4 @@ MIT License — див. файл [LICENSE](LICENSE)
291
370
 
292
371
  ---
293
372
 
294
- **Версія:** 11 · **Оновлено:** 2026-03-11
373
+ **Версія:** 13 · **Оновлено:** 2026-04-14
@@ -14,8 +14,10 @@
14
14
  - ✅ Обробка всіх типів значень: Boolean, String, Integer, DateTime, Collection, InternationalString, AnyValue
15
15
  - ✅ Серіалізація даних у зручний Python-формат (`dict`)
16
16
  - ✅ Фабрика слотів `get_slot()` — програмне створення RIM-слотів за типом
17
+ - ✅ Builder-класи для елементів `RegistryObject`, `RepositoryItemRef`, `QueryResponse`, `Classification`
17
18
  - ✅ Серіалізація `AnyValueType` через `xmltodict` (параметр `any_type=True`)
18
19
  - ✅ Підтримка вкладених колекцій та складних структур
20
+ - ✅ Безпечний доступ до вкладених даних через `deep_get()`
19
21
 
20
22
  ## Вимоги
21
23
 
@@ -54,6 +56,12 @@ print(parser.serialize())
54
56
 
55
57
  # Серіалізація з обробкою AnyValueType в dict
56
58
  print(parser.serialize(any_type=True))
59
+
60
+ from pyRegRep4 import deep_get
61
+
62
+ # Безпечний доступ до вкладених ключів
63
+ spec_id = deep_get(parser.serialize(), "doc", "SpecificationIdentifier", default="unknown")
64
+ print(spec_id)
57
65
  ```
58
66
 
59
67
  ### Програмне створення RIM-слотів
@@ -177,6 +185,75 @@ except ValueError as e:
177
185
 
178
186
  ---
179
187
 
188
+ ### Builder-класи XML елементів (`pyRegRep4.RIMElement`)
189
+
190
+ Публічні класи для побудови типових елементів RIM/query:
191
+
192
+ - `RegistryObject`
193
+ - `RepositoryItemRef`
194
+ - `QueryResponse`
195
+ - `Classification`
196
+
197
+ Усі класи мають однаковий базовий контракт:
198
+ - `element` — повертає `etree._Element` (або `ValueError`, якщо елемент ще не створено)
199
+ - `text` — серіалізований XML (`bytes`)
200
+ - `create_element(...)` — будує XML елемент і повертає `self` (chain-style)
201
+
202
+ ```python
203
+ from pyRegRep4.RIMElement import RepositoryItemRef, QueryResponse
204
+
205
+ ref = RepositoryItemRef().create_element(
206
+ "https://example.org/document.xml",
207
+ "Document",
208
+ )
209
+ print(ref.text)
210
+
211
+ response = QueryResponse().create_element(
212
+ "urn:oasis:names:tc:ebxml-regrep:ResponseStatusType:Success",
213
+ "req-1",
214
+ )
215
+ print(response.element.tag)
216
+ ```
217
+
218
+ ---
219
+
220
+ ### Функція `deep_get()` (`pyRegRep4.utils`)
221
+
222
+ ```python
223
+ deep_get(data: dict, *keys, default=None)
224
+ ```
225
+
226
+ Безпечно дістає значення з вкладеного `dict` за послідовністю ключів.
227
+
228
+ | Параметр | Тип | Опис |
229
+ |----------|-----|------|
230
+ | `data` | `dict` | Джерело даних |
231
+ | `*keys` | `Any` | Шлях ключів для проходу по вкладеному словнику |
232
+ | `default` | `Any` | Значення за замовчуванням, якщо шлях не знайдено |
233
+
234
+ **Поведінка:**
235
+ - якщо на будь-якому кроці значення не є `dict` — повертається `default`
236
+ - якщо кінцеве значення `None` — повертається `default`
237
+ - якщо `keys` не передані — повертається `data`
238
+
239
+ ```python
240
+ from pyRegRep4 import deep_get
241
+
242
+ data = {
243
+ "doc": {
244
+ "Procedure": [{"lang": "en", "value": "GetBirthCertificate"}]
245
+ }
246
+ }
247
+
248
+ value = deep_get(data, "doc", "Procedure", 0, "value", default="n/a")
249
+ print(value) # n/a (бо deep_get працює тільки з dict-ланцюжком)
250
+
251
+ procedure = deep_get(data, "doc", "Procedure", default=[])
252
+ print(procedure) # [{"lang": "en", "value": "GetBirthCertificate"}]
253
+ ```
254
+
255
+ ---
256
+
180
257
  ### Клас `NS` (`pyRegRep4.NS`)
181
258
 
182
259
  Базовий клас для управління RIM namespace. Надає метод `_tname(prefix, localname)` для генерування кваліфікованих імен тегів у нотації Clark.
@@ -192,14 +269,16 @@ except ValueError as e:
192
269
  ```
193
270
  pyRegRep/
194
271
  ├── pyRegRep4/
195
- │ ├── __init__.py # Експорт: get_slot та типи слотів
272
+ │ ├── __init__.py # Експорт: get_slot, deep_get, Parsing, serialize_any_value_type
196
273
  │ ├── RIMElement.py # Класи слотів + фабрика get_slot()
197
274
  │ ├── RIMParsing.py # Парсер Parsing + serialize()
275
+ │ ├── utils.py # deep_get() для вкладених словників
198
276
  │ └── NS.py # Базовий клас для namespace
199
277
  ├── tests/
200
278
  │ ├── conftest.py # sys.path для CI
201
279
  │ ├── test_rim_parsing.py # Тести парсера
202
280
  │ ├── test_rim_element.py # Тести get_slot() та типів слотів
281
+ │ ├── test_utils.py # Тести deep_get()
203
282
  │ ├── EDM_Ferst_Request.xml
204
283
  │ ├── EDM_Ferst_Response.xml
205
284
  │ ├── EDM_Second_Request.xml
@@ -277,4 +356,4 @@ MIT License — див. файл [LICENSE](LICENSE)
277
356
 
278
357
  ---
279
358
 
280
- **Версія:** 11 · **Оновлено:** 2026-03-11
359
+ **Версія:** 13 · **Оновлено:** 2026-04-14
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyRegRep
3
- Version: 11
3
+ Version: 13
4
4
  Summary: Your package description
5
5
  Author: Andrey Shapovalov
6
6
  Author-email: mt.andrey@gmail.com
@@ -28,8 +28,10 @@ Dynamic: license-file
28
28
  - ✅ Обробка всіх типів значень: Boolean, String, Integer, DateTime, Collection, InternationalString, AnyValue
29
29
  - ✅ Серіалізація даних у зручний Python-формат (`dict`)
30
30
  - ✅ Фабрика слотів `get_slot()` — програмне створення RIM-слотів за типом
31
+ - ✅ Builder-класи для елементів `RegistryObject`, `RepositoryItemRef`, `QueryResponse`, `Classification`
31
32
  - ✅ Серіалізація `AnyValueType` через `xmltodict` (параметр `any_type=True`)
32
33
  - ✅ Підтримка вкладених колекцій та складних структур
34
+ - ✅ Безпечний доступ до вкладених даних через `deep_get()`
33
35
 
34
36
  ## Вимоги
35
37
 
@@ -68,6 +70,12 @@ print(parser.serialize())
68
70
 
69
71
  # Серіалізація з обробкою AnyValueType в dict
70
72
  print(parser.serialize(any_type=True))
73
+
74
+ from pyRegRep4 import deep_get
75
+
76
+ # Безпечний доступ до вкладених ключів
77
+ spec_id = deep_get(parser.serialize(), "doc", "SpecificationIdentifier", default="unknown")
78
+ print(spec_id)
71
79
  ```
72
80
 
73
81
  ### Програмне створення RIM-слотів
@@ -191,6 +199,75 @@ except ValueError as e:
191
199
 
192
200
  ---
193
201
 
202
+ ### Builder-класи XML елементів (`pyRegRep4.RIMElement`)
203
+
204
+ Публічні класи для побудови типових елементів RIM/query:
205
+
206
+ - `RegistryObject`
207
+ - `RepositoryItemRef`
208
+ - `QueryResponse`
209
+ - `Classification`
210
+
211
+ Усі класи мають однаковий базовий контракт:
212
+ - `element` — повертає `etree._Element` (або `ValueError`, якщо елемент ще не створено)
213
+ - `text` — серіалізований XML (`bytes`)
214
+ - `create_element(...)` — будує XML елемент і повертає `self` (chain-style)
215
+
216
+ ```python
217
+ from pyRegRep4.RIMElement import RepositoryItemRef, QueryResponse
218
+
219
+ ref = RepositoryItemRef().create_element(
220
+ "https://example.org/document.xml",
221
+ "Document",
222
+ )
223
+ print(ref.text)
224
+
225
+ response = QueryResponse().create_element(
226
+ "urn:oasis:names:tc:ebxml-regrep:ResponseStatusType:Success",
227
+ "req-1",
228
+ )
229
+ print(response.element.tag)
230
+ ```
231
+
232
+ ---
233
+
234
+ ### Функція `deep_get()` (`pyRegRep4.utils`)
235
+
236
+ ```python
237
+ deep_get(data: dict, *keys, default=None)
238
+ ```
239
+
240
+ Безпечно дістає значення з вкладеного `dict` за послідовністю ключів.
241
+
242
+ | Параметр | Тип | Опис |
243
+ |----------|-----|------|
244
+ | `data` | `dict` | Джерело даних |
245
+ | `*keys` | `Any` | Шлях ключів для проходу по вкладеному словнику |
246
+ | `default` | `Any` | Значення за замовчуванням, якщо шлях не знайдено |
247
+
248
+ **Поведінка:**
249
+ - якщо на будь-якому кроці значення не є `dict` — повертається `default`
250
+ - якщо кінцеве значення `None` — повертається `default`
251
+ - якщо `keys` не передані — повертається `data`
252
+
253
+ ```python
254
+ from pyRegRep4 import deep_get
255
+
256
+ data = {
257
+ "doc": {
258
+ "Procedure": [{"lang": "en", "value": "GetBirthCertificate"}]
259
+ }
260
+ }
261
+
262
+ value = deep_get(data, "doc", "Procedure", 0, "value", default="n/a")
263
+ print(value) # n/a (бо deep_get працює тільки з dict-ланцюжком)
264
+
265
+ procedure = deep_get(data, "doc", "Procedure", default=[])
266
+ print(procedure) # [{"lang": "en", "value": "GetBirthCertificate"}]
267
+ ```
268
+
269
+ ---
270
+
194
271
  ### Клас `NS` (`pyRegRep4.NS`)
195
272
 
196
273
  Базовий клас для управління RIM namespace. Надає метод `_tname(prefix, localname)` для генерування кваліфікованих імен тегів у нотації Clark.
@@ -206,14 +283,16 @@ except ValueError as e:
206
283
  ```
207
284
  pyRegRep/
208
285
  ├── pyRegRep4/
209
- │ ├── __init__.py # Експорт: get_slot та типи слотів
286
+ │ ├── __init__.py # Експорт: get_slot, deep_get, Parsing, serialize_any_value_type
210
287
  │ ├── RIMElement.py # Класи слотів + фабрика get_slot()
211
288
  │ ├── RIMParsing.py # Парсер Parsing + serialize()
289
+ │ ├── utils.py # deep_get() для вкладених словників
212
290
  │ └── NS.py # Базовий клас для namespace
213
291
  ├── tests/
214
292
  │ ├── conftest.py # sys.path для CI
215
293
  │ ├── test_rim_parsing.py # Тести парсера
216
294
  │ ├── test_rim_element.py # Тести get_slot() та типів слотів
295
+ │ ├── test_utils.py # Тести deep_get()
217
296
  │ ├── EDM_Ferst_Request.xml
218
297
  │ ├── EDM_Ferst_Response.xml
219
298
  │ ├── EDM_Second_Request.xml
@@ -291,4 +370,4 @@ MIT License — див. файл [LICENSE](LICENSE)
291
370
 
292
371
  ---
293
372
 
294
- **Версія:** 11 · **Оновлено:** 2026-03-11
373
+ **Версія:** 13 · **Оновлено:** 2026-04-14
@@ -11,6 +11,8 @@ pyRegRep4/NS.py
11
11
  pyRegRep4/RIMElement.py
12
12
  pyRegRep4/RIMParsing.py
13
13
  pyRegRep4/__init__.py
14
+ pyRegRep4/utils.py
14
15
  tests/test_get_slot_simple.py
15
16
  tests/test_rim_element.py
16
- tests/test_rim_parsing.py
17
+ tests/test_rim_parsing.py
18
+ tests/test_utils.py
@@ -13,6 +13,7 @@ class NS:
13
13
  "rs": "urn:oasis:names:tc:ebxml-regrep:xsd:rs:4.0",
14
14
  "xsi": "http://www.w3.org/2001/XMLSchema-instance",
15
15
  "xsd": "http://www.w3.org/2001/XMLSchema",
16
+ "xlink": "http://www.w3.org/1999/xlink",
16
17
  "xml": "http://www.w3.org/XML/1998/namespace",
17
18
  }
18
19
 
@@ -1,6 +1,7 @@
1
1
  import datetime
2
2
  import logging
3
- from typing import Any
3
+ from abc import ABC, abstractmethod
4
+ from typing import Any, Callable
4
5
 
5
6
  from lxml import etree
6
7
 
@@ -8,14 +9,41 @@ from pyRegRep4.NS import NS
8
9
 
9
10
  _logger = logging.getLogger(__name__)
10
11
 
12
+ __all__ = [
13
+ "get_slot",
14
+ "RegistryObject",
15
+ "RepositoryItemRef",
16
+ "QueryResponse",
17
+ "Classification"
18
+ ]
19
+
20
+
21
+ class _ElementContainer(NS):
22
+ """Internal base for simple classes that wrap a single XML element."""
23
+
24
+ def __init__(self) -> None:
25
+ super().__init__()
26
+ self._element: etree._Element | None = None
27
+
28
+ @property
29
+ def element(self) -> etree._Element:
30
+ if self._element is None:
31
+ raise ValueError("XML element is not initialized")
32
+ return self._element
33
+
34
+ @property
35
+ def text(self) -> bytes:
36
+ return etree.tostring(self.element)
37
+
38
+
39
+ class Xml(ABC, _ElementContainer):
40
+ """Base class for slot-like XML objects that carry value and name."""
11
41
 
12
- class Xml(NS):
13
42
  def __init__(self, value: Any):
14
43
  super().__init__()
15
44
  self._name: str = ""
16
45
  self.value: Any = value
17
- self._element: etree._Element | None = None
18
- self._create_element()
46
+ self.create_element()
19
47
 
20
48
  @property
21
49
  def name(self) -> str:
@@ -25,24 +53,15 @@ class Xml(NS):
25
53
  def name(self, value: str) -> None:
26
54
  self._name = value
27
55
  if self._element is not None:
28
- self._create_element()
56
+ self.create_element()
29
57
 
30
- def _create_element(self):
58
+ @abstractmethod
59
+ def create_element(self):
31
60
  ...
32
61
 
33
- @property
34
- def element(self) -> etree._Element:
35
- if self._element is None:
36
- raise ValueError("XML element is not initialized")
37
- return self._element
38
-
39
- @property
40
- def text(self) -> bytes:
41
- return etree.tostring(self.element)
42
-
43
62
 
44
63
  class _BooleanValueType(Xml):
45
- def _create_element(self):
64
+ def create_element(self):
46
65
  self._element = etree.Element(
47
66
  self._tname("rim", "Slot"), nsmap=self._ns,
48
67
  attrib={"name": self.name}
@@ -56,7 +75,7 @@ class _BooleanValueType(Xml):
56
75
 
57
76
 
58
77
  class _StringValueType(Xml):
59
- def _create_element(self):
78
+ def create_element(self):
60
79
  self._element = etree.Element(
61
80
  self._tname("rim", "Slot"), nsmap=self._ns,
62
81
  attrib={"name": self.name}
@@ -70,7 +89,7 @@ class _StringValueType(Xml):
70
89
 
71
90
 
72
91
  class _TimeStamp(Xml):
73
- def _create_element(self):
92
+ def create_element(self):
74
93
  self._element = etree.Element(
75
94
  self._tname("rim", "Slot"), nsmap=self._ns,
76
95
  attrib={"name": self.name}
@@ -87,7 +106,7 @@ class _TimeStamp(Xml):
87
106
 
88
107
 
89
108
  class _CollectionValueType(Xml):
90
- def _create_element(self):
109
+ def create_element(self):
91
110
  self._element = etree.Element(
92
111
  self._tname("rim", "Slot"), nsmap=self._ns,
93
112
  attrib={"name": self.name}
@@ -110,7 +129,7 @@ class _CollectionValueType(Xml):
110
129
 
111
130
 
112
131
  class _AnyValueType(Xml):
113
- def _create_element(self):
132
+ def create_element(self):
114
133
  self._element = etree.Element(
115
134
  self._tname("rim", "Slot"), nsmap=self._ns,
116
135
  attrib={"name": self.name}
@@ -124,7 +143,7 @@ class _AnyValueType(Xml):
124
143
 
125
144
 
126
145
  class _InternationalStringValueType(Xml):
127
- def _create_element(self):
146
+ def create_element(self):
128
147
  self._element = etree.Element(
129
148
  self._tname("rim", "Slot"), nsmap=self._ns,
130
149
  attrib={"name": self.name}
@@ -226,7 +245,7 @@ def get_slot(name: str, slot_type: str, value: Any) -> Xml:
226
245
  ... except ValueError as e:
227
246
  ... print(f"Error: {e}")
228
247
  """
229
- slot_type_map = {
248
+ slot_type_map: dict[str, Callable[[Any], Xml]] = {
230
249
  "StringValueType": _StringValueType,
231
250
  "BooleanValueType": _BooleanValueType,
232
251
  "DateTimeValueType": _TimeStamp,
@@ -242,3 +261,64 @@ def get_slot(name: str, slot_type: str, value: Any) -> Xml:
242
261
  slot = slot_class(value)
243
262
  slot.name = name
244
263
  return slot
264
+
265
+
266
+ class RegistryObject(_ElementContainer):
267
+ """Builder for rim:RegistryObject elements."""
268
+
269
+ def create_element(self, type: str, id: str):
270
+ self._element = etree.Element(
271
+ self._tname("rim", "RegistryObject"),
272
+ nsmap=self._ns,
273
+ attrib={
274
+ self._tname("xsi", "type"): type,
275
+ "id": id
276
+ }
277
+ )
278
+ return self
279
+
280
+
281
+ class RepositoryItemRef(_ElementContainer):
282
+ """Builder for rim:RepositoryItemRef elements."""
283
+
284
+ def create_element(self, href: str, title: str | None = None):
285
+ attr = {self._tname("xlink", "href"): href}
286
+ if title is not None:
287
+ attr["title"] = title
288
+
289
+ self._element = etree.Element(
290
+ self._tname("rim", "RepositoryItemRef"),
291
+ nsmap=self._ns,
292
+ attrib=attr
293
+ )
294
+ return self
295
+
296
+
297
+ class QueryResponse(_ElementContainer):
298
+ """Builder for query:QueryResponse elements."""
299
+
300
+ def create_element(self, status: str, request_id: str):
301
+ self._element = etree.Element(
302
+ self._tname("query", "QueryResponse"), nsmap=self._ns,
303
+ attrib={
304
+ 'status': status,
305
+ 'requestId': request_id,
306
+ }
307
+ )
308
+ return self
309
+
310
+
311
+ class Classification(_ElementContainer):
312
+ """Builder for rim:Classification elements."""
313
+
314
+ def create_element(self, id: str, classification_scheme: str, classification_node: str):
315
+ self._element = etree.Element(
316
+ self._tname("rim", "Classification"),
317
+ nsmap=self._ns,
318
+ attrib={
319
+ "id": id,
320
+ "classificationScheme": classification_scheme,
321
+ "classificationNode": classification_node
322
+ }
323
+ )
324
+ return self
@@ -10,27 +10,26 @@ Main Components:
10
10
  """
11
11
 
12
12
  from pyRegRep4.RIMElement import (
13
+ Classification,
14
+ QueryResponse,
15
+ RegistryObject,
16
+ RepositoryItemRef,
13
17
  get_slot,
14
- Xml,
15
- _StringValueType,
16
- _BooleanValueType,
17
- _TimeStamp,
18
- _CollectionValueType,
19
- _AnyValueType,
20
- _InternationalStringValueType,
21
18
  )
19
+ from pyRegRep4.utils import deep_get
20
+ from pyRegRep4.RIMParsing import Parsing, serialize_any_value_type
22
21
 
23
22
  __all__ = [
24
23
  "get_slot",
25
- "Xml",
26
- "_StringValueType",
27
- "_BooleanValueType",
28
- "_TimeStamp",
29
- "_CollectionValueType",
30
- "_AnyValueType",
31
- "_InternationalStringValueType",
24
+ "RepositoryItemRef",
25
+ "RegistryObject",
26
+ "Classification",
27
+ "QueryResponse",
28
+ "deep_get",
29
+ "Parsing",
30
+ "serialize_any_value_type",
32
31
  ]
33
32
 
34
- __version__ = "11"
33
+ __version__ = "13"
35
34
  __author__ = "Andrey Shapovalov"
36
35
 
@@ -0,0 +1,23 @@
1
+ """Utility helpers for working with nested dictionaries."""
2
+
3
+ from typing import Any
4
+
5
+
6
+ def deep_get(data: dict[Any, Any], *keys: str, default: Any = None) -> Any:
7
+ """Safely read nested keys from a dictionary.
8
+
9
+ Args:
10
+ data: Source dictionary.
11
+ *keys: Sequence of keys to traverse.
12
+ default: Value returned when the key path is missing or the intermediate value is not a dict.
13
+
14
+ Returns:
15
+ Value by key path or "default" when the path cannot be resolved.
16
+ """
17
+ current: Any = data
18
+ for key in keys:
19
+ if not isinstance(current, dict):
20
+ return default
21
+ current = current.get(key)
22
+ return current if current is not None else default
23
+
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pyRegRep"
7
- version = "11"
7
+ version = "13"
8
8
  description = "Your package description"
9
9
  authors = [{ name="Andrey Shapovalov" }]
10
10
  readme = "README.md"
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name="pyRegRep",
5
- version="11",
5
+ version="13",
6
6
  description="Бібліотека для роботи з реєстрами та репозитаріями в Україні",
7
7
  packages=find_packages(),
8
8
  license="MIT",
@@ -18,6 +18,10 @@ from pyRegRep4.RIMElement import (
18
18
  _CollectionValueType,
19
19
  _AnyValueType,
20
20
  _InternationalStringValueType,
21
+ RegistryObject,
22
+ RepositoryItemRef,
23
+ QueryResponse,
24
+ Classification,
21
25
  )
22
26
 
23
27
 
@@ -468,3 +472,38 @@ class TestSlotElementGeneration:
468
472
  assert slot.element.attrib.get("name") == test_name
469
473
 
470
474
 
475
+ class TestSimpleElementContainers:
476
+ """Regression tests for classes sharing common element/text behavior."""
477
+
478
+ @pytest.mark.parametrize(
479
+ "obj",
480
+ [RegistryObject(), RepositoryItemRef(), QueryResponse(), Classification()],
481
+ )
482
+ def test_element_raises_before_create(self, obj):
483
+ with pytest.raises(ValueError, match="XML element is not initialized"):
484
+ _ = obj.element
485
+
486
+ def test_registry_object_create_and_serialize(self):
487
+ obj = RegistryObject()
488
+ assert obj.create_element("rim:ExtrinsicObject", "urn:uuid:test") is obj
489
+ assert b"RegistryObject" in obj.text
490
+
491
+ def test_repository_item_ref_create_and_serialize(self):
492
+ obj = RepositoryItemRef()
493
+ assert obj.create_element("https://example.org/doc.xml", "Doc") is obj
494
+ assert b"RepositoryItemRef" in obj.text
495
+ assert b"xlink:href" in obj.text
496
+
497
+ def test_query_response_create_and_serialize(self):
498
+ obj = QueryResponse()
499
+ assert obj.create_element(
500
+ "urn:oasis:names:tc:ebxml-regrep:ResponseStatusType:Success", "req-1"
501
+ ) is obj
502
+ assert b"QueryResponse" in obj.text
503
+
504
+ def test_classification_create_and_serialize(self):
505
+ obj = Classification()
506
+ assert obj.create_element("urn:uuid:cls", "urn:scheme", "Annex") is obj
507
+ assert b"Classification" in obj.text
508
+
509
+
@@ -0,0 +1,33 @@
1
+ """Unit tests for helper utilities."""
2
+
3
+ from pyRegRep4 import deep_get
4
+
5
+
6
+ class TestDeepGet:
7
+ """Tests for deep_get utility function."""
8
+
9
+ def test_returns_nested_value(self):
10
+ data = {"doc": {"meta": {"id": "ABC-123"}}}
11
+
12
+ assert deep_get(data, "doc", "meta", "id") == "ABC-123"
13
+
14
+ def test_returns_default_when_key_missing(self):
15
+ data = {"doc": {"meta": {"id": "ABC-123"}}}
16
+
17
+ assert deep_get(data, "doc", "meta", "missing", default="n/a") == "n/a"
18
+
19
+ def test_returns_default_when_intermediate_is_not_dict(self):
20
+ data = {"doc": {"meta": "plain-text"}}
21
+
22
+ assert deep_get(data, "doc", "meta", "id", default=None) is None
23
+
24
+ def test_returns_default_when_value_is_none(self):
25
+ data = {"doc": {"meta": {"id": None}}}
26
+
27
+ assert deep_get(data, "doc", "meta", "id", default="fallback") == "fallback"
28
+
29
+ def test_returns_root_data_when_keys_not_provided(self):
30
+ data = {"doc": {"meta": {"id": "ABC-123"}}}
31
+
32
+ assert deep_get(data) == data
33
+
File without changes
File without changes
File without changes
File without changes