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 | 1681 | sys.last_traceback = last_traceback |
|
1682 | 1682 | |
|
1683 | 1683 | if filename and etype is SyntaxError: |
|
1684 | # Work hard to stuff the correct filename in the exception | |
|
1685 | 1684 | try: |
|
1686 | msg, (dummy_filename, lineno, offset, line) = value | |
|
1685 | value.filename = filename | |
|
1687 | 1686 | except: |
|
1688 | 1687 | # Not the format we expect; leave it alone |
|
1689 | 1688 | pass |
|
1690 |
|
|
|
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) | |
|
1689 | ||
|
1698 | 1690 | stb = self.SyntaxTB.structured_traceback(etype, value, []) |
|
1699 | 1691 | self._showtraceback(etype, value, stb) |
|
1700 | 1692 |
@@ -100,6 +100,7 b' from IPython.core.excolors import exception_colors' | |||
|
100 | 100 | from IPython.utils import PyColorize |
|
101 | 101 | from IPython.utils import io |
|
102 | 102 | from IPython.utils import py3compat |
|
103 | from IPython.utils import pyfile | |
|
103 | 104 | from IPython.utils.data import uniq_stable |
|
104 | 105 | from IPython.utils.warn import info, error |
|
105 | 106 | |
@@ -126,6 +127,14 b' def inspect_error():' | |||
|
126 | 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 | 138 | def findsource(object): |
|
130 | 139 | """Return the entire source file and starting line number for an object. |
|
131 | 140 | |
@@ -201,9 +210,10 b' def findsource(object):' | |||
|
201 | 210 | return lines, lnum |
|
202 | 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 | 214 | # Monkeypatch inspect to apply our bugfix. This code only works with py25 |
|
205 | if sys.version_info[:2] >= (2,5): | |
|
206 | inspect.findsource = findsource | |
|
215 | #if sys.version_info[:2] >= (2,5): | |
|
216 | # inspect.findsource = findsource | |
|
207 | 217 | |
|
208 | 218 | def fix_frame_records_filenames(records): |
|
209 | 219 | """Try to fix the filenames in each record from inspect.getinnerframes(). |
@@ -514,7 +524,7 b' class ListTB(TBTools):' | |||
|
514 | 524 | Colors.lineno, lineno, Colors.Normal, |
|
515 | 525 | Colors.name, name, Colors.Normal) |
|
516 | 526 | if line: |
|
517 |
item = |
|
|
527 | item += ' %s\n' % line.strip() | |
|
518 | 528 | list.append(item) |
|
519 | 529 | # Emphasize the last entry |
|
520 | 530 | filename, lineno, name, line = extracted_list[-1] |
@@ -525,7 +535,7 b' class ListTB(TBTools):' | |||
|
525 | 535 | Colors.nameEm, name, Colors.normalEm, |
|
526 | 536 | Colors.Normal) |
|
527 | 537 | if line: |
|
528 |
item = |
|
|
538 | item += '%s %s%s\n' % (Colors.line, line.strip(), | |
|
529 | 539 | Colors.Normal) |
|
530 | 540 | list.append(item) |
|
531 | 541 | #from pprint import pformat; print 'LISTTB', pformat(list) # dbg |
@@ -548,44 +558,40 b' class ListTB(TBTools):' | |||
|
548 | 558 | have_filedata = False |
|
549 | 559 | Colors = self.Colors |
|
550 | 560 | list = [] |
|
551 | try: | |
|
552 | stype = Colors.excName + etype.__name__ + Colors.Normal | |
|
553 | except AttributeError: | |
|
554 | stype = etype # String exceptions don't get special coloring | |
|
561 | stype = Colors.excName + etype.__name__ + Colors.Normal | |
|
555 | 562 | if value is None: |
|
563 | # Not sure if this can still happen in Python 2.6 and above | |
|
556 | 564 | list.append( str(stype) + '\n') |
|
557 | 565 | else: |
|
558 | 566 | if etype is SyntaxError: |
|
559 |
|
|
|
560 | msg, (filename, lineno, offset, line) = value | |
|
561 | except: | |
|
562 | have_filedata = False | |
|
563 | else: | |
|
564 | have_filedata = True | |
|
565 | #print 'filename is',filename # dbg | |
|
566 | if not filename: filename = "<string>" | |
|
567 | list.append('%s File %s"%s"%s, line %s%d%s\n' % \ | |
|
568 | (Colors.normalEm, | |
|
569 | Colors.filenameEm, filename, Colors.normalEm, | |
|
570 | Colors.linenoEm, lineno, Colors.Normal )) | |
|
571 | if line is not None: | |
|
572 | i = 0 | |
|
573 | while i < len(line) and line[i].isspace(): | |
|
574 |
|
|
|
575 | list.append('%s %s%s\n' % (Colors.line, | |
|
576 |
|
|
|
577 |
|
|
|
578 |
|
|
|
579 |
s = ' |
|
|
580 | for c in line[i:offset-1]: | |
|
581 |
|
|
|
582 | s = s + c | |
|
583 | else: | |
|
584 | s = s + ' ' | |
|
585 | list.append('%s%s^%s\n' % (Colors.caret, s, | |
|
586 | Colors.Normal) ) | |
|
587 | value = msg | |
|
588 | s = self._some_str(value) | |
|
567 | have_filedata = True | |
|
568 | #print 'filename is',filename # dbg | |
|
569 | if not value.filename: value.filename = "<string>" | |
|
570 | list.append('%s File %s"%s"%s, line %s%d%s\n' % \ | |
|
571 | (Colors.normalEm, | |
|
572 | Colors.filenameEm, value.filename, Colors.normalEm, | |
|
573 | Colors.linenoEm, value.lineno, Colors.Normal )) | |
|
574 | if value.text is not None: | |
|
575 | i = 0 | |
|
576 | while i < len(value.text) and value.text[i].isspace(): | |
|
577 | i += 1 | |
|
578 | list.append('%s %s%s\n' % (Colors.line, | |
|
579 | value.text.strip(), | |
|
580 | Colors.Normal)) | |
|
581 | if value.offset is not None: | |
|
582 | s = ' ' | |
|
583 | for c in value.text[i:value.offset-1]: | |
|
584 | if c.isspace(): | |
|
585 | s += c | |
|
586 | else: | |
|
587 | s += ' ' | |
|
588 | list.append('%s%s^%s\n' % (Colors.caret, s, | |
|
589 | Colors.Normal) ) | |
|
590 | ||
|
591 | try: | |
|
592 | s = value.msg | |
|
593 | except Exception: | |
|
594 | s = self._some_str(value) | |
|
589 | 595 | if s: |
|
590 | 596 | list.append('%s%s:%s %s\n' % (str(stype), Colors.excName, |
|
591 | 597 | Colors.Normal, s)) |
@@ -596,7 +602,7 b' class ListTB(TBTools):' | |||
|
596 | 602 | if have_filedata: |
|
597 | 603 | ipinst = ipapi.get() |
|
598 | 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 | 607 | return list |
|
602 | 608 | |
@@ -788,15 +794,7 b' class VerboseTB(TBTools):' | |||
|
788 | 794 | # keep the original file string. |
|
789 | 795 | pass |
|
790 | 796 | link = tpl_link % file |
|
791 | try: | |
|
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") | |
|
797 | args, varargs, varkw, locals = inspect.getargvalues(frame) | |
|
800 | 798 | |
|
801 | 799 | if func == '?': |
|
802 | 800 | call = '' |
@@ -826,48 +824,9 b' class VerboseTB(TBTools):' | |||
|
826 | 824 | # disabled. |
|
827 | 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 | 827 | def linereader(file=file, lnum=[lnum], getline=linecache.getline): |
|
828 | if file.endswith(('.pyc','.pyo')): | |
|
829 | file = pyfile.source_from_cache(file) | |
|
871 | 830 | line = getline(file, lnum[0]) |
|
872 | 831 | lnum[0] += 1 |
|
873 | 832 | return line |
@@ -875,10 +834,32 b' class VerboseTB(TBTools):' | |||
|
875 | 834 | # Build the list of names on this line of code where the exception |
|
876 | 835 | # occurred. |
|
877 | 836 | 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) | |
|
837 | names = [] | |
|
838 | name_cont = False | |
|
839 | ||
|
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 | 863 | except (IndexError, UnicodeDecodeError): |
|
883 | 864 | # signals exit of tokenizer |
|
884 | 865 | pass |
@@ -888,6 +869,8 b' class VerboseTB(TBTools):' | |||
|
888 | 869 | "The error message is: %s\n" % msg) |
|
889 | 870 | error(_m) |
|
890 | 871 | |
|
872 | # Join composite names (e.g. "dict.fromkeys") | |
|
873 | names = ['.'.join(n) for n in names] | |
|
891 | 874 | # prune names list of duplicates, but keep the right order |
|
892 | 875 | unique_names = uniq_stable(names) |
|
893 | 876 |
@@ -13,7 +13,6 b' import unittest' | |||
|
13 | 13 | |
|
14 | 14 | # IPython imports |
|
15 | 15 | from IPython.lib import irunner |
|
16 | from IPython.testing.decorators import known_failure_py3 | |
|
17 | 16 | from IPython.utils.py3compat import doctest_refactor_print |
|
18 | 17 | |
|
19 | 18 | # Testing code begins |
@@ -58,8 +57,6 b' class RunnerTestCase(unittest.TestCase):' | |||
|
58 | 57 | self.assert_(mismatch==0,'Number of mismatched lines: %s' % |
|
59 | 58 | mismatch) |
|
60 | 59 | |
|
61 | # The SyntaxError appears differently in Python 3, for some reason. | |
|
62 | @known_failure_py3 | |
|
63 | 60 | def testIPython(self): |
|
64 | 61 | """Test the IPython runner.""" |
|
65 | 62 | source = doctest_refactor_print(""" |
General Comments 0
You need to be logged in to leave comments.
Login now