GnuCash  5.6-150-g038405b370+
shell.py
1 #! /usr/bin/env python3
2 #
3 # Copyright (c) 2008, Nicolas Rougier
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions are met:
8 #
9 # * Redistributions of source code must retain the above copyright
10 # notice, this list of conditions and the following disclaimer.
11 # * Redistributions in binary form must reproduce the above copyright
12 # notice, this list of conditions and the following disclaimer in the
13 # documentation and/or other materials provided with the distribution.
14 # * Neither the name of the University of California, Berkeley nor the
15 # names of its contributors may be used to endorse or promote products
16 # derived from this software without specific prior written permission.
17 #
18 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
19 # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 # DISCLAIMED. IN NO EVENT SHALL THE AUTHOR AND CONTRIBUTORS BE LIABLE FOR ANY
22 # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 
29 import os
30 import sys
31 import re
32 import rlcompleter
33 import traceback
34 import tempfile
35 
36 if not hasattr(sys, 'ps1'): sys.ps1 = '>>> '
37 if not hasattr(sys, 'ps2'): sys.ps2 = '... '
38 
39 
40 class Shell:
41  """ """
42 
43  def __init__(self, ns_globals={}, ns_locals={}):
44  """ """
45 
46  self.completer = rlcompleter.Completer (ns_globals)
47  self.command = ''
48  self.globals = ns_globals
49  self.locals = ns_locals
50  self.complete_sep = re.compile(r'[\s\{\}\[\]\(\)]')
51  self.prompt = sys.ps1
52 
53 
54  def namespace(self):
55  return self.globals
56 
57  def is_balanced (self, line):
58  """ Checks line balance for brace, bracket, parentheses and string quote
59 
60  This helper function checks for the balance of brace, bracket,
61  parentheses and string quote. Any unbalanced line means to wait until
62  some other lines are fed to the console.
63  """
64 
65  s = line
66  s = list(filter(lambda x: x in '()[]{}"\'', s))
67  # s = s.replace ("'''", "'")
68  # s = s.replace ('"""', '"')
69  instring = False
70  brackets = {'(':')', '[':']', '{':'}', '"':'"', '\'':'\''}
71  stack = []
72  while len(s):
73  if not instring:
74  if s[0] in ')]}':
75  if stack and brackets[stack[-1]] == s[0]:
76  del stack[-1]
77  else:
78  return False
79  elif s[0] in '"\'':
80  if stack and brackets[stack[-1]] == s[0]:
81  del stack[-1]
82  instring = False
83  else:
84  stack.append(s[0])
85  instring = True
86  else:
87  stack.append(s[0])
88  else:
89  if s[0] in '"\'' and stack and brackets[stack[-1]] == s[0]:
90  del stack[-1]
91  instring = False
92  s = s[1:]
93  return len(stack) == 0
94 
95 
96  def complete(self, line):
97  split_line = self.complete_sep.split(line)
98  possibilities = []
99  i = 0
100  c = self.completer.complete (split_line[-1], i)
101  while c:
102  possibilities.append(c)
103  i = i+1
104  c = self.completer.complete (split_line[-1], i)
105  if possibilities:
106  common_prefix = os.path.commonprefix (possibilities)
107  completed = line[:-len(split_line[-1])]+common_prefix
108  else:
109  completed = line
110  return completed, possibilities
111 
112 
113  def eval (self, console):
114  line = console.last_line()
115  console.write ('\n')
116  if line == '':
117  self.execute (console)
118  self.command = ''
119  self.prompt = sys.ps1
120  console.prompt('prompt')
121  return
122  self.command = self.command + line + '\n'
123  if not self.is_balanced (self.command):
124  self.prompt = sys.ps2
125  console.prompt('prompt')
126  return
127  line = line.rstrip()
128  if len(line) > 0:
129  if line[-1] == ':' or line[-1] == '\\' or line[0] in ' \11':
130  self.prompt = sys.ps2
131  console.prompt('prompt')
132  return
133  self.execute (console)
134  self.command = ''
135  self.prompt = sys.ps1
136  console.prompt('prompt')
137 
138 
139  def execute (self, console):
140  if not self.command:
141  return
142  try:
143  try:
144  r = eval (self.command, self.globals, self.locals)
145  if r is not None:
146  # System output (if any)
147  while True:
148  try:
149  buf = os.read(console.piperead, 256)
150  except:
151  break
152  else:
153  console.write (buf, 'output')
154  if len(buf) < 256: break
155  # Command output
156  print(repr(r))
157  except SyntaxError:
158  exec(str(self.command), self.globals)
159  except:
160  if hasattr (sys, 'last_type') and sys.last_type == SystemExit:
161  console.quit()
162  elif hasattr (sys, 'exc_type') and sys.exc_type == SystemExit:
163  console.quit()
164  else:
165  try:
166  tb = sys.exc_info()[2]
167  if tb:
168  tb=tb.tb_next
169  traceback.print_exception(sys.exc_info()[0], sys.exc_info()[1], tb)
170  except:
171  sys.stderr, console.stderr = console.stderr, sys.stderr
172  traceback.print_exc()
173 
def __init__(self, ns_globals={}, ns_locals={})
Definition: shell.py:43
def execute(self, console)
Definition: shell.py:139
def is_balanced(self, line)
Definition: shell.py:57