Skip to content

Commit 4f7e74d

Browse files
committed
Merge branch 'fixDuplicates'
2 parents ffdb9e3 + 67ac737 commit 4f7e74d

File tree

6 files changed

+52
-36
lines changed

6 files changed

+52
-36
lines changed

src/hsd/common.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,10 @@
99
"""
1010

1111

12-
class HsdException(Exception):
12+
class HsdError(Exception):
1313
"""Base class for exceptions in the HSD package."""
1414

1515

16-
class HsdParserError(HsdException):
17-
"""Base class for parser related errors."""
18-
19-
2016
def unquote(txt):
2117
"""Giving string without quotes if enclosed in those."""
2218
if len(txt) >= 2 and (txt[0] in "\"'") and txt[-1] == txt[0]:

src/hsd/dictbuilder.py

Lines changed: 39 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
Contains an event-driven builder for dictionary based (JSON-like) structure
99
"""
1010
import re
11-
from .common import ATTRIB_SUFFIX, HSD_ATTRIB_SUFFIX
11+
from .common import ATTRIB_SUFFIX, HSD_ATTRIB_SUFFIX, HsdError
1212
from .eventhandler import HsdEventHandler
1313

1414

@@ -40,9 +40,9 @@ def __init__(self, flatten_data: bool = False,
4040
include_hsd_attribs: bool = False):
4141
super().__init__()
4242
self._hsddict = {}
43-
self._curblock = self._hsddict
43+
self._content = self._hsddict # Content obtained for the current node
4444
self._parentblocks = []
45-
self._data = None
45+
self._attribs = []
4646
self._flatten_data = flatten_data
4747
self._include_hsd_attribs = include_hsd_attribs
4848

@@ -54,34 +54,51 @@ def hsddict(self):
5454

5555

5656
def open_tag(self, tagname, attrib, hsdattrib):
57-
if attrib is not None:
58-
self._curblock[tagname + ATTRIB_SUFFIX] = attrib
59-
if self._include_hsd_attribs and hsdattrib is not None:
60-
self._curblock[tagname + HSD_ATTRIB_SUFFIX] = hsdattrib
61-
self._parentblocks.append(self._curblock)
62-
self._curblock = {}
57+
self._attribs.append((attrib, hsdattrib))
58+
content = {} if self._content is None else self._content
59+
self._parentblocks.append(content)
60+
self._content = None
6361

6462

6563
def close_tag(self, tagname):
64+
attrib, hsdattrib = self._attribs.pop(-1)
6665
parentblock = self._parentblocks.pop(-1)
67-
prevcontent = parentblock.get(tagname)
68-
if prevcontent is not None and not isinstance(prevcontent, list):
69-
prevcontent = [prevcontent]
70-
parentblock[tagname] = prevcontent
71-
if self._data is None:
72-
content = self._curblock
73-
else:
74-
content = self._data
75-
self._data = None
76-
if prevcontent is None:
66+
prevcont = parentblock.get(tagname)
67+
if prevcont is not None:
68+
if isinstance(prevcont, dict) and isinstance(self._content, dict):
69+
prevcont = [prevcont]
70+
parentblock[tagname] = prevcont
71+
elif not (isinstance(prevcont, list)
72+
and isinstance(prevcont[0], dict)):
73+
msg = f"Invalid duplicate occurance of node '{tagname}'"
74+
raise HsdError(msg)
75+
content = {} if self._content is None else self._content
76+
if prevcont is None:
7777
parentblock[tagname] = content
78+
if attrib:
79+
parentblock[tagname + ATTRIB_SUFFIX] = attrib
80+
if self._include_hsd_attribs:
81+
parentblock[tagname + HSD_ATTRIB_SUFFIX] = hsdattrib
7882
else:
79-
prevcontent.append(content)
80-
self._curblock = parentblock
83+
prevcont.append(content)
84+
prevattrib = parentblock.get(tagname + ATTRIB_SUFFIX)
85+
if not (prevattrib is None and attrib is None):
86+
msg = f"Duplicate node '{tagname}' should not carry attributes"
87+
if self._include_hsd_attribs:
88+
prevhsdattrib = parentblock.get(tagname + HSD_ATTRIB_SUFFIX)
89+
if isinstance(prevhsdattrib, list):
90+
prevhsdattrib.append(hsdattrib)
91+
else:
92+
parentblock[tagname + HSD_ATTRIB_SUFFIX] = [prevhsdattrib,
93+
hsdattrib]
94+
self._content = parentblock
8195

8296

8397
def add_text(self, text):
84-
self._data = self._text_to_data(text)
98+
if self._content is not None:
99+
msg = f"Data appeared in an invalid context"
100+
raise HsdError(msg)
101+
self._content = self._text_to_data(text)
85102

86103

87104
def _text_to_data(self, txt):

src/hsd/io.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -223,10 +223,11 @@ def _dump_dict(obj, fobj, indentstr, use_hsd_attribs):
223223
else:
224224
attribstr = " [" + attrib + "]"
225225
if use_hsd_attribs:
226-
hsdattribs = obj.get(key + HSD_ATTRIB_SUFFIX)
227-
if hsdattribs is not None:
228-
key = hsdattribs.get("tag", key)
226+
hsdattrib = obj.get(key + HSD_ATTRIB_SUFFIX)
227+
else:
228+
hsdattrib = None
229229
if isinstance(value, dict):
230+
key = hsdattrib.get("tag", key) if hsdattrib else key
230231
if value:
231232
fobj.write("{}{}{} {{\n".format(indentstr, key, attribstr))
232233
_dump_dict(
@@ -235,12 +236,14 @@ def _dump_dict(obj, fobj, indentstr, use_hsd_attribs):
235236
else:
236237
fobj.write("{}{}{} {{}}\n".format(indentstr, key, attribstr))
237238
elif isinstance(value, list) and value and isinstance(value[0], dict):
238-
for item in value:
239+
for ind, item in enumerate(value):
240+
key = hsdattrib[ind].get("tag", key) if hsdattrib else key
239241
fobj.write("{}{}{} {{\n".format(indentstr, key, attribstr))
240242
_dump_dict(
241243
item, fobj, indentstr + _INDENT_STR, use_hsd_attribs)
242244
fobj.write("{}}}\n".format(indentstr))
243245
else:
246+
key = hsdattrib.get("tag", key) if hsdattrib else key
244247
valstr = _get_hsd_rhs(value, indentstr)
245248
fobj.write("{}{}{} {}\n"\
246249
.format(indentstr, key, attribstr, valstr))

src/hsd/parser.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ def _error(self, errorcode, lines):
296296
error_msg = (
297297
"Parsing error ({}) between lines {} - {} in file '{}'.".format(
298298
errorcode, lines[0] + 1, lines[1] + 1, self._fname))
299-
raise common.HsdParserError(error_msg)
299+
raise common.HsdError(error_msg)
300300

301301

302302

test/test.hsd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ Analysis {
5555
Atoms = 1 2 3
5656
Label = "region1"
5757
}
58-
Region {
58+
REgion {
5959
Atoms = 1 2 3
6060
Label = "region2"
6161
}

test/test_dictbuilder.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,15 @@ def test_dictbuilder():
2121

2222

2323
def test_dictbuilder_flat():
24-
dictbuilder = hsd.HsdDictBuilder(flatten_data=True)
25-
parser = hsd.HsdParser(eventhandler=dictbuilder)
24+
dictbuilder = hsd.HsdDictBuilder(flatten_data=True, include_hsd_attribs=True)
25+
parser = hsd.HsdParser(eventhandler=dictbuilder, lower_tag_names=True)
2626
with open(op.join(op.dirname(__file__), "test.hsd"), "r") as fobj:
2727
parser.feed(fobj)
2828
pyrep = dictbuilder.hsddict
2929
print("** Python structure with data flattening:\n")
3030
print(pyrep)
3131
print("\n** Turning back to HSD:\n")
32-
print(hsd.dump_string(pyrep))
32+
print(hsd.dump_string(pyrep, use_hsd_attribs=True))
3333

3434

3535
if __name__ == '__main__':

0 commit comments

Comments
 (0)