from typing import List, Iterable, Dict, Any
from collections import OrderedDict
STRING_DELIM = "/"
[docs]class Path(tuple):
def __new__(cls, iter_: Iterable[str]):
for item in iter_:
assert isinstance(item, str)
return super().__new__(cls, iter_) # type: ignore
def __str__(self) -> str:
# requires all entries of the underlying tuple to be strings!
# __str__() should be readable, __repr__() should be unique
return STRING_DELIM.join(self)
def __repr__(self) -> str:
# __str__() should be readable, __repr__() should be unique
# ==> use quotation marks. Since the string itself can also contain
# various kinds of quote constructions, just use the __repr__ method
# of the str class.
# This should normally return code that can directly interpreted
# by python to give the same Path element than the string
# represents
return "Path(({}, ))".format(
", ".join((string.__repr__() for string in self))
)
def __getitem__(self, key):
result = tuple.__getitem__(tuple(self), key)
# todo: is this good style? Stands in contrast to the behaviour of other
# iterators...
# Depending on whether key was a single int or a slice object ,
# tuple.__getitem__ will return a tuple or a string. However,
# we want our __getitem__ method to return a Path instance in either
# way!
if isinstance(result, tuple):
return Path(result)
elif isinstance(result, str):
return Path((result,)) # do not remove ',' or it's gonna be a str
[docs] def startswith(self, tag):
if not isinstance(tag, Path):
raise ValueError(
"Expecting instance of Path " "but got {}!".format(type(tag))
)
if len(tag) > len(self):
return False
return self[: len(tag)] == tag
[docs] def parent(self):
return self[: len(self) - 1]
[docs] def ancestors(self):
return [self[:i] for i in range(len(self) + 1)]
[docs]def paths2dot(paths: List[Path], full_labels=True) -> str:
"""Converts a list of numbering to their correspondent graph described in
the DOT language (see http://www.graphviz.org/doc/info/lang.html).
Not using the python graphviz package to reduce dependencies.
Args:
paths: List of numbering.
full_labels: If true, the vertices of the graph are labeled with the
full path, else only the name of the endpoint (path[:-1]) is
printed.
Returns:
graph described in DOT language as string.
"""
dot = "" # return value
# strict: don't draw multiple edges
dot += "strict digraph G {"
for path in paths:
# Note: We have to make sure that a path like "1/1" is interpreted
# as 1->"1/1"-and not as a loop of the vertex "1", so we designate
# the vertices of the tree by the positive hashes of the path object
def vid(vert):
return str(abs(hash(vert)))
vertices = [path[: level + 1] for level in range(len(path))]
vertex_ids = (vid(vertex) for vertex in vertices)
dot += "\t{} [dir=none];\n".format("->".join(vertex_ids))
# since __repr__ has a lot of quotation marks mixed in the
# representation, we adapt the labeling with the output of __str__
# we have to manually escape double quotation marks)
for vertex in vertices:
if full_labels:
label = str(vertex).replace('"', '\\"')
else:
label = str(vertex[-1]).replace('"', '\\"')
dot += '\t{} [label="{}"];\n'.format(vid(vertex), label)
dot += "}"
return dot
[docs]def stringvalues_to_pv(
stringvalues: Dict[str, float], delim=STRING_DELIM
) -> Dict[Path, float]:
assert all(isinstance(item, str) for item in stringvalues.keys())
return {
Path(item.split(delim)): value for item, value in stringvalues.items()
}
[docs]def stringlist_to_ordered_pv(
stringpairs: List[Any], delim=STRING_DELIM
) -> Dict[Path, float]:
keys = stringpairs[::2]
values = stringpairs[1::2]
assert all(isinstance(item, str) for item in keys)
assert all(isinstance(item, float) for item in values)
paths = [Path(item.split(delim)) for item in keys]
return OrderedDict(zip(paths, values))
[docs]def charvalues_to_pv(charvalues: Dict[str, float]) -> Dict[Path, float]:
assert all(isinstance(item, str) for item in charvalues.keys())
return {Path(tuple(item)): value for item, value in charvalues.items()}
[docs]def charlist_to_ordered_pv(
stringpairs: List[Any],
) -> Dict[Path, float]:
keys = stringpairs[::2]
values = stringpairs[1::2]
assert all(isinstance(item, str) for item in keys)
assert all(isinstance(item, float) for item in values)
paths = [Path(tuple(item)) for item in keys]
return OrderedDict(zip(paths, values))