def test_print_json_ensure_ascii(): console = Console(file=io.StringIO(), color_system="truecolor") console.print_json(data={"foo": "💩"}, ensure_ascii=False) result = console.file.getvalue() print(repr(result)) expected = '\x1b[1m{\x1b[0m\n \x1b[1;34m"foo"\x1b[0m: \x1b[32m"💩"\x1b[0m\n\x1b[1m}\x1b[0m\n' assert result == expected
def test_print_json_indent_none(): console = Console(file=io.StringIO(), color_system="truecolor") data = {"name": "apple", "count": 1} console.print_json(data=data, indent=None) result = console.file.getvalue() expected = '\x1b[1m{\x1b[0m\x1b[1;34m"name"\x1b[0m: \x1b[32m"apple"\x1b[0m, \x1b[1;34m"count"\x1b[0m: \x1b[1;36m1\x1b[0m\x1b[1m}\x1b[0m\n' assert result == expected
def test_print_json_data(): console = Console(file=io.StringIO(), color_system="truecolor") console.print_json(data=[False, True, None, "foo"], indent=4) result = console.file.getvalue() print(repr(result)) expected = '\x1b[1m[\x1b[0m\n \x1b[3;91mfalse\x1b[0m,\n \x1b[3;92mtrue\x1b[0m,\n \x1b[3;35mnull\x1b[0m,\n \x1b[32m"foo"\x1b[0m\n\x1b[1m]\x1b[0m\n' assert result == expected
def render_json(data: Any): """ Print nicely formatted representation of a JSON serializable python primitive. """ console = Console() console.print() console.print_json(json.dumps(data)) console.print()
def parse_apk(apkfile, workers, fn_match=None, outfile=None): console = Console() console.log(f"Parsing {apkfile} with {workers} workers ...") dexes = list(extract_dex_files(apkfile)) console.log(f"Found {len(dexes)} DEX file.") total = sum(map(lambda d: len(d.data), dexes)) progress = Progress( TextColumn("[progress.description]{task.description}"), BarColumn( complete_style='bar.complete', finished_style='bar.finished', pulse_style='bar.pulse', ), TextColumn("[progress.percentage]{task.percentage:>3.0f}%"), TimeRemainingColumn(), TimeElapsedColumn(), console=console, ) out = {} out.update(JNI_COMMON) num_classes = 0 t0 = datetime.now() with progress: task = progress.add_task("Analyzing...", total=total) with multiprocessing.Pool(workers) as pool: result = pool.imap(parse_dex_proc, dexes) for dex, count, res in result: if count == 0: console.log( "Parse {} ({} bytes) [bold red]failed: {}".format( dex.name, len(dex.data), res)) continue console.log("Parse {} ({} bytes), found {} classes.".format( dex.name, len(dex.data), count)) num_classes += count progress.update(task, advance=len(dex.data)) for cname, data in res.items(): if fn_match and not fn_match(cname): continue out.update(data) console.log("Aanlyzed {} classes, cost: {}".format(num_classes, datetime.now() - t0)) console.log("Found {} JNI methods.".format(len(out))) if not outfile: console.print_json(data=out) else: with open(outfile, 'w') as f: json.dump(out, f, indent=2, ensure_ascii=False)
def get(model_tag, output): """Print Model details by providing the model_tag \b bentoml models get FraudDetector:latest bentoml models get --output=json FraudDetector:20210709_DE14C9 """ model = model_store.get(model_tag) console = Console() if output == "path": console.print(model.path) elif output == "json": info = json.dumps(model.info.to_dict(), indent=2, default=str) console.print_json(info) else: info = yaml.dump(model.info, indent=2) console.print(info)
def parse_dx(dx: Analysis, fn_match=None, outfile=None): console = Console() out = {} out.update(JNI_COMMON) count = 0 for cx in dx.get_internal_classes(): methods = parse_class_def(cx.get_class()) count += 1 if not methods: continue cname = methods[0].jclass if fn_match and not fn_match(cname): continue for m in methods: out.update(m.as_dict) console.log(f"Parse {count} classes.") console.log(f"Found {len(out)} JNI methods.") if not outfile: console.print_json(data=out) else: with open(outfile, 'w') as f: json.dump(out, f, indent=2, ensure_ascii=False)
def test_print_json_error(): console = Console(file=io.StringIO(), color_system="truecolor") with pytest.raises(TypeError): console.print_json(["foo"], indent=4)
class Environment(object): """Provides access to the current CLI environment.""" def __init__(self): # {'path:to:command': ModuleLoader()} # {'vs:list': ModuleLoader()} self.commands = {} self.aliases = {} self.vars = {} self.client = None self.console = Console() self.err_console = Console(stderr=True) self.format = 'table' self.skip_confirmations = False self.config_file = None self._modules_loaded = False def out(self, output): """Outputs a string to the console (stdout).""" if self.format == 'json': try: self.console.print_json(output) # Tried to print not-json, so just print it out normally... except JSONDecodeError: click.echo(output) elif self.format == 'jsonraw': # Using Rich here is problematic because in the unit tests it thinks the terminal is 80 characters wide # and only prints out that many characters. click.echo(output) else: # If we want to print a list of tables, Rich doens't handle that well. if isinstance(output, list): for line in output: self.console.print(line, overflow='ignore') else: self.console.print(output, overflow='ignore') def err(self, output, newline=True): """Outputs an error string to the console (stderr).""" self.err_console.print(output, new_line_start=newline) def fmt(self, output, fmt=None): """Format output based on current the environment format.""" if fmt is None: fmt = self.format return formatting.format_output(output, fmt) def format_output_is_json(self): """Return True if format output is json or jsonraw""" return 'json' in self.format def fout(self, output): """Format the input and output to the console (stdout).""" if output is not None: try: self.out(self.fmt(output)) except UnicodeEncodeError: # If we hit an undecodeable entry, just try outputting as json. self.out(self.fmt(output, 'json')) def python_output(self, output): """Prints out python code""" self.console.print(Syntax(output, "python")) def input(self, prompt, default=None, show_default=True): """Provide a command prompt.""" return click.prompt(prompt, default=default, show_default=show_default) def getpass(self, prompt, default=None): """Provide a password prompt.""" password = click.prompt(prompt, hide_input=True, default=default) # https://github.com/softlayer/softlayer-python/issues/1436 # click.prompt uses python's getpass() in the background # https://github.com/python/cpython/blob/3.9/Lib/getpass.py#L97 # In windows, shift+insert actually inputs the below 2 characters # If we detect those 2 characters, need to manually read from the clipbaord instead # https://stackoverflow.com/questions/101128/how-do-i-read-text-from-the-clipboard if password == 'àR': # tkinter is a built in python gui, but it has clipboard reading functions. # pylint: disable=import-outside-toplevel from tkinter import Tk tk_manager = Tk() password = tk_manager.clipboard_get() # keep the window from showing tk_manager.withdraw() return password # Command loading methods def list_commands(self, *path): """Command listing.""" path_str = ':'.join(path) commands = [] for command in self.commands: # Filter based on prefix and the segment length if all([ command.startswith(path_str), len(path) == command.count(":") ]): # offset is used to exclude the path that the caller requested. offset = len(path_str) + 1 if path_str else 0 commands.append(command[offset:]) return sorted(commands) def get_command(self, *path): """Return command at the given path or raise error.""" path_str = ':'.join(path) if path_str in self.commands: return self.commands[path_str].load() return None def resolve_alias(self, path_str): """Returns the actual command name. Uses the alias mapping.""" if path_str in self.aliases: return self.aliases[path_str] return path_str def load(self): """Loads all modules.""" if self._modules_loaded is True: return self.load_modules_from_python(routes.ALL_ROUTES) self.aliases.update(routes.ALL_ALIASES) self._load_modules_from_entry_points('softlayer.cli') self._modules_loaded = True def load_modules_from_python(self, route_list): """Load modules from the native python source.""" for name, modpath in route_list: if ':' in modpath: path, attr = modpath.split(':', 1) else: path, attr = modpath, None self.commands[name] = ModuleLoader(path, attr=attr) def _load_modules_from_entry_points(self, entry_point_group): """Load modules from the entry_points (slower). Entry points can be used to add new commands to the CLI. Usage: entry_points={'softlayer.cli': ['new-cmd = mymodule.new_cmd.cli']} """ for obj in pkg_resources.iter_entry_points(group=entry_point_group, name=None): self.commands[obj.name] = obj def ensure_client(self, config_file=None, is_demo=False, proxy=None): """Create a new SLAPI client to the environment. This will be a no-op if there is already a client in this environment. """ if self.client is not None: return # Environment can be passed in explicitly. This is used for testing if is_demo: client = SoftLayer.BaseClient( transport=SoftLayer.FixtureTransport(), auth=None, ) else: # Create SL Client client = SoftLayer.create_client_from_env( proxy=proxy, config_file=config_file, ) self.client = client