stbkit-core 0.1.0a9.dev1__py3-none-any.whl

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 (60) hide show
  1. stbkit/core/__init__.py +12 -0
  2. stbkit/core/_internal/constants.py +42 -0
  3. stbkit/core/_internal/extension_utils.py +238 -0
  4. stbkit/core/_internal/io_text.py +48 -0
  5. stbkit/core/_internal/name_converter.py +170 -0
  6. stbkit/core/_internal/optional_dependencies/xmlschema_validator.py +163 -0
  7. stbkit/core/_internal/stb_logger.py +86 -0
  8. stbkit/core/_internal/transform/__init__.py +5 -0
  9. stbkit/core/_internal/transform/guid_tools.py +73 -0
  10. stbkit/core/_internal/transform/select.py +114 -0
  11. stbkit/core/_internal/xml_value_converter.py +16 -0
  12. stbkit/core/data_model/__init__.py +5 -0
  13. stbkit/core/data_model/_internal/filter_option.py +25 -0
  14. stbkit/core/data_model/_internal/stb_types.py +98 -0
  15. stbkit/core/data_model/_internal/type_validation.py +755 -0
  16. stbkit/core/data_model/common.py +967 -0
  17. stbkit/core/data_model/stb_v2_0_0.py +13633 -0
  18. stbkit/core/data_model/stb_v2_0_0.pyi +15327 -0
  19. stbkit/core/data_model/stb_v2_0_1.py +13702 -0
  20. stbkit/core/data_model/stb_v2_0_1.pyi +15450 -0
  21. stbkit/core/data_model/stb_v2_0_2.py +21677 -0
  22. stbkit/core/data_model/stb_v2_0_2.pyi +23319 -0
  23. stbkit/core/data_model/stb_v2_1_0.py +27541 -0
  24. stbkit/core/data_model/stb_v2_1_0.pyi +31553 -0
  25. stbkit/core/data_model/stb_v2_1_1.py +27577 -0
  26. stbkit/core/data_model/stb_v2_1_1.pyi +31590 -0
  27. stbkit/core/py.typed +0 -0
  28. stbkit/core/repository/__init__.py +67 -0
  29. stbkit/core/repository/_internal/ref_def_v2_0_2.py +191 -0
  30. stbkit/core/repository/_internal/ref_def_v2_1_0.py +216 -0
  31. stbkit/core/repository/_internal/ref_def_v2_1_1.py +216 -0
  32. stbkit/core/repository/repo_common.py +363 -0
  33. stbkit/core/repository/repo_v2_0_2.py +48 -0
  34. stbkit/core/repository/repo_v2_0_2.pyi +257 -0
  35. stbkit/core/repository/repo_v2_1_0.py +48 -0
  36. stbkit/core/repository/repo_v2_1_0.pyi +272 -0
  37. stbkit/core/repository/repo_v2_1_1.py +50 -0
  38. stbkit/core/repository/repo_v2_1_1.pyi +272 -0
  39. stbkit/core/serialization/__init__.py +21 -0
  40. stbkit/core/serialization/_dict_converter.py +1448 -0
  41. stbkit/core/serialization/config.py +114 -0
  42. stbkit/core/serialization/profiles.py +57 -0
  43. stbkit/core/stb_exceptions.py +203 -0
  44. stbkit/core/stb_io/__init__.py +17 -0
  45. stbkit/core/stb_io/_internal/__init__.py +5 -0
  46. stbkit/core/stb_io/_internal/loader.py +63 -0
  47. stbkit/core/stb_io/_internal/serializer.py +157 -0
  48. stbkit/core/stb_io/_internal/stream_reader.py +346 -0
  49. stbkit/core/stb_io/_internal/xml_io.py +482 -0
  50. stbkit/core/stb_reporting.py +1061 -0
  51. stbkit/core/stb_typing.py +61 -0
  52. stbkit/core/validation/__init__.py +9 -0
  53. stbkit/core/validation/_internal/common.py +78 -0
  54. stbkit/core/validation/_internal/schema_validator.py +294 -0
  55. stbkit/core/validation/_internal/validator.py +109 -0
  56. stbkit_core-0.1.0a9.dev1.dist-info/METADATA +121 -0
  57. stbkit_core-0.1.0a9.dev1.dist-info/RECORD +60 -0
  58. stbkit_core-0.1.0a9.dev1.dist-info/WHEEL +5 -0
  59. stbkit_core-0.1.0a9.dev1.dist-info/licenses/LICENSE +373 -0
  60. stbkit_core-0.1.0a9.dev1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,967 @@
1
+ # Copyright 2025-2026 TAISEI CORPORATION
2
+ # SPDX-License-Identifier: MPL-2.0
3
+ # This Source Code Form is subject to the terms of the Mozilla Public
4
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
5
+ # file, You can obtain one at https://mozilla.org/MPL/2.0/.
6
+
7
+ from __future__ import annotations
8
+
9
+ import sys
10
+ import weakref
11
+ from collections.abc import Callable, Iterable, Sequence
12
+ from dataclasses import dataclass
13
+ from enum import Enum
14
+ from types import ModuleType
15
+ from typing import (
16
+ TYPE_CHECKING,
17
+ Any,
18
+ ClassVar,
19
+ Final,
20
+ Protocol,
21
+ SupportsIndex,
22
+ cast,
23
+ get_args,
24
+ get_origin,
25
+ )
26
+
27
+ from .._internal.name_converter import private_field_name, property_name
28
+ from ..stb_exceptions import NoneAccessError, SchemaError
29
+ from ._internal.stb_types import DataType
30
+ from ._internal.type_validation import any_to_typed_value as _any_to_typed_value
31
+ from ._internal.type_validation import make_attribute_setter as _make_attribute_setter
32
+ from ._internal.type_validation import str_to_typed_value as _str_to_typed_value
33
+
34
+ if TYPE_CHECKING:
35
+ from ..stb_reporting import Phase, Reporter
36
+
37
+ _ATTR_NAME_XML_ELEMENT_NAME: Final[str] = "_xml_element_name"
38
+ _ATTR_NAME_FIELDS: Final[str] = "_fields"
39
+
40
+
41
+ class _FieldKind(Enum):
42
+ ELEMENT = "element"
43
+ ATTRIBUTE = "attribute"
44
+ CONTENT = "content"
45
+
46
+
47
+ @dataclass(frozen=True, kw_only=True, slots=True)
48
+ class _FieldInfo:
49
+ data_type: DataType | None = None
50
+ py_type: type[Any] | str = str
51
+ xml_name: str | None = None
52
+ xml_type: str | None = None
53
+ kind: _FieldKind = _FieldKind.ATTRIBUTE
54
+ required: bool = False
55
+ min_occurs: int | None = None
56
+ max_occurs: int | None = None
57
+ choices: tuple[str, ...] | None = None
58
+ _py_type_str: str | None = None
59
+
60
+ @property
61
+ def py_type_str(self) -> str:
62
+ if self._py_type_str is not None:
63
+ return self._py_type_str
64
+ result: str
65
+ if isinstance(self.py_type, str):
66
+ return self.py_type
67
+ # py_typeがlistの場合は型引数を取得してlist[型名]の形式で返す
68
+ origin = get_origin(self.py_type)
69
+ if origin is list:
70
+ args = getattr(self.py_type, "__args__", None)
71
+ if args and len(args) == 1:
72
+ arg_type = args[0]
73
+ # 型引数が型の場合はその__name__を、文字列の場合はそのまま
74
+ if isinstance(arg_type, type):
75
+ result = f"list[{arg_type.__name__}]"
76
+ else:
77
+ result = f"list[{arg_type!s}]"
78
+ else:
79
+ result = "list"
80
+ else:
81
+ result = self.py_type.__name__
82
+ object.__setattr__(self, "_py_type_str", result)
83
+ return result
84
+
85
+
86
+ class _AutoModel(type):
87
+ def __new__(
88
+ cls: type[_AutoModel],
89
+ name: str,
90
+ bases: tuple[type, ...],
91
+ namespace: dict[str, object],
92
+ ) -> type:
93
+ slots: set[str] = set()
94
+ has_weakref = False
95
+ for base in bases:
96
+ if hasattr(base, "__slots__"):
97
+ base_slots = base.__slots__
98
+ if isinstance(base_slots, str):
99
+ slots.add(base_slots)
100
+ elif isinstance(base_slots, (list, tuple)):
101
+ slots.update(base_slots)
102
+ else:
103
+ raise AssertionError(f"{base.__name__}の__slots__が不正です")
104
+ if "__weakref__" in slots:
105
+ has_weakref = True
106
+ own_slots = namespace.get("__slots__", ())
107
+ if isinstance(own_slots, str):
108
+ slots.add(own_slots)
109
+ elif isinstance(own_slots, (list, tuple)):
110
+ slots.update(own_slots)
111
+ else:
112
+ raise TypeError(f"{name}の__slots__が不正です")
113
+ fields: dict[str, _FieldInfo] = cast(
114
+ dict[str, _FieldInfo], namespace.get(_ATTR_NAME_FIELDS, {})
115
+ )
116
+ for key, field_info in fields.items():
117
+ private_name: str = f"_{key}"
118
+ slots.add(private_name)
119
+
120
+ # getter
121
+ def make_getter(
122
+ p_name: str = private_name,
123
+ f_name: str = key,
124
+ ) -> Callable[[StBridgeElement], Any]:
125
+ def getter(self: StBridgeElement) -> Any:
126
+ value: Any = getattr(self, p_name)
127
+ if value is None:
128
+ raise NoneAccessError(element=self, attr_name=f_name)
129
+ return value
130
+
131
+ return getter
132
+
133
+ # setter
134
+ def make_setter(
135
+ p_name: str = private_name,
136
+ f_info: _FieldInfo = field_info,
137
+ key_: str = key,
138
+ ) -> Callable[[StBridgeElement, Any], None]:
139
+
140
+ def setter_child(self: StBridgeElement, value: Any) -> None:
141
+ if isinstance(value, StBridgeElement):
142
+ value._attach_to_parent(self)
143
+ else:
144
+ raise TypeError(f"{key_}にはStBridgeElementを設定してください")
145
+ setattr(self, p_name, value)
146
+
147
+ def setter_children(self: StBridgeElement, value: Any) -> None:
148
+ if not isinstance(value, Sequence) or isinstance(
149
+ value, (str, bytes)
150
+ ):
151
+ raise TypeError(
152
+ f"{key_}にはStBridgeElementのリストを設定してください"
153
+ )
154
+ if isinstance(value, _StBridgeElementList):
155
+ owner: StBridgeElement | None = value._parent
156
+ if owner is not None and owner is not self:
157
+ raise ValueError(
158
+ f"{key_}には他の要素の属性に設定されているリストは入れられません。"
159
+ "list()等でコピーしてから設定してください"
160
+ )
161
+ items: list[Any] = list(value)
162
+ for item in items:
163
+ if not isinstance(item, StBridgeElement):
164
+ raise TypeError(
165
+ f"{key_}はStBridgeElementのリストである必要があります"
166
+ )
167
+ current: Any = getattr(self, p_name, None)
168
+ if isinstance(current, _StBridgeElementList):
169
+ current.clear()
170
+ current.extend(items)
171
+ else:
172
+ setattr(
173
+ self,
174
+ p_name,
175
+ _StBridgeElementList(parent=self, iterable=items),
176
+ )
177
+
178
+ if f_info.kind == _FieldKind.ELEMENT:
179
+ if f_info.max_occurs == 1:
180
+ return setter_child
181
+ else:
182
+ return setter_children
183
+ else:
184
+ if f_info.data_type is None:
185
+ raise RuntimeError(
186
+ f"{name}.{key_}のdata_typeが指定されていません"
187
+ )
188
+ return _make_attribute_setter(
189
+ key=key_,
190
+ data_type=f_info.data_type,
191
+ has_choice=f_info.choices is not None,
192
+ )
193
+
194
+ namespace[key] = property(make_getter(), make_setter())
195
+ if field_info.kind == _FieldKind.ATTRIBUTE or (
196
+ field_info.kind == _FieldKind.ELEMENT and field_info.max_occurs == 1
197
+ ):
198
+
199
+ def make_getter_or_none(
200
+ p_name: str = private_name,
201
+ ) -> Callable[[StBridgeElement], Any | None]:
202
+ def getter(self: StBridgeElement) -> Any | None:
203
+ return getattr(self, p_name)
204
+
205
+ return getter
206
+
207
+ namespace[f"{key}_or_none"] = property(
208
+ make_getter_or_none(), make_setter()
209
+ )
210
+
211
+ def __init__(self: Any, **kwargs: dict[str, Any]) -> None:
212
+ StBridgeElement.__init__(self)
213
+ for field_name, field_info in fields.items():
214
+ value: Any = kwargs.get(field_name, None)
215
+ if field_info.kind == _FieldKind.ELEMENT and field_info.max_occurs != 1:
216
+ if value is None:
217
+ value = _StBridgeElementList(parent=self)
218
+ elif isinstance(value, (list, tuple)):
219
+ value = _StBridgeElementList(parent=self, iterable=value)
220
+ if isinstance(value, StBridgeElement):
221
+ value._attach_to_parent(self)
222
+ setattr(self, f"_{field_name}", value)
223
+
224
+ if "__init__" not in namespace:
225
+ namespace["__init__"] = __init__
226
+ if has_weakref and "__weakref__" in slots:
227
+ slots.remove("__weakref__")
228
+ namespace["__slots__"] = tuple(slots)
229
+ return super().__new__(cls, name, bases, namespace)
230
+
231
+
232
+ @dataclass(slots=True, kw_only=True)
233
+ class _InvalidValue:
234
+ kind: _FieldKind
235
+ value: Any
236
+ path: str
237
+ reason: str
238
+
239
+
240
+ class _ExtensionData:
241
+ __slots__ = (
242
+ "__weakref__",
243
+ "_attributes",
244
+ "_children",
245
+ "_invalid_values",
246
+ "_parent_ref",
247
+ )
248
+
249
+ def __init__(self, *, parent: StBridgeElement) -> None:
250
+ self._attributes: dict[str, Any] | None = None
251
+ self._children: _StBridgeElementList[StBridgeElement] | None = None
252
+ self._parent_ref: weakref.ref[StBridgeElement] | None = weakref.ref(parent)
253
+ self._invalid_values: list[_InvalidValue] | None = None
254
+
255
+ def __eq__(self, other: object) -> bool:
256
+ if not isinstance(other, _ExtensionData):
257
+ return False
258
+ if self._attributes != other._attributes:
259
+ return False
260
+ return self._children == other._children
261
+
262
+ def _set_attribute(self, name: str, value: Any) -> None:
263
+ if self._attributes is None:
264
+ self._attributes = {}
265
+ self._attributes[name] = value
266
+
267
+ def _get_attribute(self, name: str) -> Any:
268
+ if self._attributes and name in self._attributes:
269
+ return self._attributes[name]
270
+ else:
271
+ raise AttributeError(f"拡張属性{name}がありません")
272
+
273
+ def _set_child(self, child: StBridgeElement) -> None:
274
+ if self._children is None:
275
+ self._children = _StBridgeElementList(
276
+ parent=self._parent_ref() if self._parent_ref else None
277
+ )
278
+ self._children.append(child)
279
+ child._attach_to_parent(self._parent_ref() if self._parent_ref else None)
280
+
281
+ def _get_child(self, index: int) -> StBridgeElement:
282
+ if self._children and 0 <= index < len(self._children):
283
+ return self._children[index]
284
+ else:
285
+ raise IndexError(f"拡張子要素のインデックス{index}が範囲外です")
286
+
287
+ def _get_children(self) -> list[StBridgeElement]:
288
+ if self._children is None:
289
+ self._children = _StBridgeElementList(
290
+ parent=self._parent_ref() if self._parent_ref else None
291
+ )
292
+ return self._children
293
+
294
+ def _set_invalid_value(self, invalid_value: _InvalidValue) -> None:
295
+ if self._invalid_values is None:
296
+ self._invalid_values = []
297
+ self._invalid_values.append(invalid_value)
298
+
299
+
300
+ class StBridgeElement(metaclass=_AutoModel):
301
+ """ST-Bridgeのすべての要素の基底クラス
302
+
303
+ 属性は`xxx`と`xxx_or_none`の2つのプロパティでアクセスできます。
304
+ `xxx`はNoneのときNoneAccessErrorを投げます。
305
+ Noneの時はNoneを返してほしい場合は`xxx_or_none`を使用します。
306
+ """
307
+
308
+ __slots__ = ("__weakref__", "_extension", "_parent_ref")
309
+ _fields: ClassVar[dict[str, _FieldInfo]] = {}
310
+
311
+ def __init__(self) -> None:
312
+ self._parent_ref: weakref.ref[StBridgeElement] | None = None
313
+ self._extension: _ExtensionData | None = None
314
+
315
+ @property
316
+ def ensure(self) -> _EnsureAccessorProtocol:
317
+ """子要素がNoneの場合、生成してからアクセスできるアクセサ。
318
+
319
+ element.ensure.xxx()の形で呼び出すと、子要素xxxがNoneの場合に
320
+ インスタンスを新規作成し設定してから返します。
321
+ Noneでない場合はそのまま返します。
322
+
323
+ Returns:
324
+ _EnsureAccessorProtocol: 子要素生成アクセサ。属性名と戻り値の型は、
325
+ バージョンごとの型スタブが与えます。
326
+
327
+ Examples:
328
+ >>> members = stb.ensure.stb_model().ensure.stb_members()
329
+ """
330
+ return _EnsureAccessorBase(self)
331
+
332
+ def __eq__(self, other: object) -> bool:
333
+ if not isinstance(other, StBridgeElement):
334
+ return False
335
+ for key in self._fields:
336
+ if getattr(self, f"_{key}") != getattr(other, f"_{key}"):
337
+ return False
338
+ return self._extension == other._extension
339
+
340
+ def __repr__(self) -> str:
341
+ field_strs = []
342
+ for key in self._fields:
343
+ value = getattr(self, f"_{key}")
344
+ if value:
345
+ field_strs.append(f"{key}={value}")
346
+ field_str = ", ".join(field_strs)
347
+ return f"{self.__class__.__name__}({field_str})"
348
+
349
+ def _set_attribute_check(self, key: str, value: Any) -> tuple[str, DataType] | None:
350
+ fi: _FieldInfo | None = self._fields.get(key)
351
+ if fi is None:
352
+ self._ensure_extension._set_attribute(key, value)
353
+ return None
354
+ if fi.kind == _FieldKind.ELEMENT:
355
+ raise TypeError(
356
+ f"{self._name_for_log(key)}は子要素なので属性を設定できません"
357
+ )
358
+ if fi.data_type is None:
359
+ raise RuntimeError(
360
+ f"{self.__class__.__name__}.{key}のdata_typeが指定されていません"
361
+ )
362
+ p_name: str = private_field_name(key)
363
+ if value is None:
364
+ setattr(self, p_name, None)
365
+ return None
366
+ if value == "":
367
+ self._ensure_extension._set_invalid_value(
368
+ _InvalidValue(
369
+ kind=_FieldKind.ATTRIBUTE,
370
+ value=value,
371
+ path=self._path_xml(attr_name=key),
372
+ reason="属性に空の値が指定されています",
373
+ )
374
+ )
375
+ setattr(self, p_name, None)
376
+ return None
377
+ return key, fi.data_type
378
+
379
+ def _set_attribute_by_any(self, key: str, value: Any) -> None:
380
+ fi: _FieldInfo | None = self._fields.get(key)
381
+ if fi is None:
382
+ self._ensure_extension._set_attribute(key, value)
383
+ return
384
+ if fi.kind == _FieldKind.ELEMENT:
385
+ raise TypeError(
386
+ f"{self._name_for_log(key)}は子要素なので属性を設定できません"
387
+ )
388
+ if fi.data_type is None:
389
+ raise RuntimeError(
390
+ f"{self.__class__.__name__}.{key}のdata_typeが指定されていません"
391
+ )
392
+ p_name: str = private_field_name(key)
393
+ if value is None:
394
+ setattr(self, p_name, None)
395
+ return
396
+ if value == "":
397
+ self._ensure_extension._set_invalid_value(
398
+ _InvalidValue(
399
+ kind=_FieldKind.ATTRIBUTE,
400
+ value=value,
401
+ path=self._path_xml(attr_name=key),
402
+ reason="属性に空の値が指定されています",
403
+ )
404
+ )
405
+ setattr(self, p_name, None)
406
+ return
407
+ try:
408
+ typed_value: Any = _any_to_typed_value(
409
+ value, fi.data_type, element=self, attr_name=key
410
+ )
411
+ setattr(self, p_name, typed_value)
412
+ except SchemaError as e:
413
+ self._ensure_extension._set_invalid_value(
414
+ _InvalidValue(
415
+ kind=_FieldKind.ATTRIBUTE,
416
+ value=value,
417
+ path=self._path_xml(attr_name=key),
418
+ reason=str(e),
419
+ )
420
+ )
421
+ setattr(self, p_name, None)
422
+
423
+ def _set_attribute_by_str(
424
+ self, key: str, value: str | None, raw_attr_name: str
425
+ ) -> None:
426
+ fi: _FieldInfo | None = self._fields.get(key)
427
+ if fi is None:
428
+ self._ensure_extension._set_attribute(raw_attr_name, value)
429
+ return
430
+ if fi.kind == _FieldKind.ELEMENT:
431
+ raise TypeError(
432
+ f"{self._name_for_log(key)}は子要素なので属性を設定できません"
433
+ )
434
+ if fi.data_type is None:
435
+ raise RuntimeError(
436
+ f"{self.__class__.__name__}.{key}のdata_typeが指定されていません"
437
+ )
438
+ p_name: str = private_field_name(key)
439
+ if value is None:
440
+ setattr(self, p_name, None)
441
+ return
442
+ if value == "":
443
+ self._ensure_extension._set_invalid_value(
444
+ _InvalidValue(
445
+ kind=_FieldKind.ATTRIBUTE,
446
+ value=value,
447
+ path=self._path_xml(attr_name=key),
448
+ reason="属性に空の値が指定されています",
449
+ )
450
+ )
451
+ setattr(self, p_name, None)
452
+ return
453
+ try:
454
+ typed_value: Any = _str_to_typed_value(
455
+ value, fi.data_type, element=self, attr_name=key
456
+ )
457
+ setattr(self, p_name, typed_value)
458
+ except SchemaError as e:
459
+ self._ensure_extension._set_invalid_value(
460
+ _InvalidValue(
461
+ kind=_FieldKind.ATTRIBUTE,
462
+ value=value,
463
+ path=self._path_xml(attr_name=key),
464
+ reason=str(e),
465
+ )
466
+ )
467
+ setattr(self, p_name, None)
468
+
469
+ def _field_info(self, attr_name: str) -> _FieldInfo:
470
+ try:
471
+ return self._fields[attr_name]
472
+ except KeyError:
473
+ raise RuntimeError(
474
+ f"{self.__class__.__name__}に属性{attr_name}はありません"
475
+ )
476
+
477
+ @classmethod
478
+ def _xml_class_name(cls) -> str:
479
+ if hasattr(cls, _ATTR_NAME_XML_ELEMENT_NAME):
480
+ return str(getattr(cls, _ATTR_NAME_XML_ELEMENT_NAME))
481
+ return cls.__name__
482
+
483
+ @classmethod
484
+ def _child_class(cls, attr_name: str) -> type[StBridgeElement]:
485
+ """子要素の型を返します。
486
+
487
+ リストの子要素の場合はリストアイテムの型を返します。
488
+ """
489
+ field_info: _FieldInfo | None = cls._fields.get(attr_name)
490
+ if field_info is None:
491
+ raise ValueError(f"{cls.__name__}に属性{attr_name}はありません")
492
+ if field_info.kind is not _FieldKind.ELEMENT:
493
+ raise ValueError(f"{cls.__name__}.{attr_name}は子要素ではありません")
494
+ py_type: type[Any] | str = field_info.py_type
495
+ if get_origin(py_type) is list:
496
+ args: tuple[Any, ...] = get_args(py_type)
497
+ if len(args) != 1:
498
+ raise RuntimeError(
499
+ f"{cls.__name__}.{attr_name}のリスト型情報が不正です"
500
+ )
501
+ py_type = args[0]
502
+ if isinstance(py_type, str):
503
+ # 文字列で保持されるものは、定義モジュールから解決する
504
+ class_name: str = py_type.removeprefix("list[").removesuffix("]")
505
+ module: ModuleType | None = sys.modules.get(cls.__module__)
506
+ if module is None:
507
+ raise ValueError(f"{cls.__module__}が読み込まれていません")
508
+ try:
509
+ py_type = getattr(module, class_name)
510
+ except AttributeError:
511
+ raise ValueError(
512
+ f"{cls.__module__}に{class_name}はありません"
513
+ ) from None
514
+ if not isinstance(py_type, type) or not issubclass(py_type, StBridgeElement):
515
+ raise TypeError(f"{cls.__name__}.{attr_name}の子要素型情報が不正です")
516
+ return py_type
517
+
518
+ def _xml_name(self, attr_name: str | None = None) -> str:
519
+ """XML上の名前
520
+
521
+ attr_nameを省略した場合はこの要素のXML要素名を返す。
522
+ attr_nameを指定した場合は、その属性のXML属性名を返す。
523
+ attr_nameが子要素の場合は、その子要素のXML要素名を返す。
524
+ """
525
+ if attr_name:
526
+ field_info: _FieldInfo = self._field_info(attr_name)
527
+ if field_info.kind is _FieldKind.ELEMENT:
528
+ return self._child_class(attr_name)._xml_class_name()
529
+ return field_info.xml_name or attr_name
530
+ else:
531
+ return self._xml_class_name()
532
+
533
+ def _is_attr(self, attr_name: str) -> bool:
534
+ return self._field_info(attr_name).kind == _FieldKind.ATTRIBUTE
535
+
536
+ def _is_child(self, attr_name: str) -> bool:
537
+ return self._field_info(attr_name).kind == _FieldKind.ELEMENT
538
+
539
+ def _is_children(self, attr_name: str) -> bool:
540
+ fi: _FieldInfo = self._field_info(attr_name)
541
+ return fi.kind == _FieldKind.ELEMENT and fi.max_occurs != 1
542
+
543
+ def _is_content(self, attr_name: str) -> bool:
544
+ return self._field_info(attr_name).kind == _FieldKind.CONTENT
545
+
546
+ def _name_for_log(self, attr_name: str | None = None) -> str:
547
+ result: str = self.__class__.__name__
548
+ args: list[str] = []
549
+ pickup_attrs: tuple[str, ...] = ("id", "name", "pos", "guid")
550
+ for attr in pickup_attrs:
551
+ p_name: str = private_field_name(attr)
552
+ if hasattr(self, p_name):
553
+ value: Any = getattr(self, p_name)
554
+ if value:
555
+ args.append(f"{attr}={value}")
556
+ if args:
557
+ result += f"({', '.join(args)})"
558
+ if attr_name:
559
+ result += f".{attr_name}"
560
+ return result
561
+
562
+ @property
563
+ def _parent(self) -> StBridgeElement | None:
564
+ return self._parent_ref() if self._parent_ref else None
565
+
566
+ def _attach_to_parent(self, parent: StBridgeElement | None) -> None:
567
+ if parent is None:
568
+ self._parent_ref = None
569
+ else:
570
+ self._parent_ref = weakref.ref(parent)
571
+
572
+ def _path_py(self, attr_name: str | None = None) -> str:
573
+ parts: list[str] = []
574
+ if attr_name:
575
+ parts.append(property_name(attr_name))
576
+ current: StBridgeElement | None = self
577
+ while current is not None:
578
+ parent: StBridgeElement | None = current._parent
579
+ if parent is not None:
580
+ for key, field in parent._fields.items():
581
+ value = getattr(parent, private_field_name(key))
582
+ if field.kind == _FieldKind.ELEMENT:
583
+ if field.max_occurs == 1:
584
+ if value is current:
585
+ parts.append(property_name(key))
586
+ break
587
+ else:
588
+ if isinstance(value, list):
589
+ for idx, item in enumerate(value):
590
+ if item is current:
591
+ parts.append(f"{property_name(key)}[{idx}]")
592
+ break
593
+ else:
594
+ continue
595
+ break
596
+ else:
597
+ parts.append(current.__class__.__name__)
598
+ current = parent
599
+ parts.reverse()
600
+ return ".".join(parts)
601
+
602
+ def _path_xml(self, attr_name: str | None = None) -> str:
603
+ """この要素のXPath。
604
+
605
+ attr_nameを指定した場合はその属性までのXPath。
606
+ """
607
+ parts: list[str] = []
608
+ if attr_name:
609
+ field_info: _FieldInfo = self._field_info(attr_name)
610
+ match field_info.kind:
611
+ case _FieldKind.ELEMENT:
612
+ parts.append(self._xml_name(attr_name))
613
+ case _FieldKind.CONTENT:
614
+ parts.append("text()")
615
+ case _FieldKind.ATTRIBUTE:
616
+ parts.append(f"@{self._xml_name(attr_name)}")
617
+ current: StBridgeElement | None = self
618
+ while current is not None:
619
+ parent: StBridgeElement | None = current._parent
620
+ if parent is None:
621
+ parts.append(current._xml_name())
622
+ parts.append("")
623
+ current = parent
624
+ continue
625
+ step: str | None = None
626
+ for key, field in parent._fields.items():
627
+ if field.kind != _FieldKind.ELEMENT:
628
+ continue
629
+ value = getattr(parent, private_field_name(key))
630
+ if field.max_occurs == 1:
631
+ if value is current:
632
+ step = current._xml_name()
633
+ break
634
+ elif isinstance(value, list):
635
+ for idx, item in enumerate(value):
636
+ if item is current:
637
+ step = f"{current._xml_name()}[{idx + 1}]"
638
+ break
639
+ if step is not None:
640
+ break
641
+ if step is None:
642
+ # 拡張子要素は_fieldsに無いため、拡張側から位置を探す。
643
+ step = parent._extension_xpath_step(current)
644
+ if step is not None:
645
+ parts.append(step)
646
+ current = parent
647
+ parts.reverse()
648
+ return "/".join(parts)
649
+
650
+ def _extension_xpath_step(self, child: StBridgeElement) -> str | None:
651
+ ext: _ExtensionData | None = self._extension
652
+ children = ext._children if ext is not None else None
653
+ if not children:
654
+ return None
655
+ name: str = child._xml_name()
656
+ index: int = 0
657
+ for item in children:
658
+ if item._xml_name() != name:
659
+ continue
660
+ index += 1
661
+ if item is child:
662
+ return f"{name}[{index}]"
663
+ return None
664
+
665
+ def _get_ext_attr(self, attr_name: str) -> Any:
666
+ if self._extension:
667
+ return self._extension._get_attribute(attr_name)
668
+ else:
669
+ raise AttributeError(f"{self._path_py(attr_name)}がありません")
670
+
671
+ def _get_ext_children(self) -> list[StBridgeElement]:
672
+ if self._extension is None:
673
+ self._extension = _ExtensionData(parent=self)
674
+ return self._extension._get_children()
675
+
676
+ def _get_ext_child(self, index: int) -> StBridgeElement:
677
+ if self._extension is None:
678
+ self._extension = _ExtensionData(parent=self)
679
+ return self._extension._get_children()[index]
680
+
681
+ def _set_parent_all(self) -> None:
682
+ for key, field in self._fields.items():
683
+ value = getattr(self, private_field_name(key))
684
+ if field.kind == _FieldKind.ELEMENT:
685
+ if field.max_occurs == 1:
686
+ if isinstance(value, StBridgeElement):
687
+ value._attach_to_parent(self)
688
+ value._set_parent_all()
689
+ else:
690
+ if isinstance(value, list):
691
+ for item in value:
692
+ if isinstance(item, StBridgeElement):
693
+ item._attach_to_parent(self)
694
+ item._set_parent_all()
695
+ if self._extension is not None and self._extension._children:
696
+ for child in self._extension._children:
697
+ child._attach_to_parent(self)
698
+ child._set_parent_all()
699
+
700
+ @classmethod
701
+ def _get_module(cls) -> ModuleType:
702
+ """このクラスが定義されているモジュール"""
703
+ module: ModuleType | None = sys.modules.get(cls.__module__)
704
+ if module is None:
705
+ raise ValueError(f"{cls.__module__}が読み込まれていません")
706
+ return module
707
+
708
+ @classmethod
709
+ def _try_get_module_version(cls) -> str | None:
710
+ """このクラスが定義されているモジュールのST-Bridgeバージョン。
711
+
712
+ VERSIONが未定義の場合はNoneを返す。
713
+ VERSIONが定義済みで型や値が不正な場合はValueErrorを投げる。
714
+ """
715
+ module: ModuleType = cls._get_module()
716
+ version: object = getattr(module, "VERSION", None)
717
+ if version is None:
718
+ return None
719
+ if not isinstance(version, str) or not version:
720
+ raise ValueError(f"{cls.__module__}のVERSION定数には空でないstrが必要です")
721
+ return version
722
+
723
+ @classmethod
724
+ def _create_instance(cls, class_name: str) -> StBridgeElement:
725
+ """同じモジュールのclass_nameで指定されたクラスのインスタンス"""
726
+ try:
727
+ module: ModuleType = sys.modules[cls.__module__]
728
+ new_class: type[StBridgeElement] = getattr(module, class_name)
729
+ return new_class()
730
+ except AttributeError:
731
+ raise ValueError(f"{cls.__module__}に{class_name}はありません")
732
+
733
+ @classmethod
734
+ def _create_child_instance(cls, attr_name: str) -> StBridgeElement:
735
+ if attr_name in cls._fields:
736
+ field_info: _FieldInfo = cls._fields[attr_name]
737
+ else:
738
+ raise ValueError(f"{cls.__name__}に属性{attr_name}はありません")
739
+ if field_info.kind is not _FieldKind.ELEMENT:
740
+ raise ValueError(f"{cls.__name__}.{attr_name}は子要素ではありません")
741
+ return cls._create_instance(field_info.py_type_str)
742
+
743
+ def _ensure_child(self, attr_name: str) -> StBridgeElement:
744
+ p_name: str = private_field_name(attr_name)
745
+ if hasattr(self, p_name):
746
+ value: Any = getattr(self, p_name)
747
+ if isinstance(value, StBridgeElement):
748
+ return value
749
+ instance: StBridgeElement = self._create_child_instance(attr_name)
750
+ setattr(self, attr_name, instance)
751
+ return instance
752
+
753
+ @property
754
+ def _ensure_extension(self) -> _ExtensionData:
755
+ if self._extension is None:
756
+ self._extension = _ExtensionData(parent=self)
757
+ return self._extension
758
+
759
+
760
+ class StBridgeRoot(StBridgeElement):
761
+ """ST-Bridgeのルート要素ST_BRIDGEの基底クラス。
762
+
763
+ バージョンごとのStBridgeクラスがこのクラスを継承する。
764
+ load,dump等のバージョンを問わず利用する関数の型ヒントではこのクラスを用いる。
765
+ """
766
+
767
+ __slots__ = ()
768
+
769
+ @property
770
+ def version(self) -> str:
771
+ """ST-Bridgeのバージョン。
772
+
773
+ Returns:
774
+ str: "2.1.0"のようなバージョン文字列。
775
+
776
+ Raises:
777
+ NotImplementedError: サブクラスではなく、この基底クラスのまま参照した場合。
778
+ """
779
+ raise NotImplementedError("versionプロパティはサブクラスで実装されます")
780
+
781
+ def _set_version(
782
+ self, *, reporter: Reporter | None = None, phase: Phase | None = None
783
+ ) -> None:
784
+ from ..stb_reporting import Code
785
+
786
+ version: str | None = self._try_get_module_version()
787
+ if version is not None:
788
+ p_field_name: str = private_field_name("version")
789
+ if hasattr(self, p_field_name):
790
+ setattr(self, p_field_name, version)
791
+ elif reporter is not None:
792
+ reporter.error(
793
+ message=f"{self.__class__.__name__}の定義モジュールにVERSIONが定義されていません",
794
+ code=Code.DEVELOPER_ERROR,
795
+ phase=phase,
796
+ stb_element=self,
797
+ attr_name="version",
798
+ )
799
+
800
+
801
+ class _StBridgeElementList[TElement: StBridgeElement](list[TElement]):
802
+ """子要素を格納するリスト
803
+
804
+ ユーザーには普通のlistとして見せるが、parent処理を行うために内部的にはこのクラスで管理する。"""
805
+
806
+ __slots__ = ("__weakref__", "_parent_ref")
807
+
808
+ def __init__(
809
+ self,
810
+ parent: StBridgeElement | None = None,
811
+ iterable: Iterable[TElement] = (),
812
+ ) -> None:
813
+ super().__init__()
814
+ self._parent_ref: weakref.ref[StBridgeElement] | None = None
815
+ if parent is not None:
816
+ self._attach_to_parent(parent)
817
+ if iterable:
818
+ self.extend(iterable)
819
+
820
+ def append(self, item: TElement) -> None:
821
+ if isinstance(item, StBridgeElement):
822
+ if self._parent is not None:
823
+ item._attach_to_parent(self._parent)
824
+ else:
825
+ raise TypeError("StBridgeElementListにはStBridgeElementのみ追加できます")
826
+ super().append(item)
827
+
828
+ def extend(self, it: Iterable[TElement]) -> None:
829
+ for x in it:
830
+ if isinstance(x, StBridgeElement):
831
+ if self._parent is not None:
832
+ x._attach_to_parent(self._parent)
833
+ else:
834
+ raise TypeError(
835
+ "StBridgeElementListにはStBridgeElementのみ追加できます"
836
+ )
837
+ super().append(x)
838
+
839
+ def insert(self, i: SupportsIndex, item: TElement) -> None:
840
+ if isinstance(item, StBridgeElement):
841
+ if self._parent is not None:
842
+ item._attach_to_parent(self._parent)
843
+ else:
844
+ raise TypeError("StBridgeElementListにはStBridgeElementのみ追加できます")
845
+ super().insert(i, item)
846
+
847
+ def remove(self, value: TElement) -> None:
848
+ super().remove(value)
849
+ if (
850
+ isinstance(value, StBridgeElement)
851
+ and self._parent is not None
852
+ and not any(x is value for x in self)
853
+ ):
854
+ value._attach_to_parent(None)
855
+
856
+ def clear(self) -> None:
857
+ for item in self:
858
+ if isinstance(item, StBridgeElement) and self._parent is not None:
859
+ item._attach_to_parent(None)
860
+ super().clear()
861
+
862
+ def __delitem__(self, key: SupportsIndex | slice[Any, Any, Any]) -> None:
863
+ old_item: TElement | None
864
+ if isinstance(key, slice):
865
+ old_items: list[TElement] = self[key]
866
+ super().__delitem__(key)
867
+ for old_item in old_items:
868
+ if (
869
+ isinstance(old_item, StBridgeElement)
870
+ and self._parent is not None
871
+ and not any(x is old_item for x in self)
872
+ ):
873
+ old_item._attach_to_parent(None)
874
+ return
875
+ old_item = self[key] if int(key) < len(self) else None
876
+ super().__delitem__(key)
877
+ if (
878
+ isinstance(old_item, StBridgeElement)
879
+ and self._parent is not None
880
+ and not any(x is old_item for x in self)
881
+ ):
882
+ old_item._attach_to_parent(None)
883
+
884
+ def pop(self, index: SupportsIndex = -1) -> TElement:
885
+ item = super().pop(index)
886
+ if (
887
+ isinstance(item, StBridgeElement)
888
+ and self._parent is not None
889
+ and not any(x is item for x in self)
890
+ ):
891
+ item._attach_to_parent(None)
892
+ return item
893
+
894
+ def __setitem__(
895
+ self,
896
+ i: SupportsIndex | slice[Any, Any, Any],
897
+ item: TElement | Iterable[TElement],
898
+ ) -> None:
899
+ if isinstance(i, slice):
900
+ raise NotImplementedError("スライスの代入はサポートしていません")
901
+ if not isinstance(item, StBridgeElement):
902
+ raise TypeError("子要素リストにはStBridgeElementのみ追加できます")
903
+ old_item: TElement = self[i]
904
+ if old_item is item:
905
+ return
906
+ if self._parent is not None:
907
+ item._attach_to_parent(self._parent)
908
+ super().__setitem__(i, item)
909
+ if self._parent is not None and not any(x is old_item for x in self):
910
+ old_item._attach_to_parent(None)
911
+
912
+ @property
913
+ def _parent(self) -> StBridgeElement | None:
914
+ return self._parent_ref() if self._parent_ref else None
915
+
916
+ def _attach_to_parent(self, parent: StBridgeElement) -> None:
917
+ self._parent_ref = weakref.ref(parent)
918
+ for item in self:
919
+ item._attach_to_parent(parent)
920
+
921
+
922
+ class _StBridgeExtensionElement(StBridgeElement):
923
+ __slots__ = ("_extension_attrs", "_name")
924
+
925
+ def __init__(self, name: str, attributes: dict[str, Any] | None = None) -> None:
926
+ super().__init__()
927
+ if attributes:
928
+ self._extension_attrs = attributes
929
+ self._name: str = name
930
+
931
+ def _xml_name(self, attr_name: str | None = None) -> str:
932
+ if attr_name is None:
933
+ return self._name
934
+ else:
935
+ return attr_name
936
+
937
+
938
+ class _EnsureAccessorProtocol(Protocol):
939
+ pass
940
+
941
+
942
+ class _EnsureAccessorBase(_EnsureAccessorProtocol):
943
+ """
944
+ ensureプロパティが返す子要素生成アクセサ。
945
+
946
+ 実行時の実装はこのクラスのみで行い、要素の型ごとの属性名と戻り値の型は
947
+ stb_v2_1_0.pyiなどのスタブが_EnsureAccessorProtocolを継承して与える。
948
+ """
949
+
950
+ __slots__ = ("_owner",)
951
+
952
+ def __init__(self, owner: StBridgeElement) -> None:
953
+ self._owner = owner
954
+
955
+ # not TYPE_CHECKINGにより__getattr__を型チェッカから隠す。
956
+ # 見えているとスタブで定義していない属性で方エラーが出ず、属性名のtypoの危険性がある。
957
+ if not TYPE_CHECKING:
958
+
959
+ def __getattr__(self, name: str) -> Any:
960
+ if name in self._owner._fields:
961
+ field_info: _FieldInfo = self._owner._field_info(name)
962
+ if field_info.kind == _FieldKind.ELEMENT:
963
+ return lambda: self._owner._ensure_child(name)
964
+ else:
965
+ raise AttributeError(
966
+ f"{self._owner.__class__.__name__}.{name}は子要素ではありません"
967
+ )