Skip to content

Commit 785ca36

Browse files
committed
Extend import functionality
1 parent 0d97116 commit 785ca36

File tree

7 files changed

+111
-13
lines changed

7 files changed

+111
-13
lines changed

src/hsd/dict.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
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
15+
from hsd.interrupts import Interrupt
1516

1617
_ItemType = Union[float, complex, int, bool, str]
1718

@@ -67,7 +68,7 @@ class HsdDictBuilder(HsdEventHandler):
6768
6869
Args:
6970
flatten_data: Whether multiline data in the HSD input should be
70-
flattened into a single list. Othewise a list of lists is created, with one list for
71+
flattened into a single list. Otherwise a list of lists is created, with one list for
7172
every line (default).
7273
lower_tag_names: Whether tag names should be all converted to lower case (to ease case
7374
insensitive processing). Default: False. If set and include_hsd_attribs is also set,
@@ -161,6 +162,13 @@ def add_text(self, text):
161162
self._data = self._text_to_data(text)
162163

163164

165+
def add_interrupt(self, interrupt):
166+
if self._curblock or self._data is not None:
167+
msg = "Data appeared in an invalid context"
168+
raise HsdError(msg)
169+
self._data = interrupt
170+
171+
164172
def _text_to_data(self, txt: str) -> _DataType:
165173
data = []
166174
for line in txt.split("\n"):
@@ -242,6 +250,11 @@ def walk(self, dictobj):
242250
self.walk(item)
243251
self._eventhandler.close_tag(key)
244252

253+
elif isinstance(value, Interrupt):
254+
self._eventhandler.open_tag(key, attrib, hsdattrib)
255+
self._eventhandler.add_interrupt(value)
256+
self._eventhandler.close_tag(key)
257+
245258
else:
246259
self._eventhandler.open_tag(key, attrib, hsdattrib)
247260
self._eventhandler.add_text(_to_text(value))

src/hsd/eventhandler.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
from abc import ABC, abstractmethod
1212
from typing import Optional
13+
from hsd.interrupts import Interrupt
1314

1415

1516
class HsdEventHandler(ABC):
@@ -43,6 +44,13 @@ def add_text(self, text: str):
4344
text: Text in the current tag.
4445
"""
4546

47+
@abstractmethod
48+
def add_interrupt(self, interrupt: Interrupt):
49+
"""Adds interrupts to the current tag.
50+
51+
Args:
52+
interrupt: Instance of the Interrupt class or its children.
53+
"""
4654

4755

4856
class HsdEventPrinter(HsdEventHandler):
@@ -75,3 +83,8 @@ def close_tag(self, tagname: str):
7583
def add_text(self, text: str):
7684
indentstr = self._indentlevel * self._indentstr
7785
print(f"{indentstr}Received text: {text}")
86+
87+
def add_interrupt(self, interrupt: Interrupt):
88+
indentstr = self._indentlevel * self._indentstr
89+
print(f"{indentstr}Received interrupt: type '{type(interrupt)}' to "
90+
f"file '{interrupt.file}'")

src/hsd/formatter.py

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,16 @@
88
"""
99

1010
from typing import List, TextIO, Union
11-
from hsd.common import HSD_ATTRIB_EQUAL, HSD_ATTRIB_NAME
11+
from hsd.common import HSD_ATTRIB_EQUAL, HSD_ATTRIB_NAME, HsdError
1212
from hsd.eventhandler import HsdEventHandler
13+
from hsd.interrupts import Interrupt, IncludeHsd, IncludeText
1314

1415

1516
_INDENT_STR = " "
1617

1718

1819
class HsdFormatter(HsdEventHandler):
19-
"""Implements an even driven HSD formatter.
20+
"""Implements an event driven HSD formatter.
2021
2122
Args:
2223
fobj: File like object to write the formatted output to.
@@ -106,10 +107,36 @@ def add_text(self, text: str):
106107
self._followed_by_equal[-1] = True
107108
else:
108109
self._indent_level += 1
109-
indentstr = self._indent_level * _INDENT_STR
110+
indentstr = self._indent_level * _INDENT_STR
110111
self._fobj.write(f" {{\n{indentstr}")
111112
text = text.replace("\n", "\n" + indentstr)
112113

113114
self._fobj.write(text)
114115
self._fobj.write("\n")
115116
self._nr_children[-1] += 1
117+
118+
119+
def add_interrupt(self, interrupt: Interrupt):
120+
121+
if isinstance(interrupt, IncludeHsd):
122+
operator = "<<+"
123+
elif isinstance(interrupt, IncludeText):
124+
operator = "<<<"
125+
else:
126+
msg = ("The 'HsdFormatter' does not support Interrupts of "
127+
f"type '{type(interrupt)}' !")
128+
raise HsdError(msg)
129+
130+
equal = self._followed_by_equal[-1]
131+
if equal:
132+
self._fobj.write(" = ")
133+
self._followed_by_equal[-1] = True
134+
else:
135+
self._indent_level += 1
136+
indentstr = self._indent_level * _INDENT_STR
137+
self._fobj.write(f" {{\n{indentstr}")
138+
139+
text = operator + ' "' + interrupt.file + '"'
140+
self._fobj.write(text)
141+
self._fobj.write("\n")
142+
self._nr_children[-1] += 1

src/hsd/interrupts.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#--------------------------------------------------------------------------------------------------#
2+
# hsd-python: package for manipulating HSD-formatted data in Python #
3+
# Copyright (C) 2011 - 2023 DFTB+ developers group #
4+
# Licensed under the BSD 2-clause license. #
5+
#--------------------------------------------------------------------------------------------------#
6+
#
7+
"""
8+
Contains hsd interrupts
9+
"""
10+
11+
from hsd.common import unquote
12+
13+
14+
class Interrupt:
15+
"""General class for interrupts"""
16+
17+
def __init__(self, file):
18+
self.file = unquote(file.strip())
19+
20+
21+
class IncludeText(Interrupt):
22+
"""class for dealing with text interrupts"""
23+
pass
24+
25+
class IncludeHsd(Interrupt):
26+
"""class for dealing with hsd interrupts"""
27+
pass

src/hsd/io.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818

1919

2020
def load(hsdfile: Union[TextIO, str], lower_tag_names: bool = False,
21-
include_hsd_attribs: bool = False, flatten_data: bool = False) -> dict:
21+
include_hsd_attribs: bool = False, flatten_data: bool = False,
22+
include_file: bool = True) -> dict:
2223
"""Loads a file with HSD-formatted data into a Python dictionary
2324
2425
Args:
@@ -36,6 +37,7 @@ def load(hsdfile: Union[TextIO, str], lower_tag_names: bool = False,
3637
flatten_data: Whether multiline data in the HSD input should be
3738
flattened into a single list. Othewise a list of lists is created,
3839
with one list for every line (default).
40+
include_file: Whether files via "<<<"/"<<+" should be included or not
3941
4042
Returns:
4143
Dictionary representing the HSD data.
@@ -45,7 +47,7 @@ def load(hsdfile: Union[TextIO, str], lower_tag_names: bool = False,
4547
"""
4648
dictbuilder = HsdDictBuilder(lower_tag_names=lower_tag_names, flatten_data=flatten_data,
4749
include_hsd_attribs=include_hsd_attribs)
48-
parser = HsdParser(eventhandler=dictbuilder)
50+
parser = HsdParser(eventhandler=dictbuilder, include_file=include_file)
4951
if isinstance(hsdfile, str):
5052
with open(hsdfile, "r") as hsddescr:
5153
parser.parse(hsddescr)
@@ -56,8 +58,8 @@ def load(hsdfile: Union[TextIO, str], lower_tag_names: bool = False,
5658

5759
def load_string(
5860
hsdstr: str, lower_tag_names: bool = False,
59-
include_hsd_attribs: bool = False, flatten_data: bool = False
60-
) -> dict:
61+
include_hsd_attribs: bool = False, flatten_data: bool = False,
62+
include_file: bool = True) -> dict:
6163
"""Loads a string with HSD-formatted data into a Python dictionary.
6264
6365
Args:
@@ -75,6 +77,7 @@ def load_string(
7577
flatten_data: Whether multiline data in the HSD input should be
7678
flattened into a single list. Othewise a list of lists is created,
7779
with one list for every line (default).
80+
include_file: Whether files via "<<<"/"<<+" should be included or not
7881
7982
Returns:
8083
Dictionary representing the HSD data.
@@ -130,7 +133,8 @@ def load_string(
130133
131134
"""
132135
fobj = io.StringIO(hsdstr)
133-
return load(fobj, lower_tag_names, include_hsd_attribs, flatten_data)
136+
return load(fobj, lower_tag_names, include_hsd_attribs, flatten_data,
137+
include_file)
134138

135139

136140
def dump(data: dict, hsdfile: Union[TextIO, str],

src/hsd/parser.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from typing import Optional, TextIO, Union
1111
from hsd import common
1212
from hsd.eventhandler import HsdEventHandler, HsdEventPrinter
13-
13+
from hsd.interrupts import IncludeHsd, IncludeText
1414

1515
SYNTAX_ERROR = 1
1616
UNCLOSED_TAG_ERROR = 2
@@ -50,11 +50,13 @@ class HsdParser:
5050
{'Temperature': 100, 'Temperature.attrib': 'Kelvin'}}}}}
5151
"""
5252

53-
def __init__(self, eventhandler: Optional[HsdEventHandler] = None):
53+
def __init__(self, eventhandler: Optional[HsdEventHandler] = None,
54+
include_file: bool = True):
5455
"""Initializes the parser.
5556
5657
Args:
5758
eventhandler: Instance of the HsdEventHandler class or its children.
59+
include_file: Whether files via "<<<"/"<<+" should be included or not
5860
"""
5961
if eventhandler is None:
6062
self._eventhandler = HsdEventPrinter()
@@ -75,6 +77,7 @@ def __init__(self, eventhandler: Optional[HsdEventHandler] = None):
7577
self._has_child = True # Whether current node has a child already
7678
self._has_text = False # whether current node contains text already
7779
self._oldbefore = "" # buffer for tagname
80+
self._include_file = include_file # Whether files via "<<<"/"<<+" should be included or not
7881

7982

8083
def parse(self, fobj: Union[TextIO, str]):
@@ -216,10 +219,19 @@ def _parse(self, line):
216219
if txtinc:
217220
self._text("".join(self._buffer) + before)
218221
self._buffer = []
219-
self._eventhandler.add_text(self._include_txt(after[2:]))
222+
if self._include_file:
223+
text = self._include_txt(after[2:])
224+
self._eventhandler.add_text(text)
225+
else:
226+
interrupt = IncludeText(after[2:])
227+
self._eventhandler.add_interrupt(interrupt)
220228
break
221229
if hsdinc:
222-
self._include_hsd(after[2:])
230+
if self._include_file:
231+
self._include_hsd(after[2:])
232+
else:
233+
interrupt = IncludeHsd(after[2:])
234+
self._eventhandler.add_interrupt(interrupt)
223235
break
224236
self._buffer.append(before + sign)
225237

test/test_parser.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ def close_tag(self, tagname):
109109
def add_text(self, text):
110110
self.events.append((_ADD_TEXT_EVENT, text))
111111

112+
def add_interrupt(self, interrupt):
113+
pass
112114

113115
@pytest.mark.parametrize(
114116
"hsd_input,expected_events",

0 commit comments

Comments
 (0)