4 srfsh.py - provides a basic shell for issuing OpenSRF requests
10 - runs <count> opensrf.math requests and prints the average time
12 request <service> <method> [<param1>, <param2>, ...]
13 - performs an opensrf request
16 - Queries the router. Query options: services service-stats service-nodes
19 - sets an environment variable
22 - Returns the value for the environment variable
24 Environment variables:
25 SRFSH_OUTPUT_NET_OBJ_KEYS = true - If a network object is array-encoded and key registry exists for the object type, annotate the object with field names
28 SRFSH_OUTPUT_FORMAT_JSON = true - Use JSON pretty printer
29 = false - Print raw JSON
31 SRFSH_OUTPUT_PAGED = true - Paged output. Uses "less -EX"
32 = false - Output is not paged
34 SRFSH_LOCALE = <locale> - request responses to be returned in locale <locale> if available
38 import os, sys, time, readline, atexit, re, pydoc
39 import osrf.json, osrf.system, osrf.ses, osrf.conf, osrf.log, osrf.net
41 router_command_map = {
42 'services' : 'opensrf.router.info.class.list',
43 'service-stats' : 'opensrf.router.info.stats.class.node.all',
44 'service-nodes' : 'opensrf.router.info.stats.class.all'
47 # List of words to use for readline tab completion
48 tab_complete_words = [
59 # add the router commands to the tab-complete list
60 for rcommand in router_command_map.keys():
61 tab_complete_words.append(rcommand)
63 # -------------------------------------------------------------------
65 # -------------------------------------------------------------------
69 'request' : handle_request,
70 'router' : handle_router,
71 'math_bench' : handle_math_bench,
81 line = raw_input("srfsh# ")
86 if str.lower(line) == 'exit' or str.lower(line) == 'quit':
89 parts = str.split(line)
90 command = parts.pop(0)
92 if command not in command_map:
93 report("unknown command: '%s'\n" % command)
96 command_map[command](parts)
98 except EOFError: # ^-d
101 except KeyboardInterrupt: # ^-c
109 def handle_router(parts):
112 report("usage: router <query>\n")
117 if query not in router_command_map:
118 report("router query options: %s\n" % ','.join(router_command_map.keys()))
121 return handle_request(['router', router_command_map[query]])
123 # -------------------------------------------------------------------
124 # Set env variables to control behavior
125 # -------------------------------------------------------------------
126 def handle_set(parts):
128 pattern = re.compile('(.*)=(.*)').match(cmd)
129 key = pattern.group(1)
130 val = pattern.group(2)
132 report("%s = %s\n" % (key, val))
134 def handle_get(parts):
136 report("%s=%s\n" % (parts[0], get_var(parts[0])))
141 # -------------------------------------------------------------------
143 # -------------------------------------------------------------------
144 def handle_help(foo):
147 # -------------------------------------------------------------------
148 # performs an opensrf request
149 # -------------------------------------------------------------------
150 def handle_request(parts):
153 report("usage: request <service> <api_name> [<param1>, <param2>, ...]\n")
156 service = parts.pop(0)
157 method = parts.pop(0)
158 locale = __get_locale()
159 jstr = '[%s]' % "".join(parts)
163 params = osrf.json.to_object(jstr)
165 report("Error parsing JSON: %s\n" % jstr)
168 ses = osrf.ses.ClientSession(service, locale=locale)
172 req = ses.request2(method, tuple(params))
179 resp = req.recv(timeout=120)
180 except osrf.net.XMPPNoRecipient:
181 report("Unable to communicate with %s\n" % service)
187 total = time.time() - start
188 content = resp.content()
190 if content is not None:
191 if get_var('SRFSH_OUTPUT_NET_OBJ_KEYS') == 'true':
192 report("Received Data: %s\n" % osrf.json.debug_net_object(content))
194 if get_var('SRFSH_OUTPUT_FORMAT_JSON') == 'true':
195 report("Received Data: %s\n" % osrf.json.pprint(osrf.json.to_json(content)))
197 report("Received Data: %s\n" % osrf.json.to_json(content))
202 report('-'*60 + "\n")
203 report("Total request time: %f\n" % total)
204 report('-'*60 + "\n")
207 def handle_math_bench(parts):
209 count = int(parts.pop(0))
210 ses = osrf.ses.ClientSession('opensrf.math')
213 for cnt in range(100):
215 sys.stdout.write('.')
217 sys.stdout.write( str( cnt / 10 ) )
220 for cnt in range(count):
222 starttime = time.time()
223 req = ses.request('add', 1, 2)
224 resp = req.recv(timeout=2)
225 endtime = time.time()
227 if resp.content() == 3:
228 sys.stdout.write("+")
230 times.append( endtime - starttime )
232 print "What happened? %s" % str(resp.content())
235 if not ( (cnt + 1) % 100):
236 print ' [%d]' % (cnt + 1)
242 print "\naverage time %f" % (total / len(times))
247 # -------------------------------------------------------------------
248 # Defines the tab-completion handling and sets up the readline history
249 # -------------------------------------------------------------------
250 def setup_readline():
252 class SrfshCompleter(object):
254 def __init__(self, words):
258 def complete(self, prefix, index):
260 if prefix != self.prefix:
264 # find all words that start with this prefix
265 self.matching_words = [
266 w for w in self.words if w.startswith(prefix)
269 if len(self.matching_words) == 0:
272 if len(self.matching_words) == 1:
273 return self.matching_words[0]
275 sys.stdout.write('\n%s\nsrfsh# %s' %
276 (' '.join(self.matching_words), readline.get_line_buffer()))
280 completer = SrfshCompleter(tuple(tab_complete_words))
281 readline.parse_and_bind("tab: complete")
282 readline.set_completer(completer.complete)
284 histfile = os.path.join(get_var('HOME'), ".srfsh_history")
286 readline.read_history_file(histfile)
289 atexit.register(readline.write_history_file, histfile)
291 readline.set_completer_delims(readline.get_completer_delims().replace('-',''))
295 file = os.path.join(get_var('HOME'), ".srfsh.xml")
296 osrf.system.System.connect(config_file=file, config_context='srfsh')
299 # Load the user defined external plugins
300 # XXX Make this a real module interface, with tab-complete words, commands, etc.
302 plugins = osrf.conf.get('plugins')
305 # XXX standard srfsh.xml does not yet define <plugins> element
306 #print("No plugins defined in /srfsh/plugins/plugin\n")
309 plugins = osrf.conf.get('plugins.plugin')
310 if not isinstance(plugins, list):
313 for module in plugins:
314 name = module['module']
315 init = module['init']
316 print "Loading module %s..." % name
319 string = 'import %s\n%s.%s()' % (name, name, init)
324 sys.stderr.write("\nError importing plugin %s, with init symbol %s: \n%s\n" % (name, init, e))
327 osrf.system.System.net_disconnect()
329 _output_buffer = '' # collect output for pager
330 def report(text, flush=False):
331 global _output_buffer
333 if get_var('SRFSH_OUTPUT_PAGED') == 'true':
334 _output_buffer += text
336 if flush and _output_buffer != '':
337 pipe = os.popen('less -EX', 'w')
338 pipe.write(_output_buffer)
343 sys.stdout.write(text)
349 if not get_var('SRFSH_OUTPUT_NET_OBJ_KEYS'):
350 set_var('SRFSH_OUTPUT_NET_OBJ_KEYS', 'false')
352 if not get_var('SRFSH_OUTPUT_FORMAT_JSON'):
353 set_var('SRFSH_OUTPUT_FORMAT_JSON', 'true')
355 if not get_var('SRFSH_OUTPUT_PAGED'):
356 set_var('SRFSH_OUTPUT_PAGED', 'true')
358 # XXX Do we need to differ between LANG and LC_MESSAGES?
359 if not get_var('SRFSH_LOCALE'):
360 set_var('SRFSH_LOCALE', get_var('LC_ALL'))
362 def set_var(key, val):
363 os.environ[key] = val
366 return os.environ.get(key, '')
370 Return the defined locale for this srfsh session.
372 A locale in OpenSRF is currently defined as a [a-z]{2}-[A-Z]{2} pattern.
373 This function munges the LC_ALL setting to conform to that pattern; for
374 example, trimming en_CA.UTF-8 to en-CA.
377 >>> srfsh.set_var('SRFSH_LOCALE', 'zz-ZZ')
378 >>> print __get_locale()
380 >>> srfsh.set_var('SRFSH_LOCALE', 'en_CA.UTF-8')
381 >>> print __get_locale()
385 env_locale = get_var('SRFSH_LOCALE')
387 pattern = re.compile(r'^\s*([a-z]+)[^a-zA-Z]([A-Z]+)').search(env_locale)
388 lang = pattern.group(1)
389 region = pattern.group(2)
390 locale = "%s-%s" % (lang, region)
396 if __name__ == '__main__':