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.
- {pyregrep-11 → pyregrep-13}/PKG-INFO +82 -3
- {pyregrep-11 → pyregrep-13}/README.md +81 -2
- {pyregrep-11 → pyregrep-13}/pyRegRep.egg-info/PKG-INFO +82 -3
- {pyregrep-11 → pyregrep-13}/pyRegRep.egg-info/SOURCES.txt +3 -1
- {pyregrep-11 → pyregrep-13}/pyRegRep4/NS.py +1 -0
- {pyregrep-11 → pyregrep-13}/pyRegRep4/RIMElement.py +103 -23
- {pyregrep-11 → pyregrep-13}/pyRegRep4/__init__.py +14 -15
- pyregrep-13/pyRegRep4/utils.py +23 -0
- {pyregrep-11 → pyregrep-13}/pyproject.toml +1 -1
- {pyregrep-11 → pyregrep-13}/setup.py +1 -1
- {pyregrep-11 → pyregrep-13}/tests/test_rim_element.py +39 -0
- pyregrep-13/tests/test_utils.py +33 -0
- {pyregrep-11 → pyregrep-13}/LICENSE +0 -0
- {pyregrep-11 → pyregrep-13}/pyRegRep.egg-info/dependency_links.txt +0 -0
- {pyregrep-11 → pyregrep-13}/pyRegRep.egg-info/requires.txt +0 -0
- {pyregrep-11 → pyregrep-13}/pyRegRep.egg-info/top_level.txt +0 -0
- {pyregrep-11 → pyregrep-13}/pyRegRep4/RIMParsing.py +0 -0
- {pyregrep-11 → pyregrep-13}/setup.cfg +0 -0
- {pyregrep-11 → pyregrep-13}/tests/test_get_slot_simple.py +0 -0
- {pyregrep-11 → pyregrep-13}/tests/test_rim_parsing.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyRegRep
|
|
3
|
-
Version:
|
|
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
|
-
**Версія:**
|
|
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
|
-
**Версія:**
|
|
359
|
+
**Версія:** 13 · **Оновлено:** 2026-04-14
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyRegRep
|
|
3
|
-
Version:
|
|
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
|
-
**Версія:**
|
|
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
|
|
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.
|
|
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.
|
|
56
|
+
self.create_element()
|
|
29
57
|
|
|
30
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"
|
|
24
|
+
"RepositoryItemRef",
|
|
25
|
+
"RegistryObject",
|
|
26
|
+
"Classification",
|
|
27
|
+
"QueryResponse",
|
|
28
|
+
"deep_get",
|
|
29
|
+
"Parsing",
|
|
30
|
+
"serialize_any_value_type",
|
|
32
31
|
]
|
|
33
32
|
|
|
34
|
-
__version__ = "
|
|
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
|
+
|
|
@@ -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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|