Skip to content

Commit f110351

Browse files
authored
feat: Allow for filtering of output (#174)
* feat: Allow for filtering of output - Command output may be filtered by `|` followed by whitespace and a regular expression (or a simple string). - The regular expression is applied to each line of output, and only lines that match are displayed. - The expression may be negated by using `|!` instead of `|`. - Substitution is not supported. Examples: - `network list_used_addresses 172.16.20.0 | bl23-.*mu2[17]` will output only lines matching the regular expression. - `network list_used_addresses 172.16.20.0 |! mu2[17]` will output only lines *not* matching the regular expression. This applies all commands that output to the console, but it does *not* apply to errors, warnings, or informational messages. This allows filters to be used without concern for missing important information. This is a cleaner Singleton-based implementation of #171.
1 parent 688c594 commit f110351

File tree

14 files changed

+604
-481
lines changed

14 files changed

+604
-481
lines changed

mreg_cli/bacnet.py

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,28 @@
22
from .history import history
33
from .host import host
44
from .log import cli_error, cli_info
5-
from .util import delete, get, get_list, host_info_by_name, post, print_table
5+
from .util import add_formatted_table_for_output, delete, get, get_list, host_info_by_name, post
66

77

8-
def bacnetid_add(args):
8+
def bacnetid_add(args) -> None:
99
info = host_info_by_name(args.name)
1010
if "bacnetid" in info and info["bacnetid"] is not None:
11-
cli_error(
12-
"{} already has BACnet ID {}.".format(info["name"], info["bacnetid"]["id"])
13-
)
11+
cli_error("{} already has BACnet ID {}.".format(info["name"], info["bacnetid"]["id"]))
1412
postdata = {"hostname": info["name"]}
1513
path = "/api/v1/bacnet/ids/"
1614
bacnetid = args.id
1715
if bacnetid:
1816
response = get(path + bacnetid, ok404=True)
1917
if response:
2018
j = response.json()
21-
cli_error(
22-
"BACnet ID {} is already in use by {}".format(j["id"], j["hostname"])
23-
)
19+
cli_error("BACnet ID {} is already in use by {}".format(j["id"], j["hostname"]))
2420
postdata["id"] = bacnetid
2521
history.record_post(path, "", postdata)
2622
post(path, **postdata)
2723
info = host_info_by_name(args.name)
2824
if "bacnetid" in info and info["bacnetid"] is not None:
2925
b = info["bacnetid"]
30-
cli_info(
31-
"Assigned BACnet ID {} to {}".format(b["id"], info["name"]), print_msg=True
32-
)
26+
cli_info("Assigned BACnet ID {} to {}".format(b["id"], info["name"]), print_msg=True)
3327

3428

3529
host.add_command(
@@ -44,7 +38,7 @@ def bacnetid_add(args):
4438
)
4539

4640

47-
def bacnetid_remove(args):
41+
def bacnetid_remove(args) -> None:
4842
info = host_info_by_name(args.name)
4943
if "bacnetid" not in info or info["bacnetid"] is None:
5044
cli_error("{} does not have a BACnet ID assigned.".format(info["name"]))
@@ -68,7 +62,7 @@ def bacnetid_remove(args):
6862
)
6963

7064

71-
def bacnetid_list(args):
65+
def bacnetid_list(args) -> None:
7266
minval = 0
7367
if args.min is not None:
7468
minval = args.min
@@ -80,7 +74,7 @@ def bacnetid_list(args):
8074
if maxval > 4194302:
8175
cli_error("The maximum ID value is 4194302.")
8276
r = get_list("/api/v1/bacnet/ids/", {"id__range": "{},{}".format(minval, maxval)})
83-
print_table(("ID", "Hostname"), ("id", "hostname"), r)
77+
add_formatted_table_for_output(("ID", "Hostname"), ("id", "hostname"), r)
8478

8579

8680
host.add_command(

mreg_cli/cli.py

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import argparse
2+
import html
23
import os
4+
import shlex
35

46
from prompt_toolkit import HTML, print_formatted_text
57
from prompt_toolkit.completion import Completer, Completion
@@ -90,9 +92,7 @@ def add_command(
9092
flags = []
9193
if not self.sub:
9294
self.sub = _create_command_group(self.parser)
93-
parser = self.sub.add_parser(
94-
prog, description=description, epilog=epilog, help=short_desc
95-
)
95+
parser = self.sub.add_parser(prog, description=description, epilog=epilog, help=short_desc)
9696
for f in flags:
9797
# Need to create a dict with the parameters so only used
9898
# parameters are sent, or else exceptions are raised. Ex: if
@@ -122,7 +122,11 @@ def add_command(
122122
self.children[prog] = new_cmd
123123
return new_cmd
124124

125-
def parse(self, args):
125+
def parse(self, command: str) -> None:
126+
"""Parse and execute a command."""
127+
128+
args = shlex.split(command, comments=True)
129+
126130
try:
127131
args = self.parser.parse_args(args)
128132
# If the command has a callback function, call it.
@@ -251,8 +255,6 @@ def source(files, ignore_errors, verbose):
251255
newlines.
252256
The files may contain comments. The comment symbol is #.
253257
"""
254-
import html
255-
import shlex
256258

257259
rec = recorder.Recorder()
258260

@@ -272,16 +274,10 @@ def source(files, ignore_errors, verbose):
272274
if rec.is_recording() and not line.lstrip().startswith("source"):
273275
rec.record_command(line)
274276

275-
# With comments=True shlex will remove comments from the line
276-
# when splitting. Comment symbol is #
277-
s = shlex.split(line, comments=True)
278-
279277
# In verbose mode all commands are printed before execution.
280-
if verbose and s:
281-
print_formatted_text(
282-
HTML(f"<i>> {html.escape(line.strip())}</i>")
283-
)
284-
cli.parse(s)
278+
if verbose:
279+
print_formatted_text(HTML(f"<i>> {html.escape(line.strip())}</i>"))
280+
cli.parse(line)
285281
if cli.last_errno != 0:
286282
print_formatted_text(
287283
HTML(
@@ -324,8 +320,7 @@ def _source(args):
324320
Flag(
325321
"-ignore-errors",
326322
description=(
327-
"Continue command execution on error. Default is to "
328-
"stop execution on error."
323+
"Continue command execution on error. Default is to stop execution on error."
329324
),
330325
short_desc="Stop on error.",
331326
action="store_true",

mreg_cli/dhcp.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ def assoc_mac_to_ip(mac, ip, force=False):
9090
#########################################
9191

9292

93-
def assoc(args):
93+
def assoc(args) -> None:
9494
# .name .mac .force
9595
"""Associate MAC address with host. If host got multiple A/AAAA records an
9696
IP must be given instead of name.
@@ -107,8 +107,10 @@ def assoc(args):
107107

108108
dhcp.add_command(
109109
prog="assoc",
110-
description="Associate MAC address with host. If host got multiple A/AAAA "
111-
"records an IP must be given instead of name.",
110+
description=(
111+
"Associate MAC address with host. If host got multiple A/AAAA "
112+
"records an IP must be given instead of name."
113+
),
112114
short_desc="Add MAC address to host.",
113115
callback=assoc,
114116
flags=[
@@ -124,7 +126,7 @@ def assoc(args):
124126
############################################
125127

126128

127-
def disassoc(args):
129+
def disassoc(args) -> None:
128130
"""Disassociate MAC address with host/ip. If host got multiple A/AAAA
129131
records an IP must be given instead of name.
130132
"""
@@ -148,8 +150,10 @@ def disassoc(args):
148150

149151
dhcp.add_command(
150152
prog="disassoc",
151-
description="Disassociate MAC address with host/ip. If host got multiple "
152-
"A/AAAA records an IP must be given instead of name.",
153+
description=(
154+
"Disassociate MAC address with host/ip. If host got multiple "
155+
"A/AAAA records an IP must be given instead of name."
156+
),
153157
short_desc="Disassociate MAC address.",
154158
callback=disassoc,
155159
flags=[

0 commit comments

Comments
 (0)