def get_object(client: CommandClient, argv: List[str]) -> CommandClient: """ Constructs a path to object and returns given object (if it exists). """ if argv[0] == "cmd": argv = argv[1:] # flag noting if we have consumed arg1 as the selector, eg screen[0] parsed_next = False for arg0, arg1 in itertools.zip_longest(argv, argv[1:]): # previous argument was an item, skip here if parsed_next: parsed_next = False continue # check if it is an item try: client = client.navigate(arg0, arg1) parsed_next = True continue except SelectError: pass # check if it is an attr try: client = client.navigate(arg0, None) continue except SelectError: pass print("Specified object does not exist: " + " ".join(argv)) sys.exit(1) return client
def __init__(self, client: CommandInterface, completekey="tab") -> None: # Readline is imported here to prevent issues with terminal resizing # which would result from readline being imported when qtile is first # started self.readline = import_module("readline") self._command_client = CommandClient(client) self._completekey = completekey self._builtins = [i[3:] for i in dir(self) if i.startswith("do_")]
def _ls(self, client: CommandClient, object_type: str | None) -> tuple[list[str], list[str]]: if object_type is not None: allow_root, items = client.items(object_type) str_items = [str(i) for i in items] if allow_root: children = client.navigate(object_type, None).children else: children = [] return children, str_items else: return client.children, []
def get_formated_info(obj: CommandClient, cmd: str, args=True, short=True) -> str: """Get documentation for command/function and format it. Returns: * args=True, short=True - '*' if arguments are present and a summary line. * args=True, short=False - (function args) and a summary line. * args=False - a summary line. If 'doc' function is not present in object or there is no doc string for given cmd it returns empty string. The arguments are extracted from doc[0] line, the summary is constructed from doc[1] line. """ doc = obj.call("doc", cmd).splitlines() tdoc = doc[0] doc_args = tdoc[tdoc.find("("):tdoc.find(")") + 1].strip() short_description = doc[1] if len(doc) > 1 else "" if not args: doc_args = "" elif short: doc_args = " " if doc_args == "()" else "*" return (doc_args + " " + short_description).rstrip()
def cmd_obj(args) -> None: "Runs tool according to specified arguments." if args.obj_spec: sock_file = args.socket or find_sockfile() ipc_client = Client(sock_file) cmd_object = IPCCommandInterface(ipc_client) cmd_client = CommandClient(cmd_object) obj = get_object(cmd_client, args.obj_spec) if args.function == "help": try: print_commands("-o " + " ".join(args.obj_spec), obj) except CommandError: if len(args.obj_spec) == 1: print( f"{args.obj_spec} object needs a specified identifier e.g. '-o bar top'." ) sys.exit(1) else: raise elif args.info: print( args.function + get_formated_info(obj, args.function, args=True, short=False)) else: ret = run_function(obj, args.function, args.args) if ret is not None: pprint.pprint(ret) else: print_base_objects() sys.exit(1)
def _find_node( self, src: CommandClient, *paths: str) -> Tuple[Optional[CommandClient], Optional[str]]: """Find an object in the command graph Return the object in the command graph at the specified path relative to the given node. """ if len(paths) == 0: return src, None path, *next_path = paths if path == "..": return self._find_node(src.parent or src, *next_path) if path not in src.children: return None, None if len(next_path) == 0: return src, path item, *maybe_next_path = next_path allow_root, items = src.items(path) for transformation in [str, int]: try: transformed_item = transformation(item) except ValueError: continue if transformed_item in items: next_node = src.navigate(path, transformed_item) return self._find_node(next_node, *maybe_next_path) if not allow_root: return None, None next_node = src.navigate(path, None) return self._find_node(next_node, *next_path)
def run_function(client: CommandClient, funcname: str, args: List[str]) -> str: "Run command with specified args on given object." try: ret = client.call(funcname, *args) except SelectError: print("error: Sorry no function ", funcname) sys.exit(1) except CommandError as e: print("error: Command '{}' returned error: {}".format( funcname, str(e))) sys.exit(1) except CommandException as e: print("error: Sorry cannot run function '{}' with arguments {}: {}". format(funcname, args, str(e))) sys.exit(1) return ret
def print_commands(prefix: str, obj: CommandClient) -> None: """Print available commands for given object.""" prefix += " -f " cmds = obj.call("commands") output = [] for cmd in cmds: doc_args = get_formated_info(obj, cmd) pcmd = prefix + cmd output.append([pcmd, doc_args]) max_cmd = max(len(pcmd) for pcmd, _ in output) # Print formatted output formatting = "{:<%d}\t{}" % (max_cmd + 1) for line in output: print(formatting.format(line[0], line[1]))
class QSh: """Qtile shell instance""" def __init__(self, client: CommandInterface, completekey="tab") -> None: # Readline is imported here to prevent issues with terminal resizing # which would result from readline being imported when qtile is first # started self.readline = import_module("readline") self._command_client = CommandClient(client) self._completekey = completekey self._builtins = [i[3:] for i in dir(self) if i.startswith("do_")] def complete(self, arg, state) -> str | None: buf = self.readline.get_line_buffer() completers = self._complete(buf, arg) if completers and state < len(completers): return completers[state] return None def _complete(self, buf, arg) -> list[str]: if not re.search(r" |\(", buf) or buf.startswith("help "): options = self._builtins + self._command_client.commands lst = [i for i in options if i.startswith(arg)] return lst elif buf.startswith("cd ") or buf.startswith("ls "): path, sep, last = arg.rpartition("/") node, rest_path = self._find_path(path) if node is None: return [] children, items = self._ls(node, rest_path) options = children + items completions = [ path + sep + i for i in options if i.startswith(last) ] if len(completions) == 1: # add a slash to continue completing the next part of the path return [completions[0] + "/"] return completions return [] @property def prompt(self) -> str: return "{} > ".format(format_selectors(self._command_client.selectors)) def columnize(self, lst, update_termwidth=True) -> str: if update_termwidth: self.termwidth = terminal_width() ret = [] if lst: lst = list(map(str, lst)) mx = max(map(len, lst)) cols = self.termwidth // (mx + 2) or 1 # We want `(n-1) * cols + 1 <= len(lst) <= n * cols` to return `n` # If we subtract 1, then do `// cols`, we get `n - 1`, so we can then add 1 rows = (len(lst) - 1) // cols + 1 for i in range(rows): # Because Python array slicing can go beyond the array bounds, # we don't need to be careful with the values here sl = lst[i * cols:(i + 1) * cols] sl = [x + " " * (mx - len(x)) for x in sl] ret.append(" ".join(sl)) return "\n".join(ret) def _ls(self, client: CommandClient, object_type: str | None) -> tuple[list[str], list[str]]: if object_type is not None: allow_root, items = client.items(object_type) str_items = [str(i) for i in items] if allow_root: children = client.navigate(object_type, None).children else: children = [] return children, str_items else: return client.children, [] def _find_path(self, path: str) -> tuple[CommandClient | None, str | None]: """Find an object relative to the current node Finds and returns the command graph node that is defined relative to the current node. """ root = self._command_client.root if path.startswith( "/") else self._command_client parts = [i for i in path.split("/") if i] return self._find_node(root, *parts) def _find_node(self, src: CommandClient, *paths: str) -> tuple[CommandClient | None, str | None]: """Find an object in the command graph Return the object in the command graph at the specified path relative to the given node. """ if len(paths) == 0: return src, None path, *next_path = paths if path == "..": return self._find_node(src.parent or src, *next_path) if path not in src.children: return None, None if len(next_path) == 0: return src, path item, *maybe_next_path = next_path allow_root, items = src.items(path) for transformation in [str, int]: try: transformed_item = transformation(item) except ValueError: continue if transformed_item in items: next_node = src.navigate(path, transformed_item) return self._find_node(next_node, *maybe_next_path) if not allow_root: return None, None next_node = src.navigate(path, None) return self._find_node(next_node, *next_path) def do_cd(self, arg: str | None) -> str: """Change to another path. Examples ======== cd layout/0 cd ../layout """ if arg is None: self._command_client = self._command_client.root return "/" next_node, rest_path = self._find_path(arg) if next_node is None: return "No such path." if rest_path is None: self._command_client = next_node else: allow_root, _ = next_node.items(rest_path) if not allow_root: return "Item required for {}".format(rest_path) self._command_client = next_node.navigate(rest_path, None) return format_selectors(self._command_client.selectors) or "/" def do_ls(self, arg: str | None) -> str: """List contained items on a node. Examples ======== > ls > ls ../layout """ if arg: node, rest_path = self._find_path(arg) if not node: return "No such path." base_path = arg.rstrip("/") + "/" else: node = self._command_client rest_path = None base_path = "" assert node is not None objects, items = self._ls(node, rest_path) formatted_ls = ["{}{}/".format(base_path, i) for i in objects] + [ "{}[{}]/".format(base_path[:-1], i) for i in items ] return self.columnize(formatted_ls) def do_pwd(self, arg) -> str: """Returns the current working location This is the same information as presented in the qshell prompt, but is very useful when running iqshell. Examples ======== > pwd / > cd bar/top bar['top']> pwd bar['top'] """ return format_selectors(self._command_client.selectors) or "/" def do_help(self, arg: str | None) -> str: """Give help on commands and builtins When invoked without arguments, provides an overview of all commands. When passed as an argument, also provides a detailed help on a specific command or builtin. Examples ======== > help > help command """ if not arg: lst = [ "help command -- Help for a specific command.", "", "Builtins", "========", self.columnize(self._builtins), ] cmds = self._command_client.commands if cmds: lst.extend([ "", "Commands for this object", "========================", self.columnize(cmds), ]) return "\n".join(lst) elif arg in self._command_client.commands: return self._command_client.call("doc", arg) elif arg in self._builtins: c = getattr(self, "do_" + arg) ret = inspect.getdoc(c) assert ret is not None return ret else: return "No such command: %s" % arg def do_exit(self, args) -> None: """Exit qshell""" sys.exit(0) do_quit = do_exit do_q = do_exit def process_line(self, line: str) -> Any: builtin_match = re.fullmatch(r"(?P<cmd>\w+)(?:\s+(?P<arg>\S*))?", line) if builtin_match: cmd = builtin_match.group("cmd") args = builtin_match.group("arg") if cmd in self._builtins: builtin = getattr(self, "do_" + cmd) val = builtin(args) return val else: return "Invalid builtin: {}".format(cmd) command_match = re.fullmatch(r"(?P<cmd>\w+)\((?P<args>[\w\s,]*)\)", line) if command_match: cmd = command_match.group("cmd") args = command_match.group("args") if args: cmd_args = tuple(map(str.strip, args.split(","))) else: cmd_args = () if cmd not in self._command_client.commands: return "Command does not exist: {}".format(cmd) try: return self._command_client.call(cmd, *cmd_args) except CommandException as e: return ( "Caught command exception (is the command invoked incorrectly?): {}\n" .format(e)) return "Invalid command: {}".format(line) def loop(self) -> None: self.readline.set_completer(self.complete) self.readline.parse_and_bind(self._completekey + ": complete") self.readline.set_completer_delims(" ()|") while True: try: line = input(self.prompt) except (EOFError, KeyboardInterrupt): print() return if not line: continue try: val = self.process_line(line) except CommandError as e: val = "Caught command error (is the current path still valid?): {}\n".format( e) if isinstance(val, str): print(val) elif val: pprint.pprint(val)
def __init__(self, client: CommandInterface, completekey="tab") -> None: self._command_client = CommandClient(client) self._completekey = completekey self._builtins = [i[3:] for i in dir(self) if i.startswith("do_")]
# mod + #'s to focus tags # applications, etc, mod v, mod shift c, mod d # bottom MIDDLE CLICK TO CYCLE/DROP (try mapping shift_l + alt_l + "Next" keycode 117) # screenshots # in max mode, I can't see other windows behind # NOTESSSS # client.window.get_wm_class() gets a window class (instead of its name) import libqtile from libqtile import qtile from libqtile.config import Key, KeyChord, Screen, Group, Drag, Click, Match from libqtile.lazy import lazy from libqtile import layout, bar, widget, hook from libqtile.log_utils import logger from libqtile.command.client import CommandClient c = CommandClient() from typing import List # noqa: F401 logger.warning(f"WOW LOG::: {layout.__name__}") logger.warning(dir(c)) logger.warning(f"lazy dir is useless{dir(lazy)}") modkey = mod = "mod4" keys = [ Key([mod, "shift"], "c", lazy.window.kill()), # Switch between windows in current stack pane # Key([mod], "k", lazy.layout.down()),