Skip to content

Commit e503e6a

Browse files
committed
Move tagname lowering from parser to builder
1 parent ec7f5f4 commit e503e6a

5 files changed

Lines changed: 61 additions & 49 deletions

File tree

src/hsd/dict.py

Lines changed: 32 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"""
1010
import re
1111
from typing import List, Tuple, Union
12-
from hsd.common import np, ATTRIB_SUFFIX, HSD_ATTRIB_SUFFIX, HsdError,\
12+
from hsd.common import HSD_ATTRIB_NAME, np, ATTRIB_SUFFIX, HSD_ATTRIB_SUFFIX, HsdError,\
1313
QUOTING_CHARS, SPECIAL_CHARS
1414
from hsd.eventhandler import HsdEventHandler, HsdEventPrinter
1515

@@ -42,14 +42,16 @@ class HsdDictBuilder(HsdEventHandler):
4242
4343
Args:
4444
flatten_data: Whether multiline data in the HSD input should be
45-
flattened into a single list. Othewise a list of lists is created,
46-
with one list for every line (default).
47-
include_hsd_attribs: Whether the HSD-attributes (processing related
48-
attributes, like original tag name, line information, etc.) should
49-
be stored.
45+
flattened into a single list. Othewise a list of lists is created, with one list for
46+
every line (default).
47+
lower_tag_names: Whether tag names should be all converted to lower case (to ease case
48+
insensitive processing). Default: False. If set and include_hsd_attribs is also set,
49+
the original tag names can be retrieved from the "name" hsd attributes.
50+
include_hsd_attribs: Whether the HSD-attributes (processing related attributes, like
51+
original tag name, line information, etc.) should be stored (default: False).
5052
"""
5153

52-
def __init__(self, flatten_data: bool = False,
54+
def __init__(self, flatten_data: bool = False, lower_tag_names: bool = False,
5355
include_hsd_attribs: bool = False):
5456
super().__init__()
5557
self._hsddict: dict = {}
@@ -58,6 +60,7 @@ def __init__(self, flatten_data: bool = False,
5860
self._data: Union[None, _DataType] = None
5961
self._attribs: List[Tuple[str, dict]] = []
6062
self._flatten_data: bool = flatten_data
63+
self._lower_tag_names: bool = lower_tag_names
6164
self._include_hsd_attribs: bool = include_hsd_attribs
6265

6366

@@ -79,46 +82,49 @@ def open_tag(self, tagname, attrib, hsdattrib):
7982
def close_tag(self, tagname):
8083
attrib, hsdattrib = self._attribs.pop(-1)
8184
parentblock = self._parentblocks.pop(-1)
85+
key = tagname.lower() if self._lower_tag_names else tagname
8286
prevcont = parentblock.get(tagname)
87+
8388
if self._data is not None:
8489
if prevcont is None:
85-
parentblock[tagname] = self._data
90+
parentblock[key] = self._data
8691
elif isinstance(prevcont, list) and len(prevcont) > 0 and isinstance(prevcont[0], dict):
8792
prevcont.append({None: self._data})
8893
elif isinstance(prevcont, dict):
89-
parentblock[tagname] = [prevcont, {None: self._data}]
94+
parentblock[key] = [prevcont, {None: self._data}]
9095
else:
91-
parentblock[tagname] = [{None: prevcont}, {None: self._data}]
96+
parentblock[key] = [{None: prevcont}, {None: self._data}]
9297
else:
9398
if prevcont is None:
94-
parentblock[tagname] = self._curblock
99+
parentblock[key] = self._curblock
95100
elif isinstance(prevcont, list) and len(prevcont) > 0 and isinstance(prevcont[0], dict):
96101
prevcont.append(self._curblock)
97102
elif isinstance(prevcont, dict):
98-
parentblock[tagname] = [prevcont, self._curblock]
103+
parentblock[key] = [prevcont, self._curblock]
99104
else:
100-
parentblock[tagname] = [{None: prevcont}, self._curblock]
105+
parentblock[key] = [{None: prevcont}, self._curblock]
101106

102-
if prevcont is None:
103-
if attrib:
104-
parentblock[tagname + ATTRIB_SUFFIX] = attrib
105-
if self._include_hsd_attribs:
106-
parentblock[tagname + HSD_ATTRIB_SUFFIX] = hsdattrib
107-
else:
108-
prevattrib = parentblock.get(tagname + ATTRIB_SUFFIX)
107+
if attrib and prevcont is None:
108+
parentblock[key + ATTRIB_SUFFIX] = attrib
109+
elif prevcont is not None:
110+
prevattrib = parentblock.get(key + ATTRIB_SUFFIX)
109111
if isinstance(prevattrib, list):
110112
prevattrib.append(attrib)
111113
else:
112-
parentblock[tagname + ATTRIB_SUFFIX] = [prevattrib, attrib]
113-
print(f"parentblock[{tagname} + {ATTRIB_SUFFIX}] = [{prevattrib}, {attrib}]")
114+
parentblock[key + ATTRIB_SUFFIX] = [prevattrib, attrib]
114115

115-
if self._include_hsd_attribs:
116-
prevhsdattrib = parentblock.get(tagname + HSD_ATTRIB_SUFFIX)
116+
if self._include_hsd_attribs:
117+
if self._lower_tag_names:
118+
hsdattrib = {} if hsdattrib is None else hsdattrib
119+
hsdattrib[HSD_ATTRIB_NAME] = tagname
120+
if prevcont is None:
121+
parentblock[key + HSD_ATTRIB_SUFFIX] = hsdattrib
122+
else:
123+
prevhsdattrib = parentblock.get(key + HSD_ATTRIB_SUFFIX)
117124
if isinstance(prevhsdattrib, list):
118125
prevhsdattrib.append(hsdattrib)
119126
else:
120-
parentblock[tagname + HSD_ATTRIB_SUFFIX] = [prevhsdattrib,
121-
hsdattrib]
127+
parentblock[key + HSD_ATTRIB_SUFFIX] = [prevhsdattrib, hsdattrib]
122128
self._curblock = parentblock
123129
self._data = None
124130

src/hsd/io.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,9 @@ def load(hsdfile: Union[TextIO, str], lower_tag_names: bool = False,
4343
Examples:
4444
See :func:`hsd.load_string` for examples of usage.
4545
"""
46-
dictbuilder = HsdDictBuilder(flatten_data=flatten_data,
46+
dictbuilder = HsdDictBuilder(lower_tag_names=lower_tag_names, flatten_data=flatten_data,
4747
include_hsd_attribs=include_hsd_attribs)
48-
parser = HsdParser(eventhandler=dictbuilder,
49-
lower_tag_names=lower_tag_names)
48+
parser = HsdParser(eventhandler=dictbuilder)
5049
if isinstance(hsdfile, str):
5150
with open(hsdfile, "r") as hsddescr:
5251
parser.parse(hsddescr)

src/hsd/parser.py

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,6 @@ class HsdParser:
2929
Arguments:
3030
eventhandler: Object which should handle the HSD-events triggered
3131
during parsing. When not specified, HsdEventPrinter() is used.
32-
lower_tag_names: Whether tag names should be lowered during parsing.
33-
If the option is set, the original tag name will be stored among
34-
the hsd attributes.
3532
3633
Examples:
3734
>>> from io import StringIO
@@ -53,8 +50,7 @@ class HsdParser:
5350
{'Temperature': 100, 'Temperature.attrib': 'Kelvin'}}}}}
5451
"""
5552

56-
def __init__(self, eventhandler: Optional[HsdEventHandler] = None,
57-
lower_tag_names: bool = False):
53+
def __init__(self, eventhandler: Optional[HsdEventHandler] = None):
5854
"""Initializes the parser.
5955
6056
Args:
@@ -79,7 +75,6 @@ def __init__(self, eventhandler: Optional[HsdEventHandler] = None,
7975
self._has_child = True # Whether current node has a child already
8076
self._has_text = False # whether current node contains text already
8177
self._oldbefore = "" # buffer for tagname
82-
self._lower_tag_names = lower_tag_names # whether tag names should be lower cased
8378

8479

8580
def parse(self, fobj: Union[TextIO, str]):
@@ -258,9 +253,6 @@ def _starttag(self, tagname, closeprev):
258253
if len(tagname_stripped.split()) > 1:
259254
self._error(SYNTAX_ERROR, (self._currline, self._currline))
260255
self._hsdattrib[common.HSD_ATTRIB_LINE] = self._currline
261-
if self._lower_tag_names:
262-
self._hsdattrib[common.HSD_ATTRIB_NAME] = tagname_stripped
263-
tagname_stripped = tagname_stripped.lower()
264256
self._eventhandler.open_tag(tagname_stripped, self._attrib,
265257
self._hsdattrib)
266258
self._opened_tags.append(

test/test_dict.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@
137137
_TESTS_NO_HSDATTRIB_CASES,
138138
ids=_TESTS_NO_HSDATTRIB_NAMES
139139
)
140-
def test_builder_nohsdattr(hsdstr, hsddict):
140+
def test_dict_builder_nohsdattr(hsdstr, hsddict):
141141
"""Test transformation from hsd to dictionary without HSD attributes."""
142142
dictbuilder = hsd.HsdDictBuilder(include_hsd_attribs=False)
143143
parser = hsd.HsdParser(eventhandler=dictbuilder)
@@ -151,7 +151,7 @@ def test_builder_nohsdattr(hsdstr, hsddict):
151151
_TESTS_HSDATTRIB_CASES,
152152
ids=_TESTS_HSDATTRIB_NAMES
153153
)
154-
def test_builder_hsdattr(hsdstr, hsddict):
154+
def test_dict_builder_hsdattr(hsdstr, hsddict):
155155
"""Test transformation from hsd to dictionary with HSD attributes."""
156156
dictbuilder = hsd.HsdDictBuilder(include_hsd_attribs=True)
157157
parser = hsd.HsdParser(eventhandler=dictbuilder)
@@ -165,10 +165,10 @@ def test_builder_hsdattr(hsdstr, hsddict):
165165
_TESTS_HSDATTRIB_LOWER_CASES,
166166
ids=_TESTS_HSDATTRIB_LOWER_NAMES
167167
)
168-
def test_builder_hsdattr_lower(hsdstr, hsddict):
168+
def test_dict_builder_hsdattr_lower(hsdstr, hsddict):
169169
"""Test transformation from hsd to dictionary with HSD attributes and case lowering."""
170-
dictbuilder = hsd.HsdDictBuilder(include_hsd_attribs=True)
171-
parser = hsd.HsdParser(eventhandler=dictbuilder, lower_tag_names=True)
170+
dictbuilder = hsd.HsdDictBuilder(include_hsd_attribs=True, lower_tag_names=True)
171+
parser = hsd.HsdParser(eventhandler=dictbuilder)
172172
fobj = io.StringIO(hsdstr)
173173
parser.parse(fobj)
174174
assert dictbuilder.hsddict == hsddict
@@ -179,7 +179,7 @@ def test_builder_hsdattr_lower(hsdstr, hsddict):
179179
_TESTS_HSDATTRIB_CASES,
180180
ids=_TESTS_HSDATTRIB_NAMES
181181
)
182-
def test_walker_hsdattr(hsdstr, hsddict):
182+
def test_dict_walker_hsdattr(hsdstr, hsddict):
183183
"""Test transformation from dictionary to string using HSD attributes."""
184184
output = io.StringIO()
185185
formatter = hsd.HsdFormatter(output, use_hsd_attribs=True)
@@ -193,7 +193,7 @@ def test_walker_hsdattr(hsdstr, hsddict):
193193
_TESTS_HSDATTRIB_LOWER_CASES,
194194
ids=_TESTS_HSDATTRIB_LOWER_NAMES
195195
)
196-
def test_walker_hsdattr_lower(hsdstr, hsddict):
196+
def test_dict_walker_hsdattr_lower(hsdstr, hsddict):
197197
"""Test transformation from dictionary to string using HSD attributes."""
198198
output = io.StringIO()
199199
formatter = hsd.HsdFormatter(output, use_hsd_attribs=True)

test/test_parser.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,19 @@
5050
]
5151
)
5252
),
53+
(
54+
"Variable", (
55+
"""$Variable = 12\nValue = $Variable\n""",
56+
[
57+
(_OPEN_TAG_EVENT, "$Variable", None, {_HSD_LINE: 0, _HSD_EQUAL: True}),
58+
(_ADD_TEXT_EVENT, "12"),
59+
(_CLOSE_TAG_EVENT, "$Variable"),
60+
(_OPEN_TAG_EVENT, "Value", None, {_HSD_LINE: 1, _HSD_EQUAL: True}),
61+
(_ADD_TEXT_EVENT, "$Variable"),
62+
(_CLOSE_TAG_EVENT, "Value")
63+
]
64+
)
65+
),
5366
]
5467

5568
_VALID_TEST_NAMES, _VALID_TEST_CASES = zip(*_VALID_TESTS)
@@ -87,8 +100,8 @@ class _TestEventHandler(hsd.HsdEventHandler):
87100
def __init__(self):
88101
self.events = []
89102

90-
def open_tag(self, tagname, attrib, hsdoptions):
91-
self.events.append((_OPEN_TAG_EVENT, tagname, attrib, hsdoptions))
103+
def open_tag(self, tagname, attrib, hsdattrib):
104+
self.events.append((_OPEN_TAG_EVENT, tagname, attrib, hsdattrib))
92105

93106
def close_tag(self, tagname):
94107
self.events.append((_CLOSE_TAG_EVENT, tagname))
@@ -102,7 +115,8 @@ def add_text(self, text):
102115
_VALID_TEST_CASES,
103116
ids=_VALID_TEST_NAMES
104117
)
105-
def test_valid_parser_events(hsd_input, expected_events):
118+
def test_parser_events(hsd_input, expected_events):
119+
"""Test valid parser events"""
106120
testhandler = _TestEventHandler()
107121
parser = hsd.HsdParser(eventhandler=testhandler)
108122
hsdfile = io.StringIO(hsd_input)
@@ -115,7 +129,8 @@ def test_valid_parser_events(hsd_input, expected_events):
115129
_FAILING_TEST_CASES,
116130
ids=_FAILING_TEST_NAMES
117131
)
118-
def test_invalid_parser_events(hsd_input):
132+
def test_parser_exceptions(hsd_input):
133+
"""Test exception raised by the parser"""
119134
testhandler = _TestEventHandler()
120135
parser = hsd.HsdParser(eventhandler=testhandler)
121136
hsdfile = io.StringIO(hsd_input)

0 commit comments

Comments
 (0)