GnuCash 2.4.99
console.py
00001 #! /usr/bin/env python
00002 #
00003 # Copyright (c) 2008, Nicolas Rougier
00004 # All rights reserved.
00005 #
00006 # Redistribution and use in source and binary forms, with or without
00007 # modification, are permitted provided that the following conditions are met:
00008 #
00009 #     * Redistributions of source code must retain the above copyright
00010 #       notice, this list of conditions and the following disclaimer.
00011 #     * Redistributions in binary form must reproduce the above copyright
00012 #       notice, this list of conditions and the following disclaimer in the
00013 #       documentation and/or other materials provided with the distribution.
00014 #     * Neither the name of the University of California, Berkeley nor the
00015 #       names of its contributors may be used to endorse or promote products
00016 #       derived from this software without specific prior written permission.
00017 #
00018 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
00019 # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00020 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00021 # DISCLAIMED. IN NO EVENT SHALL THE AUTHOR AND CONTRIBUTORS BE LIABLE FOR ANY
00022 # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00023 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00024 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00025 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00026 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00027 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00028 
00029 import os
00030 import sys
00031 import re
00032 import tempfile
00033 import readline
00034 import gobject
00035 import gtk
00036 import pango
00037 from StringIO import StringIO
00038 import shell
00039 try:    import ishell
00040 except: pass
00041 
00042 ansi_colors =  {'0;30': '#2E3436',
00043                 '0;31': '#CC0000',
00044                 '0;32': '#4E9A06',
00045                 '0;33': '#C4A000',
00046                 '0;34': '#3465A4',
00047                 '0;35': '#75507B',
00048                 '0;36': '#06989A',
00049                 '0;37': '#D3D7CF',
00050                 '1;30': '#555753',
00051                 '1;31': '#EF2929',
00052                 '1;32': '#8AE234',
00053                 '1;33': '#FCE94F',
00054                 '1;34': '#729FCF',
00055                 '1;35': '#AD7FA8',
00056                 '1;36': '#34E2E2',
00057                 '1;37': '#EEEEEC'}
00058 
00059 # ------------------------------------------------------------- class ConsoleOut
00060 class ConsoleOut:
00061     """
00062     A fake output file object.  It sends output to the console widget,
00063     and if asked for a file number, returns one set on instance creation
00064     """
00065     
00066     def __init__(self, console, fn=-1, style=None):
00067         self.fn = fn
00068         self.console = console
00069         self.style = style
00070     def close(self): pass
00071     flush = close
00072     def fileno(self):    return self.fn
00073     def isatty(self):    return False
00074     def read(self, a):   return ''
00075     def readline(self):  return ''
00076     def readlines(self): return []
00077     def write(self, s):
00078         self.console.write (s, self.style)
00079     def writelines(self, l):
00080         for s in l:
00081             self.console.write (s, self.style)
00082     def seek(self, a):   raise IOError, (29, 'Illegal seek')
00083     def tell(self):      raise IOError, (29, 'Illegal seek')
00084     truncate = tell
00085 
00086 
00087 # -------------------------------------------------------------- class ConsoleIn
00088 class ConsoleIn:
00089     """
00090     A fake input file object.  It receives input from a GTK TextView widget,
00091     and if asked for a file number, returns one set on instance creation
00092     """
00093     def __init__(self, console, fn=-1):
00094         self.fn = fn
00095         self.console = console
00096     def close(self): pass
00097     flush = close
00098     def fileno(self):    return self.fn
00099     def isatty(self):    return False
00100     def read(self, a):   return self.readline()
00101     def readline(self):
00102         self.console.input_mode = True
00103         buffer = self.console.buffer
00104         #console.write('\n')
00105         iter = buffer.get_iter_at_mark(buffer.get_insert())
00106         buffer.move_mark (buffer.get_mark('linestart'), iter)
00107         while self.console.input_mode:
00108             #while gtk.events_pending():
00109             gtk.main_iteration()
00110         s = self.console.input
00111         self.console.input = ''
00112         return s+'\n'
00113     def readlines(self): return []
00114     def write(self, s):  return None
00115     def writelines(self, l): return None
00116     def seek(self, a):   raise IOError, (29, 'Illegal seek')
00117     def tell(self):      raise IOError, (29, 'Illegal seek')
00118     truncate = tell
00119 
00120 
00121 # ---------------------------------------------------------------- class Console
00122 class Console (gtk.ScrolledWindow):
00123     """ GTK python console """
00124 
00125     def __init__(self, argv=[], shelltype='python', banner=[],
00126                  filename=None, size=100):
00127 
00128         """ Console interface building + initialization"""
00129 
00130         # GTK interface
00131         self.do_quit = False
00132         gtk.ScrolledWindow.__init__(self)
00133         self.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
00134         self.set_shadow_type (gtk.SHADOW_NONE)
00135         self.set_border_width(0)
00136         self.view = gtk.TextView()
00137         self.view.modify_font (pango.FontDescription("Mono 10"))
00138         self.view.set_editable (True)
00139         self.view.set_wrap_mode(True)
00140         self.view.set_left_margin(0)
00141         self.view.set_right_margin(0)
00142         self.buffer = self.view.get_buffer()
00143         self.buffer.create_tag ('title',
00144                                 indent = 2,
00145                                 weight=pango.WEIGHT_BOLD,
00146                                 foreground='blue',
00147                                 font='Mono 12')
00148         self.buffer.create_tag ('subtitle',
00149                                 indent = 2,
00150                                 foreground='blue',
00151                                 font='Mono 8')
00152         self.buffer.create_tag ('output',
00153                                 foreground = 'blue',
00154                                 font='Mono 10')
00155         self.buffer.create_tag ('error',
00156                                 foreground='red',
00157                                 style=pango.STYLE_OBLIQUE,
00158                                 font='Mono 10')
00159         self.buffer.create_tag ('prompt',
00160                                 foreground='blue',
00161                                 weight=pango.WEIGHT_BOLD,
00162                                 font='Mono 10')
00163         self.buffer.create_tag('0')
00164         self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
00165         for code in ansi_colors:
00166             self.buffer.create_tag(code,
00167                                    foreground=ansi_colors[code],
00168                                    weight=700)
00169         for text, style in banner:
00170             self.write (text, style)
00171         iter = self.buffer.get_iter_at_mark(self.buffer.get_insert())
00172         self.buffer.create_mark ('linestart', iter, True)
00173         self.view.add_events(gtk.gdk.KEY_PRESS_MASK)
00174         self.view.connect ('key-press-event', self.key_press_event)
00175         self.add(self.view)
00176         self.show_all()
00177         self.killbuffer = None
00178 
00179         # Console stuff
00180         self.argv = argv
00181         self.history_init(filename, size)
00182         self.cout = StringIO()
00183         self.cout.truncate(0)
00184         if shelltype=='ipython':
00185             self.shell = ishell.Shell(argv,locals(),globals(),
00186                                 cout=self.cout, cerr=self.cout,
00187                                 input_func=self.raw_input)
00188         else:
00189             self.shell = shell.Shell(locals(),globals())
00190         self.interrupt = False
00191         self.input_mode = False
00192         self.input = None
00193         self.stdout = ConsoleOut (self, sys.stdout.fileno(), 'output')
00194         self.stderr = ConsoleOut (self, sys.stderr.fileno(), 'error')
00195         self.stdin  = ConsoleIn  (self, sys.stdin.fileno())
00196 
00197         # Create a named pipe for system stdout/stderr redirection
00198         self.fifoname = tempfile.mktemp()
00199         if not os.path.exists (self.fifoname):
00200             os.mkfifo (self.fifoname)
00201         self.piperead  = os.open (self.fifoname, os.O_RDONLY | os.O_NONBLOCK)
00202         self.pipewrite = os.open (self.fifoname, os.O_WRONLY | os.O_NONBLOCK)
00203         self.shell.eval(self)
00204         self.cout.truncate(0)
00205 
00206     def history_init(self, filename, size):
00207         self.history_file = filename
00208         self.history_size = size
00209         if filename and os.path.exists(filename):
00210             readline.read_history_file(filename)
00211         readline.set_history_length(size)
00212         self.history_reset()
00213 
00214     def history_save(self):
00215         if self.history_file:
00216             readline.write_history_file(self.history_file)
00217 
00218     def history_add(self, item):
00219         if len(item):
00220             readline.add_history (item)
00221         self.history_reset()
00222 
00223     def history_reset(self):
00224         self.history_index = readline.get_current_history_length()+1
00225 
00226     def history_next(self):
00227         self.history_index += 1
00228         if self.history_index <= readline.get_current_history_length():
00229             return '' or readline.get_history_item (self.history_index)
00230         self.history_index = readline.get_current_history_length()+1
00231         return ''
00232 
00233     def history_prev(self):
00234         if self.history_index > 1:
00235             self.history_index -= 1
00236         else:
00237             self.history_index = 1
00238         return '' or readline.get_history_item (self.history_index)
00239 
00240     def raw_input(self, prompt=''):
00241         if self.interrupt:
00242             self.interrupt = False
00243             raise KeyboardInterrupt
00244         return self.last_line()
00245 
00246     def grab_focus (self):
00247         """ Give focus to the TextView """
00248 
00249         self.view.grab_focus()
00250 
00251     def write (self, text, style=None):
00252         """ Write text using given style (if any) """
00253         segments = self.color_pat.split(text)
00254         segment = segments.pop(0)
00255         start,end = self.buffer.get_bounds()
00256         if style==None:
00257             self.buffer.insert(end, segment)
00258         else:
00259             self.buffer.insert_with_tags_by_name(end, segment, style)
00260         if segments:
00261             ansi_tags = self.color_pat.findall(text)
00262             for tag in ansi_tags:
00263                 i = segments.index(tag)
00264                 self.buffer.insert_with_tags_by_name(self.buffer.get_end_iter(),
00265                                                      segments[i+1], tag)
00266                 segments.pop(i)
00267         self.view.scroll_mark_onscreen(self.buffer.get_insert())
00268 
00269     def overwrite (self, text, style=None):
00270         """ Overwrite text after prompt with text """
00271 
00272         mark = self.buffer.get_mark('linestart')
00273         start = self.buffer.get_iter_at_mark(mark)
00274         end = self.buffer.get_end_iter()
00275         self.buffer.delete (start,end)
00276         self.write (text, style)
00277 
00278     def last_line (self):
00279         """ Get last line (without prompt) """
00280         
00281         mark = self.buffer.get_mark('linestart')
00282         start = self.buffer.get_iter_at_mark(mark)
00283         end = self.buffer.get_end_iter()
00284         return self.buffer.get_text (start,end,True)
00285 
00286 
00287     def prompt (self, style=None):
00288         """ Display prompt """
00289 
00290         iter = self.buffer.get_end_iter()
00291         self.buffer.place_cursor (iter)
00292         self.write (self.shell.prompt, style)
00293         iter = self.buffer.get_iter_at_mark(self.buffer.get_insert())
00294         self.buffer.move_mark (self.buffer.get_mark('linestart'), iter)
00295         self.history_reset()
00296         self.view.scroll_mark_onscreen(self.buffer.get_insert())
00297         while gtk.events_pending():
00298             gtk.main_iteration()
00299 
00300     def key_press_event (self, widget, event):
00301         """ Handle key press event """
00302 
00303         keyname = gtk.gdk.keyval_name (event.keyval)
00304 
00305         # New command
00306         if keyname in ['Return', 'KP_Enter']:
00307             line = self.last_line()
00308             self.history_add (line)
00309             if self.input_mode:
00310                 self.input_mode = False
00311                 self.input = self.last_line()
00312                 self.write('\n')
00313             else:
00314                 self.execute()
00315             return True
00316 
00317         # Prevent cursor to go back past prompt
00318         elif keyname in ['Left', 'BackSpace']:
00319             mark = self.buffer.get_mark('linestart')
00320             linestart = self.buffer.get_iter_at_mark(mark)
00321             iter = self.buffer.get_iter_at_mark(self.buffer.get_insert())
00322             if iter.compare(linestart) <= 0:
00323                 return True
00324 
00325         elif keyname == 'Right':
00326             return False
00327         
00328         # Next history item
00329         elif keyname == 'Down':
00330             self.overwrite (self.history_next())
00331             return True
00332 
00333         # Previous history item
00334         elif keyname == 'Up':
00335             self.overwrite (self.history_prev())
00336             return True
00337 
00338         # Move cursor just after prompt
00339         elif keyname == 'Home':
00340             mark = self.buffer.get_mark('linestart')
00341             linestart = self.buffer.get_iter_at_mark(mark)
00342             self.buffer.place_cursor (linestart)
00343             return True
00344 
00345         # Completion if line not empty
00346         elif keyname == 'Tab':
00347             line = self.last_line()
00348             if not line.strip():
00349                 return False
00350             completed, possibilities = self.shell.complete(line)
00351             if len(possibilities) > 1:
00352                 slice = line
00353                 self.write('\n')
00354                 for symbol in possibilities:
00355                     self.write(symbol+'\n')
00356                 self.prompt('prompt')
00357             self.overwrite(completed or slice)
00358             return True
00359 
00360         # Controls
00361         elif event.state & gtk.gdk.CONTROL_MASK:
00362             if keyname in ['a','A']:
00363                 mark = self.buffer.get_mark('linestart')
00364                 linestart = self.buffer.get_iter_at_mark(mark)
00365                 self.buffer.place_cursor (linestart)
00366                 return True
00367             elif keyname in ['e','E']:
00368                 end = self.buffer.get_end_iter()
00369                 self.buffer.place_cursor (end)
00370                 return True
00371             elif keyname in ['k','K']:
00372                 start = self.buffer.get_iter_at_mark (self.buffer.get_insert())
00373                 end = self.buffer.get_end_iter()
00374                 self.killbuffer = self.buffer.get_text(start,end)
00375                 self.buffer.delete(start,end)
00376                 return True
00377             elif keyname in ['y','Y']:
00378                 if self.killbuffer:
00379                     iter = self.buffer.get_iter_at_mark (self.buffer.get_insert())
00380                     self.buffer.insert(iter, self.killbuffer)
00381                 return True
00382             elif keyname in ['l', 'L']:
00383                 start = self.buffer.get_start_iter()
00384                 end = self.buffer.get_end_iter()
00385                 end.backward_sentence_start()
00386                 self.buffer.delete (start,end)
00387             elif keyname in ['d', 'D']:
00388                 if not len(self.last_line().strip()):
00389                     self.quit()
00390 
00391         # Editing before prompt is forbidden
00392         else:
00393             mark = self.buffer.get_mark('linestart')
00394             linestart = self.buffer.get_iter_at_mark(mark)
00395             iter = self.buffer.get_iter_at_mark(self.buffer.get_insert())
00396             if iter.compare(linestart) < 0:
00397                 iter = self.buffer.get_end_iter()
00398                 self.buffer.place_cursor (iter)
00399         return False
00400 
00401 
00402     def execute (self):
00403         # Python stdout, stderr, stdin redirection
00404         sys.stdout, self.stdout = self.stdout, sys.stdout
00405         sys.stderr, self.stderr = self.stderr, sys.stderr
00406         sys.stdin,  self.stdin  = self.stdin,  sys.stdin
00407 
00408         # System stdout, stderr redirection
00409         sys_stdout = os.dup(1)
00410         sys_stderr = os.dup(2)
00411         os.dup2 (self.pipewrite, 1)
00412         os.dup2 (self.pipewrite, 2)
00413 
00414         self.shell.eval(self)
00415         self.view.scroll_mark_onscreen(self.buffer.get_insert())
00416         while gtk.events_pending():
00417             gtk.main_iteration()
00418 
00419         # Get system output and remove system redirection
00420         os.dup2 (sys_stdout, 1)
00421         os.dup2 (sys_stderr, 2)
00422         os.close (sys_stdout)
00423         os.close (sys_stderr)
00424 
00425         # Remove python redirection
00426         sys.stdout, self.stdout = self.stdout, sys.stdout
00427         sys.stderr, self.stderr = self.stderr, sys.stderr
00428         sys.stdin,  self.stdin  = self.stdin,  sys.stdin
00429 
00430 
00431     def quit(self):
00432         """ Quit console """
00433 
00434         gtk.main_quit()
00435         self.history_save()
00436         try:
00437             os.close (self.piperead)
00438             os.close (self.pipewrite)
00439         except:
00440             pass
00441         if os.path.exists (self.fifoname):
00442             os.remove (self.fifoname)
00443         self.do_quit = True
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines