]> git.evergreen-ils.org Git - OpenSRF.git/blob - src/python/srfsh.py
killed the terminal colors in srfsh.py output. They are cute and all, but are likely...
[OpenSRF.git] / src / python / srfsh.py
1 #!/usr/bin/python
2 # vim:et:ts=4
3 """
4 srfsh.py - provides a basic shell for issuing OpenSRF requests
5
6   help
7     - show this menu
8
9   math_bench <count>
10     - runs <count> opensrf.math requests and reports the average time
11
12   request <service> <method> [<param1>, <param2>, ...]
13     - performs an opensrf request
14
15   set VAR=<value>
16     - sets an environment variable
17
18   Environment variables:
19     SRFSH_OUTPUT = pretty - print pretty JSON and key/value pairs for network objects
20                  = raw - print formatted JSON 
21
22     SRFSH_LOCALE = <locale> - request responses to be returned in locale <locale> if available
23 """
24
25 import os, sys, time, readline, atexit, re
26 import osrf.json, osrf.system, osrf.ses, osrf.conf, osrf.log, osrf.net
27
28 # -------------------------------------------------------------------
29 # main listen loop
30 # -------------------------------------------------------------------
31 def do_loop():
32     while True:
33
34         try:
35             line = raw_input("srfsh% ")
36             if not len(line): 
37                 continue
38             if str.lower(line) == 'exit' or str.lower(line) == 'quit': 
39                 break
40             parts = str.split(line)
41
42             command = parts[0]
43         
44             if command == 'request':
45                 parts.pop(0)
46                 handle_request(parts)
47                 continue
48
49             if command == 'math_bench':
50                 parts.pop(0)
51                 handle_math_bench(parts)
52                 continue
53
54             if command == 'help':
55                 handle_help()
56                 continue
57
58             if command == 'set':
59                 parts.pop(0)
60                 handle_set(parts)
61
62             if command == 'get':
63                 parts.pop(0)
64                 handle_get(parts)
65
66
67
68         except KeyboardInterrupt:
69             print ""
70
71         except EOFError:
72             print "exiting..."
73             sys.exit(0)
74
75
76 # -------------------------------------------------------------------
77 # Set env variables to control behavior
78 # -------------------------------------------------------------------
79 def handle_set(parts):
80     cmd = "".join(parts)
81     pattern = re.compile('(.*)=(.*)').match(cmd)
82     key = pattern.group(1)
83     val = pattern.group(2)
84     set_var(key, val)
85     print "%s = %s" % (key, val)
86
87 def handle_get(parts):
88     try:
89         print get_var(parts[0])
90     except:
91         print ""
92
93
94 # -------------------------------------------------------------------
95 # Prints help info
96 # -------------------------------------------------------------------
97 def handle_help():
98     print __doc__
99
100 # -------------------------------------------------------------------
101 # performs an opensrf request
102 # -------------------------------------------------------------------
103 def handle_request(parts):
104     service = parts.pop(0)
105     method = parts.pop(0)
106     locale = __get_locale()
107     jstr = '[%s]' % "".join(parts)
108     params = None
109
110     try:
111         params = osrf.json.to_object(jstr)
112     except:
113         print "Error parsing JSON: %s" % jstr
114         return
115
116     ses = osrf.ses.ClientSession(service, locale=locale)
117
118     start = time.time()
119
120     req = ses.request2(method, tuple(params))
121
122
123     while True:
124         resp = None
125         try:
126             resp = req.recv(timeout=120)
127         except osrf.net.XMPPNoRecipient:
128             print "Unable to communicate with %s" % service
129             total = 0
130             break
131
132         osrf.log.log_internal("Looping through receive request")
133         if not resp:
134             break
135         total = time.time() - start
136
137         print str(resp.content())
138         otp = get_var('SRFSH_OUTPUT')
139         if otp == 'pretty':
140             print "\n" + osrf.json.debug_net_object(resp.content())
141         else:
142             print osrf.json.pprint(osrf.json.to_json(resp.content()))
143
144     req.cleanup()
145     ses.cleanup()
146
147     print '-'*60
148     print "Total request time: %f" % total
149     print '-'*60
150
151
152 def handle_math_bench(parts):
153
154     count = int(parts.pop(0))
155     ses = osrf.ses.ClientSession('opensrf.math')
156     times = []
157
158     for cnt in range(100):
159         if cnt % 10:
160             sys.stdout.write('.')
161         else:
162             sys.stdout.write( str( cnt / 10 ) )
163     print ""
164
165
166     for cnt in range(count):
167     
168         starttime = time.time()
169         req = ses.request('add', 1, 2)
170         resp = req.recv(timeout=2)
171         endtime = time.time()
172     
173         if resp.content() == 3:
174             sys.stdout.write("+")
175             sys.stdout.flush()
176             times.append( endtime - starttime )
177         else:
178             print "What happened? %s" % str(resp.content())
179     
180         req.cleanup()
181         if not ( (cnt + 1) % 100):
182             print ' [%d]' % (cnt + 1)
183     
184     ses.cleanup()
185     total = 0
186     for cnt in times:
187         total += cnt 
188     print "\naverage time %f" % (total / len(times))
189
190
191
192
193 # -------------------------------------------------------------------
194 # Defines the tab-completion handling and sets up the readline history 
195 # -------------------------------------------------------------------
196 def setup_readline():
197     class SrfshCompleter(object):
198         def __init__(self, words):
199             self.words = words
200             self.prefix = None
201     
202         def complete(self, prefix, index):
203             if prefix != self.prefix:
204                 # find all words that start with this prefix
205                 self.matching_words = [
206                     w for w in self.words if w.startswith(prefix)
207                 ]
208                 self.prefix = prefix
209                 try:
210                     return self.matching_words[index]
211                 except IndexError:
212                     return None
213     
214     words = 'request', 'help', 'exit', 'quit', 'opensrf.settings', 'opensrf.math', 'set'
215     completer = SrfshCompleter(words)
216     readline.parse_and_bind("tab: complete")
217     readline.set_completer(completer.complete)
218
219     histfile = os.path.join(get_var('HOME'), ".srfsh_history")
220     try:
221         readline.read_history_file(histfile)
222     except IOError:
223         pass
224     atexit.register(readline.write_history_file, histfile)
225
226 def do_connect():
227     file = os.path.join(get_var('HOME'), ".srfsh.xml")
228     print_stdout("Connecting to opensrf...")
229     osrf.system.System.connect(config_file=file, config_context='srfsh')
230     print_stdout('OK\n')
231
232 def load_plugins():
233     # Load the user defined external plugins
234     # XXX Make this a real module interface, with tab-complete words, commands, etc.
235     try:
236         plugins = osrf.conf.get('plugins')
237
238     except:
239         # XXX standard srfsh.xml does not yet define <plugins> element
240         #print_stdout("No plugins defined in /srfsh/plugins/plugin\n")
241         return
242
243     plugins = osrf.conf.get('plugins.plugin')
244     if not isinstance(plugins, list):
245         plugins = [plugins]
246
247     for module in plugins:
248         name = module['module']
249         init = module['init']
250         print_stdout("Loading module %s..." % name)
251
252         try:
253             string = 'import %s\n%s.%s()' % (name, name, init)
254             exec(string)
255             print_stdout('OK\n')
256
257         except Exception, e:
258             sys.stderr.write("\nError importing plugin %s, with init symbol %s: \n%s\n" % (name, init, e))
259
260 def set_vars():
261     if not get_var('SRFSH_OUTPUT'):
262         set_var('SRFSH_OUTPUT', 'raw')
263
264     # XXX Do we need to differ between LANG and LC_MESSAGES?
265     if not get_var('SRFSH_LOCALE'):
266         set_var('SRFSH_LOCALE', get_var('LC_ALL'))
267
268 def set_var(key, val):
269     os.environ[key] = val
270
271 def get_var(key):
272     return os.environ.get(key, '')
273     
274 def __get_locale():
275     """
276     Return the defined locale for this srfsh session.
277
278     A locale in OpenSRF is currently defined as a [a-z]{2}-[A-Z]{2} pattern.
279     This function munges the LC_ALL setting to conform to that pattern; for
280     example, trimming en_CA.UTF-8 to en-CA.
281
282     >>> import srfsh
283     >>> srfsh.set_var('SRFSH_LOCALE', 'zz-ZZ')
284     >>> print __get_locale()
285     zz-ZZ
286     >>> srfsh.set_var('SRFSH_LOCALE', 'en_CA.UTF-8')
287     >>> print __get_locale()
288     en-CA
289     """
290
291     env_locale = get_var('SRFSH_LOCALE')
292     if env_locale:
293         pattern = re.compile(r'^\s*([a-z]+)[^a-zA-Z]([A-Z]+)').search(env_locale)
294         lang = pattern.group(1)
295         region = pattern.group(2)
296         locale = "%s-%s" % (lang, region)
297     else:
298         locale = 'en-US'
299
300     return locale
301     
302 def print_stdout(string):
303     sys.stdout.write(string)
304     sys.stdout.flush()
305
306
307 if __name__ == '__main__':
308
309     # Kick it off
310     set_vars()
311     setup_readline()
312     do_connect()
313     load_plugins()
314     do_loop()
315