5959
6060stdscr = None
6161
62-
6362class Struct (object ):
64- pass # When we inherit, a __dict__ is added (object uses slots)
63+ """Simple class for instantiating objects we can add arbitrary attributes
64+ to and use for various arbitrary things."""
65+ pass
6566
6667
6768class FakeStdin (object ):
@@ -158,6 +159,43 @@ def make_colours():
158159
159160
160161class Interpreter (code .InteractiveInterpreter ):
162+
163+ def __init__ (self ):
164+ """The syntaxerror callback can be set at any time and will be called
165+ on a caught syntax error. The purpose for this in bpython is so that
166+ the repl can be instantiated after the interpreter (which it
167+ necessarily must be with the current factoring) and then an exception
168+ callback can be added to the Interpeter instance afterwards - more
169+ specifically, this is so that autoindentation does not occur after a
170+ traceback."""
171+
172+ self .syntaxerror_callback = None
173+ # Unfortunately code.InteractiveInterpreter is a classic class, so no super()
174+ code .InteractiveInterpreter .__init__ (self )
175+
176+ def showsyntaxerror (self , filename = None ):
177+ """Override the regular handler, the code's copied and pasted from
178+ code.py, as per showtraceback, but with the syntaxerror callback called
179+ and the text in a pretty colour."""
180+ if self .syntaxerror_callback is not None :
181+ self .syntaxerror_callback ()
182+
183+ type , value , sys .last_traceback = sys .exc_info ()
184+ sys .last_type = type
185+ sys .last_value = value
186+ if filename and type is SyntaxError :
187+ # Work hard to stuff the correct filename in the exception
188+ try :
189+ msg , (dummy_filename , lineno , offset , line ) = value
190+ except :
191+ # Not the format we expect; leave it alone
192+ pass
193+ else :
194+ # Stuff in the right filename
195+ value = SyntaxError (msg , (filename , lineno , offset , line ))
196+ sys .last_value = value
197+ list = traceback .format_exception_only (type , value )
198+ map (self .writetb , list )
161199
162200 def showtraceback (self ):
163201 """This needs to override the default traceback thing
@@ -280,8 +318,8 @@ def cw(self):
280318 left of the cursor"""
281319
282320 if self .cpos :
283- # I don't know if autocomplete should be disabled if the cursor
284- # isn't at the end of the line, but that's what this does for now.
321+ # I don't know if autocomplete should be disabled if the cursor
322+ # isn't at the end of the line, but that's what this does for now.
285323 return
286324
287325 l = len (self .s )
@@ -318,7 +356,7 @@ def getpydocspec(f, func):
318356 return None
319357
320358 args = [i .strip () for i in s .groups ()[1 ].split (',' )]
321- return [func , (args , None , None , None )]#None, None, None]
359+ return [func , (args , None , None , None )]
322360
323361 def getargspec (func ):
324362 try :
@@ -330,7 +368,8 @@ def getargspec(func):
330368 else :
331369 try :
332370 f = eval (func , self .interp .locals )
333- except Exception : # Same deal with the exceptions :(
371+ except Exception :
372+ # Same deal with the exceptions :(
334373 return None
335374
336375 try :
@@ -339,8 +378,6 @@ def getargspec(func):
339378 else :
340379 argspec = inspect .getargspec (f )
341380 self .argspec = [func , argspec ]
342- #[0]]#"Args for %s: " + ", ".join(argspec[0])
343- #self.argspec = self.argspec % func
344381 return True
345382
346383 except (NameError , TypeError , KeyError ):
@@ -349,7 +386,8 @@ def getargspec(func):
349386 return None
350387 self .argspec = t
351388 return True
352- except AttributeError : # no __init__
389+ except AttributeError :
390+ # This happens if no __init__ is found
353391 return None
354392
355393 def parse_parens (s ):
@@ -423,9 +461,10 @@ def _complete(self, unused_tab=False):
423461
424462 try :
425463 self .completer .complete (cw , 0 )
426- except Exception : # This sucks, but it's either that or list all the
427- # exceptions that could possibly be raised here, so if anyone wants to do that,
428- # feel free to send me a patch.
464+ except Exception :
465+ # This sucks, but it's either that or list all the exceptions that could
466+ # possibly be raised here, so if anyone wants to do that, feel free to send me
467+ # a patch.
429468 e = True
430469 else :
431470 e = False
@@ -435,7 +474,7 @@ def _complete(self, unused_tab=False):
435474 return False
436475
437476 if not e and self .completer .matches :
438- # remove duplicates and restore order
477+ # remove duplicates and restore order
439478 self .matches = sorted (set (self .completer .matches ))
440479
441480 if len (self .matches ) == 1 and not OPTS .auto_display_list :
@@ -470,7 +509,6 @@ def show_list(self, items, topline=None):
470509 height_offset = 0
471510
472511 def lsize ():
473- # wl = longest word length (and a space)
474512 wl = max (len (i ) for i in v_items ) + 1
475513 if not wl :
476514 wl = 1
@@ -490,7 +528,7 @@ def lsize():
490528 return True
491529
492530 if items :
493- # visible items (we'll append until we can't fit any more in)
531+ # visible items (we'll append until we can't fit any more in)
494532 v_items = [items [0 ][:max_w - 3 ]]
495533 lsize ()
496534 else :
@@ -527,7 +565,7 @@ def lsize():
527565 if height_offset and display_rows + 5 >= max_h :
528566 del v_items [- (cols * (height_offset )):]
529567
530- self .list_win .resize (rows + 2 , w )#(cols + 1) * wl + 3)
568+ self .list_win .resize (rows + 2 , w )
531569
532570 if down :
533571 self .list_win .mvwin (y + 1 , 0 )
@@ -807,7 +845,7 @@ def reevaluate(self):
807845
808846 self .evaluating = False
809847 #map(self.push, self.history)
810- #^-- That's how simple this function was at first :(
848+ #^-- That's how simple this method was at first :(
811849
812850 def prompt (self , more ):
813851 """Show the appropriate Python prompt"""
@@ -1004,7 +1042,8 @@ def bs(self, delete_tabs=True):
10041042 y -= 1
10051043 x = gethw ()[1 ]
10061044
1007- if not self .cpos : # I know the nested if blocks look nasty. :(
1045+ if not self .cpos :
1046+ # I know the nested if blocks look nasty. :(
10081047 if self .atbol () and delete_tabs :
10091048 n = len (self .s ) % OPTS .tab_length
10101049 if not n :
@@ -1021,10 +1060,10 @@ def bs(self, delete_tabs=True):
10211060
10221061 def bs_word (self ):
10231062 pos = len (self .s ) - self .cpos - 1
1024- # First we delete any space to the left of the cursor.
1063+ # First we delete any space to the left of the cursor.
10251064 while pos >= 0 and self .s [pos ] == ' ' :
10261065 pos -= self .bs ()
1027- # Then we delete a full word.
1066+ # Then we delete a full word.
10281067 while pos >= 0 and self .s [pos ] != ' ' :
10291068 pos -= self .bs ()
10301069
@@ -1202,7 +1241,7 @@ def strbase(self, l):
12021241
12031242 sl = sorted (l , key = str .__len__ )
12041243 for i , c in enumerate (l [- 1 ]):
1205- # I hate myself. Please email seamusmb@gmail.com to call him an dickhead for
1244+ # I hate myself. Please email seamusmb@gmail.com to call him a dickhead for
12061245# insisting that I make bpython 2.4-compatible. I couldn't be bothered
12071246# refactoring, so ghetto all() it is:
12081247 if not reduce (lambda x , y : (x and y ) or False ,
@@ -1284,12 +1323,20 @@ def get_line(self):
12841323 idiot)."""
12851324
12861325 self .ts = ''
1287- n_indent = re .split ('[^\t ]' , self .s , 1 )[0 ].count ('\t ' )
1288- indent = self .s .endswith (':' )
1326+
1327+ indent_spaces = 0
1328+ for c in self .s :
1329+ if c == ' ' :
1330+ indent_spaces += 1
1331+ else :
1332+ break
1333+
1334+ indent = self .s .rstrip ().endswith (':' )
1335+
12891336 self .s = ''
12901337 self .iy , self .ix = self .scr .getyx ()
12911338
1292- for _ in range (n_indent ):
1339+ for _ in range (indent_spaces // OPTS . tab_length ):
12931340 self .c = '\t '
12941341 self .p_key ()
12951342
@@ -1305,18 +1352,25 @@ def get_line(self):
13051352 if self .p_key () is None :
13061353 return self .s
13071354
1355+ def clear_current_line (self ):
1356+ """This is used as the exception callback for the Interpreter instance.
1357+ It prevents autoindentation from occuring after a traceback."""
1358+
1359+ self .s = ''
1360+
13081361 def get_key (self ):
13091362 while True :
13101363 if self .idle :
13111364 self .idle (self )
13121365 try :
13131366 key = self .scr .getkey ()
1314- except curses .error : # I'm quite annoyed with the ambiguity of
1315- # this exception handler. I previously caught "curses.error, x" and accessed
1316- # x.message and checked that it was "no input", which seemed a crappy way of
1317- # doing it. But then I ran it on a different computer and the exception
1318- # seems to have entirely different attributes. So let's hope getkey() doesn't
1319- # raise any other crazy curses exceptions. :)
1367+ except curses .error :
1368+ # I'm quite annoyed with the ambiguity of this exception handler. I previously
1369+ # caught "curses.error, x" and accessed x.message and checked that it was "no
1370+ # input", which seemed a crappy way of doing it. But then I ran it on a
1371+ # different computer and the exception seems to have entirely different
1372+ # attributes. So let's hope getkey() doesn't raise any other crazy curses
1373+ # exceptions. :)
13201374 continue
13211375 else :
13221376 return key
@@ -1476,9 +1530,9 @@ def init_wins(scr, cols):
14761530
14771531 main_win = curses .newwin (h - 1 , w , 0 , 0 )
14781532 main_win .scrollok (True )
1479- main_win .keypad (1 ) # Thanks to Angus Gibson for pointing out
1480- # this missing line which was causing problems that needed dirty
1481- # hackery to fix. :)
1533+ main_win .keypad (1 )
1534+ # Thanks to Angus Gibson for pointing out this missing line which was causing
1535+ # problems that needed dirty hackery to fix. :)
14821536
14831537 statusbar = Statusbar (scr , main_win ,
14841538 ".:: <C-d> Exit <C-r> Rewind <F2> Save <F8> Pastebin ::." ,
@@ -1533,7 +1587,8 @@ def do_resize(caller):
15331587 global DO_RESIZE
15341588 h , w = gethw ()
15351589 if not h :
1536- return # Hopefully this shouldn't happen. :)
1590+ # Hopefully this shouldn't happen. :)
1591+ return
15371592
15381593 curses .endwin ()
15391594 os .environ ["LINES" ] = str (h )
@@ -1543,8 +1598,7 @@ def do_resize(caller):
15431598
15441599 caller .resize ()
15451600 caller .statusbar .resize ()
1546- # The list win resizes itself every time it appears so no need to do it
1547- # here.
1601+ # The list win resizes itself every time it appears so no need to do it here.
15481602
15491603
15501604def loadrc ():
@@ -1629,6 +1683,8 @@ def main_curses(scr):
16291683 interpreter = Interpreter ()
16301684
16311685 repl = Repl (main_win , interpreter , statusbar , idle )
1686+ interpreter .syntaxerror_callback = repl .clear_current_line
1687+
16321688 repl ._C = cols
16331689
16341690 sys .stdout = repl
@@ -1650,8 +1706,8 @@ def main():
16501706 o = curses .wrapper (main_curses )
16511707 except :
16521708 tb = traceback .format_exc ()
1653- # I don't know why this is necessary; without it the wrapper doesn't always
1654- # do its job.
1709+ # I don't know why this is necessary; without it the wrapper doesn't always do
1710+ # its job.
16551711 if stdscr is not None :
16561712 stdscr .keypad (0 )
16571713 curses .echo ()
@@ -1663,7 +1719,7 @@ def main():
16631719 print tb
16641720 sys .exit (1 )
16651721
1666- # Fake stdout data so everything's still visible after exiting
1722+ # Fake stdout data so everything's still visible after exiting
16671723 sys .stdout .write (o )
16681724 sys .stdout .flush ()
16691725
0 commit comments