From 62aee0acdcfd68b4e6e7700722ebcd92e9fea83b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=99=EC=A3=BC?= Date: Wed, 26 May 2021 11:57:24 +0900 Subject: [PATCH 01/13] Start Implementing `Node` --- python/dom/Node.py | 8 ++++++++ python/dom/NotImplemented/Node.py | 2 -- python/dom/NotImplemented/__init__.py | 1 - python/dom/__init__.py | 1 + python/dom/type_checking.py | 1 + 5 files changed, 10 insertions(+), 3 deletions(-) create mode 100644 python/dom/Node.py delete mode 100644 python/dom/NotImplemented/Node.py diff --git a/python/dom/Node.py b/python/dom/Node.py new file mode 100644 index 0000000..a9a0393 --- /dev/null +++ b/python/dom/Node.py @@ -0,0 +1,8 @@ +class Node: + """Interface `Node` + + The `Node` interface is the primary datatype for the entire Document Object Model. It represents a single node in the document tree. While all objects implementing the `Node` interface expose methods for dealing with children, not all objects implementing the `Node` interface may have children. For example, `Text` nodes may not have children, and adding children to such nodes results in a `DOMException` being raised. + + The attributes `nodeName`, `nodeValue` and attributes are included as a mechanism to get at node information without casting down to the specific derived interface. In cases where there is no obvious mapping of these attributes for a specific `nodeType` (e.g., `nodeValue` for an Element or `attributes` for a Comment), this returns `None`. Note that the specialized interfaces may contain additional and more convenient mechanisms to get and set the relevant information. + """ + pass diff --git a/python/dom/NotImplemented/Node.py b/python/dom/NotImplemented/Node.py deleted file mode 100644 index e5d5f12..0000000 --- a/python/dom/NotImplemented/Node.py +++ /dev/null @@ -1,2 +0,0 @@ -class Node: - pass \ No newline at end of file diff --git a/python/dom/NotImplemented/__init__.py b/python/dom/NotImplemented/__init__.py index eba7af7..0e362b7 100644 --- a/python/dom/NotImplemented/__init__.py +++ b/python/dom/NotImplemented/__init__.py @@ -12,7 +12,6 @@ from python.dom.NotImplemented.Entity import Entity from python.dom.NotImplemented.EntityReference import EntityReference from python.dom.NotImplemented.NamedNodeMap import NamedNodeMap -from python.dom.NotImplemented.Node import Node from python.dom.NotImplemented.NodeList import NodeList from python.dom.NotImplemented.Notation import Notation from python.dom.NotImplemented.ProcessingInstruction import ProcessingInstruction diff --git a/python/dom/__init__.py b/python/dom/__init__.py index 63f0114..4afc3ec 100644 --- a/python/dom/__init__.py +++ b/python/dom/__init__.py @@ -2,3 +2,4 @@ from python.dom.DOMException import DOMException from python.dom.DOMString import DOMString +from python.dom.Node import Node diff --git a/python/dom/type_checking.py b/python/dom/type_checking.py index c46815c..be5b87a 100644 --- a/python/dom/type_checking.py +++ b/python/dom/type_checking.py @@ -6,6 +6,7 @@ from python.dom.NotImplemented import * from python.dom.DOMException import DOMException from python.dom.DOMString import DOMString + from python.dom.Node import Node AnyNode = Union[ Node, From 91d67b5d4c1d231d74ec135558fcbf388bb11716 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=99=EC=A3=BC?= Date: Thu, 27 May 2021 10:00:44 +0900 Subject: [PATCH 02/13] Add definition group `NodeType` --- python/dom/Node.py | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/python/dom/Node.py b/python/dom/Node.py index a9a0393..247ca36 100644 --- a/python/dom/Node.py +++ b/python/dom/Node.py @@ -1,3 +1,25 @@ +import enum + + +class NodeType(enum.IntEnum): + """Definition group `NodeType` + + An integer indicating which type of node this is. + """ + ELEMENT_NODE = 1 # The node is a `Element`. + ATTRIBUTE_NODE = 2 # The node is an `Attr`. + TEXT_NODE = 3 # The node is a `Text` node. + CDATA_SECTION_NODE = 4 # The node is a `CDATASection`. + ENTITY_REFERENCE_NODE = 5 # The node is an `EntityReference`. + ENTITY_NODE = 6 # The node is an `Entity`. + PROCESSING_INSTRUCTION_NODE = 7 # The node is a `ProcessingInstruction`. + COMMENT_NODE = 8 # The node is a `Comment`. + DOCUMENT_NODE = 9 # The node is a `Document`. + DOCUMENT_TYPE_NODE = 10 # The node is a `DocumentType`. + DOCUMENT_FRAGMENT_NODE = 11 # The node is a `DocumentFragment`. + NOTATION_NODE = 12 # The node is a `Notation`. + + class Node: """Interface `Node` @@ -5,4 +27,15 @@ class Node: The attributes `nodeName`, `nodeValue` and attributes are included as a mechanism to get at node information without casting down to the specific derived interface. In cases where there is no obvious mapping of these attributes for a specific `nodeType` (e.g., `nodeValue` for an Element or `attributes` for a Comment), this returns `None`. Note that the specialized interfaces may contain additional and more convenient mechanisms to get and set the relevant information. """ - pass + ATTRIBUTE_NODE = NodeType.ATTRIBUTE_NODE + TEXT_NODE = NodeType.TEXT_NODE + ELEMENT_NODE = NodeType.ELEMENT_NODE + CDATA_SECTION_NODE = NodeType.CDATA_SECTION_NODE + ENTITY_REFERENCE_NODE = NodeType.ENTITY_REFERENCE_NODE + ENTITY_NODE = NodeType.ENTITY_NODE + PROCESSING_INSTRUCTION_NODE = NodeType.PROCESSING_INSTRUCTION_NODE + COMMENT_NODE = NodeType.COMMENT_NODE + DOCUMENT_NODE = NodeType.DOCUMENT_NODE + DOCUMENT_TYPE_NODE = NodeType.DOCUMENT_TYPE_NODE + DOCUMENT_FRAGMENT_NODE = NodeType.DOCUMENT_FRAGMENT_NODE + NOTATION_NODE = NodeType.NOTATION_NODE From 41bb0945767d593d3b928623f28a6e1e801061f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=99=EC=A3=BC?= Date: Thu, 27 May 2021 10:02:17 +0900 Subject: [PATCH 03/13] Create class constructor --- python/dom/Node.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/python/dom/Node.py b/python/dom/Node.py index 247ca36..5027129 100644 --- a/python/dom/Node.py +++ b/python/dom/Node.py @@ -39,3 +39,6 @@ class Node: DOCUMENT_TYPE_NODE = NodeType.DOCUMENT_TYPE_NODE DOCUMENT_FRAGMENT_NODE = NodeType.DOCUMENT_FRAGMENT_NODE NOTATION_NODE = NodeType.NOTATION_NODE + + def __init__(self) -> None: + pass From 066dbcaf3e4efa17c06cfc3d81638d5150ae441f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=99=EC=A3=BC?= Date: Thu, 27 May 2021 10:22:39 +0900 Subject: [PATCH 04/13] Add readonly attribute `node_type` --- python/dom/Node.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/python/dom/Node.py b/python/dom/Node.py index 5027129..878974f 100644 --- a/python/dom/Node.py +++ b/python/dom/Node.py @@ -1,4 +1,7 @@ import enum +from typing import AnyStr + +from python.dom.DOMString import DOMString class NodeType(enum.IntEnum): @@ -25,7 +28,7 @@ class Node: The `Node` interface is the primary datatype for the entire Document Object Model. It represents a single node in the document tree. While all objects implementing the `Node` interface expose methods for dealing with children, not all objects implementing the `Node` interface may have children. For example, `Text` nodes may not have children, and adding children to such nodes results in a `DOMException` being raised. - The attributes `nodeName`, `nodeValue` and attributes are included as a mechanism to get at node information without casting down to the specific derived interface. In cases where there is no obvious mapping of these attributes for a specific `nodeType` (e.g., `nodeValue` for an Element or `attributes` for a Comment), this returns `None`. Note that the specialized interfaces may contain additional and more convenient mechanisms to get and set the relevant information. + The attributes `nodeName`, `nodeValue` and attributes are included as a mechanism to get at node information without casting down to the specific derived interface. In cases where there is no obvious mapping of these attributes for a specific `node_type` (e.g., `nodeValue` for an Element or `attributes` for a Comment), this returns `None`. Note that the specialized interfaces may contain additional and more convenient mechanisms to get and set the relevant information. """ ATTRIBUTE_NODE = NodeType.ATTRIBUTE_NODE TEXT_NODE = NodeType.TEXT_NODE @@ -40,5 +43,12 @@ class Node: DOCUMENT_FRAGMENT_NODE = NodeType.DOCUMENT_FRAGMENT_NODE NOTATION_NODE = NodeType.NOTATION_NODE - def __init__(self) -> None: - pass + def __init__(self, + node_type: NodeType) -> None: + self._node_type: NodeType = node_type + + @property + def node_type(self) -> NodeType: + """A code representing the type of the underlying object. + """ + return self._node_type From bfc687853237de7229357cc3c0cd6f647fa65d2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=99=EC=A3=BC?= Date: Thu, 27 May 2021 10:26:40 +0900 Subject: [PATCH 05/13] Add readonly attribute `node_name` (abstract) --- python/dom/Node.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/python/dom/Node.py b/python/dom/Node.py index 878974f..cccc83e 100644 --- a/python/dom/Node.py +++ b/python/dom/Node.py @@ -28,7 +28,7 @@ class Node: The `Node` interface is the primary datatype for the entire Document Object Model. It represents a single node in the document tree. While all objects implementing the `Node` interface expose methods for dealing with children, not all objects implementing the `Node` interface may have children. For example, `Text` nodes may not have children, and adding children to such nodes results in a `DOMException` being raised. - The attributes `nodeName`, `nodeValue` and attributes are included as a mechanism to get at node information without casting down to the specific derived interface. In cases where there is no obvious mapping of these attributes for a specific `node_type` (e.g., `nodeValue` for an Element or `attributes` for a Comment), this returns `None`. Note that the specialized interfaces may contain additional and more convenient mechanisms to get and set the relevant information. + The attributes `node_name`, `nodeValue` and attributes are included as a mechanism to get at node information without casting down to the specific derived interface. In cases where there is no obvious mapping of these attributes for a specific `node_type` (e.g., `nodeValue` for an Element or `attributes` for a Comment), this returns `None`. Note that the specialized interfaces may contain additional and more convenient mechanisms to get and set the relevant information. """ ATTRIBUTE_NODE = NodeType.ATTRIBUTE_NODE TEXT_NODE = NodeType.TEXT_NODE @@ -47,6 +47,12 @@ def __init__(self, node_type: NodeType) -> None: self._node_type: NodeType = node_type + @property + def node_name(self) -> DOMString: + """The name of this node, depending on its type. + """ + raise NotImplementedError + @property def node_type(self) -> NodeType: """A code representing the type of the underlying object. From 6e21a59dc9c85955c7021f505c437a04e590d3ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=99=EC=A3=BC?= Date: Thu, 27 May 2021 10:46:31 +0900 Subject: [PATCH 06/13] Add protected boolean attribute `_read_only` --- python/dom/Node.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/python/dom/Node.py b/python/dom/Node.py index cccc83e..4698343 100644 --- a/python/dom/Node.py +++ b/python/dom/Node.py @@ -44,8 +44,10 @@ class Node: NOTATION_NODE = NodeType.NOTATION_NODE def __init__(self, - node_type: NodeType) -> None: + node_type: NodeType, + read_only: bool = False) -> None: self._node_type: NodeType = node_type + self._read_only: bool = read_only @property def node_name(self) -> DOMString: From 1508e367dfee59092df6aadc726127ad13d13eaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=99=EC=A3=BC?= Date: Thu, 27 May 2021 11:19:49 +0900 Subject: [PATCH 07/13] Add attribute `node_value` (abstract) --- python/dom/Node.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/python/dom/Node.py b/python/dom/Node.py index 4698343..527dc42 100644 --- a/python/dom/Node.py +++ b/python/dom/Node.py @@ -1,6 +1,7 @@ import enum from typing import AnyStr +from python.dom.DOMException import DOMException from python.dom.DOMString import DOMString @@ -28,7 +29,7 @@ class Node: The `Node` interface is the primary datatype for the entire Document Object Model. It represents a single node in the document tree. While all objects implementing the `Node` interface expose methods for dealing with children, not all objects implementing the `Node` interface may have children. For example, `Text` nodes may not have children, and adding children to such nodes results in a `DOMException` being raised. - The attributes `node_name`, `nodeValue` and attributes are included as a mechanism to get at node information without casting down to the specific derived interface. In cases where there is no obvious mapping of these attributes for a specific `node_type` (e.g., `nodeValue` for an Element or `attributes` for a Comment), this returns `None`. Note that the specialized interfaces may contain additional and more convenient mechanisms to get and set the relevant information. + The attributes `node_name`, `node_value` and attributes are included as a mechanism to get at node information without casting down to the specific derived interface. In cases where there is no obvious mapping of these attributes for a specific `node_type` (e.g., `node_value` for an Element or `attributes` for a Comment), this returns `None`. Note that the specialized interfaces may contain additional and more convenient mechanisms to get and set the relevant information. """ ATTRIBUTE_NODE = NodeType.ATTRIBUTE_NODE TEXT_NODE = NodeType.TEXT_NODE @@ -55,6 +56,23 @@ def node_name(self) -> DOMString: """ raise NotImplementedError + @property + def node_value(self) -> DOMString: + """The value of this node, depending on its type. + + Raises: + DOMException: + - `NO_MODIFICATION_ALLOWED_ERR`: Raised when the node is readonly. (on setting) + - `DOMSTRING_SIZE_ERR`: Raised when it would return more characters than fit in a `DOMString` variable on the implementation platform. (on retrieval) + """ + raise NotImplementedError + + @node_value.setter + def node_value(self, value: AnyStr) -> None: + if self._read_only: + raise DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR) + raise NotImplementedError + @property def node_type(self) -> NodeType: """A code representing the type of the underlying object. From 16508a2a5998f419277a8b9235a66f6f9073e286 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=99=EC=A3=BC?= Date: Thu, 27 May 2021 12:39:01 +0900 Subject: [PATCH 08/13] Add readonly attribute `parent_node` also create a method * `_set_parent_node()` --- python/dom/Node.py | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/python/dom/Node.py b/python/dom/Node.py index 527dc42..1c74a95 100644 --- a/python/dom/Node.py +++ b/python/dom/Node.py @@ -1,8 +1,10 @@ import enum -from typing import AnyStr +import warnings +from typing import AnyStr, Optional from python.dom.DOMException import DOMException from python.dom.DOMString import DOMString +from python.dom.type_checking import AnyNode class NodeType(enum.IntEnum): @@ -46,9 +48,12 @@ class Node: def __init__(self, node_type: NodeType, + parent_node: Optional[AnyNode] = None, read_only: bool = False) -> None: + self._set_parent_node(parent_node, node_type) self._node_type: NodeType = node_type self._read_only: bool = read_only + self._parent_node: Optional[AnyNode] # parent node should be set with `_set_parent_node` method. @property def node_name(self) -> DOMString: @@ -78,3 +83,28 @@ def node_type(self) -> NodeType: """A code representing the type of the underlying object. """ return self._node_type + + @property + def parent_node(self) -> Optional[AnyNode]: + """The parent of this node. All nodes, except `Document`, `DocumentFragment`, and `Attr` may have a parent. However, if a node has just been created and not yet added to the tree, or if it has been removed from the tree, this is null. + """ + return self._parent_node + + def _set_parent_node(self, + parent_node: Optional[AnyNode], + node_type: Optional[NodeType] = None) -> None: + """Sets parent node of this node. If given `parent_node` is a node that cannot have its parent, this method will raise a warning. + + Args: + parent_node: A node which is the parent of this node. + node_type: Optional; Value to be assumed as the type of this node. If `node_type` is not given, it will be replaced with the type of this node. + """ + if node_type is None: + node_type = self.node_type + if parent_node is not None: + if self.node_type in [Node.DOCUMENT_NODE, + Node.DOCUMENT_FRAGMENT_NODE, + Node.ATTRIBUTE_NODE]: + warnings.warn('Document, DocumentFragment, ' + 'and Attr may not have a parent.') + self._parent_node = parent_node From 18953265f07d6fa28a3242750c609433b229dc2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=99=EC=A3=BC?= Date: Thu, 27 May 2021 12:42:10 +0900 Subject: [PATCH 09/13] Add readonly attribute `child_nodes` --- python/dom/Node.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/python/dom/Node.py b/python/dom/Node.py index 1c74a95..e9bf19a 100644 --- a/python/dom/Node.py +++ b/python/dom/Node.py @@ -4,6 +4,7 @@ from python.dom.DOMException import DOMException from python.dom.DOMString import DOMString +from python.dom.NotImplemented.NodeList import NodeList from python.dom.type_checking import AnyNode @@ -54,6 +55,8 @@ def __init__(self, self._node_type: NodeType = node_type self._read_only: bool = read_only self._parent_node: Optional[AnyNode] # parent node should be set with `_set_parent_node` method. + self._child_nodes: NodeList = NodeList() + @property def node_name(self) -> DOMString: @@ -108,3 +111,9 @@ def _set_parent_node(self, warnings.warn('Document, DocumentFragment, ' 'and Attr may not have a parent.') self._parent_node = parent_node + + @property + def child_nodes(self) -> NodeList: + """A `NodeList` that contains all children of this node. If there are no children, this is a `NodeList` containing no nodes. The content of the returned `NodeList` is "live" in the sense that, for instance, changes to the children of the node object that it was created from are immediately reflected in the nodes returned by the `NodeList` accessors; it is not a static snapshot of the content of the node. This is true for every `NodeList`, including the ones returned by the `getElementsByTagName` method. + """ + return self._child_nodes From 8d6812ce6f7541c18528b521d40aaef6c883aa5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=99=EC=A3=BC?= Date: Thu, 27 May 2021 12:50:40 +0900 Subject: [PATCH 10/13] Add readonly attributes `first_child`, `last_child` --- python/dom/Node.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/python/dom/Node.py b/python/dom/Node.py index e9bf19a..34a4a7a 100644 --- a/python/dom/Node.py +++ b/python/dom/Node.py @@ -117,3 +117,21 @@ def child_nodes(self) -> NodeList: """A `NodeList` that contains all children of this node. If there are no children, this is a `NodeList` containing no nodes. The content of the returned `NodeList` is "live" in the sense that, for instance, changes to the children of the node object that it was created from are immediately reflected in the nodes returned by the `NodeList` accessors; it is not a static snapshot of the content of the node. This is true for every `NodeList`, including the ones returned by the `getElementsByTagName` method. """ return self._child_nodes + + @property + def first_child(self) -> Optional[AnyNode]: + """The first child of this node. If there is no such node, this returns `None`. + """ + if self.child_nodes.length == 0: + return None + else: + return self.child_nodes.item(0) + + @property + def last_child(self) -> Optional[AnyNode]: + """The last child of this node. If there is no such node, this returns `None`. + """ + if self.child_nodes.length == 0: + return None + else: + return self.child_nodes.item(self.child_nodes.length-1) From 59d286cbbaa0c594b048217c4fb19c45f6ab851c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=99=EC=A3=BC?= Date: Thu, 27 May 2021 12:58:38 +0900 Subject: [PATCH 11/13] Add readonly attributes `previous_sibling`, `next_sibling` --- python/dom/Node.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/python/dom/Node.py b/python/dom/Node.py index 34a4a7a..a4c61f1 100644 --- a/python/dom/Node.py +++ b/python/dom/Node.py @@ -56,6 +56,8 @@ def __init__(self, self._read_only: bool = read_only self._parent_node: Optional[AnyNode] # parent node should be set with `_set_parent_node` method. self._child_nodes: NodeList = NodeList() + self._previous_sibling: Optional[AnyNode] = None + self._next_sibling: Optional[AnyNode] = None @property @@ -135,3 +137,15 @@ def last_child(self) -> Optional[AnyNode]: return None else: return self.child_nodes.item(self.child_nodes.length-1) + + @property + def previous_sibling(self) -> Optional[AnyNode]: + """The node immediately preceding this node. If there is no such node, this returns `None`. + """ + return self._previous_sibling + + @property + def next_sibling(self) -> Optional[AnyNode]: + """The node immediately following this node. If there is no such node, this returns `None`. + """ + return self._next_sibling From aa05fd403348bbc15f071c0eadddca3b34dcf88d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=99=EC=A3=BC?= Date: Thu, 27 May 2021 13:00:12 +0900 Subject: [PATCH 12/13] Add readonly attribute `attributes` (abstract) --- python/dom/Node.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/python/dom/Node.py b/python/dom/Node.py index a4c61f1..c030c1d 100644 --- a/python/dom/Node.py +++ b/python/dom/Node.py @@ -1,4 +1,5 @@ import enum +from python.dom.NotImplemented.NamedNodeMap import NamedNodeMap import warnings from typing import AnyStr, Optional @@ -149,3 +150,9 @@ def next_sibling(self) -> Optional[AnyNode]: """The node immediately following this node. If there is no such node, this returns `None`. """ return self._next_sibling + + @property + def attributes(self) -> Optional[NamedNodeMap]: + """A `NamedNodeMap` containing the attributes of this node (if it is an `Element`) or `None` otherwise. + """ + raise NotImplementedError From 634bc3ddf7f77f8f0c4303cfe65b22270db1eede Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=99=EC=A3=BC?= Date: Thu, 27 May 2021 13:04:54 +0900 Subject: [PATCH 13/13] Add readonly attribute `owner_document` --- python/dom/Node.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/python/dom/Node.py b/python/dom/Node.py index c030c1d..5f6fdc4 100644 --- a/python/dom/Node.py +++ b/python/dom/Node.py @@ -1,10 +1,11 @@ import enum -from python.dom.NotImplemented.NamedNodeMap import NamedNodeMap import warnings from typing import AnyStr, Optional +from python.dom.NotImplemented.Document import Document from python.dom.DOMException import DOMException from python.dom.DOMString import DOMString +from python.dom.NotImplemented.NamedNodeMap import NamedNodeMap from python.dom.NotImplemented.NodeList import NodeList from python.dom.type_checking import AnyNode @@ -49,6 +50,7 @@ class Node: NOTATION_NODE = NodeType.NOTATION_NODE def __init__(self, + owner_document: Document, node_type: NodeType, parent_node: Optional[AnyNode] = None, read_only: bool = False) -> None: @@ -59,6 +61,7 @@ def __init__(self, self._child_nodes: NodeList = NodeList() self._previous_sibling: Optional[AnyNode] = None self._next_sibling: Optional[AnyNode] = None + self._owner_document: Document = owner_document @property @@ -156,3 +159,9 @@ def attributes(self) -> Optional[NamedNodeMap]: """A `NamedNodeMap` containing the attributes of this node (if it is an `Element`) or `None` otherwise. """ raise NotImplementedError + + @property + def owner_document(self) -> Optional[Document]: + """The `Document` object associated with this node. This is also the `Document` object used to create new nodes. When this node is a `Document` this is `None`. + """ + return self._owner_document