##// END OF EJS Templates
Merge pull request #1229 from takluyver/i1225...
Fernando Perez -
r5881:83dea09a merge
parent child Browse files
Show More
@@ -0,0 +1,23 b''
1 """Utilities for working with Python source files.
2
3 Exposes various functions from recent Python standard libraries, along with
4 equivalents for older Python versions.
5 """
6 import os.path
7
8 try: # Python 3.2
9 from imp import source_from_cache, cache_from_source
10 except ImportError:
11 # Python <= 3.1: .pyc files go next to .py
12 def source_from_cache(path):
13 basename, ext = os.path.splitext(path)
14 if ext not in {'.pyc', '.pyo'}:
15 raise ValueError('Not a cached Python file extension', ext)
16 # Should we look for .pyw files?
17 return basename + '.py'
18
19 def cache_from_source(path, debug_override=None):
20 if debug_override is None:
21 debug_override = __debug__
22 basename, ext = os.path.splitext(path)
23 return basename + '.pyc' if debug_override else '.pyo'
@@ -1681,20 +1681,12 b' class InteractiveShell(SingletonConfigurable, Magic):'
1681 sys.last_traceback = last_traceback
1681 sys.last_traceback = last_traceback
1682
1682
1683 if filename and etype is SyntaxError:
1683 if filename and etype is SyntaxError:
1684 # Work hard to stuff the correct filename in the exception
1685 try:
1684 try:
1686 msg, (dummy_filename, lineno, offset, line) = value
1685 value.filename = filename
1687 except:
1686 except:
1688 # Not the format we expect; leave it alone
1687 # Not the format we expect; leave it alone
1689 pass
1688 pass
1690 else:
1689
1691 # Stuff in the right filename
1692 try:
1693 # Assume SyntaxError is a class exception
1694 value = SyntaxError(msg, (filename, lineno, offset, line))
1695 except:
1696 # If that failed, assume SyntaxError is a string
1697 value = msg, (filename, lineno, offset, line)
1698 stb = self.SyntaxTB.structured_traceback(etype, value, [])
1690 stb = self.SyntaxTB.structured_traceback(etype, value, [])
1699 self._showtraceback(etype, value, stb)
1691 self._showtraceback(etype, value, stb)
1700
1692
@@ -100,6 +100,7 b' from IPython.core.excolors import exception_colors'
100 from IPython.utils import PyColorize
100 from IPython.utils import PyColorize
101 from IPython.utils import io
101 from IPython.utils import io
102 from IPython.utils import py3compat
102 from IPython.utils import py3compat
103 from IPython.utils import pyfile
103 from IPython.utils.data import uniq_stable
104 from IPython.utils.data import uniq_stable
104 from IPython.utils.warn import info, error
105 from IPython.utils.warn import info, error
105
106
@@ -126,6 +127,14 b' def inspect_error():'
126 'Below is the traceback from this internal error.\n')
127 'Below is the traceback from this internal error.\n')
127
128
128
129
130 # N.B. This function is a monkeypatch we are currently not applying.
131 # It was written some time ago, to fix an apparent Python bug with
132 # codeobj.co_firstlineno . Unfortunately, we don't know under what conditions
133 # the bug occurred, so we can't tell if it has been fixed. If it reappears, we
134 # will apply the monkeypatch again. Also, note that findsource() is not called
135 # by our code at this time - we don't know if it was when the monkeypatch was
136 # written, or if the monkeypatch is needed for some other code (like a debugger).
137 # For the discussion about not applying it, see gh-1229. TK, Jan 2011.
129 def findsource(object):
138 def findsource(object):
130 """Return the entire source file and starting line number for an object.
139 """Return the entire source file and starting line number for an object.
131
140
@@ -201,9 +210,10 b' def findsource(object):'
201 return lines, lnum
210 return lines, lnum
202 raise IOError('could not find code object')
211 raise IOError('could not find code object')
203
212
213 # Not applying the monkeypatch - see above the function for details. TK, Jan 2012
204 # Monkeypatch inspect to apply our bugfix. This code only works with py25
214 # Monkeypatch inspect to apply our bugfix. This code only works with py25
205 if sys.version_info[:2] >= (2,5):
215 #if sys.version_info[:2] >= (2,5):
206 inspect.findsource = findsource
216 # inspect.findsource = findsource
207
217
208 def fix_frame_records_filenames(records):
218 def fix_frame_records_filenames(records):
209 """Try to fix the filenames in each record from inspect.getinnerframes().
219 """Try to fix the filenames in each record from inspect.getinnerframes().
@@ -514,7 +524,7 b' class ListTB(TBTools):'
514 Colors.lineno, lineno, Colors.Normal,
524 Colors.lineno, lineno, Colors.Normal,
515 Colors.name, name, Colors.Normal)
525 Colors.name, name, Colors.Normal)
516 if line:
526 if line:
517 item = item + ' %s\n' % line.strip()
527 item += ' %s\n' % line.strip()
518 list.append(item)
528 list.append(item)
519 # Emphasize the last entry
529 # Emphasize the last entry
520 filename, lineno, name, line = extracted_list[-1]
530 filename, lineno, name, line = extracted_list[-1]
@@ -525,7 +535,7 b' class ListTB(TBTools):'
525 Colors.nameEm, name, Colors.normalEm,
535 Colors.nameEm, name, Colors.normalEm,
526 Colors.Normal)
536 Colors.Normal)
527 if line:
537 if line:
528 item = item + '%s %s%s\n' % (Colors.line, line.strip(),
538 item += '%s %s%s\n' % (Colors.line, line.strip(),
529 Colors.Normal)
539 Colors.Normal)
530 list.append(item)
540 list.append(item)
531 #from pprint import pformat; print 'LISTTB', pformat(list) # dbg
541 #from pprint import pformat; print 'LISTTB', pformat(list) # dbg
@@ -548,44 +558,40 b' class ListTB(TBTools):'
548 have_filedata = False
558 have_filedata = False
549 Colors = self.Colors
559 Colors = self.Colors
550 list = []
560 list = []
551 try:
561 stype = Colors.excName + etype.__name__ + Colors.Normal
552 stype = Colors.excName + etype.__name__ + Colors.Normal
553 except AttributeError:
554 stype = etype # String exceptions don't get special coloring
555 if value is None:
562 if value is None:
563 # Not sure if this can still happen in Python 2.6 and above
556 list.append( str(stype) + '\n')
564 list.append( str(stype) + '\n')
557 else:
565 else:
558 if etype is SyntaxError:
566 if etype is SyntaxError:
559 try:
567 have_filedata = True
560 msg, (filename, lineno, offset, line) = value
568 #print 'filename is',filename # dbg
561 except:
569 if not value.filename: value.filename = "<string>"
562 have_filedata = False
570 list.append('%s File %s"%s"%s, line %s%d%s\n' % \
563 else:
571 (Colors.normalEm,
564 have_filedata = True
572 Colors.filenameEm, value.filename, Colors.normalEm,
565 #print 'filename is',filename # dbg
573 Colors.linenoEm, value.lineno, Colors.Normal ))
566 if not filename: filename = "<string>"
574 if value.text is not None:
567 list.append('%s File %s"%s"%s, line %s%d%s\n' % \
575 i = 0
568 (Colors.normalEm,
576 while i < len(value.text) and value.text[i].isspace():
569 Colors.filenameEm, filename, Colors.normalEm,
577 i += 1
570 Colors.linenoEm, lineno, Colors.Normal ))
578 list.append('%s %s%s\n' % (Colors.line,
571 if line is not None:
579 value.text.strip(),
572 i = 0
580 Colors.Normal))
573 while i < len(line) and line[i].isspace():
581 if value.offset is not None:
574 i = i+1
582 s = ' '
575 list.append('%s %s%s\n' % (Colors.line,
583 for c in value.text[i:value.offset-1]:
576 line.strip(),
584 if c.isspace():
577 Colors.Normal))
585 s += c
578 if offset is not None:
586 else:
579 s = ' '
587 s += ' '
580 for c in line[i:offset-1]:
588 list.append('%s%s^%s\n' % (Colors.caret, s,
581 if c.isspace():
589 Colors.Normal) )
582 s = s + c
590
583 else:
591 try:
584 s = s + ' '
592 s = value.msg
585 list.append('%s%s^%s\n' % (Colors.caret, s,
593 except Exception:
586 Colors.Normal) )
594 s = self._some_str(value)
587 value = msg
588 s = self._some_str(value)
589 if s:
595 if s:
590 list.append('%s%s:%s %s\n' % (str(stype), Colors.excName,
596 list.append('%s%s:%s %s\n' % (str(stype), Colors.excName,
591 Colors.Normal, s))
597 Colors.Normal, s))
@@ -596,7 +602,7 b' class ListTB(TBTools):'
596 if have_filedata:
602 if have_filedata:
597 ipinst = ipapi.get()
603 ipinst = ipapi.get()
598 if ipinst is not None:
604 if ipinst is not None:
599 ipinst.hooks.synchronize_with_editor(filename, lineno, 0)
605 ipinst.hooks.synchronize_with_editor(value.filename, value.lineno, 0)
600
606
601 return list
607 return list
602
608
@@ -788,15 +794,7 b' class VerboseTB(TBTools):'
788 # keep the original file string.
794 # keep the original file string.
789 pass
795 pass
790 link = tpl_link % file
796 link = tpl_link % file
791 try:
797 args, varargs, varkw, locals = inspect.getargvalues(frame)
792 args, varargs, varkw, locals = inspect.getargvalues(frame)
793 except:
794 # This can happen due to a bug in python2.3. We should be
795 # able to remove this try/except when 2.4 becomes a
796 # requirement. Bug details at http://python.org/sf/1005466
797 inspect_error()
798 traceback.print_exc(file=self.ostream)
799 info("\nIPython's exception reporting continues...\n")
800
798
801 if func == '?':
799 if func == '?':
802 call = ''
800 call = ''
@@ -826,48 +824,9 b' class VerboseTB(TBTools):'
826 # disabled.
824 # disabled.
827 call = tpl_call_fail % func
825 call = tpl_call_fail % func
828
826
829 # Initialize a list of names on the current line, which the
830 # tokenizer below will populate.
831 names = []
832
833 def tokeneater(token_type, token, start, end, line):
834 """Stateful tokeneater which builds dotted names.
835
836 The list of names it appends to (from the enclosing scope) can
837 contain repeated composite names. This is unavoidable, since
838 there is no way to disambguate partial dotted structures until
839 the full list is known. The caller is responsible for pruning
840 the final list of duplicates before using it."""
841
842 # build composite names
843 if token == '.':
844 try:
845 names[-1] += '.'
846 # store state so the next token is added for x.y.z names
847 tokeneater.name_cont = True
848 return
849 except IndexError:
850 pass
851 if token_type == tokenize.NAME and token not in keyword.kwlist:
852 if tokeneater.name_cont:
853 # Dotted names
854 names[-1] += token
855 tokeneater.name_cont = False
856 else:
857 # Regular new names. We append everything, the caller
858 # will be responsible for pruning the list later. It's
859 # very tricky to try to prune as we go, b/c composite
860 # names can fool us. The pruning at the end is easy
861 # to do (or the caller can print a list with repeated
862 # names if so desired.
863 names.append(token)
864 elif token_type == tokenize.NEWLINE:
865 raise IndexError
866 # we need to store a bit of state in the tokenizer to build
867 # dotted names
868 tokeneater.name_cont = False
869
870 def linereader(file=file, lnum=[lnum], getline=linecache.getline):
827 def linereader(file=file, lnum=[lnum], getline=linecache.getline):
828 if file.endswith(('.pyc','.pyo')):
829 file = pyfile.source_from_cache(file)
871 line = getline(file, lnum[0])
830 line = getline(file, lnum[0])
872 lnum[0] += 1
831 lnum[0] += 1
873 return line
832 return line
@@ -875,10 +834,32 b' class VerboseTB(TBTools):'
875 # Build the list of names on this line of code where the exception
834 # Build the list of names on this line of code where the exception
876 # occurred.
835 # occurred.
877 try:
836 try:
878 # This builds the names list in-place by capturing it from the
837 names = []
879 # enclosing scope.
838 name_cont = False
880 for token in generate_tokens(linereader):
839
881 tokeneater(*token)
840 for token_type, token, start, end, line in generate_tokens(linereader):
841 # build composite names
842 if token_type == tokenize.NAME and token not in keyword.kwlist:
843 if name_cont:
844 # Continuation of a dotted name
845 try:
846 names[-1].append(token)
847 except IndexError:
848 names.append([token])
849 name_cont = False
850 else:
851 # Regular new names. We append everything, the caller
852 # will be responsible for pruning the list later. It's
853 # very tricky to try to prune as we go, b/c composite
854 # names can fool us. The pruning at the end is easy
855 # to do (or the caller can print a list with repeated
856 # names if so desired.
857 names.append([token])
858 elif token == '.':
859 name_cont = True
860 elif token_type == tokenize.NEWLINE:
861 break
862
882 except (IndexError, UnicodeDecodeError):
863 except (IndexError, UnicodeDecodeError):
883 # signals exit of tokenizer
864 # signals exit of tokenizer
884 pass
865 pass
@@ -888,6 +869,8 b' class VerboseTB(TBTools):'
888 "The error message is: %s\n" % msg)
869 "The error message is: %s\n" % msg)
889 error(_m)
870 error(_m)
890
871
872 # Join composite names (e.g. "dict.fromkeys")
873 names = ['.'.join(n) for n in names]
891 # prune names list of duplicates, but keep the right order
874 # prune names list of duplicates, but keep the right order
892 unique_names = uniq_stable(names)
875 unique_names = uniq_stable(names)
893
876
@@ -13,7 +13,6 b' import unittest'
13
13
14 # IPython imports
14 # IPython imports
15 from IPython.lib import irunner
15 from IPython.lib import irunner
16 from IPython.testing.decorators import known_failure_py3
17 from IPython.utils.py3compat import doctest_refactor_print
16 from IPython.utils.py3compat import doctest_refactor_print
18
17
19 # Testing code begins
18 # Testing code begins
@@ -58,8 +57,6 b' class RunnerTestCase(unittest.TestCase):'
58 self.assert_(mismatch==0,'Number of mismatched lines: %s' %
57 self.assert_(mismatch==0,'Number of mismatched lines: %s' %
59 mismatch)
58 mismatch)
60
59
61 # The SyntaxError appears differently in Python 3, for some reason.
62 @known_failure_py3
63 def testIPython(self):
60 def testIPython(self):
64 """Test the IPython runner."""
61 """Test the IPython runner."""
65 source = doctest_refactor_print("""
62 source = doctest_refactor_print("""
General Comments 0
You need to be logged in to leave comments. Login now