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.
- stbkit/core/__init__.py +12 -0
- stbkit/core/_internal/constants.py +42 -0
- stbkit/core/_internal/extension_utils.py +238 -0
- stbkit/core/_internal/io_text.py +48 -0
- stbkit/core/_internal/name_converter.py +170 -0
- stbkit/core/_internal/optional_dependencies/xmlschema_validator.py +163 -0
- stbkit/core/_internal/stb_logger.py +86 -0
- stbkit/core/_internal/transform/__init__.py +5 -0
- stbkit/core/_internal/transform/guid_tools.py +73 -0
- stbkit/core/_internal/transform/select.py +114 -0
- stbkit/core/_internal/xml_value_converter.py +16 -0
- stbkit/core/data_model/__init__.py +5 -0
- stbkit/core/data_model/_internal/filter_option.py +25 -0
- stbkit/core/data_model/_internal/stb_types.py +98 -0
- stbkit/core/data_model/_internal/type_validation.py +755 -0
- stbkit/core/data_model/common.py +967 -0
- stbkit/core/data_model/stb_v2_0_0.py +13633 -0
- stbkit/core/data_model/stb_v2_0_0.pyi +15327 -0
- stbkit/core/data_model/stb_v2_0_1.py +13702 -0
- stbkit/core/data_model/stb_v2_0_1.pyi +15450 -0
- stbkit/core/data_model/stb_v2_0_2.py +21677 -0
- stbkit/core/data_model/stb_v2_0_2.pyi +23319 -0
- stbkit/core/data_model/stb_v2_1_0.py +27541 -0
- stbkit/core/data_model/stb_v2_1_0.pyi +31553 -0
- stbkit/core/data_model/stb_v2_1_1.py +27577 -0
- stbkit/core/data_model/stb_v2_1_1.pyi +31590 -0
- stbkit/core/py.typed +0 -0
- stbkit/core/repository/__init__.py +67 -0
- stbkit/core/repository/_internal/ref_def_v2_0_2.py +191 -0
- stbkit/core/repository/_internal/ref_def_v2_1_0.py +216 -0
- stbkit/core/repository/_internal/ref_def_v2_1_1.py +216 -0
- stbkit/core/repository/repo_common.py +363 -0
- stbkit/core/repository/repo_v2_0_2.py +48 -0
- stbkit/core/repository/repo_v2_0_2.pyi +257 -0
- stbkit/core/repository/repo_v2_1_0.py +48 -0
- stbkit/core/repository/repo_v2_1_0.pyi +272 -0
- stbkit/core/repository/repo_v2_1_1.py +50 -0
- stbkit/core/repository/repo_v2_1_1.pyi +272 -0
- stbkit/core/serialization/__init__.py +21 -0
- stbkit/core/serialization/_dict_converter.py +1448 -0
- stbkit/core/serialization/config.py +114 -0
- stbkit/core/serialization/profiles.py +57 -0
- stbkit/core/stb_exceptions.py +203 -0
- stbkit/core/stb_io/__init__.py +17 -0
- stbkit/core/stb_io/_internal/__init__.py +5 -0
- stbkit/core/stb_io/_internal/loader.py +63 -0
- stbkit/core/stb_io/_internal/serializer.py +157 -0
- stbkit/core/stb_io/_internal/stream_reader.py +346 -0
- stbkit/core/stb_io/_internal/xml_io.py +482 -0
- stbkit/core/stb_reporting.py +1061 -0
- stbkit/core/stb_typing.py +61 -0
- stbkit/core/validation/__init__.py +9 -0
- stbkit/core/validation/_internal/common.py +78 -0
- stbkit/core/validation/_internal/schema_validator.py +294 -0
- stbkit/core/validation/_internal/validator.py +109 -0
- stbkit_core-0.1.0a9.dev1.dist-info/METADATA +121 -0
- stbkit_core-0.1.0a9.dev1.dist-info/RECORD +60 -0
- stbkit_core-0.1.0a9.dev1.dist-info/WHEEL +5 -0
- stbkit_core-0.1.0a9.dev1.dist-info/licenses/LICENSE +373 -0
- 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
|
+
)
|