Skip to content

Commit

Permalink
Extend import functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
jubich committed Mar 30, 2023
1 parent 0d97116 commit 785ca36
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 13 deletions.
15 changes: 14 additions & 1 deletion src/hsd/dict.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from hsd.common import HSD_ATTRIB_NAME, np, ATTRIB_SUFFIX, HSD_ATTRIB_SUFFIX, HsdError,\
QUOTING_CHARS, SPECIAL_CHARS
from hsd.eventhandler import HsdEventHandler, HsdEventPrinter
from hsd.interrupts import Interrupt

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

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


def add_interrupt(self, interrupt):
if self._curblock or self._data is not None:
msg = "Data appeared in an invalid context"
raise HsdError(msg)
self._data = interrupt


def _text_to_data(self, txt: str) -> _DataType:
data = []
for line in txt.split("\n"):
Expand Down Expand Up @@ -242,6 +250,11 @@ def walk(self, dictobj):
self.walk(item)
self._eventhandler.close_tag(key)

elif isinstance(value, Interrupt):
self._eventhandler.open_tag(key, attrib, hsdattrib)
self._eventhandler.add_interrupt(value)
self._eventhandler.close_tag(key)

else:
self._eventhandler.open_tag(key, attrib, hsdattrib)
self._eventhandler.add_text(_to_text(value))
Expand Down
13 changes: 13 additions & 0 deletions src/hsd/eventhandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

from abc import ABC, abstractmethod
from typing import Optional
from hsd.interrupts import Interrupt


class HsdEventHandler(ABC):
Expand Down Expand Up @@ -43,6 +44,13 @@ def add_text(self, text: str):
text: Text in the current tag.
"""

@abstractmethod
def add_interrupt(self, interrupt: Interrupt):
"""Adds interrupts to the current tag.
Args:
interrupt: Instance of the Interrupt class or its children.
"""


class HsdEventPrinter(HsdEventHandler):
Expand Down Expand Up @@ -75,3 +83,8 @@ def close_tag(self, tagname: str):
def add_text(self, text: str):
indentstr = self._indentlevel * self._indentstr
print(f"{indentstr}Received text: {text}")

def add_interrupt(self, interrupt: Interrupt):
indentstr = self._indentlevel * self._indentstr
print(f"{indentstr}Received interrupt: type '{type(interrupt)}' to "
f"file '{interrupt.file}'")
33 changes: 30 additions & 3 deletions src/hsd/formatter.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,16 @@
"""

from typing import List, TextIO, Union
from hsd.common import HSD_ATTRIB_EQUAL, HSD_ATTRIB_NAME
from hsd.common import HSD_ATTRIB_EQUAL, HSD_ATTRIB_NAME, HsdError
from hsd.eventhandler import HsdEventHandler
from hsd.interrupts import Interrupt, IncludeHsd, IncludeText


_INDENT_STR = " "


class HsdFormatter(HsdEventHandler):
"""Implements an even driven HSD formatter.
"""Implements an event driven HSD formatter.
Args:
fobj: File like object to write the formatted output to.
Expand Down Expand Up @@ -106,10 +107,36 @@ def add_text(self, text: str):
self._followed_by_equal[-1] = True
else:
self._indent_level += 1
indentstr = self._indent_level * _INDENT_STR
indentstr = self._indent_level * _INDENT_STR
self._fobj.write(f" {{\n{indentstr}")
text = text.replace("\n", "\n" + indentstr)

self._fobj.write(text)
self._fobj.write("\n")
self._nr_children[-1] += 1


def add_interrupt(self, interrupt: Interrupt):

if isinstance(interrupt, IncludeHsd):
operator = "<<+"
elif isinstance(interrupt, IncludeText):
operator = "<<<"
else:
msg = ("The 'HsdFormatter' does not support Interrupts of "
f"type '{type(interrupt)}' !")
raise HsdError(msg)

equal = self._followed_by_equal[-1]
if equal:
self._fobj.write(" = ")
self._followed_by_equal[-1] = True
else:
self._indent_level += 1
indentstr = self._indent_level * _INDENT_STR
self._fobj.write(f" {{\n{indentstr}")

text = operator + ' "' + interrupt.file + '"'
self._fobj.write(text)
self._fobj.write("\n")
self._nr_children[-1] += 1
27 changes: 27 additions & 0 deletions src/hsd/interrupts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#--------------------------------------------------------------------------------------------------#
# hsd-python: package for manipulating HSD-formatted data in Python #
# Copyright (C) 2011 - 2023 DFTB+ developers group #
# Licensed under the BSD 2-clause license. #
#--------------------------------------------------------------------------------------------------#
#
"""
Contains hsd interrupts
"""

from hsd.common import unquote


class Interrupt:
"""General class for interrupts"""

def __init__(self, file):
self.file = unquote(file.strip())


class IncludeText(Interrupt):
"""class for dealing with text interrupts"""
pass

class IncludeHsd(Interrupt):
"""class for dealing with hsd interrupts"""
pass
14 changes: 9 additions & 5 deletions src/hsd/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@


def load(hsdfile: Union[TextIO, str], lower_tag_names: bool = False,
include_hsd_attribs: bool = False, flatten_data: bool = False) -> dict:
include_hsd_attribs: bool = False, flatten_data: bool = False,
include_file: bool = True) -> dict:
"""Loads a file with HSD-formatted data into a Python dictionary
Args:
Expand All @@ -36,6 +37,7 @@ def load(hsdfile: Union[TextIO, str], lower_tag_names: bool = False,
flatten_data: Whether multiline data in the HSD input should be
flattened into a single list. Othewise a list of lists is created,
with one list for every line (default).
include_file: Whether files via "<<<"/"<<+" should be included or not
Returns:
Dictionary representing the HSD data.
Expand All @@ -45,7 +47,7 @@ def load(hsdfile: Union[TextIO, str], lower_tag_names: bool = False,
"""
dictbuilder = HsdDictBuilder(lower_tag_names=lower_tag_names, flatten_data=flatten_data,
include_hsd_attribs=include_hsd_attribs)
parser = HsdParser(eventhandler=dictbuilder)
parser = HsdParser(eventhandler=dictbuilder, include_file=include_file)
if isinstance(hsdfile, str):
with open(hsdfile, "r") as hsddescr:
parser.parse(hsddescr)
Expand All @@ -56,8 +58,8 @@ def load(hsdfile: Union[TextIO, str], lower_tag_names: bool = False,

def load_string(
hsdstr: str, lower_tag_names: bool = False,
include_hsd_attribs: bool = False, flatten_data: bool = False
) -> dict:
include_hsd_attribs: bool = False, flatten_data: bool = False,
include_file: bool = True) -> dict:
"""Loads a string with HSD-formatted data into a Python dictionary.
Args:
Expand All @@ -75,6 +77,7 @@ def load_string(
flatten_data: Whether multiline data in the HSD input should be
flattened into a single list. Othewise a list of lists is created,
with one list for every line (default).
include_file: Whether files via "<<<"/"<<+" should be included or not
Returns:
Dictionary representing the HSD data.
Expand Down Expand Up @@ -130,7 +133,8 @@ def load_string(
"""
fobj = io.StringIO(hsdstr)
return load(fobj, lower_tag_names, include_hsd_attribs, flatten_data)
return load(fobj, lower_tag_names, include_hsd_attribs, flatten_data,
include_file)


def dump(data: dict, hsdfile: Union[TextIO, str],
Expand Down
20 changes: 16 additions & 4 deletions src/hsd/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from typing import Optional, TextIO, Union
from hsd import common
from hsd.eventhandler import HsdEventHandler, HsdEventPrinter

from hsd.interrupts import IncludeHsd, IncludeText

SYNTAX_ERROR = 1
UNCLOSED_TAG_ERROR = 2
Expand Down Expand Up @@ -50,11 +50,13 @@ class HsdParser:
{'Temperature': 100, 'Temperature.attrib': 'Kelvin'}}}}}
"""

def __init__(self, eventhandler: Optional[HsdEventHandler] = None):
def __init__(self, eventhandler: Optional[HsdEventHandler] = None,
include_file: bool = True):
"""Initializes the parser.
Args:
eventhandler: Instance of the HsdEventHandler class or its children.
include_file: Whether files via "<<<"/"<<+" should be included or not
"""
if eventhandler is None:
self._eventhandler = HsdEventPrinter()
Expand All @@ -75,6 +77,7 @@ def __init__(self, eventhandler: Optional[HsdEventHandler] = None):
self._has_child = True # Whether current node has a child already
self._has_text = False # whether current node contains text already
self._oldbefore = "" # buffer for tagname
self._include_file = include_file # Whether files via "<<<"/"<<+" should be included or not


def parse(self, fobj: Union[TextIO, str]):
Expand Down Expand Up @@ -216,10 +219,19 @@ def _parse(self, line):
if txtinc:
self._text("".join(self._buffer) + before)
self._buffer = []
self._eventhandler.add_text(self._include_txt(after[2:]))
if self._include_file:
text = self._include_txt(after[2:])
self._eventhandler.add_text(text)
else:
interrupt = IncludeText(after[2:])
self._eventhandler.add_interrupt(interrupt)
break
if hsdinc:
self._include_hsd(after[2:])
if self._include_file:
self._include_hsd(after[2:])
else:
interrupt = IncludeHsd(after[2:])
self._eventhandler.add_interrupt(interrupt)
break
self._buffer.append(before + sign)

Expand Down
2 changes: 2 additions & 0 deletions test/test_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ def close_tag(self, tagname):
def add_text(self, text):
self.events.append((_ADD_TEXT_EVENT, text))

def add_interrupt(self, interrupt):
pass

@pytest.mark.parametrize(
"hsd_input,expected_events",
Expand Down

0 comments on commit 785ca36

Please sign in to comment.