Loading cccli/command/account.py +25 −4 Original line number Diff line number Diff line Loading @@ -15,6 +15,8 @@ class Command_addaccount(TqlCommand): def __init__(self, cli, argv0): TqlCommand.__init__(self, cli, argv0) self.remove_option("--direct") self.remove_option("--raw") self.set_usage("%prog [options] <account name> <role> [password]") def __call__(self, argv): Loading @@ -25,20 +27,27 @@ class Command_addaccount(TqlCommand): elif len(self.args) == 3: _pass = self.args[2] if self.printer.isinteractive(): self.printer.warn("Removing last line from history") self.printer.warn("Password detected, removing last line from history") self.printer.history.removelast() else: raise cmdBadArgument() try: # add account self.rpccall("addaccount", self.args[0], self.args[1], _status=False) self.cli.rpc.call("addaccount", self.args[0], self.args[1]) except RpcError as e: raise cmdError("RPCError: %s"%str(e)) # set password if _pass is not None: self.rpccall("passwd", "a=%s"%self.args[0], _pass) self.rpccall("passwd", "a=%s"%self.args[0], _pass, _direct=True) class Command_delaccount(TqlCommand): '''Delete an account''' def __init__(self, cli, argv0): TqlCommand.__init__(self, cli, argv0) self.tql_filter += "&a" def __call__(self, argv): self.parse_args(argv) if len(self.args) != 1: Loading @@ -49,6 +58,10 @@ class Command_delaccount(TqlCommand): class Command_close(TqlCommand): '''Disable accounts''' def __init__(self, cli, argv0): TqlCommand.__init__(self, cli, argv0) self.tql_filter += "&a&-close" def __call__(self, argv): self.parse_args(argv) if len(self.args) != 1: Loading @@ -59,6 +72,10 @@ class Command_close(TqlCommand): class Command_declose(TqlCommand): '''Enable accounts''' def __init__(self, cli, argv0): TqlCommand.__init__(self, cli, argv0) self.tql_filter += "&a&close" def __call__(self, argv): self.parse_args(argv) if len(self.args) != 1: Loading @@ -69,6 +86,10 @@ class Command_declose(TqlCommand): class Command_passwd(TqlCommand): '''Change account password''' def __init__(self, cli, argv0): TqlCommand.__init__(self, cli, argv0) self.tql_filter += "&a" def __init__(self, cli, argv0): TqlCommand.__init__(self, cli, argv0) self.set_usage("%prog [options] [tql] [password]") Loading cccli/command/command.py +121 −29 Original line number Diff line number Diff line Loading @@ -74,54 +74,146 @@ class TqlCommand(OptionCommand): OptionCommand.__init__(self, cli, argv0) self.rpc = cli.rpc self.set_usage("%prog [options] <tql>") # set tql filter stuff self.tql_filter = "" self.add_option("-r", "--raw", action="callback", dest="raw", callback=self._cb_raw, help="Don't append security filter to TQL") # set tql check stuff self.add_option("-d", "--direct", action="store_true", dest="direct", help="Directly send TQL to server") # set tql status stuff self.add_option("-s", "--status", action="store_true", dest="status", help="Show status of each tql object") self.add_option("-q", "--quiet", action="store_false", dest="status", help="Dont status of call request") # tql printer option self.add_option("--print-tql", action="store_true", dest="tql_print", help="Print TQL before sending to server") # set tagdisplay stuff self.tdr = self.cli.tagdisplay.resolve self.tdc = self.cli.tagdisplay.color self.tdtc = self.cli.tagdisplay.titlecolor self.add_option("-n", "--no-tagdisplay", action="callback", dest="tagdisplay", self.add_option("--no-tagdisplay", action="callback", dest="tagdisplay", callback=self._cb_notagdisplay, help="No tagdisplay custom display") def _cb_notagdisplay(self, option, opt, value, parser): '''Callback for option --no-tagdisplay''' self.tdr = lambda tagname, tagvalue: tagvalue self.tdc = self.cli.tagdisplay.default_color self.tdtc = self.cli.tagdisplay.default_titlecolor def _cb_raw(self, option, opt, value, parser): '''Callback for option --raw''' self.tql_filter = "" def rpccall(self, *args, **kwargs): '''Call a RPC method an show tql return''' # check for status printing if "_status" in kwargs: _status = kwargs["_status"] del kwargs["_status"] elif self.options.status: _status = True else: _status = False # Do RPC Call ''' Call a RPC method an show tql return _callback: call function _callback after each rpccall _status: display call status _tql_index: is index in args where filter should be appended (def: 1) _tql_print: print tql with filter _exception: catch or not RPCError exception ''' # set default option value _options = { "status": True, "direct": False, "exception": False, "tql": "", "tql_index": 1, "tql_print": False, "callback": None, } # check for options modifiers for o in _options.keys(): _o = "_%s"%o if _o in kwargs: _options[o] = kwargs[_o] del kwargs[_o] elif o in dir(self.options): x = getattr(self.options, o) if x is not None: _options[o] = x # check tql index and get a copy if _options["tql_index"] < 0 or _options["tql_index"] >= len(args): raise cmdError("No indexed TQL") # append filter (empty if raw mode) if self.tql_filter != "": l = list(args) l[_options["tql_index"]] += self.tql_filter args = tuple(l) # Tql printer if _options["tql_print"]: self.printer.out("TQL: %s"%args[_options["tql_index"]]) # Tql check if _options["direct"]: return self._unsecure_rpccall(_options, args, kwargs) return self._secure_rpccall(_options, args, kwargs) def _unsecure_rpccall(self, _options, args, kwargs): '''Just call an RPC without checking before''' try: d = self.rpc.call(*args, **kwargs) if _status: self.show_status(d) if _options["callback"] is not None: _options["callback"](d) if _options["status"]: self.print_status(d) return d except RpcError as e: if _options["exception"]: raise raise cmdError("RPCError: %s"%str(e)) def show_status(self, ans): '''Show status of an Tql request''' def _secure_rpccall(self, _options, args, kwargs): '''Call RPC after listing, confirmation and with id''' # get objects id try: self.printer.out("Status:") for o in ans: s = "%sid: %s%s %sstatus: %s%s %smessage:%s%s%s"%( self.tdtc("id"), self.tdc("id"), self.tdr("id", o), self.tdtc("status"), self.tdc("status"), ans[o][0], self.tdtc("message"), self.tdc("message"), ans[o][1], color["reset"]) if s: self.printer.out(s) except Exception: pass objs = self.cli.rpc.call("list", args[_options["tql_index"]]) except RpcError as e: raise cmdError("RPCError: %s"%str(e)) # no result, goodbye if len(objs) == 0: raise cmdError("No selected object by TQL.") self.printer.out("Objects:") self.print_taglist(objs) self.printer.out("Objects count: %s"%len(objs)) # be sure boby want do that if self.printer.ask("%sProceed?%s (yes): "%(color["lred"], color["reset"])) != "yes": raise cmdWarning("User aborted") # bobby doing many things, he needs to be really sure! if len(objs) > 5: self.printer.out("%sYou will act on more than 5 objets!%s"%(color["uyellow"], color["reset"])) if self.printer.ask("%sAre you really sure?%s (Yes Mistress): " %(color["lred"], color["reset"])) != "Yes Mistress": raise cmdWarning("User aborted") # per validated id execution (this is a kind of atomic implementation) for obj in objs: dobj = dict(obj) try: l = list(args) l[_options["tql_index"]] = "id=%s"%dobj["id"] d = self.cli.rpc.call(*tuple(l), **kwargs) if _options["callback"] is not None: _options["callback"](d) if _options["status"]: self.print_status(d) except RpcError as e: self.printer.error("RPCError: %s"%str(e)) def print_taglist(self, objs): '''Trivial listing of tag''' for o in objs: self.print_tags(o) def print_tags(self, taglist): '''Display a tag with tagdisplay settings''' line = list() for (tn, tv) in taglist: line.append("%s%s:%s%s"%(self.tdtc(tn), tn, self.tdc(tn), self.tdr(tn, tv))) self.printer.out("%s%s"%(" ".join(line), color["reset"])) def print_status(self, outputlist): '''Display status from an object list ''' for o in outputlist: self.print_tags(o[1][1]) cccli/command/host.py +9 −1 Original line number Diff line number Diff line Loading @@ -14,6 +14,7 @@ class Command_exec(TqlCommand): def __init__(self, cli, argv0): TqlCommand.__init__(self, cli, argv0) self.set_usage("%prog [options] <tql> <command>") self.tql_filter += "&con&r~'host|hv'" def __call__(self, argv): # arg parse Loading @@ -21,8 +22,13 @@ class Command_exec(TqlCommand): if len(self.args) != 2: raise cmdBadArgument() # rpc call self.rpccall("exec", self.args[0], self.args[1]) self.rpccall("execute", self.args[0], self.args[1], _callback=self._cb_print_output) def _cb_print_output(self, d): '''Print output of execute by object''' for o in d: self.printer.out("%s output:"%o[0]) self.printer.out("".join(o[1][0][0:]), nl="") class Command_shutdown(TqlCommand): '''Shutdown a physical host''' Loading @@ -36,6 +42,8 @@ class Command_shutdown(TqlCommand): help="Halt after shutdown") self.add_option("-F", action="store_false", dest="graceful", default=True, help="do not go through init but go down real fast") self.tql_filter += "&con&r~'host|hv'" def __call__(self, argv): # arg parse Loading cccli/command/kill.py +4 −0 Original line number Diff line number Diff line Loading @@ -13,6 +13,10 @@ from cccli.command.command import TqlCommand class Command_kill(TqlCommand): '''Kill a server connection''' def __init__(self, cli, argv0): TqlCommand.__init__(self, cli, argv0) self.tql_filter += "&con" def __call__(self, argv): # args parse self.parse_args(argv) Loading cccli/command/list.py +17 −39 Original line number Diff line number Diff line Loading @@ -20,78 +20,56 @@ class Command_list(TqlCommand): help="column aligment display") self.add_option("-l", action="store_true", dest="align", help="line aligment display") self.remove_option("-s") self.remove_option("--quiet") self.remove_option("--direct") def __call__(self, argv): self.parse_args(argv) if len(self.args) == 0: self.args.append("") objs = self.rpccall("list", str.join("", self.args)) objs = self.rpccall("list", str.join("", self.args), _status=False, _direct=True) if len(objs) == 0: return if self.options.align: self._list_align(objs) self.list_align(objs) elif self.options.table: self._list_table(objs) self.list_table(objs) else: self._trivial_list(objs) self.print_taglist(objs) def _trivial_list(self, objs): '''Trivial listing of tag''' for o in objs: id = self.tdr("id", o.pop("id")) tags = " ".join([ "%s%s:%s%s"%(self.tdtc(t), t, self.tdc(t), self.tdr(t, v)) for (t,v) in o.items() ]) self.printer.out("%sid=%s%s %s%s"%(self.tdtc("id"), self.tdc("id"), id, tags, color["reset"])) def _list_align(self, objs): def list_align(self, objs): '''Listing line aligned''' # get max size by tag tags = dict() for o in objs: for t,v in o.items(): for (t, v) in o: tags[t] = max(len(self.tdr(t, v)), tags.get(t, len(t))) # extract id size idsize = tags.pop("id") # dislay each object by line for o in objs: # show id tag line = "%sid=%s%s"%(self.tdtc("id"), self.tdc("id"), self.tdr("id", o.pop("id")).ljust(idsize + 2)) # show others tags for tagname in sorted(tags.keys()): line = str() for (tagname,tagvalue) in o: line += "%s%s:%s%s"%(self.tdtc(tagname), tagname, self.tdc(tagname), self.tdr(tagname, o.get(tagname, u"")).ljust(tags[tagname] + 1)) self.tdr(tagname, tagvalue).ljust(tags[tagname] + 1)) self.printer.out("%s%s"%(line, color["reset"])) def _list_table(self, objs): def list_table(self, objs): '''Listing table style''' # get max size by tag tags = dict() for o in objs: for t,v in o.items(): for (t,v) in o: tags[t] = max(len(self.tdr(t, v)), tags.get(t, len(t))) # extract id size idsize = tags.pop("id") # print id title self.printer.out(self.tdtc("id"), nl="") self.printer.out("id".ljust(idsize+1), nl=" ") # print others titles # print title for t,v in tags.items(): self.printer.out(self.tdtc(t), nl="") self.printer.out(t.ljust(v), nl=" ") self.printer.out(color["reset"]) # print obj for obj in objs: # print id first self.printer.out(self.tdc("id"), nl="") self.printer.out(self.tdr("id", obj.pop("id")).ljust(idsize+1), nl=" ") obj = dict(obj) # print others tags for (t, v) in tags.items(): self.printer.out(self.tdc(t), nl="") Loading Loading
cccli/command/account.py +25 −4 Original line number Diff line number Diff line Loading @@ -15,6 +15,8 @@ class Command_addaccount(TqlCommand): def __init__(self, cli, argv0): TqlCommand.__init__(self, cli, argv0) self.remove_option("--direct") self.remove_option("--raw") self.set_usage("%prog [options] <account name> <role> [password]") def __call__(self, argv): Loading @@ -25,20 +27,27 @@ class Command_addaccount(TqlCommand): elif len(self.args) == 3: _pass = self.args[2] if self.printer.isinteractive(): self.printer.warn("Removing last line from history") self.printer.warn("Password detected, removing last line from history") self.printer.history.removelast() else: raise cmdBadArgument() try: # add account self.rpccall("addaccount", self.args[0], self.args[1], _status=False) self.cli.rpc.call("addaccount", self.args[0], self.args[1]) except RpcError as e: raise cmdError("RPCError: %s"%str(e)) # set password if _pass is not None: self.rpccall("passwd", "a=%s"%self.args[0], _pass) self.rpccall("passwd", "a=%s"%self.args[0], _pass, _direct=True) class Command_delaccount(TqlCommand): '''Delete an account''' def __init__(self, cli, argv0): TqlCommand.__init__(self, cli, argv0) self.tql_filter += "&a" def __call__(self, argv): self.parse_args(argv) if len(self.args) != 1: Loading @@ -49,6 +58,10 @@ class Command_delaccount(TqlCommand): class Command_close(TqlCommand): '''Disable accounts''' def __init__(self, cli, argv0): TqlCommand.__init__(self, cli, argv0) self.tql_filter += "&a&-close" def __call__(self, argv): self.parse_args(argv) if len(self.args) != 1: Loading @@ -59,6 +72,10 @@ class Command_close(TqlCommand): class Command_declose(TqlCommand): '''Enable accounts''' def __init__(self, cli, argv0): TqlCommand.__init__(self, cli, argv0) self.tql_filter += "&a&close" def __call__(self, argv): self.parse_args(argv) if len(self.args) != 1: Loading @@ -69,6 +86,10 @@ class Command_declose(TqlCommand): class Command_passwd(TqlCommand): '''Change account password''' def __init__(self, cli, argv0): TqlCommand.__init__(self, cli, argv0) self.tql_filter += "&a" def __init__(self, cli, argv0): TqlCommand.__init__(self, cli, argv0) self.set_usage("%prog [options] [tql] [password]") Loading
cccli/command/command.py +121 −29 Original line number Diff line number Diff line Loading @@ -74,54 +74,146 @@ class TqlCommand(OptionCommand): OptionCommand.__init__(self, cli, argv0) self.rpc = cli.rpc self.set_usage("%prog [options] <tql>") # set tql filter stuff self.tql_filter = "" self.add_option("-r", "--raw", action="callback", dest="raw", callback=self._cb_raw, help="Don't append security filter to TQL") # set tql check stuff self.add_option("-d", "--direct", action="store_true", dest="direct", help="Directly send TQL to server") # set tql status stuff self.add_option("-s", "--status", action="store_true", dest="status", help="Show status of each tql object") self.add_option("-q", "--quiet", action="store_false", dest="status", help="Dont status of call request") # tql printer option self.add_option("--print-tql", action="store_true", dest="tql_print", help="Print TQL before sending to server") # set tagdisplay stuff self.tdr = self.cli.tagdisplay.resolve self.tdc = self.cli.tagdisplay.color self.tdtc = self.cli.tagdisplay.titlecolor self.add_option("-n", "--no-tagdisplay", action="callback", dest="tagdisplay", self.add_option("--no-tagdisplay", action="callback", dest="tagdisplay", callback=self._cb_notagdisplay, help="No tagdisplay custom display") def _cb_notagdisplay(self, option, opt, value, parser): '''Callback for option --no-tagdisplay''' self.tdr = lambda tagname, tagvalue: tagvalue self.tdc = self.cli.tagdisplay.default_color self.tdtc = self.cli.tagdisplay.default_titlecolor def _cb_raw(self, option, opt, value, parser): '''Callback for option --raw''' self.tql_filter = "" def rpccall(self, *args, **kwargs): '''Call a RPC method an show tql return''' # check for status printing if "_status" in kwargs: _status = kwargs["_status"] del kwargs["_status"] elif self.options.status: _status = True else: _status = False # Do RPC Call ''' Call a RPC method an show tql return _callback: call function _callback after each rpccall _status: display call status _tql_index: is index in args where filter should be appended (def: 1) _tql_print: print tql with filter _exception: catch or not RPCError exception ''' # set default option value _options = { "status": True, "direct": False, "exception": False, "tql": "", "tql_index": 1, "tql_print": False, "callback": None, } # check for options modifiers for o in _options.keys(): _o = "_%s"%o if _o in kwargs: _options[o] = kwargs[_o] del kwargs[_o] elif o in dir(self.options): x = getattr(self.options, o) if x is not None: _options[o] = x # check tql index and get a copy if _options["tql_index"] < 0 or _options["tql_index"] >= len(args): raise cmdError("No indexed TQL") # append filter (empty if raw mode) if self.tql_filter != "": l = list(args) l[_options["tql_index"]] += self.tql_filter args = tuple(l) # Tql printer if _options["tql_print"]: self.printer.out("TQL: %s"%args[_options["tql_index"]]) # Tql check if _options["direct"]: return self._unsecure_rpccall(_options, args, kwargs) return self._secure_rpccall(_options, args, kwargs) def _unsecure_rpccall(self, _options, args, kwargs): '''Just call an RPC without checking before''' try: d = self.rpc.call(*args, **kwargs) if _status: self.show_status(d) if _options["callback"] is not None: _options["callback"](d) if _options["status"]: self.print_status(d) return d except RpcError as e: if _options["exception"]: raise raise cmdError("RPCError: %s"%str(e)) def show_status(self, ans): '''Show status of an Tql request''' def _secure_rpccall(self, _options, args, kwargs): '''Call RPC after listing, confirmation and with id''' # get objects id try: self.printer.out("Status:") for o in ans: s = "%sid: %s%s %sstatus: %s%s %smessage:%s%s%s"%( self.tdtc("id"), self.tdc("id"), self.tdr("id", o), self.tdtc("status"), self.tdc("status"), ans[o][0], self.tdtc("message"), self.tdc("message"), ans[o][1], color["reset"]) if s: self.printer.out(s) except Exception: pass objs = self.cli.rpc.call("list", args[_options["tql_index"]]) except RpcError as e: raise cmdError("RPCError: %s"%str(e)) # no result, goodbye if len(objs) == 0: raise cmdError("No selected object by TQL.") self.printer.out("Objects:") self.print_taglist(objs) self.printer.out("Objects count: %s"%len(objs)) # be sure boby want do that if self.printer.ask("%sProceed?%s (yes): "%(color["lred"], color["reset"])) != "yes": raise cmdWarning("User aborted") # bobby doing many things, he needs to be really sure! if len(objs) > 5: self.printer.out("%sYou will act on more than 5 objets!%s"%(color["uyellow"], color["reset"])) if self.printer.ask("%sAre you really sure?%s (Yes Mistress): " %(color["lred"], color["reset"])) != "Yes Mistress": raise cmdWarning("User aborted") # per validated id execution (this is a kind of atomic implementation) for obj in objs: dobj = dict(obj) try: l = list(args) l[_options["tql_index"]] = "id=%s"%dobj["id"] d = self.cli.rpc.call(*tuple(l), **kwargs) if _options["callback"] is not None: _options["callback"](d) if _options["status"]: self.print_status(d) except RpcError as e: self.printer.error("RPCError: %s"%str(e)) def print_taglist(self, objs): '''Trivial listing of tag''' for o in objs: self.print_tags(o) def print_tags(self, taglist): '''Display a tag with tagdisplay settings''' line = list() for (tn, tv) in taglist: line.append("%s%s:%s%s"%(self.tdtc(tn), tn, self.tdc(tn), self.tdr(tn, tv))) self.printer.out("%s%s"%(" ".join(line), color["reset"])) def print_status(self, outputlist): '''Display status from an object list ''' for o in outputlist: self.print_tags(o[1][1])
cccli/command/host.py +9 −1 Original line number Diff line number Diff line Loading @@ -14,6 +14,7 @@ class Command_exec(TqlCommand): def __init__(self, cli, argv0): TqlCommand.__init__(self, cli, argv0) self.set_usage("%prog [options] <tql> <command>") self.tql_filter += "&con&r~'host|hv'" def __call__(self, argv): # arg parse Loading @@ -21,8 +22,13 @@ class Command_exec(TqlCommand): if len(self.args) != 2: raise cmdBadArgument() # rpc call self.rpccall("exec", self.args[0], self.args[1]) self.rpccall("execute", self.args[0], self.args[1], _callback=self._cb_print_output) def _cb_print_output(self, d): '''Print output of execute by object''' for o in d: self.printer.out("%s output:"%o[0]) self.printer.out("".join(o[1][0][0:]), nl="") class Command_shutdown(TqlCommand): '''Shutdown a physical host''' Loading @@ -36,6 +42,8 @@ class Command_shutdown(TqlCommand): help="Halt after shutdown") self.add_option("-F", action="store_false", dest="graceful", default=True, help="do not go through init but go down real fast") self.tql_filter += "&con&r~'host|hv'" def __call__(self, argv): # arg parse Loading
cccli/command/kill.py +4 −0 Original line number Diff line number Diff line Loading @@ -13,6 +13,10 @@ from cccli.command.command import TqlCommand class Command_kill(TqlCommand): '''Kill a server connection''' def __init__(self, cli, argv0): TqlCommand.__init__(self, cli, argv0) self.tql_filter += "&con" def __call__(self, argv): # args parse self.parse_args(argv) Loading
cccli/command/list.py +17 −39 Original line number Diff line number Diff line Loading @@ -20,78 +20,56 @@ class Command_list(TqlCommand): help="column aligment display") self.add_option("-l", action="store_true", dest="align", help="line aligment display") self.remove_option("-s") self.remove_option("--quiet") self.remove_option("--direct") def __call__(self, argv): self.parse_args(argv) if len(self.args) == 0: self.args.append("") objs = self.rpccall("list", str.join("", self.args)) objs = self.rpccall("list", str.join("", self.args), _status=False, _direct=True) if len(objs) == 0: return if self.options.align: self._list_align(objs) self.list_align(objs) elif self.options.table: self._list_table(objs) self.list_table(objs) else: self._trivial_list(objs) self.print_taglist(objs) def _trivial_list(self, objs): '''Trivial listing of tag''' for o in objs: id = self.tdr("id", o.pop("id")) tags = " ".join([ "%s%s:%s%s"%(self.tdtc(t), t, self.tdc(t), self.tdr(t, v)) for (t,v) in o.items() ]) self.printer.out("%sid=%s%s %s%s"%(self.tdtc("id"), self.tdc("id"), id, tags, color["reset"])) def _list_align(self, objs): def list_align(self, objs): '''Listing line aligned''' # get max size by tag tags = dict() for o in objs: for t,v in o.items(): for (t, v) in o: tags[t] = max(len(self.tdr(t, v)), tags.get(t, len(t))) # extract id size idsize = tags.pop("id") # dislay each object by line for o in objs: # show id tag line = "%sid=%s%s"%(self.tdtc("id"), self.tdc("id"), self.tdr("id", o.pop("id")).ljust(idsize + 2)) # show others tags for tagname in sorted(tags.keys()): line = str() for (tagname,tagvalue) in o: line += "%s%s:%s%s"%(self.tdtc(tagname), tagname, self.tdc(tagname), self.tdr(tagname, o.get(tagname, u"")).ljust(tags[tagname] + 1)) self.tdr(tagname, tagvalue).ljust(tags[tagname] + 1)) self.printer.out("%s%s"%(line, color["reset"])) def _list_table(self, objs): def list_table(self, objs): '''Listing table style''' # get max size by tag tags = dict() for o in objs: for t,v in o.items(): for (t,v) in o: tags[t] = max(len(self.tdr(t, v)), tags.get(t, len(t))) # extract id size idsize = tags.pop("id") # print id title self.printer.out(self.tdtc("id"), nl="") self.printer.out("id".ljust(idsize+1), nl=" ") # print others titles # print title for t,v in tags.items(): self.printer.out(self.tdtc(t), nl="") self.printer.out(t.ljust(v), nl=" ") self.printer.out(color["reset"]) # print obj for obj in objs: # print id first self.printer.out(self.tdc("id"), nl="") self.printer.out(self.tdr("id", obj.pop("id")).ljust(idsize+1), nl=" ") obj = dict(obj) # print others tags for (t, v) in tags.items(): self.printer.out(self.tdc(t), nl="") Loading