##// 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,43 +558,39 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:
552 stype = Colors.excName + etype.__name__ + Colors.Normal
561 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:
560 msg, (filename, lineno, offset, line) = value
561 except:
562 have_filedata = False
563 else:
564 have_filedata = True
567 have_filedata = True
565 #print 'filename is',filename # dbg
568 #print 'filename is',filename # dbg
566 if not filename: filename = "<string>"
569 if not value.filename: value.filename = "<string>"
567 list.append('%s File %s"%s"%s, line %s%d%s\n' % \
570 list.append('%s File %s"%s"%s, line %s%d%s\n' % \
568 (Colors.normalEm,
571 (Colors.normalEm,
569 Colors.filenameEm, filename, Colors.normalEm,
572 Colors.filenameEm, value.filename, Colors.normalEm,
570 Colors.linenoEm, lineno, Colors.Normal ))
573 Colors.linenoEm, value.lineno, Colors.Normal ))
571 if line is not None:
574 if value.text is not None:
572 i = 0
575 i = 0
573 while i < len(line) and line[i].isspace():
576 while i < len(value.text) and value.text[i].isspace():
574 i = i+1
577 i += 1
575 list.append('%s %s%s\n' % (Colors.line,
578 list.append('%s %s%s\n' % (Colors.line,
576 line.strip(),
579 value.text.strip(),
577 Colors.Normal))
580 Colors.Normal))
578 if offset is not None:
581 if value.offset is not None:
579 s = ' '
582 s = ' '
580 for c in line[i:offset-1]:
583 for c in value.text[i:value.offset-1]:
581 if c.isspace():
584 if c.isspace():
582 s = s + c
585 s += c
583 else:
586 else:
584 s = s + ' '
587 s += ' '
585 list.append('%s%s^%s\n' % (Colors.caret, s,
588 list.append('%s%s^%s\n' % (Colors.caret, s,
586 Colors.Normal) )
589 Colors.Normal) )
587 value = msg
590
591 try:
592 s = value.msg
593 except Exception:
588 s = self._some_str(value)
594 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,
@@ -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:
792 args, varargs, varkw, locals = inspect.getargvalues(frame)
797 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,33 +824,29 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
827 def linereader(file=file, lnum=[lnum], getline=linecache.getline):
830 # tokenizer below will populate.
828 if file.endswith(('.pyc','.pyo')):
831 names = []
829 file = pyfile.source_from_cache(file)
832
830 line = getline(file, lnum[0])
833 def tokeneater(token_type, token, start, end, line):
831 lnum[0] += 1
834 """Stateful tokeneater which builds dotted names.
832 return line
835
833
836 The list of names it appends to (from the enclosing scope) can
834 # Build the list of names on this line of code where the exception
837 contain repeated composite names. This is unavoidable, since
835 # occurred.
838 there is no way to disambguate partial dotted structures until
836 try:
839 the full list is known. The caller is responsible for pruning
837 names = []
840 the final list of duplicates before using it."""
838 name_cont = False
841
839
840 for token_type, token, start, end, line in generate_tokens(linereader):
842 # build composite names
841 # build composite names
843 if token == '.':
842 if token_type == tokenize.NAME and token not in keyword.kwlist:
843 if name_cont:
844 # Continuation of a dotted name
844 try:
845 try:
845 names[-1] += '.'
846 names[-1].append(token)
846 # store state so the next token is added for x.y.z names
847 tokeneater.name_cont = True
848 return
849 except IndexError:
847 except IndexError:
850 pass
848 names.append([token])
851 if token_type == tokenize.NAME and token not in keyword.kwlist:
849 name_cont = False
852 if tokeneater.name_cont:
853 # Dotted names
854 names[-1] += token
855 tokeneater.name_cont = False
856 else:
850 else:
857 # Regular new names. We append everything, the caller
851 # Regular new names. We append everything, the caller
858 # will be responsible for pruning the list later. It's
852 # will be responsible for pruning the list later. It's
@@ -860,25 +854,12 b' class VerboseTB(TBTools):'
860 # names can fool us. The pruning at the end is easy
854 # names can fool us. The pruning at the end is easy
861 # to do (or the caller can print a list with repeated
855 # to do (or the caller can print a list with repeated
862 # names if so desired.
856 # names if so desired.
863 names.append(token)
857 names.append([token])
858 elif token == '.':
859 name_cont = True
864 elif token_type == tokenize.NEWLINE:
860 elif token_type == tokenize.NEWLINE:
865 raise IndexError
861 break
866 # we need to store a bit of state in the tokenizer to build
867 # dotted names
868 tokeneater.name_cont = False
869
862
870 def linereader(file=file, lnum=[lnum], getline=linecache.getline):
871 line = getline(file, lnum[0])
872 lnum[0] += 1
873 return line
874
875 # Build the list of names on this line of code where the exception
876 # occurred.
877 try:
878 # This builds the names list in-place by capturing it from the
879 # enclosing scope.
880 for token in generate_tokens(linereader):
881 tokeneater(*token)
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