From 48a3a737b01e3a4d9244659090970b9a12fea25d Mon Sep 17 00:00:00 2001 From: SysFAB Date: Tue, 26 Aug 2025 18:29:52 +0300 Subject: [PATCH 1/6] feat: add .is_closed attribute to an XMLElement --- osmxml/element.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/osmxml/element.py b/osmxml/element.py index 2ead995..a39b3fe 100644 --- a/osmxml/element.py +++ b/osmxml/element.py @@ -13,6 +13,7 @@ def __init__( self._name = name self._attributes = attributes.copy() if attributes is not None else [] self._children = children.copy() if children is not None else [] + self._is_closed = True @property def name(self) -> str: @@ -41,6 +42,15 @@ def children(self, children: List[XML]): self._children = children + @property + def is_closed(self) -> bool: + return self.is_closed + + @is_closed.setter + def is_closed(self, value: bool): + self._is_closed = value + + def add_attribute(self, attribute: XMLAttribute): self._attributes.append(attribute) From 1371d4d948e58999e43b8553d58f8abd4ccafbda Mon Sep 17 00:00:00 2001 From: SysFAB Date: Tue, 26 Aug 2025 18:32:26 +0300 Subject: [PATCH 2/6] feat: add unclosed elements support to to_string method in XMLElement --- osmxml/element.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osmxml/element.py b/osmxml/element.py index a39b3fe..1af0513 100644 --- a/osmxml/element.py +++ b/osmxml/element.py @@ -90,7 +90,12 @@ def to_string(self) -> str: children_str = "".join(list_children_str) - element_str = "<{name}{attrs}>{children}\n".format( + closed_template = "<{name}{attrs}>{children}\n" + non_closed_template = "<{name}{attrs}>{children}\n" + + template = closed_template if self.is_closed else non_closed_template + + element_str = template.format( name=self.name, attrs=attrs_str, children=children_str From c93b0ee59c29f0f77a23bec04314907a7ebe79e8 Mon Sep 17 00:00:00 2001 From: SysFAB Date: Tue, 26 Aug 2025 18:33:15 +0300 Subject: [PATCH 3/6] feat: add is_closed to __repr__ of XMLElement --- osmxml/element.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osmxml/element.py b/osmxml/element.py index 1af0513..83c9942 100644 --- a/osmxml/element.py +++ b/osmxml/element.py @@ -116,9 +116,11 @@ def __repr__(self): repr = 'XMLElement(name="{name}",' repr = "".join([repr, " attributes=len({attrs_len}),"]) repr = "".join([repr, " children=len({children_len}))"]) + repr = "".join([repr, " is_closed={is_closed})"]) repr = repr.format( name=self.name, attrs_len=len(self.attributes), children_len=len(self.children), + is_closed=self.is_closed, ) return repr From f6f96de95be4fe59f97a998fdbec9102cdde524d Mon Sep 17 00:00:00 2001 From: SysFAB Date: Tue, 26 Aug 2025 18:59:58 +0300 Subject: [PATCH 4/6] feat: starting to implement stream parser --- osmxml/__init__.py | 2 ++ osmxml/stream_parser.py | 71 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 osmxml/stream_parser.py diff --git a/osmxml/__init__.py b/osmxml/__init__.py index e06e557..11d73e5 100644 --- a/osmxml/__init__.py +++ b/osmxml/__init__.py @@ -6,6 +6,7 @@ from .attribute import XMLAttribute from .text import XMLTextElement from .parser import XMLParser +from .stream_parser import XMLStreamParser __all__ = [ "XML", @@ -13,4 +14,5 @@ "XMLAttribute", "XMLTextElement", "XMLParser", + "XMLStreamParser", ] diff --git a/osmxml/stream_parser.py b/osmxml/stream_parser.py new file mode 100644 index 0000000..b9989c5 --- /dev/null +++ b/osmxml/stream_parser.py @@ -0,0 +1,71 @@ +from .element import XMLElement +from .attribute import XMLAttribute +from .text import XMLTextElement + +class XMLStreamParser: + def __init__(self): + self._element_buffer = [] + self._leftovers = "" + + self._current_element = None + self._current_attribute = None + + def _get_symbol(self, symbol: str) -> bool: + return self._leftovers.startswith(symbol) + + def _get_text(self) -> tuple[bool, str]: + if self._leftovers[0] in "<>": + return False, None + + i = 0 + while i < len(self._leftovers) and self._leftovers[i] not in "<> ": + i += 1 + + text = self._leftovers[:i] + return True, text + + def _consume_symbol(self, symbol: str) -> bool: + if self._leftovers.startswith(symbol): + self._leftovers = self._leftovers[len(symbol):] + return True + return False + + def _consume_text(self, text) -> bool: + if self._leftovers.startswith(text): + self._leftovers = self._leftovers[len(text):] + return True + return False + + def _consume_identation(self) -> bool: + while len(self._leftovers) > 0: + if self._leftovers[0] in [" ", "\n", "\t"]: + self._leftovers = self._leftovers[1:] + else: + break + + def _is_last_element_closed(self) -> bool: + return self._element_buffer[-1].is_closed + + + def parse(self, xml_string: str) -> List[XMLElement]: + if (not xml_string): + return [] + + self._leftovers += xml_string + + if (len(self._element_buffer) == 0) or (self._is_last_element_closed()): + self._consume_identation() + + if (self._get_symbol("<")): + + # Closing element + if (self._get_symbol("/")): + # Closed element + self._consume_symbol("/") + name = self._get_text() + self._consume_symbol(">") + return self._element_buffer.pop() + else: + # Continue parsing current element + + From 340412e65ae287471f7bb7c2123c64cdcf33f0ed Mon Sep 17 00:00:00 2001 From: SysFAB Date: Tue, 26 Aug 2025 19:08:38 +0300 Subject: [PATCH 5/6] fix: fix (just to launch) --- osmxml/stream_parser.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osmxml/stream_parser.py b/osmxml/stream_parser.py index b9989c5..94591c3 100644 --- a/osmxml/stream_parser.py +++ b/osmxml/stream_parser.py @@ -1,3 +1,5 @@ +from typing import Optional, List + from .element import XMLElement from .attribute import XMLAttribute from .text import XMLTextElement @@ -67,5 +69,6 @@ def parse(self, xml_string: str) -> List[XMLElement]: return self._element_buffer.pop() else: # Continue parsing current element + pass From f35dac5cf0ae9e05e4641ecb26f436ee23ac35fb Mon Sep 17 00:00:00 2001 From: SysFAB Date: Tue, 26 Aug 2025 19:10:14 +0300 Subject: [PATCH 6/6] fix: fix recursion in element.py --- osmxml/element.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osmxml/element.py b/osmxml/element.py index 83c9942..0a30d64 100644 --- a/osmxml/element.py +++ b/osmxml/element.py @@ -44,7 +44,7 @@ def children(self, children: List[XML]): @property def is_closed(self) -> bool: - return self.is_closed + return self._is_closed @is_closed.setter def is_closed(self, value: bool):