예제 #1
0
def test_indent_lines():
    console = Console(width=100, color_system=None)
    console.begin_capture()
    console.print(Pretty([100, 200], indent_guides=True), width=8)
    expected = """\
[
│   100,
│   200
]
"""
    result = console.end_capture()
    print(repr(result))
    print(result)
    assert result == expected
예제 #2
0
def test_render():
    tree = Tree("foo")
    tree.add("bar", style="italic")
    baz_tree = tree.add("baz", guide_style="bold red", style="on blue")
    baz_tree.add("1")
    baz_tree.add("2")
    tree.add("egg")

    console = Console(width=20, force_terminal=True, color_system="standard")
    console.begin_capture()
    console.print(tree)
    result = console.end_capture()
    print(repr(result))
    expected = "foo                 \n├── \x1b[3mbar\x1b[0m\x1b[3m             \x1b[0m\n\x1b[44m├── \x1b[0m\x1b[44mbaz\x1b[0m\x1b[44m             \x1b[0m\n\x1b[44m│   \x1b[0m\x1b[1;31;44m┣━━ \x1b[0m\x1b[44m1\x1b[0m\x1b[44m           \x1b[0m\n\x1b[44m│   \x1b[0m\x1b[1;31;44m┗━━ \x1b[0m\x1b[44m2\x1b[0m\x1b[44m           \x1b[0m\n└── egg             \n"
    assert result == expected
예제 #3
0
파일: test_tree.py 프로젝트: ptmcg/rich
def test_render_tree_hide_root_non_win32():
    tree = Tree("foo", hide_root=True)
    tree.add("bar", style="italic")
    baz_tree = tree.add("baz", guide_style="bold red", style="on blue")
    baz_tree.add("1")
    baz_tree.add("2")
    tree.add("egg")

    console = Console(width=20,
                      force_terminal=True,
                      color_system="standard",
                      _environ={})
    console.begin_capture()
    console.print(tree)
    result = console.end_capture()
    print(repr(result))
    expected = "\x1b[3mbar\x1b[0m\x1b[3m                 \x1b[0m\n\x1b[44mbaz\x1b[0m\x1b[44m                 \x1b[0m\n\x1b[31;44m┣━━ \x1b[0m\x1b[44m1\x1b[0m\x1b[44m               \x1b[0m\n\x1b[31;44m┗━━ \x1b[0m\x1b[44m2\x1b[0m\x1b[44m               \x1b[0m\negg                 \n"
    assert result == expected
예제 #4
0
파일: test_pretty.py 프로젝트: ptmcg/rich
def test_ipy_display_hook__multiple_special_reprs():
    """
    The case where there are multiple IPython special _repr_*_
    methods on the object, and one of them returns None but another
    one does not.
    """
    console = Console(file=io.StringIO(), force_jupyter=True)

    class Thing:
        def _repr_latex_(self):
            return None

        def _repr_html_(self):
            return "hello"

    console.begin_capture()
    _ipy_display_hook(Thing(), console=console)

    assert console.end_capture() == ""
예제 #5
0
def test_spinner_render():
    time = 0.0

    def get_time():
        nonlocal time
        return time

    console = Console(width=80,
                      color_system=None,
                      force_terminal=True,
                      get_time=get_time)
    console.begin_capture()
    spinner = Spinner("dots", "Foo")
    console.print(spinner)
    time += 80 / 1000
    console.print(spinner)
    result = console.end_capture()
    print(repr(result))
    expected = "⠋ Foo\n⠙ Foo\n"
    assert result == expected
예제 #6
0
파일: test_pretty.py 프로젝트: ptmcg/rich
def test_ipy_display_hook__special_repr_raises_exception():
    """
    When an IPython special repr method raises an exception,
    we treat it as if it doesn't exist and look for the next.
    """
    console = Console(file=io.StringIO(), force_jupyter=True)

    class Thing:
        def _repr_markdown_(self):
            raise Exception()

        def _repr_latex_(self):
            return None

        def _repr_html_(self):
            return "hello"

    console.begin_capture()
    _ipy_display_hook(Thing(), console=console)

    assert console.end_capture() == ""
예제 #7
0
def test_traceback_console_theme_applies():
    """
    Ensure that themes supplied via Console init work on Tracebacks.
    Regression test for https://github.com/Textualize/rich/issues/1786
    """
    r, g, b = 123, 234, 123
    console = Console(
        force_terminal=True,
        _environ={"COLORTERM": "truecolor"},
        theme=Theme({"traceback.title": f"rgb({r},{g},{b})"}),
    )

    console.begin_capture()
    try:
        1 / 0
    except Exception:
        console.print_exception()

    result = console.end_capture()

    assert f"\\x1b[38;2;{r};{g};{b}mTraceback \\x1b[0m" in repr(result)
예제 #8
0
def test_pretty_namedtuple():
    console = Console(color_system=None)
    console.begin_capture()

    example_namedtuple = StockKeepingUnit(
        "Sparkling British Spring Water",
        "Carbonated spring water",
        0.9,
        "water",
        ["its amazing!", "its terrible!"],
    )

    result = pretty_repr(example_namedtuple)

    print(result)
    assert (result == """StockKeepingUnit(
    name='Sparkling British Spring Water',
    description='Carbonated spring water',
    price=0.9,
    category='water',
    reviews=['its amazing!', 'its terrible!']
)""")
예제 #9
0
def test_progress_max_refresh() -> None:
    """Test max_refresh argment."""
    time = 0.0

    def get_time() -> float:
        nonlocal time
        try:
            return time
        finally:
            time = time + 1.0

    console = Console(
        color_system=None,
        width=80,
        legacy_windows=False,
        force_terminal=True,
        _environ={},
    )
    column = TextColumn("{task.description}")
    column.max_refresh = 3
    progress = Progress(
        column,
        get_time=get_time,
        auto_refresh=False,
        console=console,
    )
    console.begin_capture()
    with progress:
        task_id = progress.add_task("start")
        for tick in range(6):
            progress.update(task_id, description=f"tick {tick}")
            progress.refresh()
    result = console.end_capture()
    print(repr(result))
    assert (
        result ==
        "\x1b[?25l\r\x1b[2Kstart\r\x1b[2Kstart\r\x1b[2Ktick 1\r\x1b[2Ktick 1\r\x1b[2Ktick 3\r\x1b[2Ktick 3\r\x1b[2Ktick 5\r\x1b[2Ktick 5\n\x1b[?25h"
    )
예제 #10
0
def parse_caas_response(data: Union[dict, List[dict]]) -> List[str]:
    """Parses Response Object from caas API updates output attribute

    """
    console = Console(emoji=False, record=True)
    data = utils.unlistify(data)
    out = []
    lines = f"[reset]{'-' * 22}"

    if data.get("_global_result", {}).get("status", '') == 0:
        global_res = "[bright_green]Success[/bright_green]"
    else:
        global_res = "[red]Failure[/red]"
    out += [lines, f"Global Result: {global_res}", lines]

    if data.get("cli_cmds_result"):
        out += ["\n -- [cyan bold]Command Results[/cyan bold] --"]
        for cmd_resp in data["cli_cmds_result"]:
            for _c, _r in cmd_resp.items():
                _r_code = _r.get("status")
                if _r_code == 0:
                    _r_pretty = "[bright_green]OK[/bright_green]"
                elif _r_code == 2:
                    _r_pretty = "[dark_orange3]WARNING[/dark_orange3]"
                else:
                    _r_pretty = f"[red]ERROR[/red]" if _r_code == 1 else f"[red]ERROR ({_r_code})[/red]"

                out += [f" [{_r_pretty}] {_c}"]
                cmd_status = _r.get('status_str')
                if cmd_status:
                    _r_txt = f"[italic]{escape(cmd_status)}[/italic]"
                    out += [lines, _r_txt, lines]

        console.begin_capture()
        console.print("\n".join(out))
        out = console.end_capture()

    return out
예제 #11
0
def test_spinner_update():
    time = 0.0

    def get_time():
        nonlocal time
        return time

    console = Console(width=20, force_terminal=True, get_time=get_time)
    console.begin_capture()
    spinner = Spinner("dots")
    console.print(spinner)

    spinner.update(text="Bar", style="green", speed=2)
    time += 80 / 1000
    console.print(spinner)

    spinner.update(text=Rule("Bar"))
    time += 80 / 1000
    console.print(spinner)

    result = console.end_capture()
    print(repr(result))
    expected = f"⠋\n\x1b[32m⠙\x1b[0m Bar\n\x1b[32m⠸\x1b[0m \x1b[92m────── \x1b[0mBar\x1b[92m ───────\x1b[0m\n"
    assert result == expected
예제 #12
0
def test_render_simple():
    console = Console(width=80)
    console.begin_capture()
    console.print(Text("foo"))
    result = console.end_capture()
    assert result == "foo\n"
예제 #13
0
def test_pprint_max_string():
    console = Console(color_system=None)
    console.begin_capture()
    pprint(["Hello" * 20], console=console, max_string=8)
    assert console.end_capture() == """['HelloHel'+92]\n"""
예제 #14
0
def test_pprint_max_items():
    console = Console(color_system=None)
    console.begin_capture()
    pprint({"foo": 1, "bar": 2, "egg": 3}, console=console, max_length=2)
    assert console.end_capture() == """{'foo': 1, 'bar': 2, ... +1}\n"""
예제 #15
0
def test_pprint_max_values():
    console = Console(color_system=None)
    console.begin_capture()
    pprint([1, 2, 3, 4, 5, 6, 7, 8, 9, 0], console=console, max_length=2)
    assert console.end_capture() == "[1, 2, ... +8]\n"
예제 #16
0
def test_pprint():
    console = Console(color_system=None)
    console.begin_capture()
    pprint(1, console=console)
    assert console.end_capture() == "1\n"
예제 #17
0
def test_justify():
    console = Console(width=20, log_path=False, log_time=False, color_system=None)
    console.begin_capture()
    console.log("foo", justify="right")
    result = console.end_capture()
    assert result == "                 foo\n"
예제 #18
0
    def output(
        self,
        outdata: Union[List[str], Dict[str, Any]],
        tablefmt: str = "rich",
        title: str = None,
        caption: str = None,
        account: str = None,
        config=None,
        set_width_cols: dict = None,
        full_cols: Union[List[str], str] = [],
        fold_cols: Union[List[str], str] = [],
        ok_status: Union[int, List[int], Tuple[int, str], List[Tuple[int, str]]] = None,
    ) -> str:
        # log.debugv(f"data passed to output():\n{pprint(outdata, indent=4)}")
        def _do_subtables(data: list, tablefmt: str = "rich"):
            out = []
            for inner_dict in data:  # the object: switch/vlan etc dict
                for key, val in inner_dict.items():
                    if not isinstance(val, (list, dict, tuple)):
                        if val is None:
                            inner_dict[key] = ''
                        elif isinstance(val, str) and val.lower() in ['up', 'down']:
                            color = 'red' if val.lower() == 'down' else 'green'
                            if tablefmt == 'rich':
                                inner_dict[key] = f'[b {color}]{val.title()}[/b {color}]'
                            else:
                                inner_dict[key] = typer.style(val.title(), fg=color)
                        else:
                            if tablefmt == 'rich':
                                inner_dict[key] = Text(str(val), style=None)
                            else:
                                inner_dict[key] = str(val)
                    else:
                        val = self.listify(val)
                        if val and tablefmt == "rich" and hasattr(val[0], 'keys'):
                            inner_table = Table(*(k for k in val[0].keys()),
                                                show_header=True,
                                                # padding=(0, 0),
                                                pad_edge=False,
                                                collapse_padding=True,
                                                show_edge=False,
                                                header_style="bold cyan",
                                                box=SIMPLE
                                                )
                            _ = [inner_table.add_row(*[self.do_pretty(kk, str(vv)) for kk, vv in v.items()]) for v in val]
                            console.begin_capture()
                            console.print(inner_table)
                            inner_dict[key] = console.end_capture()
                        elif val and tablefmt == "tabulate" and hasattr(val[0], 'keys'):
                            inner_table = tabulate(val, headers="keys", tablefmt=tablefmt)
                            inner_dict[key] = inner_table
                        else:
                            if all(isinstance(v, str) for v in val):
                                inner_dict[key] = ", ".join(val)
                out.append(inner_dict)
            return out

        raw_data = outdata
        _lexer = table_data = None

        if config and config.sanitize and raw_data and all(isinstance(x, dict) for x in raw_data):
            redact = ["mac", "serial", "neighborMac", "neighborSerial", "neighborPortMac", "longitude", "latitude"]
            outdata = [{k: d[k] if k not in redact else "--redacted--" for k in d} for d in raw_data]

        # -- // List[str, ...] \\ --  Bypass all formatters, (config file output, etc...)
        if outdata and all(isinstance(x, str) for x in outdata):
            tablefmt = "strings"

        # -- convert List[dict] --> Dict[dev_name: dict] for yaml/json outputs
        if tablefmt in ['json', 'yaml', 'yml']:
            outdata = self.listify(outdata)
            if outdata and 'name' in outdata[0]:
                outdata: Dict[str, Dict[str, Any]] = {
                    item['name']: {k: v for k, v in item.items() if k != 'name'}
                    for item in outdata
                }

        if tablefmt == "json":
            raw_data = json.dumps(outdata, indent=4)
            _lexer = lexers.JsonLexer

        elif tablefmt in ["yml", "yaml"]:
            raw_data = yaml.dump(outdata, sort_keys=False)
            _lexer = lexers.YamlLexer

        elif tablefmt == "csv":
            raw_data = table_data = "\n".join(
                            [
                                ",".join(
                                    [
                                        k if outdata.index(d) == 0 else str(v)
                                        for k, v in d.items()
                                        if k not in CUST_KEYS
                                    ])
                                for d in outdata
                            ])

        elif tablefmt == "rich":
            from rich.console import Console
            from rich.table import Table
            from rich.box import HORIZONTALS, SIMPLE
            from rich.text import Text
            # from rich.progress import Progress
            from centralcli import constants
            console = Console(record=True, emoji=False)

            customer_id, customer_name = "", ""
            # outdata = self.listify(outdata)

            # -- // List[dict, ...] \\ --
            if outdata and all(isinstance(x, dict) for x in outdata):
                customer_id = outdata[0].get("customer_id", "")
                customer_name = outdata[0].get("customer_name", "")
                outdata = [{k: v for k, v in d.items() if k not in CUST_KEYS} for d in outdata]

                table = Table(
                    # show_edge=False,
                    show_header=True,
                    title=title,
                    header_style='magenta',
                    show_lines=False,
                    box=HORIZONTALS,
                    row_styles=['none', 'dark_sea_green']
                )

                fold_cols = [*fold_cols, 'description']
                _min_max = {'min': 10, 'max': 30}
                set_width_cols = set_width_cols or {'name': _min_max, 'model': _min_max}
                # default full cols #TODO clean this up
                _full_cols = ['mac', 'serial', 'ip', 'public ip', 'version', 'radio', 'id']
                full_cols = [*full_cols, *_full_cols]

                for k in outdata[0].keys():
                    if k in fold_cols:
                        table.add_column(k, overflow='fold', max_width=115, justify='left')
                    elif k in set_width_cols:
                        table.add_column(
                            k, min_width=set_width_cols[k]['min'],
                            max_width=set_width_cols[k]['max'],
                            justify='left'
                        )
                    elif k in full_cols:
                        table.add_column(k, no_wrap=True, justify='left')
                    else:
                        table.add_column(k, justify='left')

                formatted = _do_subtables(outdata)
                [table.add_row(*list(in_dict.values())) for in_dict in formatted]

                if title:
                    table.title = f'[italic cornflower_blue]{constants.what_to_pretty(title)}'
                if account or caption:
                    table.caption_justify = 'left'
                    table.caption = '' if not account else f'[italic dark_olive_green2] Account: {account}'
                    if caption:
                        table.caption = f"[italic dark_olive_green2]{table.caption}  {caption}"

                data_header = f"--\n{'Customer ID:':15}{customer_id}\n{'Customer Name:':15} {customer_name}\n--\n"

                # TODO look into this. console.capture stopped working reliably this works
                console.begin_capture()
                console.print(table)
                table_data = console.end_capture()
                raw_data = typer.unstyle(table_data)

                if customer_id:
                    raw_data = f"{data_header}{raw_data}"
                    table_data = f"{data_header}{table_data}"

        elif tablefmt == "tabulate":
            customer_id = customer_name = ""
            outdata = self.listify(outdata)

            # -- // List[dict, ...] \\ --
            if outdata and all(isinstance(x, dict) for x in outdata):
                customer_id = outdata[0].get("customer_id", "")
                customer_name = outdata[0].get("customer_name", "")
                outdata = [{k: v for k, v in d.items() if k not in CUST_KEYS} for d in outdata]
                raw_data = outdata

                outdata = _do_subtables(outdata, tablefmt=tablefmt)
                # outdata = [dict((k, v) for k, v in zip(outdata[0].keys(), val)) for val in outdata]

                table_data = tabulate(outdata, headers="keys", tablefmt=tablefmt)
                td = table_data.splitlines(keepends=True)
                if td:
                    table_data = f"{typer.style(td[0], fg='cyan')}{''.join(td[1:])}"

                data_header = f"--\n{'Customer ID:':15}{customer_id}\n" \
                              f"{'Customer Name:':15} {customer_name}\n--\n"
                table_data = f"{data_header}{table_data}" if customer_id else f"{table_data}"
                raw_data = f"{data_header}{raw_data}" if customer_id else f"{raw_data}"

        else:  # strings output No formatting
            # -- // List[str, ...] \\ --
            if len(outdata) == 1:
                if "\n" not in outdata[0]:
                    # we can format green as only success output is sent through formatter.
                    table_data = typer.style(f"  {outdata[0]}", fg="green")
                    raw_data = outdata[0]
                else:  # template / config file output
                    # get rid of double nl @ EoF (configs)
                    raw_data = table_data = "{}\n".format('\n'.join(outdata).rstrip('\n'))
            else:
                raw_data = table_data = '\n'.join(outdata)
                # Not sure what hit's this, but it was created so something must
                log.debug("List[str] else hit")

        if _lexer and raw_data:
            table_data = highlight(bytes(raw_data, 'UTF-8'),
                                   _lexer(),
                                   formatters.Terminal256Formatter(style='solarized-dark')
                                   )

        return self.Output(rawdata=raw_data, prettydata=table_data, config=config)
예제 #19
0
def test_render_single_node():
    tree = Tree("foo")
    console = Console(color_system=None, width=20)
    console.begin_capture()
    console.print(tree)
    assert console.end_capture() == "foo                 \n"
예제 #20
0
파일: test_pretty.py 프로젝트: ptmcg/rich
def test_ipy_display_hook__console_renderables_on_newline():
    console = Console(file=io.StringIO(), force_jupyter=True)
    console.begin_capture()
    _ipy_display_hook(Text("hello"), console=console)
    assert console.end_capture() == "\nhello\n"
예제 #21
0
def test_expand_false():
    console = Console(width=100, color_system=None)
    console.begin_capture()
    console.print(Padding("foo", 1, expand=False))
    assert console.end_capture() == "     \n foo \n     \n"
예제 #22
0
import json
import io
from time import time
from rich.console import Console
from rich.pretty import Pretty

console = Console(file=io.StringIO(), color_system="truecolor", width=100)

with open("cats.json") as fh:
    cats = json.load(fh)

console.begin_capture()
start = time()
pretty = Pretty(cats)
console.print(pretty, overflow="ignore", crop=False)
result = console.end_capture()
taken = (time() - start) * 1000
print(result)

print(console.file.getvalue())
print(f"{taken:.1f}")
예제 #23
0
def config_(
    group_dev: str = typer.Argument(
        ...,
        metavar="GROUP|DEVICE",
        help="Group or device to update.",
        autocompletion=cli.cache.group_dev_ap_gw_completion
    ),
    # TODO simplify structure can just remove device arg
    # device: str = typer.Argument(
    #     None,
    #     autocompletion=cli.cache.dev_ap_gw_completion
    #     # TODO dev type gw or ap only
    #     # autocompletion=lambda incomplete: [
    #     #    c for c in cli.cache.dev_completion(incomplete, dev_type="gw") if c.lower().startswith(incomplete.lower())
    #     # ]
    # ),
    # TODO collect multi-line input as option to paste in config
    cli_file: Path = typer.Argument(..., help="File containing desired config/template in CLI format.", exists=True, autocompletion=lambda incomplete: tuple()),
    var_file: Path = typer.Argument(None, help="File containing variables for j2 config template.", exists=True, autocompletion=lambda incomplete: tuple()),
    # TODO --vars PATH  help="File containing variables to convert jinja2 template."
    yes: bool = typer.Option(False, "-Y", help="Bypass confirmation prompts - Assume Yes"),
    yes_: bool = typer.Option(False, "-y", hidden=True),
    do_gw: bool = typer.Option(None, "--gw", help="Show group level config for gateways."),
    do_ap: bool = typer.Option(None, "--ap", help="Show group level config for APs."),
    debug: bool = typer.Option(False, "--debug", envvar="ARUBACLI_DEBUG", help="Enable Additional Debug Logging",),
    default: bool = typer.Option(False, "-d", is_flag=True, help="Use default central account", show_default=False,),
    account: str = typer.Option("central_info",
                                envvar="ARUBACLI_ACCOUNT",
                                help="The Aruba Central Account to use (must be defined in the config)",),
) -> None:
    yes = yes_ if yes_ else yes
    group_dev: CentralObject = cli.cache.get_identifier(group_dev, qry_funcs=["group", "dev"], device_type=["ap", "gw"])
    config_out = utils.generate_template(cli_file, var_file=var_file)
    cli_cmds = utils.validate_config(config_out)

    # TODO render.py module with helper function to return styled rule/line
    console = Console(record=True, emoji=False)
    console.begin_capture()
    console.rule("Configuration to be sent")
    console.print("\n".join([f"[green]{line}[/green]" for line in cli_cmds]))
    console.rule()
    console.print(f"\nUpdating {'group' if group_dev.is_group else group_dev.generic_type.upper()} [cyan]{group_dev.name}")
    _msg = console.end_capture()

    if group_dev.is_group:
        device = None
        if not do_ap and not do_gw:
            print("Invalid Input, --gw or --ap option must be supplied for group level config.")
            raise typer.Exit(1)
    else:  # group_dev is a device iden
        device = group_dev

    if do_gw or (device and device.generic_type == "gw"):
        if device and device.generic_type != "gw":
            print(f"Invalid input: --gw option conflicts with {device.name} which is an {device.generic_type}")
            raise typer.Exit(1)
        use_caas = True
        caasapi = caas.CaasAPI(central=cli.central)  # XXX Burried import
        node_iden = group_dev.name if group_dev.is_group else group_dev.mac
    elif do_ap or (device and device.generic_type == "ap"):
        if device and device.generic_type != "ap":
            print(f"Invalid input: --ap option conflicts with {device.name} which is a {device.generic_type}")
            raise typer.Exit(1)
        use_caas = False
        node_iden = group_dev.name if group_dev.is_group else group_dev.serial

    typer.echo(_msg)
    if yes or typer.confirm("Proceed?", abort=True):
        if use_caas:
            resp = cli.central.request(caasapi.send_commands, node_iden, cli_cmds)
            cli.display_results(resp, cleaner=cleaner.parse_caas_response)
        else:
            # FIXME this is OK for group level ap config , for AP this method is not valid
            if group_dev.is_dev:
                print("Not Implemented yet for AP device level updates")
                raise typer.Exit(1)
            resp = cli.central.request(cli.central.replace_ap_config, node_iden, cli_cmds)
            cli.display_results(resp, tablefmt="action")
예제 #24
0
    def display_results(
        self,
        resp: Union[Response, List[Response]] = None,
        data: Union[List[dict], List[str], dict, None] = None,
        tablefmt: TableFormat = "rich",
        title: str = None,
        caption: str = None,
        pager: bool = False,
        outfile: Path = None,
        sort_by: str = None,
        reverse: bool = False,
        stash: bool = True,
        pad: int = None,
        exit_on_fail: bool = False,
        ok_status: Union[int, List[int], Dict[int, str]] = None,
        set_width_cols: dict = None,
        full_cols: Union[List[str], str] = [],
        fold_cols: Union[List[str], str] = [],
        cleaner: callable = None,
        **cleaner_kwargs,
    ) -> None:
        """Output Formatted API Response to display and optionally to file

        one of resp or data attribute is required

        Args:
            resp (Union[Response, List[Response], None], optional): API Response objects.
            data (Union[List[dict], List[str], None], optional): API Response output data.
            tablefmt (str, optional): Format of output. Defaults to "rich" (tabular).
                Valid Values: "json", "yaml", "csv", "rich", "simple", "tabulate", "raw", "action"
                Where "raw" is unformatted raw response and "action" is formatted for POST|PATCH etc.
                where the result is a simple success/error.
            title: (str, optional): Title of output table.
                Only applies to "rich" tablefmt. Defaults to None.
            caption: (str, optional): Caption displayed at bottome of table.
                Only applies to "rich" tablefmt. Defaults to None.
            pager (bool, optional): Page Output / or not. Defaults to True.
            outfile (Path, optional): path/file of output file. Defaults to None.
            sort_by (Union[str, List[str], None] optional): column or columns to sort output on.
            reverse (bool, optional): reverse the output.
            stash (bool, optional): stash (cache) the output of the command.  The CLI can re-display with
                show last.  Default: True
            ok_status (Union[int, List[int], Tuple[int, str], List[Tuple[int, str]]], optional): By default
                responses with status_code 2xx are considered OK and are rendered as green by
                Output class.  provide int or list of int to override additional status_codes that
                should also be rendered as success/green.  provide a dict with {int: str, ...}
                where string can be any color supported by Output class or "neutral" "success" "fail"
                where neutral is no formatting, and success / fail will use the default green / red respectively.
            set_width_cols (Dict[str: Dict[str, int]]): Passed to output function defines cols with min/max width
                example: {'details': {'min': 10, 'max': 30}, 'device': {'min': 5, 'max': 15}}
            full_cols (list): columns to ensure are displayed at full length (no wrap no truncate)
            cleaner (callable, optional): The Cleaner function to use.
        """
        # TODO remove ok_status, and handle in CentralAPI method (set resp.ok = True)
        if pad:
            log.error("Deprecated pad parameter referenced in display_results",
                      show=True)

        if resp is not None:
            resp = utils.listify(resp)

            # update caption with rate limit
            if resp[-1].rl:
                rl_str = f"[italic dark_olive_green2]{resp[-1].rl}[/]".lstrip()
                caption = f"{caption}\n  {rl_str}" if caption else f"  {rl_str}"

            for idx, r in enumerate(resp):
                # Multi request url line
                m_colors = {
                    "GET": "bright_green",
                    "DELETE": "red",
                    "PATH": "dark_orange3",
                    "PUT": "dark_orange3",
                    "POST": "dark_orange3"
                }
                fg = "bright_green" if r else "red"
                conditions = [
                    len(resp) > 1, tablefmt in ["action", "raw"], r.ok
                    and not r.output
                ]
                if any(conditions):
                    _url = r.url if not hasattr(r.url,
                                                "raw_path_qs") else r.url.path
                    m_color = m_colors.get(r.method, "reset")
                    print(f"Request {idx + 1} [[{m_color}]{r.method}[reset]: "
                          f"[cyan]{_url}[/cyan]]\n [fg]Response[reset]:")

                if self.raw_out:
                    tablefmt = "raw"
                if not r.output:
                    c = Console(record=True)
                    c.begin_capture()
                    c.print(f"  Status Code: [{fg}]{r.status}[/]")
                    c.print(
                        f"  :warning: Empty Response.  This may be normal.")
                    r.output = c.end_capture()

                if not r or tablefmt in ["action", "raw"]:

                    if tablefmt == "raw":
                        # dots = f"[{fg}]{'.' * 16}[/{fg}]"
                        status_code = f"[{fg}]status code: {r.status}[/{fg}]"
                        print(r.url)
                        print(status_code)
                        if not r.ok:
                            print(r.error)
                        # print(f"{dots}\n{status_code}\n{dots}")
                        print(
                            "[bold cyan]Unformatted response from Aruba Central API GW[/bold cyan]"
                        )
                        print(r.raw)

                        if outfile:
                            self.write_file(outfile, r.raw)

                    else:
                        print(f"[{fg}]{r}")

                    if idx + 1 == len(resp):
                        console.print(f"\n{rl_str}")

                else:
                    self._display_results(r.output,
                                          tablefmt=tablefmt,
                                          title=title,
                                          caption=caption,
                                          pager=pager,
                                          outfile=outfile,
                                          sort_by=sort_by,
                                          reverse=reverse,
                                          stash=stash,
                                          pad=pad,
                                          set_width_cols=set_width_cols,
                                          full_cols=full_cols,
                                          fold_cols=fold_cols,
                                          cleaner=cleaner,
                                          **cleaner_kwargs)

            # TODO make elegant caas send-cmds uses this logic
            if cleaner and cleaner.__name__ == "parse_caas_response":
                print(caption)

            if exit_on_fail and not all([r.ok for r in resp]):
                raise typer.Exit(1)

        elif data:
            self._display_results(data,
                                  tablefmt=tablefmt,
                                  title=title,
                                  caption=caption,
                                  pager=pager,
                                  outfile=outfile,
                                  sort_by=sort_by,
                                  reverse=reverse,
                                  stash=stash,
                                  pad=pad,
                                  set_width_cols=set_width_cols,
                                  full_cols=full_cols,
                                  fold_cols=fold_cols,
                                  cleaner=cleaner,
                                  **cleaner_kwargs)