comparison sat_frontends/jp/cmd_debug.py @ 3040:fee60f17ebac

jp: jp asyncio port: /!\ this commit is huge. Jp is temporarily not working with `dbus` bridge /!\ This patch implements the port of jp to asyncio, so it is now correctly using the bridge asynchronously, and it can be used with bridges like `pb`. This also simplify the code, notably for things which were previously implemented with many callbacks (like pagination with RSM). During the process, some behaviours have been modified/fixed, in jp and backends, check diff for details.
author Goffi <goffi@goffi.org>
date Wed, 25 Sep 2019 08:56:41 +0200
parents ab2696e34d29
children cf843dd7c345
comparison
equal deleted inserted replaced
3039:a1bc34f90fa5 3040:fee60f17ebac
46 46
47 class Method(base.CommandBase, BridgeCommon): 47 class Method(base.CommandBase, BridgeCommon):
48 def __init__(self, host): 48 def __init__(self, host):
49 base.CommandBase.__init__(self, host, "method", help=_("call a bridge method")) 49 base.CommandBase.__init__(self, host, "method", help=_("call a bridge method"))
50 BridgeCommon.__init__(self) 50 BridgeCommon.__init__(self)
51 self.need_loop = True
52 51
53 def add_parser_options(self): 52 def add_parser_options(self):
54 self.parser.add_argument( 53 self.parser.add_argument(
55 "method", type=str, help=_("name of the method to execute") 54 "method", type=str, help=_("name of the method to execute")
56 ) 55 )
57 self.parser.add_argument( 56 self.parser.add_argument(
58 "arg", nargs="*", help=_("argument of the method") 57 "arg", nargs="*", help=_("argument of the method")
59 ) 58 )
60 59
61 def method_cb(self, ret=None): 60 async def start(self):
62 if ret is not None: 61 method = getattr(self.host.bridge, self.args.method)
63 self.disp(str(ret)) 62 import inspect
64 self.host.quit() 63 argspec = inspect.getargspec(method)
65 64
66 def method_eb(self, failure): 65 kwargs = {}
67 self.disp( 66 if 'profile_key' in argspec.args:
68 _("Error while executing {}: {}".format(self.args.method, failure)), 67 kwargs['profile_key'] = self.profile
69 error=True, 68 elif 'profile' in argspec.args:
70 ) 69 kwargs['profile'] = self.profile
71 self.host.quit(C.EXIT_ERROR)
72 70
73 def start(self):
74 method = getattr(self.host.bridge, self.args.method)
75 args = self.evalArgs() 71 args = self.evalArgs()
72
76 try: 73 try:
77 method( 74 ret = await method(
78 *args, 75 *args,
79 profile=self.profile, 76 **kwargs,
80 callback=self.method_cb,
81 errback=self.method_eb
82 ) 77 )
83 except TypeError: 78 except Exception as e:
84 # maybe the method doesn't need a profile ? 79 self.disp(_(f"Error while executing {self.args.method}: {e}"), error=True)
85 try: 80 self.host.quit(C.EXIT_ERROR)
86 method(*args, callback=self.method_cb, errback=self.method_eb) 81 else:
87 except TypeError: 82 if ret is not None:
88 self.method_eb(_("bad arguments")) 83 self.disp(str(ret))
84 self.host.quit()
89 85
90 86
91 class Signal(base.CommandBase, BridgeCommon): 87 class Signal(base.CommandBase, BridgeCommon):
92 def __init__(self, host): 88 def __init__(self, host):
93 base.CommandBase.__init__( 89 base.CommandBase.__init__(
101 ) 97 )
102 self.parser.add_argument( 98 self.parser.add_argument(
103 "arg", nargs="*", help=_("argument of the signal") 99 "arg", nargs="*", help=_("argument of the signal")
104 ) 100 )
105 101
106 def start(self): 102 async def start(self):
107 args = self.evalArgs() 103 args = self.evalArgs()
108 json_args = json.dumps(args) 104 json_args = json.dumps(args)
109 # XXX: we use self.args.profile and not self.profile 105 # XXX: we use self.args.profile and not self.profile
110 # because we want the raw profile_key (so plugin handle C.PROF_KEY_NONE) 106 # because we want the raw profile_key (so plugin handle C.PROF_KEY_NONE)
111 self.host.bridge.debugFakeSignal(self.args.signal, json_args, self.args.profile) 107 try:
108 await self.host.bridge.debugFakeSignal(self.args.signal, json_args, self.args.profile)
109 except Exception as e:
110 self.disp(_(f"Can't send fake signal: {e}"), error=True)
111 self.host.quit(C.EXIT_ERROR)
112 else:
113 self.host.quit()
112 114
113 115
114 class Bridge(base.CommandBase): 116 class Bridge(base.CommandBase):
115 subcommands = (Method, Signal) 117 subcommands = (Method, Signal)
116 118
128 use_verbose=True, 130 use_verbose=True,
129 use_profile=False, 131 use_profile=False,
130 use_output=C.OUTPUT_XML, 132 use_output=C.OUTPUT_XML,
131 help=_("monitor XML stream"), 133 help=_("monitor XML stream"),
132 ) 134 )
133 self.need_loop = True
134 135
135 def add_parser_options(self): 136 def add_parser_options(self):
136 self.parser.add_argument( 137 self.parser.add_argument(
137 "-d", 138 "-d",
138 "--direction", 139 "--direction",
139 choices=("in", "out", "both"), 140 choices=("in", "out", "both"),
140 default="both", 141 default="both",
141 help=_("stream direction filter"), 142 help=_("stream direction filter"),
142 ) 143 )
143 144
144 def printXML(self, direction, xml_data, profile): 145 async def printXML(self, direction, xml_data, profile):
145 if self.args.direction == "in" and direction != "IN": 146 if self.args.direction == "in" and direction != "IN":
146 return 147 return
147 if self.args.direction == "out" and direction != "OUT": 148 if self.args.direction == "out" and direction != "OUT":
148 return 149 return
149 verbosity = self.host.verbosity 150 verbosity = self.host.verbosity
153 whiteping = True 154 whiteping = True
154 else: 155 else:
155 whiteping = False 156 whiteping = False
156 157
157 if verbosity: 158 if verbosity:
158 profile_disp = " ({})".format(profile) if verbosity > 1 else "" 159 profile_disp = f" ({profile})" if verbosity > 1 else ""
159 if direction == "IN": 160 if direction == "IN":
160 self.disp( 161 self.disp(
161 A.color( 162 A.color(
162 A.BOLD, A.FG_YELLOW, "<<<===== IN ====", A.FG_WHITE, profile_disp 163 A.BOLD, A.FG_YELLOW, "<<<===== IN ====", A.FG_WHITE, profile_disp
163 ) 164 )
170 ) 171 )
171 if whiteping: 172 if whiteping:
172 self.disp("[WHITESPACE PING]") 173 self.disp("[WHITESPACE PING]")
173 else: 174 else:
174 try: 175 try:
175 self.output(xml_data) 176 await self.output(xml_data)
176 except Exception: 177 except Exception:
177 #  initial stream is not valid XML, 178 #  initial stream is not valid XML,
178 # in this case we print directly to data 179 # in this case we print directly to data
179 #  FIXME: we should test directly lxml.etree.XMLSyntaxError 180 #  FIXME: we should test directly lxml.etree.XMLSyntaxError
180 # but importing lxml directly here is not clean 181 # but importing lxml directly here is not clean
181 # should be wrapped in a custom Exception 182 # should be wrapped in a custom Exception
182 self.disp(xml_data) 183 self.disp(xml_data)
183 self.disp("") 184 self.disp("")
184 185
185 def start(self): 186 async def start(self):
186 self.host.bridge.register_signal("xmlLog", self.printXML, "plugin") 187 self.host.bridge.register_signal("xmlLog", self.printXML, "plugin")
187 188
188 189
189 class Debug(base.CommandBase): 190 class Debug(base.CommandBase):
190 subcommands = (Bridge, Monitor) 191 subcommands = (Bridge, Monitor)