Skip to content

Commit

Permalink
ofparse: make styles configurable
Browse files Browse the repository at this point in the history
Refactor how style objects are created so they can be defined in an
external configuration file.

Change in the default behavior: the default style is none

Signed-off-by: Adrian Moreno <[email protected]>
  • Loading branch information
amorenoz committed Sep 7, 2021
1 parent a77cb1b commit fc0e95c
Show file tree
Hide file tree
Showing 7 changed files with 138 additions and 117 deletions.
52 changes: 16 additions & 36 deletions ovs_dbg/ofparse/console.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,35 +85,16 @@ class ConsoleFormatter(FlowFormatter):
rich.console.Console()
"""

default_style = Style(color="white")

default_style_obj = FlowStyle(
{
"key": Style(color="#B0C4DE"),
"value": Style(color="#B0C4DE"),
"delim": Style(color="#B0C4DE"),
"value.type.IPAddress": Style(color="#008700"),
"value.type.IPMask": Style(color="#008700"),
"value.type.EthMask": Style(color="#008700"),
"value.ct": Style(color="bright_black"),
"value.ufid": Style(color="#870000"),
"value.clone": Style(color="bright_black"),
"value.controller": Style(color="bright_black"),
"flag": Style(color="#875fff"),
"key.drop": Style(color="red"),
"key.resubmit": Style(color="#00d700"),
"key.output": Style(color="#00d700"),
"key.highlighted": Style(color="#f20905", underline=True),
"value.highlighted": Style(color="#f20905", underline=True),
"delim.highlighted": Style(color="#f20905", underline=True),
}
)

def __init__(self, console=None, style_obj=None, default_style=None, **kwargs):
def __init__(self, opts=None, console=None, **kwargs):
super(ConsoleFormatter, self).__init__()
self.console = console or Console(**kwargs)
style = self.style_from_opts(opts)
self.console = console or Console(no_color=(style is None), **kwargs)
self.style = style or FlowStyle()

def print_flow(self, flow, style=None):
def style_from_opts(self, opts):
return self._style_from_opts(opts, "console", Style)

def print_flow(self, flow):
"""
Prints a flow to the console
Expand All @@ -123,10 +104,10 @@ def print_flow(self, flow, style=None):
"""

buf = ConsoleBuffer(Text())
self.format_flow(buf, flow, style)
self.format_flow(buf, flow)
self.console.print(buf.text)

def format_flow(self, buf, flow, style=None):
def format_flow(self, buf, flow):
"""
Formats the flow into the provided buffer as a rich.Text
Expand All @@ -135,10 +116,7 @@ def format_flow(self, buf, flow, style=None):
flow (ovs_dbg.OFPFlow): the flow to format
style (FlowStyle): Optional; style object to use
"""
style_obj = style or self.default_style_obj
return super(ConsoleFormatter, self).format_flow(
buf, flow, style_obj, self.default_style
)
return super(ConsoleFormatter, self).format_flow(buf, flow, self.style)


def hash_pallete(hue, saturation, value):
Expand All @@ -162,7 +140,7 @@ def get_style(string):
return get_style


def print_context(console, paged=False, styles=True):
def print_context(console, opts):
"""
Returns a printing context
Expand All @@ -171,7 +149,7 @@ def print_context(console, paged=False, styles=True):
paged (bool): Wheter to page the output
style (bool): Whether to force the use of styled pager
"""
if paged:
if opts.get("paged"):
# Internally pydoc's pager library is used which returns a
# plain pager if both stdin and stdout are not tty devices
#
Expand All @@ -180,6 +158,8 @@ def print_context(console, paged=False, styles=True):
if not sys.stdin.isatty() and sys.stdout.isatty():
setattr(sys.stdin, "isatty", lambda: True)

return console.pager(styles=styles)
with_style = opts.get("style") is not None

return console.pager(styles=with_style)

return contextlib.nullcontext()
17 changes: 9 additions & 8 deletions ovs_dbg/ofparse/dp.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
print_context,
hash_pallete,
)
from ovs_dbg.ofparse.format import FlowStyle
from ovs_dbg.ofparse.html import HTMLBuffer, HTMLFormatter
from ovs_dbg.odp import ODPFlow
from ovs_dbg.filter import OFFilter
Expand Down Expand Up @@ -62,15 +63,15 @@ def callback(flow):
)

tree = Tree("Datapath Flows (logical)")
console = Console(color_system=None if opts["no_color"] else "256")
ofconsole = ConsoleFormatter(console)
ofconsole = ConsoleFormatter(opts)
console = ofconsole.console

# HSV_tuples = [(x / size, 0.7, 0.8) for x in range(size)]
recirc_style_gen = hash_pallete(
hue=[x / 50 for x in range(0, 50)], saturation=[0.7], value=[0.8]
)

style = ConsoleFormatter.default_style_obj
style = ofconsole.style
style.set_default_value_style(Style(color="bright_black"))
style.set_key_style("output", Style(color="green"))
style.set_value_style("output", Style(color="green"))
Expand All @@ -79,13 +80,13 @@ def callback(flow):

def append_to_tree(parent, flow):
buf = ConsoleBuffer(Text())
ofconsole.format_flow(buf=buf, flow=flow, style=style)
ofconsole.format_flow(buf, flow)
tree_elem = parent.add(buf.text)
return tree_elem

process_flow_tree(flow_list, tree, 0, append_to_tree)

with print_context(console, opts["paged"], not opts["no_color"]):
with print_context(console, opts):
console.print(tree)


Expand Down Expand Up @@ -224,9 +225,9 @@ def callback(flow):


class HTMLFlowTree:
def __init__(self, flow=None, style=None):
def __init__(self, flow=None, opts=None):
self._flow = flow
self._style = style
self._formatter = HTMLFormatter(opts)
self._subflows = list()

def append(self, flow):
Expand All @@ -245,7 +246,7 @@ def render(self, item=0):
id=self._flow.id
)
buf = HTMLBuffer()
HTMLFormatter().format_flow(buf, self._flow, self._style)
self._formatter.format_flow(buf, self._flow, self._style)
html_obj += buf.text
html_obj += "</div>"
if self._subflows:
Expand Down
78 changes: 50 additions & 28 deletions ovs_dbg/ofparse/format.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class FlowStyle:
- key.highlighted (if key is found in hightlighted)
- key.{key}
- key
- default
In order to determine the style for a "value", the following items in the
dictionary are fetched:
Expand All @@ -23,11 +24,11 @@ class FlowStyle:
- value.{key}
- value.type.{value.__class__.__name__}
- value
- default
Additionally, the following style items can be defined:
- delim: for delimiters
- delim.highlighted: for delimiters of highlighted key-values
- extra: for extra characters
"""

def __init__(self, initial=None):
Expand Down Expand Up @@ -71,22 +72,22 @@ def get(self, key):

def get_delim_style(self, highlighted=False):
delim_style_lookup = ["delim.highlighted"] if highlighted else []
delim_style_lookup.extend(["delim"])
delim_style_lookup.extend(["delim", "default"])
return next(
(self._styles.get(s) for s in delim_style_lookup if self._styles.get(s)),
None,
)

def get_flag_style(self):
return self._styles.get("flag")
return self._styles.get("flag") or self._styles.get("default")

def get_key_style(self, kv, highlighted=False):
key = kv.meta.kstring

key_style_lookup = (
["key.highlighted.%s" % key, "key.highlighted"] if highlighted else []
)
key_style_lookup.extend(["key.%s" % key, "key"])
key_style_lookup.extend(["key.%s" % key, "key", "default"])

style = next(
(self._styles.get(s) for s in key_style_lookup if self._styles.get(s)),
Expand All @@ -113,6 +114,7 @@ def get_value_style(self, kv, highlighted=False):
"value.%s" % key,
"value.type.%s" % value_type,
"value",
"default",
]
)

Expand All @@ -138,29 +140,59 @@ def highlight(self, keys):
"""
self._highlighted = keys

def format_flow(self, buf, flow, style_obj=None, default_style=None):
def _style_from_opts(self, opts, opts_key, style_constructor):
"""Create style object from options
Args:
opts (dict): Options dictionary
opts_key (str): The options style key to extract (e.g: console or html)
style_constructor(callable): A callable that creates a derived style object
"""
if not opts or not opts.get("style"):
return None

section_name = ".".join(["styles", opts.get("style")])
if section_name not in opts.get("config").sections():
raise Exception("Style not present in config file")

config = opts.get("config")[section_name]
style = {}
for key in config:
(_, console, style_full_key) = key.partition(opts_key + ".")
if not console:
continue

(style_key, _, prop) = style_full_key.rpartition(".")
if not prop or not style_key:
raise Exception("malformed style config: {}".format(key))

if not style.get(style_key):
style[style_key] = {}
style[style_key][prop] = config[key]

return FlowStyle({k: style_constructor(**v) for k, v in style.items()})

def format_flow(self, buf, flow, style_obj=None):
"""
Formats the flow into the provided buffer
Args:
buf (FlowBuffer): the flow buffer to append to
flow (ovs_dbg.OFPFlow): the flow to format
style_obj (FlowStyle): Optional; style to use
default_style (Any): Optional; default style to use
"""
last_printed_pos = 0
style_obj = style_obj or FlowStyle()

for section in sorted(flow.sections, key=lambda x: x.pos):
buf.append_extra(
flow.orig[last_printed_pos : section.pos],
style=style_obj.get("extra") or default_style,
)
self.format_kv_list(
buf, section.data, section.string, style_obj, default_style
style=style_obj.get("default"),
)
self.format_kv_list(buf, section.data, section.string, style_obj)
last_printed_pos = section.pos + len(section.string)

def format_kv_list(self, buf, kv_list, full_str, style_obj, default_style=None):
def format_kv_list(self, buf, kv_list, full_str, style_obj):
"""
Format a KeyValue List
Expand All @@ -169,23 +201,19 @@ def format_kv_list(self, buf, kv_list, full_str, style_obj, default_style=None):
kv_list (list[KeyValue]: the KeyValue list to format
full_str (str): the full string containing all k-v
style_obj (FlowStyle): a FlowStyle object to use
default_style (Any): the default style to pass to the buffer if
the provided style_obj did not return a valid style
"""
for i in range(len(kv_list)):
kv = kv_list[i]
written = self.format_kv(
buf, kv, style_obj=style_obj, default_style=default_style
)
written = self.format_kv(buf, kv, style_obj=style_obj)

end = kv_list[i + 1].meta.kpos if i < (len(kv_list) - 1) else len(full_str)

buf.append_extra(
full_str[(kv.meta.kpos + written) : end].rstrip("\n\r"),
style=style_obj.get("extra") or default_style,
style=style_obj.get("default"),
)

def format_kv(self, buf, kv, style_obj, default_style=None):
def format_kv(self, buf, kv, style_obj):
"""Format a KeyValue
A formatted keyvalue has the following parts:
Expand All @@ -195,37 +223,31 @@ def format_kv(self, buf, kv, style_obj, default_style=None):
buf (FlowBuffer): buffer to append the KeyValue to
kv (KeyValue): The KeyValue to print
style_obj (FlowStyle): The style object to use
default_style (Any): The default object to use in case the style_obj
fails to find a valid style
Returns the number of printed characters
"""
ret = 0
key = kv.meta.kstring
highlighted = key in self._highlighted

key_style = style_obj.get_key_style(kv, highlighted) or default_style
key_style = style_obj.get_key_style(kv, highlighted)
buf.append_key(kv, key_style) # format value
ret += len(key)

if not kv.meta.vstring:
return ret

if kv.meta.delim not in ("\n", "\t", "\r", ""):
buf.append_delim(
kv, style_obj.get_delim_style(highlighted) or default_style
)
buf.append_delim(kv, style_obj.get_delim_style(highlighted))
ret += len(kv.meta.delim)

value_style = style_obj.get_value_style(kv, highlighted) or default_style
value_style = style_obj.get_value_style(kv, highlighted)

buf.append_value(kv, value_style) # format value
ret += len(kv.meta.vstring)

if kv.meta.end_delim:
buf.append_end_delim(
kv, style_obj.get_delim_style(highlighted) or default_style
)
buf.append_end_delim(kv, style_obj.get_delim_style(highlighted))
ret += len(kv.meta.end_delim)

return ret
Expand Down
Loading

0 comments on commit fc0e95c

Please sign in to comment.