beresta 2.1.0__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.
beresta/__init__.py ADDED
@@ -0,0 +1,42 @@
1
+ """
2
+ Берёста — быстрая DOM-библиотека XML на C++17 для Python (через ctypes).
3
+
4
+ import beresta
5
+
6
+ with beresta.parse("<shop><item code='A1'>Чай</item></shop>") as document:
7
+ item = document.root.first_child
8
+ print(item.get_attribute("code"), item.text)
9
+ print(beresta.XPath("count(//item)").number(document))
10
+
11
+ Без зависимостей: нужен только сам Python и динамическая библиотека,
12
+ которую сборка кладёт в beresta/_native/<os>-<arch>/.
13
+
14
+ Beresta is a fast XML DOM library in C++17 for Python (through ctypes).
15
+
16
+ No dependencies: only Python itself and the native library,
17
+ which the build puts into beresta/_native/<os>-<arch>/.
18
+ """
19
+
20
+ from ._factory import new_document, parse, parse_file
21
+ from ._loader import platform_name
22
+ from ._native import LIB as _LIB
23
+ from .document import Document
24
+ from .element import Element
25
+ from .errors import BerestaError
26
+ from .flags import NodeType, ParseFlags, WriteFlags
27
+ from .languages import language, languages, set_language
28
+ from .node import Node
29
+ from .schema import Diagnostic, Schema
30
+ from .xpath import XPath
31
+
32
+ __all__ = [
33
+ "BerestaError", "Diagnostic", "Document", "Element", "Node", "NodeType",
34
+ "ParseFlags", "Schema", "WriteFlags", "XPath",
35
+ "language", "languages", "new_document", "parse", "parse_file", "platform_name",
36
+ "set_language", "version",
37
+ ]
38
+
39
+
40
+ def version() -> str:
41
+ """Версия нативной библиотеки, например "2.1.0". / The native library version, for example "2.1.0"."""
42
+ return _LIB.beresta_version().decode("ascii")
@@ -0,0 +1,45 @@
1
+ """
2
+ Нарушения схемы и чтение их нативного списка. Общее для проверки
3
+ готового дерева (Schema.validate) и потоковой проверки байтов и файлов
4
+ (Schema.validate_bytes / validate_file) — одна функция вместо трёх копий.
5
+
6
+ Schema violations and reading their native list. Common for the validation of
7
+ a ready tree (Schema.validate) and the streaming validation of bytes and files
8
+ (Schema.validate_bytes / validate_file) — one function instead of three copies.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from dataclasses import dataclass
14
+ from typing import List
15
+
16
+ from ._native import LIB
17
+ from .errors import take_text
18
+
19
+
20
+ @dataclass(frozen=True)
21
+ class Diagnostic:
22
+ """Одно нарушение: текст с путём к элементу и место в исходнике (0 — неизвестно).
23
+ One violation: the text with the element path and the place in the source (0 — unknown)."""
24
+
25
+ message: str
26
+ line: int
27
+ column: int
28
+
29
+
30
+ def take_diagnostics(diagnostics) -> List[Diagnostic]:
31
+ """Список нарушений из нативного дескриптора; дескриптор освобождается в finally.
32
+ The list of violations from a native handle; the handle is freed in finally."""
33
+ try:
34
+ return [_diagnostic_at(diagnostics, index)
35
+ for index in range(LIB.beresta_diagnostics_count(diagnostics))]
36
+ finally:
37
+ LIB.beresta_diagnostics_free(diagnostics)
38
+
39
+
40
+ def _diagnostic_at(diagnostics, index: int) -> Diagnostic:
41
+ """Чтение одного нарушения из нативного списка. / Reads one violation from the native list."""
42
+ message = take_text(LIB.beresta_diagnostics_message(diagnostics, index))
43
+ line = LIB.beresta_diagnostics_line(diagnostics, index)
44
+ column = LIB.beresta_diagnostics_column(diagnostics, index)
45
+ return Diagnostic(message, line, column)
@@ -0,0 +1,45 @@
1
+ """Примесь документа: корень, поиск по ID, копия, запись в файл.
2
+ Document mixin: the root, lookup by ID, a copy, writing to a file."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import os
7
+ from typing import Optional
8
+
9
+ from ._native import LIB, encode, out_handle
10
+ from ._wrapping import wrap_node
11
+ from .errors import check
12
+ from .flags import WriteFlags
13
+
14
+
15
+ class DocumentContentMixin:
16
+ """Операции уровня документа. / Document-level operations."""
17
+
18
+ @property
19
+ def raw_handle(self) -> int:
20
+ """Дескриптор без проверки закрытия (нужен фабрике обёрток).
21
+ The handle without the closed check (needed by the wrapper factory)."""
22
+ return self._pointer
23
+
24
+ @property
25
+ def root(self):
26
+ """Корневой элемент или None. / The root element or None."""
27
+ return wrap_node(LIB.beresta_document_root(self.handle), self)
28
+
29
+ def element_by_id(self, identifier: str):
30
+ """Элемент с атрибутом типа ID (по DTD) или xml:id.
31
+ An element with an ID-typed attribute (by DTD) or xml:id."""
32
+ return wrap_node(LIB.beresta_document_element_by_id(self.handle, encode(identifier)), self)
33
+
34
+ def clone(self):
35
+ """Независимая глубокая копия документа. / An independent deep copy of the document."""
36
+ from .document import Document
37
+
38
+ result = out_handle()
39
+ check(LIB.beresta_document_clone(self.handle, result))
40
+ return Document(result.value)
41
+
42
+ def save(self, path, flags: WriteFlags = WriteFlags.DEFAULT, encoding: Optional[str] = None) -> None:
43
+ """Запись в файл; encoding=None — UTF-8, иначе, например, "windows-1251".
44
+ Writing to a file; encoding=None means UTF-8, otherwise, for example, "windows-1251"."""
45
+ check(LIB.beresta_document_save(self.handle, encode(os.fspath(path)), int(flags), encode(encoding)))
@@ -0,0 +1,49 @@
1
+ """Примесь: атрибуты элемента. / Mixin: element attributes."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Dict, Optional
6
+
7
+ from ._native import LIB, encode
8
+ from .errors import check, take_text
9
+
10
+
11
+ class ElementAttributesMixin:
12
+ """Чтение, запись и перебор атрибутов (порядок — как в документе).
13
+ Reading, writing and iterating attributes (in document order)."""
14
+
15
+ def get_attribute(self, name: str, default: Optional[str] = None) -> Optional[str]:
16
+ """Значение атрибута или default, если атрибута нет.
17
+ The attribute value or default if there is no such attribute."""
18
+ value = take_text(LIB.beresta_element_get_attribute(self.handle, encode(name)))
19
+ if value is None:
20
+ return default
21
+ return value
22
+
23
+ def set_attribute(self, name: str, value: str) -> None:
24
+ check(LIB.beresta_element_set_attribute(self.handle, encode(name), encode(value)))
25
+
26
+ def has_attribute(self, name: str) -> bool:
27
+ result = LIB.beresta_element_has_attribute(self.handle, encode(name))
28
+ if result < 0:
29
+ check(LIB.beresta_last_error_code())
30
+ return result == 1
31
+
32
+ def remove_attribute(self, name: str) -> bool:
33
+ """True — атрибут был и удалён, False — его не было.
34
+ True means the attribute existed and was removed, False means it did not exist."""
35
+ result = LIB.beresta_element_remove_attribute(self.handle, encode(name))
36
+ if result < 0:
37
+ check(LIB.beresta_last_error_code())
38
+ return result == 1
39
+
40
+ @property
41
+ def attributes(self) -> Dict[str, str]:
42
+ """Снимок всех атрибутов: {имя: значение} в порядке документа.
43
+ A snapshot of all attributes: {name: value} in document order."""
44
+ result = {}
45
+ handle = self.handle
46
+ for index in range(LIB.beresta_element_attribute_count(handle)):
47
+ name = take_text(LIB.beresta_element_attribute_name(handle, index))
48
+ result[name] = take_text(LIB.beresta_element_attribute_value(handle, index))
49
+ return result
beresta/_factory.py ADDED
@@ -0,0 +1,53 @@
1
+ """Создание документов: разбор текста, байтов и файлов, документ с нуля.
2
+ Creating documents: parsing text, bytes and files, a document from scratch."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import os
7
+ from typing import Optional, Union
8
+
9
+ from ._native import LIB, encode, out_handle
10
+ from .document import Document
11
+ from .errors import check
12
+ from .flags import ParseFlags
13
+
14
+
15
+ def _schema_handle(schema) -> Optional[int]:
16
+ """Дескриптор схемы или NULL, если проверка по схеме не нужна.
17
+ The schema handle or NULL if no schema validation is needed."""
18
+ if schema is None:
19
+ return None
20
+ return schema.handle
21
+
22
+
23
+ def parse(source: Union[str, bytes], flags: ParseFlags = ParseFlags.DEFAULT, schema=None) -> Document:
24
+ """
25
+ Разбор XML. str кодируется в UTF-8; bytes передаются как есть —
26
+ кодировку (UTF-16, windows-1251, ...) библиотека определит сама.
27
+
28
+ Parses XML. str is encoded to UTF-8; bytes are passed as is —
29
+ the library detects the encoding (UTF-16, windows-1251, ...) itself.
30
+ """
31
+ data = source
32
+ if isinstance(source, str):
33
+ data = source.encode("utf-8")
34
+ result = out_handle()
35
+ check(LIB.beresta_document_parse(data, len(data), int(flags), _schema_handle(schema), result))
36
+ return Document(result.value)
37
+
38
+
39
+ def parse_file(path, flags: ParseFlags = ParseFlags.DEFAULT, schema=None) -> Document:
40
+ """Разбор файла (ParseFlags.FILE_RESOURCES разрешает внешние DTD рядом с ним).
41
+ Parses a file (ParseFlags.FILE_RESOURCES allows external DTDs next to it)."""
42
+ result = out_handle()
43
+ status = LIB.beresta_document_parse_file(encode(os.fspath(path)), int(flags), _schema_handle(schema), result)
44
+ check(status)
45
+ return Document(result.value)
46
+
47
+
48
+ def new_document(root_name: Optional[str] = None) -> Document:
49
+ """Новый документ; root_name=None — без корневого элемента.
50
+ A new document; root_name=None means without a root element."""
51
+ result = out_handle()
52
+ check(LIB.beresta_document_create(encode(root_name), result))
53
+ return Document(result.value)
beresta/_loader.py ADDED
@@ -0,0 +1,110 @@
1
+ """
2
+ Поиск и загрузка динамической библиотеки «Берёсты» через ctypes.
3
+
4
+ Порядок поиска (первый найденный файл побеждает):
5
+ 1. переменная окружения BERESTA_LIBRARY — явный путь к файлу;
6
+ 2. папка пакета _native/<os>-<arch>/ — туда бинарник кладут
7
+ CMake (-DBERESTA_BUILD_PYTHON=ON) и Makefile (make PYTHON=1);
8
+ 3. системный поиск (ctypes.util.find_library) — библиотека,
9
+ установленная в систему (/usr/lib, PATH и т.п.).
10
+
11
+ Имена ОС и архитектуры совпадают с именами в CMake, Makefile и
12
+ Java-обёртке: windows/linux/macos и x86_64/aarch64/x86.
13
+
14
+ Finding and loading the Beresta shared library through ctypes.
15
+ Lookup order (the first file found wins):
16
+ 1. the BERESTA_LIBRARY environment variable — an explicit file path;
17
+ 2. the package folder _native/<os>-<arch>/ — the binary is put there by
18
+ CMake (-DBERESTA_BUILD_PYTHON=ON) and the Makefile (make PYTHON=1);
19
+ 3. the system lookup (ctypes.util.find_library) — a library
20
+ installed in the system (/usr/lib, PATH and so on).
21
+ The OS and architecture names match CMake, the Makefile and the
22
+ Java wrapper: windows/linux/macos and x86_64/aarch64/x86.
23
+ """
24
+
25
+ from __future__ import annotations
26
+
27
+ import ctypes
28
+ import ctypes.util
29
+ import os
30
+ import platform
31
+ import struct
32
+ import sys
33
+ from pathlib import Path
34
+
35
+ # Каноническое имя файла библиотеки на каждой ОС
36
+ # The canonical library file name on each OS
37
+ _LIBRARY_FILES = {
38
+ "windows": "beresta.dll",
39
+ "linux": "libberesta.so",
40
+ "macos": "libberesta.dylib",
41
+ }
42
+
43
+ # Разные системы называют одну архитектуру по-разному
44
+ # Different systems name the same architecture differently
45
+ _ARCH_ALIASES = {
46
+ "amd64": "x86_64", "x86_64": "x86_64", "x64": "x86_64",
47
+ "arm64": "aarch64", "aarch64": "aarch64",
48
+ "i386": "x86", "i686": "x86", "x86": "x86",
49
+ }
50
+
51
+
52
+ def platform_name() -> str:
53
+ """Имя платформы вида windows-x86_64 (имя папки в _native).
54
+ The platform name like windows-x86_64 (the folder name in _native)."""
55
+ if sys.platform.startswith("win"):
56
+ system = "windows"
57
+ elif sys.platform == "darwin":
58
+ system = "macos"
59
+ else:
60
+ system = sys.platform.rstrip("0123456789")
61
+ machine = platform.machine().lower()
62
+ arch = _ARCH_ALIASES.get(machine, machine)
63
+ # 32-битный Python на 64-битном процессоре загрузит только 32-битную библиотеку
64
+ # A 32-bit Python on a 64-bit processor can load only a 32-bit library
65
+ if arch == "x86_64" and struct.calcsize("P") == 4:
66
+ arch = "x86"
67
+ return system + "-" + arch
68
+
69
+
70
+ def library_file_name() -> str:
71
+ """Имя файла библиотеки для текущей ОС. / The library file name for the current OS."""
72
+ system = platform_name().split("-")[0]
73
+ return _LIBRARY_FILES.get(system, "libberesta.so")
74
+
75
+
76
+ def _candidates() -> list:
77
+ """Кандидаты в порядке приоритета (несуществующие отбрасываются позже).
78
+ Candidates in priority order (missing ones are dropped later)."""
79
+ result = []
80
+ explicit = os.environ.get("BERESTA_LIBRARY")
81
+ if explicit:
82
+ result.append(explicit)
83
+ bundled = Path(__file__).resolve().parent / "_native" / platform_name() / library_file_name()
84
+ result.append(str(bundled))
85
+ system_wide = ctypes.util.find_library("beresta")
86
+ if system_wide:
87
+ result.append(system_wide)
88
+ return result
89
+
90
+
91
+ def load_library() -> ctypes.CDLL:
92
+ """Загрузка библиотеки; понятная ошибка, если бинарник не найден.
93
+ Loads the library; a clear error if the binary is not found."""
94
+ tried = []
95
+ for candidate in _candidates():
96
+ if os.path.isabs(candidate) and not os.path.exists(candidate):
97
+ tried.append(candidate)
98
+ continue
99
+ return ctypes.CDLL(candidate)
100
+ # Библиотека ещё не загружена, каталога сообщений нет — текст на двух языках
101
+ # The library is not loaded yet, so there is no message catalog — the text is bilingual
102
+ raise OSError(
103
+ "Берёста: динамическая библиотека не найдена для платформы "
104
+ + platform_name() + ". Соберите её с флагом PYTHON=1 (make) или "
105
+ + "-DBERESTA_BUILD_PYTHON=ON (CMake) либо задайте BERESTA_LIBRARY. "
106
+ + "Проверенные пути: " + ", ".join(tried)
107
+ + " / Beresta: the native library was not found for the platform "
108
+ + platform_name() + ". Build it with PYTHON=1 (make) or "
109
+ + "-DBERESTA_BUILD_PYTHON=ON (CMake), or set BERESTA_LIBRARY. "
110
+ + "Paths tried: " + ", ".join(tried))
beresta/_native.py ADDED
@@ -0,0 +1,42 @@
1
+ """
2
+ Единственный экземпляр загруженной библиотеки (паттерн Singleton на уровне
3
+ модуля: Python импортирует модуль ровно один раз, поэтому библиотека
4
+ загружается и связывается с сигнатурами тоже один раз).
5
+
6
+ The single instance of the loaded library (the Singleton pattern at module
7
+ level: Python imports a module exactly once, so the library is also
8
+ loaded and bound to the signatures once).
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from ctypes import c_void_p
14
+ from typing import Optional
15
+
16
+ from ._loader import load_library
17
+ from ._signatures import bind_signatures
18
+
19
+
20
+ def _initialize():
21
+ """Загрузка библиотеки и назначение сигнатур всем функциям.
22
+ Loads the library and assigns signatures to all functions."""
23
+ library = load_library()
24
+ bind_signatures(library)
25
+ return library
26
+
27
+
28
+ LIB = _initialize()
29
+
30
+
31
+ def encode(text: Optional[str]) -> Optional[bytes]:
32
+ """Строка Python -> байты UTF-8 для параметра const char* (None -> NULL).
33
+ A Python string -> UTF-8 bytes for a const char* parameter (None -> NULL)."""
34
+ if text is None:
35
+ return None
36
+ return str(text).encode("utf-8")
37
+
38
+
39
+ def out_handle() -> c_void_p:
40
+ """Пустая ячейка для выходного параметра вида beresta_document* out.
41
+ An empty cell for an output parameter like beresta_document* out."""
42
+ return c_void_p()
@@ -0,0 +1,44 @@
1
+ """Примесь: доступ к детям как к последовательности Python. / Mixin: access to children as a Python sequence."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Iterator
6
+
7
+ from ._native import LIB
8
+ from ._wrapping import wrap_node
9
+ from .errors import check
10
+
11
+
12
+ class NodeChildrenMixin:
13
+ """len(node), node[i], for child in node: ..."""
14
+
15
+ @property
16
+ def child_count(self) -> int:
17
+ count = LIB.beresta_node_child_count(self.handle)
18
+ if count < 0:
19
+ check(LIB.beresta_last_error_code())
20
+ return count
21
+
22
+ @property
23
+ def children(self) -> list:
24
+ """Список детей (снимок: последующие изменения дерева в нём не видны).
25
+ The list of children (a snapshot: later tree changes are not visible in it)."""
26
+ return list(iter(self))
27
+
28
+ def __len__(self) -> int:
29
+ return self.child_count
30
+
31
+ def __getitem__(self, index: int):
32
+ """Ребёнок по индексу; отрицательный индекс — с конца, как у list.
33
+ A child by index; a negative index counts from the end, as in list."""
34
+ if index < 0:
35
+ index += self.child_count
36
+ return wrap_node(LIB.beresta_node_child(self.handle, index), self.document)
37
+
38
+ def __iter__(self) -> Iterator:
39
+ """Обход детей через соседей — O(1) на шаг, без лишних списков.
40
+ Iterates children through siblings — O(1) per step, without extra lists."""
41
+ child = self.first_child
42
+ while child is not None:
43
+ yield child
44
+ child = child.next_sibling
@@ -0,0 +1,46 @@
1
+ """Примесь: тип, имя, текст и сериализация узла. / Mixin: node type, name, text and serialization."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from ._native import LIB, encode
6
+ from .errors import check, take_text
7
+ from .flags import NodeType, WriteFlags
8
+
9
+
10
+ class NodeContentMixin:
11
+ """Свойства содержимого узла. / Node content properties."""
12
+
13
+ @property
14
+ def type(self) -> NodeType:
15
+ """Тип узла (NodeType); запоминается при первом запросе или фабрикой.
16
+ The node type (NodeType); remembered on the first request or by the factory."""
17
+ handle = self.handle
18
+ if self._kind is None:
19
+ code = LIB.beresta_node_type(handle)
20
+ if code < 0:
21
+ check(LIB.beresta_last_error_code())
22
+ self._kind = code
23
+ return NodeType(self._kind)
24
+
25
+ @property
26
+ def name(self) -> str:
27
+ """Имя по соглашению DOM: тег, цель PI, "#text", "#comment"...
28
+ The name by the DOM convention: a tag, a PI target, "#text", "#comment"..."""
29
+ return take_text(LIB.beresta_node_name(self.handle))
30
+
31
+ @property
32
+ def text(self) -> str:
33
+ """Текстовое содержимое (у элемента — весь текст потомков).
34
+ The text content (for an element — all text of its descendants)."""
35
+ return take_text(LIB.beresta_node_text(self.handle))
36
+
37
+ @text.setter
38
+ def text(self, value: str) -> None:
39
+ """У элемента все дети заменяются одним текстовым узлом.
40
+ For an element all children are replaced with one text node."""
41
+ check(LIB.beresta_node_set_text(self.handle, encode(value)))
42
+
43
+ def to_xml(self, flags: WriteFlags = WriteFlags.DEFAULT) -> str:
44
+ """Сериализация узла с потомками (по умолчанию — без лишних пробелов).
45
+ Serializes the node with its descendants (by default — without extra spaces)."""
46
+ return take_text(LIB.beresta_node_to_string(self.handle, int(flags)))
@@ -0,0 +1,32 @@
1
+ """Примесь: добавление новых узлов в конец детей. / Mixin: appending new nodes to the end of the children."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Optional
6
+
7
+ from ._native import LIB, encode
8
+ from ._wrapping import wrap_node
9
+
10
+
11
+ class NodeEditingMixin:
12
+ """Каждый метод возвращает созданный узел — удобно строить дерево цепочкой.
13
+ Each method returns the created node — convenient for building a tree in a chain."""
14
+
15
+ def append_element(self, name: str):
16
+ """Новый элемент; у пустого документа он становится корнем.
17
+ A new element; in an empty document it becomes the root."""
18
+ return wrap_node(LIB.beresta_node_append_element(self.handle, encode(name)), self.document)
19
+
20
+ def append_text(self, text: str):
21
+ return wrap_node(LIB.beresta_node_append_text(self.handle, encode(text)), self.document)
22
+
23
+ def append_comment(self, text: str):
24
+ return wrap_node(LIB.beresta_node_append_comment(self.handle, encode(text)), self.document)
25
+
26
+ def append_cdata(self, text: str):
27
+ return wrap_node(LIB.beresta_node_append_cdata(self.handle, encode(text)), self.document)
28
+
29
+ def append_pi(self, target: str, data: Optional[str] = None):
30
+ """Инструкция обработки <?target data?>. / A processing instruction <?target data?>."""
31
+ pointer = LIB.beresta_node_append_pi(self.handle, encode(target), encode(data))
32
+ return wrap_node(pointer, self.document)
@@ -0,0 +1,32 @@
1
+ """Примесь: навигация по дереву (родитель, соседи, первый и последний ребёнок).
2
+ Mixin: tree navigation (parent, siblings, first and last child)."""
3
+
4
+ from __future__ import annotations
5
+
6
+ from ._native import LIB
7
+ from ._wrapping import wrap_node
8
+
9
+
10
+ class NodeNavigationMixin:
11
+ """Соседи и родитель находятся за O(1): ядро хранит индекс узла у родителя.
12
+ Siblings and the parent are found in O(1): the core stores the node's index in its parent."""
13
+
14
+ @property
15
+ def parent(self):
16
+ return wrap_node(LIB.beresta_node_parent(self.handle), self.document)
17
+
18
+ @property
19
+ def first_child(self):
20
+ return wrap_node(LIB.beresta_node_first_child(self.handle), self.document)
21
+
22
+ @property
23
+ def last_child(self):
24
+ return wrap_node(LIB.beresta_node_last_child(self.handle), self.document)
25
+
26
+ @property
27
+ def next_sibling(self):
28
+ return wrap_node(LIB.beresta_node_next_sibling(self.handle), self.document)
29
+
30
+ @property
31
+ def previous_sibling(self):
32
+ return wrap_node(LIB.beresta_node_previous_sibling(self.handle), self.document)
@@ -0,0 +1,50 @@
1
+ """Примесь: удаление, копирование и перенос поддерева. / Mixin: removing, copying and moving a subtree."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from ._native import LIB, out_handle
6
+ from ._wrapping import transfer_wrappers, wrap_node
7
+ from .errors import check
8
+
9
+
10
+ class NodeStructureMixin:
11
+ """Изменение структуры дерева. / Changing the tree structure."""
12
+
13
+ def remove(self) -> None:
14
+ """
15
+ Удаление узла с потомками. После вызова этот объект и обёртки
16
+ потомков недействительны — обращаться к ним нельзя.
17
+
18
+ Removes the node with its descendants. After the call this object and the
19
+ wrappers of its descendants are invalid — they must not be used.
20
+ """
21
+ check(LIB.beresta_node_remove(self.handle))
22
+
23
+ def copy_to(self, parent):
24
+ """Глубокая копия в конец детей parent (можно в другой документ).
25
+ A deep copy appended to the children of parent (another document is allowed)."""
26
+ result = out_handle()
27
+ check(LIB.beresta_node_copy_to(self.handle, parent.handle, result))
28
+ return wrap_node(result.value, parent.document)
29
+
30
+ def move_to(self, parent) -> None:
31
+ """
32
+ Перенос (без копирования) в конец детей parent, в том числе в другой
33
+ документ. Этот объект остаётся тем же узлом; после переноса его
34
+ документ — parent.document, и закрытие прежнего документа узел не
35
+ затрагивает. Обёртки потомков, полученные раньше, тоже переходят
36
+ к новому документу. Корень документа и сам документ переносить
37
+ нельзя (BerestaError HIERARCHY_VIOLATION / INVALID_ARGUMENT).
38
+
39
+ Moves the node (without copying) to the end of the children of parent,
40
+ including into another document. This object stays the same node; after the
41
+ move its document is parent.document, and closing the former document does
42
+ not affect the node. The wrappers of descendants obtained earlier move to the
43
+ new document too. The document root and the document itself cannot be moved
44
+ (BerestaError HIERARCHY_VIOLATION / INVALID_ARGUMENT).
45
+ """
46
+ source = self.document
47
+ target = parent.document
48
+ check(LIB.beresta_node_move_to(self.handle, parent.handle))
49
+ if source is not target:
50
+ transfer_wrappers(self, source, target)