Source code for SDF.file_io.sdf
from textwrap import wrap
from typing import Union, Optional, TextIO
from xml.etree.ElementTree import XMLParser, parse, Element
from xml.sax.saxutils import escape, quoteattr
from SDF.data_model import Dataset, SDFObject, Workspace
from SDF.sdf_rc import ENCODING, TABSIZE, LINEWIDTH
[docs]def load_from_sdf(filename: Union[str, TextIO]) -> SDFObject:
parser = XMLParser(encoding=ENCODING)
root = parse(filename, parser=parser).getroot()
if root.tag == "workspace":
return Workspace.from_xml_element(root)
elif root.tag == "dataset":
return Dataset.from_xml_element(root)
else:
raise ValueError(f"Expected the root to be a <dataset> or <workspace>, got {root.tag}")
[docs]def write_sdf(obj: SDFObject, file: Union[str, TextIO]) -> None:
if isinstance(obj, Workspace):
doctype = "workspace"
elif isinstance(obj, Dataset):
doctype = "dataset"
else:
raise TypeError(f"Can only write datasets and workspaces, got {obj}")
if isinstance(file, str):
fp = open(file, "w")
elif hasattr(file, "write"):
fp = file
else:
raise ValueError("Given file is not a filename and not writable")
fp.write(f'<?xml version="1.0" encoding="{ENCODING}"?>\n')
fp.write(f'<!DOCTYPE {doctype}>\n')
__xml_to_file(obj.to_xml_element(), fp)
if isinstance(file, str):
fp.close()
def __xml_to_file(element: Optional[Element], file: TextIO, indent_level: int = 0) -> None:
"""Writes an XML string of this object to a given file"""
if element is None:
return
indent = " " * indent_level * TABSIZE
# opening tag
file.write(indent)
file.write(f"<{element.tag}")
for key, value in element.attrib.items():
file.write(f" {escape(key.strip())}={quoteattr(value.strip())}")
if len(element) == 0 and (element.text is None or element.text.isspace()):
file.write("/>\n")
return
else:
file.write(">\n")
# text
__text_to_file(element.text, file, indent_level+1)
# children
for child in element:
__xml_to_file(child, file, indent_level+1)
# closing tag
file.write(indent)
file.write(f"</{element.tag}>\n")
def __text_to_file(text: Optional[str], file: TextIO, indent_level: int = 0):
"""Writes a given string to a given file. Respects indentation and wraps long lines"""
if text is None:
return
indent = " " * indent_level * TABSIZE
for line in text.splitlines(keepends=False):
if len(indent) + len(line) <= LINEWIDTH:
file.write(indent + line + "\n")
else:
for wline in wrap(line, width=LINEWIDTH, initial_indent=indent, break_on_hyphens=False,
subsequent_indent=indent, break_long_words=False, tabsize=TABSIZE):
file.write(wline + "\n")