##// END OF EJS Templates
Fix references to raw_input()
Thomas Kluyver -
Show More
@@ -1,215 +1,216 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """sys.excepthook for IPython itself, leaves a detailed report on disk.
2 """sys.excepthook for IPython itself, leaves a detailed report on disk.
3
3
4 Authors:
4 Authors:
5
5
6 * Fernando Perez
6 * Fernando Perez
7 * Brian E. Granger
7 * Brian E. Granger
8 """
8 """
9
9
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11 # Copyright (C) 2001-2007 Fernando Perez. <fperez@colorado.edu>
11 # Copyright (C) 2001-2007 Fernando Perez. <fperez@colorado.edu>
12 # Copyright (C) 2008-2011 The IPython Development Team
12 # Copyright (C) 2008-2011 The IPython Development Team
13 #
13 #
14 # Distributed under the terms of the BSD License. The full license is in
14 # Distributed under the terms of the BSD License. The full license is in
15 # the file COPYING, distributed as part of this software.
15 # the file COPYING, distributed as part of this software.
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17
17
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19 # Imports
19 # Imports
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21 from __future__ import print_function
21 from __future__ import print_function
22
22
23 import os
23 import os
24 import sys
24 import sys
25 import traceback
25 import traceback
26 from pprint import pformat
26 from pprint import pformat
27
27
28 from IPython.core import ultratb
28 from IPython.core import ultratb
29 from IPython.core.release import author_email
29 from IPython.core.release import author_email
30 from IPython.utils.sysinfo import sys_info
30 from IPython.utils.sysinfo import sys_info
31 from IPython.utils.py3compat import input
31
32
32 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
33 # Code
34 # Code
34 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
35
36
36 # Template for the user message.
37 # Template for the user message.
37 _default_message_template = """\
38 _default_message_template = """\
38 Oops, {app_name} crashed. We do our best to make it stable, but...
39 Oops, {app_name} crashed. We do our best to make it stable, but...
39
40
40 A crash report was automatically generated with the following information:
41 A crash report was automatically generated with the following information:
41 - A verbatim copy of the crash traceback.
42 - A verbatim copy of the crash traceback.
42 - A copy of your input history during this session.
43 - A copy of your input history during this session.
43 - Data on your current {app_name} configuration.
44 - Data on your current {app_name} configuration.
44
45
45 It was left in the file named:
46 It was left in the file named:
46 \t'{crash_report_fname}'
47 \t'{crash_report_fname}'
47 If you can email this file to the developers, the information in it will help
48 If you can email this file to the developers, the information in it will help
48 them in understanding and correcting the problem.
49 them in understanding and correcting the problem.
49
50
50 You can mail it to: {contact_name} at {contact_email}
51 You can mail it to: {contact_name} at {contact_email}
51 with the subject '{app_name} Crash Report'.
52 with the subject '{app_name} Crash Report'.
52
53
53 If you want to do it now, the following command will work (under Unix):
54 If you want to do it now, the following command will work (under Unix):
54 mail -s '{app_name} Crash Report' {contact_email} < {crash_report_fname}
55 mail -s '{app_name} Crash Report' {contact_email} < {crash_report_fname}
55
56
56 To ensure accurate tracking of this issue, please file a report about it at:
57 To ensure accurate tracking of this issue, please file a report about it at:
57 {bug_tracker}
58 {bug_tracker}
58 """
59 """
59
60
60 _lite_message_template = """
61 _lite_message_template = """
61 If you suspect this is an IPython bug, please report it at:
62 If you suspect this is an IPython bug, please report it at:
62 https://github.com/ipython/ipython/issues
63 https://github.com/ipython/ipython/issues
63 or send an email to the mailing list at {email}
64 or send an email to the mailing list at {email}
64
65
65 You can print a more detailed traceback right now with "%tb", or use "%debug"
66 You can print a more detailed traceback right now with "%tb", or use "%debug"
66 to interactively debug it.
67 to interactively debug it.
67
68
68 Extra-detailed tracebacks for bug-reporting purposes can be enabled via:
69 Extra-detailed tracebacks for bug-reporting purposes can be enabled via:
69 {config}Application.verbose_crash=True
70 {config}Application.verbose_crash=True
70 """
71 """
71
72
72
73
73 class CrashHandler(object):
74 class CrashHandler(object):
74 """Customizable crash handlers for IPython applications.
75 """Customizable crash handlers for IPython applications.
75
76
76 Instances of this class provide a :meth:`__call__` method which can be
77 Instances of this class provide a :meth:`__call__` method which can be
77 used as a ``sys.excepthook``. The :meth:`__call__` signature is::
78 used as a ``sys.excepthook``. The :meth:`__call__` signature is::
78
79
79 def __call__(self, etype, evalue, etb)
80 def __call__(self, etype, evalue, etb)
80 """
81 """
81
82
82 message_template = _default_message_template
83 message_template = _default_message_template
83 section_sep = '\n\n'+'*'*75+'\n\n'
84 section_sep = '\n\n'+'*'*75+'\n\n'
84
85
85 def __init__(self, app, contact_name=None, contact_email=None,
86 def __init__(self, app, contact_name=None, contact_email=None,
86 bug_tracker=None, show_crash_traceback=True, call_pdb=False):
87 bug_tracker=None, show_crash_traceback=True, call_pdb=False):
87 """Create a new crash handler
88 """Create a new crash handler
88
89
89 Parameters
90 Parameters
90 ----------
91 ----------
91 app : Application
92 app : Application
92 A running :class:`Application` instance, which will be queried at
93 A running :class:`Application` instance, which will be queried at
93 crash time for internal information.
94 crash time for internal information.
94
95
95 contact_name : str
96 contact_name : str
96 A string with the name of the person to contact.
97 A string with the name of the person to contact.
97
98
98 contact_email : str
99 contact_email : str
99 A string with the email address of the contact.
100 A string with the email address of the contact.
100
101
101 bug_tracker : str
102 bug_tracker : str
102 A string with the URL for your project's bug tracker.
103 A string with the URL for your project's bug tracker.
103
104
104 show_crash_traceback : bool
105 show_crash_traceback : bool
105 If false, don't print the crash traceback on stderr, only generate
106 If false, don't print the crash traceback on stderr, only generate
106 the on-disk report
107 the on-disk report
107
108
108 Non-argument instance attributes:
109 Non-argument instance attributes:
109
110
110 These instances contain some non-argument attributes which allow for
111 These instances contain some non-argument attributes which allow for
111 further customization of the crash handler's behavior. Please see the
112 further customization of the crash handler's behavior. Please see the
112 source for further details.
113 source for further details.
113 """
114 """
114 self.crash_report_fname = "Crash_report_%s.txt" % app.name
115 self.crash_report_fname = "Crash_report_%s.txt" % app.name
115 self.app = app
116 self.app = app
116 self.call_pdb = call_pdb
117 self.call_pdb = call_pdb
117 #self.call_pdb = True # dbg
118 #self.call_pdb = True # dbg
118 self.show_crash_traceback = show_crash_traceback
119 self.show_crash_traceback = show_crash_traceback
119 self.info = dict(app_name = app.name,
120 self.info = dict(app_name = app.name,
120 contact_name = contact_name,
121 contact_name = contact_name,
121 contact_email = contact_email,
122 contact_email = contact_email,
122 bug_tracker = bug_tracker,
123 bug_tracker = bug_tracker,
123 crash_report_fname = self.crash_report_fname)
124 crash_report_fname = self.crash_report_fname)
124
125
125
126
126 def __call__(self, etype, evalue, etb):
127 def __call__(self, etype, evalue, etb):
127 """Handle an exception, call for compatible with sys.excepthook"""
128 """Handle an exception, call for compatible with sys.excepthook"""
128
129
129 # do not allow the crash handler to be called twice without reinstalling it
130 # do not allow the crash handler to be called twice without reinstalling it
130 # this prevents unlikely errors in the crash handling from entering an
131 # this prevents unlikely errors in the crash handling from entering an
131 # infinite loop.
132 # infinite loop.
132 sys.excepthook = sys.__excepthook__
133 sys.excepthook = sys.__excepthook__
133
134
134 # Report tracebacks shouldn't use color in general (safer for users)
135 # Report tracebacks shouldn't use color in general (safer for users)
135 color_scheme = 'NoColor'
136 color_scheme = 'NoColor'
136
137
137 # Use this ONLY for developer debugging (keep commented out for release)
138 # Use this ONLY for developer debugging (keep commented out for release)
138 #color_scheme = 'Linux' # dbg
139 #color_scheme = 'Linux' # dbg
139 try:
140 try:
140 rptdir = self.app.ipython_dir
141 rptdir = self.app.ipython_dir
141 except:
142 except:
142 rptdir = os.getcwdu()
143 rptdir = os.getcwdu()
143 if rptdir is None or not os.path.isdir(rptdir):
144 if rptdir is None or not os.path.isdir(rptdir):
144 rptdir = os.getcwdu()
145 rptdir = os.getcwdu()
145 report_name = os.path.join(rptdir,self.crash_report_fname)
146 report_name = os.path.join(rptdir,self.crash_report_fname)
146 # write the report filename into the instance dict so it can get
147 # write the report filename into the instance dict so it can get
147 # properly expanded out in the user message template
148 # properly expanded out in the user message template
148 self.crash_report_fname = report_name
149 self.crash_report_fname = report_name
149 self.info['crash_report_fname'] = report_name
150 self.info['crash_report_fname'] = report_name
150 TBhandler = ultratb.VerboseTB(
151 TBhandler = ultratb.VerboseTB(
151 color_scheme=color_scheme,
152 color_scheme=color_scheme,
152 long_header=1,
153 long_header=1,
153 call_pdb=self.call_pdb,
154 call_pdb=self.call_pdb,
154 )
155 )
155 if self.call_pdb:
156 if self.call_pdb:
156 TBhandler(etype,evalue,etb)
157 TBhandler(etype,evalue,etb)
157 return
158 return
158 else:
159 else:
159 traceback = TBhandler.text(etype,evalue,etb,context=31)
160 traceback = TBhandler.text(etype,evalue,etb,context=31)
160
161
161 # print traceback to screen
162 # print traceback to screen
162 if self.show_crash_traceback:
163 if self.show_crash_traceback:
163 print(traceback, file=sys.stderr)
164 print(traceback, file=sys.stderr)
164
165
165 # and generate a complete report on disk
166 # and generate a complete report on disk
166 try:
167 try:
167 report = open(report_name,'w')
168 report = open(report_name,'w')
168 except:
169 except:
169 print('Could not create crash report on disk.', file=sys.stderr)
170 print('Could not create crash report on disk.', file=sys.stderr)
170 return
171 return
171
172
172 # Inform user on stderr of what happened
173 # Inform user on stderr of what happened
173 print('\n'+'*'*70+'\n', file=sys.stderr)
174 print('\n'+'*'*70+'\n', file=sys.stderr)
174 print(self.message_template.format(**self.info), file=sys.stderr)
175 print(self.message_template.format(**self.info), file=sys.stderr)
175
176
176 # Construct report on disk
177 # Construct report on disk
177 report.write(self.make_report(traceback))
178 report.write(self.make_report(traceback))
178 report.close()
179 report.close()
179 raw_input("Hit <Enter> to quit (your terminal may close):")
180 input("Hit <Enter> to quit (your terminal may close):")
180
181
181 def make_report(self,traceback):
182 def make_report(self,traceback):
182 """Return a string containing a crash report."""
183 """Return a string containing a crash report."""
183
184
184 sec_sep = self.section_sep
185 sec_sep = self.section_sep
185
186
186 report = ['*'*75+'\n\n'+'IPython post-mortem report\n\n']
187 report = ['*'*75+'\n\n'+'IPython post-mortem report\n\n']
187 rpt_add = report.append
188 rpt_add = report.append
188 rpt_add(sys_info())
189 rpt_add(sys_info())
189
190
190 try:
191 try:
191 config = pformat(self.app.config)
192 config = pformat(self.app.config)
192 rpt_add(sec_sep)
193 rpt_add(sec_sep)
193 rpt_add('Application name: %s\n\n' % self.app_name)
194 rpt_add('Application name: %s\n\n' % self.app_name)
194 rpt_add('Current user configuration structure:\n\n')
195 rpt_add('Current user configuration structure:\n\n')
195 rpt_add(config)
196 rpt_add(config)
196 except:
197 except:
197 pass
198 pass
198 rpt_add(sec_sep+'Crash traceback:\n\n' + traceback)
199 rpt_add(sec_sep+'Crash traceback:\n\n' + traceback)
199
200
200 return ''.join(report)
201 return ''.join(report)
201
202
202
203
203 def crash_handler_lite(etype, evalue, tb):
204 def crash_handler_lite(etype, evalue, tb):
204 """a light excepthook, adding a small message to the usual traceback"""
205 """a light excepthook, adding a small message to the usual traceback"""
205 traceback.print_exception(etype, evalue, tb)
206 traceback.print_exception(etype, evalue, tb)
206
207
207 from IPython.core.interactiveshell import InteractiveShell
208 from IPython.core.interactiveshell import InteractiveShell
208 if InteractiveShell.initialized():
209 if InteractiveShell.initialized():
209 # we are in a Shell environment, give %magic example
210 # we are in a Shell environment, give %magic example
210 config = "%config "
211 config = "%config "
211 else:
212 else:
212 # we are not in a shell, show generic config
213 # we are not in a shell, show generic config
213 config = "c."
214 config = "c."
214 print(_lite_message_template.format(email=author_email, config=config), file=sys.stderr)
215 print(_lite_message_template.format(email=author_email, config=config), file=sys.stderr)
215
216
@@ -1,350 +1,350 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 Paging capabilities for IPython.core
3 Paging capabilities for IPython.core
4
4
5 Authors:
5 Authors:
6
6
7 * Brian Granger
7 * Brian Granger
8 * Fernando Perez
8 * Fernando Perez
9
9
10 Notes
10 Notes
11 -----
11 -----
12
12
13 For now this uses ipapi, so it can't be in IPython.utils. If we can get
13 For now this uses ipapi, so it can't be in IPython.utils. If we can get
14 rid of that dependency, we could move it there.
14 rid of that dependency, we could move it there.
15 -----
15 -----
16 """
16 """
17
17
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19 # Copyright (C) 2008-2011 The IPython Development Team
19 # Copyright (C) 2008-2011 The IPython Development Team
20 #
20 #
21 # Distributed under the terms of the BSD License. The full license is in
21 # Distributed under the terms of the BSD License. The full license is in
22 # the file COPYING, distributed as part of this software.
22 # the file COPYING, distributed as part of this software.
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24
24
25 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
26 # Imports
26 # Imports
27 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
28 from __future__ import print_function
28 from __future__ import print_function
29
29
30 import os
30 import os
31 import re
31 import re
32 import sys
32 import sys
33 import tempfile
33 import tempfile
34
34
35 from io import UnsupportedOperation
35 from io import UnsupportedOperation
36
36
37 from IPython import get_ipython
37 from IPython import get_ipython
38 from IPython.core.error import TryNext
38 from IPython.core.error import TryNext
39 from IPython.utils.data import chop
39 from IPython.utils.data import chop
40 from IPython.utils import io
40 from IPython.utils import io
41 from IPython.utils.process import system
41 from IPython.utils.process import system
42 from IPython.utils.terminal import get_terminal_size
42 from IPython.utils.terminal import get_terminal_size
43 from IPython.utils import py3compat
43 from IPython.utils import py3compat
44
44
45
45
46 #-----------------------------------------------------------------------------
46 #-----------------------------------------------------------------------------
47 # Classes and functions
47 # Classes and functions
48 #-----------------------------------------------------------------------------
48 #-----------------------------------------------------------------------------
49
49
50 esc_re = re.compile(r"(\x1b[^m]+m)")
50 esc_re = re.compile(r"(\x1b[^m]+m)")
51
51
52 def page_dumb(strng, start=0, screen_lines=25):
52 def page_dumb(strng, start=0, screen_lines=25):
53 """Very dumb 'pager' in Python, for when nothing else works.
53 """Very dumb 'pager' in Python, for when nothing else works.
54
54
55 Only moves forward, same interface as page(), except for pager_cmd and
55 Only moves forward, same interface as page(), except for pager_cmd and
56 mode."""
56 mode."""
57
57
58 out_ln = strng.splitlines()[start:]
58 out_ln = strng.splitlines()[start:]
59 screens = chop(out_ln,screen_lines-1)
59 screens = chop(out_ln,screen_lines-1)
60 if len(screens) == 1:
60 if len(screens) == 1:
61 print(os.linesep.join(screens[0]), file=io.stdout)
61 print(os.linesep.join(screens[0]), file=io.stdout)
62 else:
62 else:
63 last_escape = ""
63 last_escape = ""
64 for scr in screens[0:-1]:
64 for scr in screens[0:-1]:
65 hunk = os.linesep.join(scr)
65 hunk = os.linesep.join(scr)
66 print(last_escape + hunk, file=io.stdout)
66 print(last_escape + hunk, file=io.stdout)
67 if not page_more():
67 if not page_more():
68 return
68 return
69 esc_list = esc_re.findall(hunk)
69 esc_list = esc_re.findall(hunk)
70 if len(esc_list) > 0:
70 if len(esc_list) > 0:
71 last_escape = esc_list[-1]
71 last_escape = esc_list[-1]
72 print(last_escape + os.linesep.join(screens[-1]), file=io.stdout)
72 print(last_escape + os.linesep.join(screens[-1]), file=io.stdout)
73
73
74 def _detect_screen_size(screen_lines_def):
74 def _detect_screen_size(screen_lines_def):
75 """Attempt to work out the number of lines on the screen.
75 """Attempt to work out the number of lines on the screen.
76
76
77 This is called by page(). It can raise an error (e.g. when run in the
77 This is called by page(). It can raise an error (e.g. when run in the
78 test suite), so it's separated out so it can easily be called in a try block.
78 test suite), so it's separated out so it can easily be called in a try block.
79 """
79 """
80 TERM = os.environ.get('TERM',None)
80 TERM = os.environ.get('TERM',None)
81 if not((TERM=='xterm' or TERM=='xterm-color') and sys.platform != 'sunos5'):
81 if not((TERM=='xterm' or TERM=='xterm-color') and sys.platform != 'sunos5'):
82 # curses causes problems on many terminals other than xterm, and
82 # curses causes problems on many terminals other than xterm, and
83 # some termios calls lock up on Sun OS5.
83 # some termios calls lock up on Sun OS5.
84 return screen_lines_def
84 return screen_lines_def
85
85
86 try:
86 try:
87 import termios
87 import termios
88 import curses
88 import curses
89 except ImportError:
89 except ImportError:
90 return screen_lines_def
90 return screen_lines_def
91
91
92 # There is a bug in curses, where *sometimes* it fails to properly
92 # There is a bug in curses, where *sometimes* it fails to properly
93 # initialize, and then after the endwin() call is made, the
93 # initialize, and then after the endwin() call is made, the
94 # terminal is left in an unusable state. Rather than trying to
94 # terminal is left in an unusable state. Rather than trying to
95 # check everytime for this (by requesting and comparing termios
95 # check everytime for this (by requesting and comparing termios
96 # flags each time), we just save the initial terminal state and
96 # flags each time), we just save the initial terminal state and
97 # unconditionally reset it every time. It's cheaper than making
97 # unconditionally reset it every time. It's cheaper than making
98 # the checks.
98 # the checks.
99 term_flags = termios.tcgetattr(sys.stdout)
99 term_flags = termios.tcgetattr(sys.stdout)
100
100
101 # Curses modifies the stdout buffer size by default, which messes
101 # Curses modifies the stdout buffer size by default, which messes
102 # up Python's normal stdout buffering. This would manifest itself
102 # up Python's normal stdout buffering. This would manifest itself
103 # to IPython users as delayed printing on stdout after having used
103 # to IPython users as delayed printing on stdout after having used
104 # the pager.
104 # the pager.
105 #
105 #
106 # We can prevent this by manually setting the NCURSES_NO_SETBUF
106 # We can prevent this by manually setting the NCURSES_NO_SETBUF
107 # environment variable. For more details, see:
107 # environment variable. For more details, see:
108 # http://bugs.python.org/issue10144
108 # http://bugs.python.org/issue10144
109 NCURSES_NO_SETBUF = os.environ.get('NCURSES_NO_SETBUF', None)
109 NCURSES_NO_SETBUF = os.environ.get('NCURSES_NO_SETBUF', None)
110 os.environ['NCURSES_NO_SETBUF'] = ''
110 os.environ['NCURSES_NO_SETBUF'] = ''
111
111
112 # Proceed with curses initialization
112 # Proceed with curses initialization
113 try:
113 try:
114 scr = curses.initscr()
114 scr = curses.initscr()
115 except AttributeError:
115 except AttributeError:
116 # Curses on Solaris may not be complete, so we can't use it there
116 # Curses on Solaris may not be complete, so we can't use it there
117 return screen_lines_def
117 return screen_lines_def
118
118
119 screen_lines_real,screen_cols = scr.getmaxyx()
119 screen_lines_real,screen_cols = scr.getmaxyx()
120 curses.endwin()
120 curses.endwin()
121
121
122 # Restore environment
122 # Restore environment
123 if NCURSES_NO_SETBUF is None:
123 if NCURSES_NO_SETBUF is None:
124 del os.environ['NCURSES_NO_SETBUF']
124 del os.environ['NCURSES_NO_SETBUF']
125 else:
125 else:
126 os.environ['NCURSES_NO_SETBUF'] = NCURSES_NO_SETBUF
126 os.environ['NCURSES_NO_SETBUF'] = NCURSES_NO_SETBUF
127
127
128 # Restore terminal state in case endwin() didn't.
128 # Restore terminal state in case endwin() didn't.
129 termios.tcsetattr(sys.stdout,termios.TCSANOW,term_flags)
129 termios.tcsetattr(sys.stdout,termios.TCSANOW,term_flags)
130 # Now we have what we needed: the screen size in rows/columns
130 # Now we have what we needed: the screen size in rows/columns
131 return screen_lines_real
131 return screen_lines_real
132 #print '***Screen size:',screen_lines_real,'lines x',\
132 #print '***Screen size:',screen_lines_real,'lines x',\
133 #screen_cols,'columns.' # dbg
133 #screen_cols,'columns.' # dbg
134
134
135 def page(strng, start=0, screen_lines=0, pager_cmd=None):
135 def page(strng, start=0, screen_lines=0, pager_cmd=None):
136 """Print a string, piping through a pager after a certain length.
136 """Print a string, piping through a pager after a certain length.
137
137
138 The screen_lines parameter specifies the number of *usable* lines of your
138 The screen_lines parameter specifies the number of *usable* lines of your
139 terminal screen (total lines minus lines you need to reserve to show other
139 terminal screen (total lines minus lines you need to reserve to show other
140 information).
140 information).
141
141
142 If you set screen_lines to a number <=0, page() will try to auto-determine
142 If you set screen_lines to a number <=0, page() will try to auto-determine
143 your screen size and will only use up to (screen_size+screen_lines) for
143 your screen size and will only use up to (screen_size+screen_lines) for
144 printing, paging after that. That is, if you want auto-detection but need
144 printing, paging after that. That is, if you want auto-detection but need
145 to reserve the bottom 3 lines of the screen, use screen_lines = -3, and for
145 to reserve the bottom 3 lines of the screen, use screen_lines = -3, and for
146 auto-detection without any lines reserved simply use screen_lines = 0.
146 auto-detection without any lines reserved simply use screen_lines = 0.
147
147
148 If a string won't fit in the allowed lines, it is sent through the
148 If a string won't fit in the allowed lines, it is sent through the
149 specified pager command. If none given, look for PAGER in the environment,
149 specified pager command. If none given, look for PAGER in the environment,
150 and ultimately default to less.
150 and ultimately default to less.
151
151
152 If no system pager works, the string is sent through a 'dumb pager'
152 If no system pager works, the string is sent through a 'dumb pager'
153 written in python, very simplistic.
153 written in python, very simplistic.
154 """
154 """
155
155
156 # Some routines may auto-compute start offsets incorrectly and pass a
156 # Some routines may auto-compute start offsets incorrectly and pass a
157 # negative value. Offset to 0 for robustness.
157 # negative value. Offset to 0 for robustness.
158 start = max(0, start)
158 start = max(0, start)
159
159
160 # first, try the hook
160 # first, try the hook
161 ip = get_ipython()
161 ip = get_ipython()
162 if ip:
162 if ip:
163 try:
163 try:
164 ip.hooks.show_in_pager(strng)
164 ip.hooks.show_in_pager(strng)
165 return
165 return
166 except TryNext:
166 except TryNext:
167 pass
167 pass
168
168
169 # Ugly kludge, but calling curses.initscr() flat out crashes in emacs
169 # Ugly kludge, but calling curses.initscr() flat out crashes in emacs
170 TERM = os.environ.get('TERM','dumb')
170 TERM = os.environ.get('TERM','dumb')
171 if TERM in ['dumb','emacs'] and os.name != 'nt':
171 if TERM in ['dumb','emacs'] and os.name != 'nt':
172 print(strng)
172 print(strng)
173 return
173 return
174 # chop off the topmost part of the string we don't want to see
174 # chop off the topmost part of the string we don't want to see
175 str_lines = strng.splitlines()[start:]
175 str_lines = strng.splitlines()[start:]
176 str_toprint = os.linesep.join(str_lines)
176 str_toprint = os.linesep.join(str_lines)
177 num_newlines = len(str_lines)
177 num_newlines = len(str_lines)
178 len_str = len(str_toprint)
178 len_str = len(str_toprint)
179
179
180 # Dumb heuristics to guesstimate number of on-screen lines the string
180 # Dumb heuristics to guesstimate number of on-screen lines the string
181 # takes. Very basic, but good enough for docstrings in reasonable
181 # takes. Very basic, but good enough for docstrings in reasonable
182 # terminals. If someone later feels like refining it, it's not hard.
182 # terminals. If someone later feels like refining it, it's not hard.
183 numlines = max(num_newlines,int(len_str/80)+1)
183 numlines = max(num_newlines,int(len_str/80)+1)
184
184
185 screen_lines_def = get_terminal_size()[1]
185 screen_lines_def = get_terminal_size()[1]
186
186
187 # auto-determine screen size
187 # auto-determine screen size
188 if screen_lines <= 0:
188 if screen_lines <= 0:
189 try:
189 try:
190 screen_lines += _detect_screen_size(screen_lines_def)
190 screen_lines += _detect_screen_size(screen_lines_def)
191 except (TypeError, UnsupportedOperation):
191 except (TypeError, UnsupportedOperation):
192 print(str_toprint, file=io.stdout)
192 print(str_toprint, file=io.stdout)
193 return
193 return
194
194
195 #print 'numlines',numlines,'screenlines',screen_lines # dbg
195 #print 'numlines',numlines,'screenlines',screen_lines # dbg
196 if numlines <= screen_lines :
196 if numlines <= screen_lines :
197 #print '*** normal print' # dbg
197 #print '*** normal print' # dbg
198 print(str_toprint, file=io.stdout)
198 print(str_toprint, file=io.stdout)
199 else:
199 else:
200 # Try to open pager and default to internal one if that fails.
200 # Try to open pager and default to internal one if that fails.
201 # All failure modes are tagged as 'retval=1', to match the return
201 # All failure modes are tagged as 'retval=1', to match the return
202 # value of a failed system command. If any intermediate attempt
202 # value of a failed system command. If any intermediate attempt
203 # sets retval to 1, at the end we resort to our own page_dumb() pager.
203 # sets retval to 1, at the end we resort to our own page_dumb() pager.
204 pager_cmd = get_pager_cmd(pager_cmd)
204 pager_cmd = get_pager_cmd(pager_cmd)
205 pager_cmd += ' ' + get_pager_start(pager_cmd,start)
205 pager_cmd += ' ' + get_pager_start(pager_cmd,start)
206 if os.name == 'nt':
206 if os.name == 'nt':
207 if pager_cmd.startswith('type'):
207 if pager_cmd.startswith('type'):
208 # The default WinXP 'type' command is failing on complex strings.
208 # The default WinXP 'type' command is failing on complex strings.
209 retval = 1
209 retval = 1
210 else:
210 else:
211 tmpname = tempfile.mktemp('.txt')
211 tmpname = tempfile.mktemp('.txt')
212 tmpfile = open(tmpname,'wt')
212 tmpfile = open(tmpname,'wt')
213 tmpfile.write(strng)
213 tmpfile.write(strng)
214 tmpfile.close()
214 tmpfile.close()
215 cmd = "%s < %s" % (pager_cmd,tmpname)
215 cmd = "%s < %s" % (pager_cmd,tmpname)
216 if os.system(cmd):
216 if os.system(cmd):
217 retval = 1
217 retval = 1
218 else:
218 else:
219 retval = None
219 retval = None
220 os.remove(tmpname)
220 os.remove(tmpname)
221 else:
221 else:
222 try:
222 try:
223 retval = None
223 retval = None
224 # if I use popen4, things hang. No idea why.
224 # if I use popen4, things hang. No idea why.
225 #pager,shell_out = os.popen4(pager_cmd)
225 #pager,shell_out = os.popen4(pager_cmd)
226 pager = os.popen(pager_cmd, 'w')
226 pager = os.popen(pager_cmd, 'w')
227 try:
227 try:
228 pager_encoding = pager.encoding or sys.stdout.encoding
228 pager_encoding = pager.encoding or sys.stdout.encoding
229 pager.write(py3compat.cast_bytes_py2(
229 pager.write(py3compat.cast_bytes_py2(
230 strng, encoding=pager_encoding))
230 strng, encoding=pager_encoding))
231 finally:
231 finally:
232 retval = pager.close()
232 retval = pager.close()
233 except IOError as msg: # broken pipe when user quits
233 except IOError as msg: # broken pipe when user quits
234 if msg.args == (32, 'Broken pipe'):
234 if msg.args == (32, 'Broken pipe'):
235 retval = None
235 retval = None
236 else:
236 else:
237 retval = 1
237 retval = 1
238 except OSError:
238 except OSError:
239 # Other strange problems, sometimes seen in Win2k/cygwin
239 # Other strange problems, sometimes seen in Win2k/cygwin
240 retval = 1
240 retval = 1
241 if retval is not None:
241 if retval is not None:
242 page_dumb(strng,screen_lines=screen_lines)
242 page_dumb(strng,screen_lines=screen_lines)
243
243
244
244
245 def page_file(fname, start=0, pager_cmd=None):
245 def page_file(fname, start=0, pager_cmd=None):
246 """Page a file, using an optional pager command and starting line.
246 """Page a file, using an optional pager command and starting line.
247 """
247 """
248
248
249 pager_cmd = get_pager_cmd(pager_cmd)
249 pager_cmd = get_pager_cmd(pager_cmd)
250 pager_cmd += ' ' + get_pager_start(pager_cmd,start)
250 pager_cmd += ' ' + get_pager_start(pager_cmd,start)
251
251
252 try:
252 try:
253 if os.environ['TERM'] in ['emacs','dumb']:
253 if os.environ['TERM'] in ['emacs','dumb']:
254 raise EnvironmentError
254 raise EnvironmentError
255 system(pager_cmd + ' ' + fname)
255 system(pager_cmd + ' ' + fname)
256 except:
256 except:
257 try:
257 try:
258 if start > 0:
258 if start > 0:
259 start -= 1
259 start -= 1
260 page(open(fname).read(),start)
260 page(open(fname).read(),start)
261 except:
261 except:
262 print('Unable to show file',repr(fname))
262 print('Unable to show file',repr(fname))
263
263
264
264
265 def get_pager_cmd(pager_cmd=None):
265 def get_pager_cmd(pager_cmd=None):
266 """Return a pager command.
266 """Return a pager command.
267
267
268 Makes some attempts at finding an OS-correct one.
268 Makes some attempts at finding an OS-correct one.
269 """
269 """
270 if os.name == 'posix':
270 if os.name == 'posix':
271 default_pager_cmd = 'less -r' # -r for color control sequences
271 default_pager_cmd = 'less -r' # -r for color control sequences
272 elif os.name in ['nt','dos']:
272 elif os.name in ['nt','dos']:
273 default_pager_cmd = 'type'
273 default_pager_cmd = 'type'
274
274
275 if pager_cmd is None:
275 if pager_cmd is None:
276 try:
276 try:
277 pager_cmd = os.environ['PAGER']
277 pager_cmd = os.environ['PAGER']
278 except:
278 except:
279 pager_cmd = default_pager_cmd
279 pager_cmd = default_pager_cmd
280 return pager_cmd
280 return pager_cmd
281
281
282
282
283 def get_pager_start(pager, start):
283 def get_pager_start(pager, start):
284 """Return the string for paging files with an offset.
284 """Return the string for paging files with an offset.
285
285
286 This is the '+N' argument which less and more (under Unix) accept.
286 This is the '+N' argument which less and more (under Unix) accept.
287 """
287 """
288
288
289 if pager in ['less','more']:
289 if pager in ['less','more']:
290 if start:
290 if start:
291 start_string = '+' + str(start)
291 start_string = '+' + str(start)
292 else:
292 else:
293 start_string = ''
293 start_string = ''
294 else:
294 else:
295 start_string = ''
295 start_string = ''
296 return start_string
296 return start_string
297
297
298
298
299 # (X)emacs on win32 doesn't like to be bypassed with msvcrt.getch()
299 # (X)emacs on win32 doesn't like to be bypassed with msvcrt.getch()
300 if os.name == 'nt' and os.environ.get('TERM','dumb') != 'emacs':
300 if os.name == 'nt' and os.environ.get('TERM','dumb') != 'emacs':
301 import msvcrt
301 import msvcrt
302 def page_more():
302 def page_more():
303 """ Smart pausing between pages
303 """ Smart pausing between pages
304
304
305 @return: True if need print more lines, False if quit
305 @return: True if need print more lines, False if quit
306 """
306 """
307 io.stdout.write('---Return to continue, q to quit--- ')
307 io.stdout.write('---Return to continue, q to quit--- ')
308 ans = msvcrt.getwch()
308 ans = msvcrt.getwch()
309 if ans in ("q", "Q"):
309 if ans in ("q", "Q"):
310 result = False
310 result = False
311 else:
311 else:
312 result = True
312 result = True
313 io.stdout.write("\b"*37 + " "*37 + "\b"*37)
313 io.stdout.write("\b"*37 + " "*37 + "\b"*37)
314 return result
314 return result
315 else:
315 else:
316 def page_more():
316 def page_more():
317 ans = raw_input('---Return to continue, q to quit--- ')
317 ans = py3compat.input('---Return to continue, q to quit--- ')
318 if ans.lower().startswith('q'):
318 if ans.lower().startswith('q'):
319 return False
319 return False
320 else:
320 else:
321 return True
321 return True
322
322
323
323
324 def snip_print(str,width = 75,print_full = 0,header = ''):
324 def snip_print(str,width = 75,print_full = 0,header = ''):
325 """Print a string snipping the midsection to fit in width.
325 """Print a string snipping the midsection to fit in width.
326
326
327 print_full: mode control:
327 print_full: mode control:
328
328
329 - 0: only snip long strings
329 - 0: only snip long strings
330 - 1: send to page() directly.
330 - 1: send to page() directly.
331 - 2: snip long strings and ask for full length viewing with page()
331 - 2: snip long strings and ask for full length viewing with page()
332
332
333 Return 1 if snipping was necessary, 0 otherwise."""
333 Return 1 if snipping was necessary, 0 otherwise."""
334
334
335 if print_full == 1:
335 if print_full == 1:
336 page(header+str)
336 page(header+str)
337 return 0
337 return 0
338
338
339 print(header, end=' ')
339 print(header, end=' ')
340 if len(str) < width:
340 if len(str) < width:
341 print(str)
341 print(str)
342 snip = 0
342 snip = 0
343 else:
343 else:
344 whalf = int((width -5)/2)
344 whalf = int((width -5)/2)
345 print(str[:whalf] + ' <...> ' + str[-whalf:])
345 print(str[:whalf] + ' <...> ' + str[-whalf:])
346 snip = 1
346 snip = 1
347 if snip and print_full == 2:
347 if snip and print_full == 2:
348 if raw_input(header+' Snipped. View (y/n)? [N]').lower() == 'y':
348 if py3compat.input(header+' Snipped. View (y/n)? [N]').lower() == 'y':
349 page(str)
349 page(str)
350 return snip
350 return snip
@@ -1,584 +1,584 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Tests for the inputsplitter module.
2 """Tests for the inputsplitter module.
3
3
4 Authors
4 Authors
5 -------
5 -------
6 * Fernando Perez
6 * Fernando Perez
7 * Robert Kern
7 * Robert Kern
8 """
8 """
9 from __future__ import print_function
9 from __future__ import print_function
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11 # Copyright (C) 2010-2011 The IPython Development Team
11 # Copyright (C) 2010-2011 The IPython Development Team
12 #
12 #
13 # Distributed under the terms of the BSD License. The full license is in
13 # Distributed under the terms of the BSD License. The full license is in
14 # the file COPYING, distributed as part of this software.
14 # the file COPYING, distributed as part of this software.
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16
16
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18 # Imports
18 # Imports
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20 # stdlib
20 # stdlib
21 import unittest
21 import unittest
22 import sys
22 import sys
23
23
24 # Third party
24 # Third party
25 import nose.tools as nt
25 import nose.tools as nt
26
26
27 # Our own
27 # Our own
28 from IPython.core import inputsplitter as isp
28 from IPython.core import inputsplitter as isp
29 from IPython.core.tests.test_inputtransformer import syntax, syntax_ml
29 from IPython.core.tests.test_inputtransformer import syntax, syntax_ml
30 from IPython.testing import tools as tt
30 from IPython.testing import tools as tt
31 from IPython.utils import py3compat
31 from IPython.utils import py3compat
32 from IPython.utils.py3compat import string_types
32 from IPython.utils.py3compat import string_types, input
33
33
34 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
35 # Semi-complete examples (also used as tests)
35 # Semi-complete examples (also used as tests)
36 #-----------------------------------------------------------------------------
36 #-----------------------------------------------------------------------------
37
37
38 # Note: at the bottom, there's a slightly more complete version of this that
38 # Note: at the bottom, there's a slightly more complete version of this that
39 # can be useful during development of code here.
39 # can be useful during development of code here.
40
40
41 def mini_interactive_loop(input_func):
41 def mini_interactive_loop(input_func):
42 """Minimal example of the logic of an interactive interpreter loop.
42 """Minimal example of the logic of an interactive interpreter loop.
43
43
44 This serves as an example, and it is used by the test system with a fake
44 This serves as an example, and it is used by the test system with a fake
45 raw_input that simulates interactive input."""
45 raw_input that simulates interactive input."""
46
46
47 from IPython.core.inputsplitter import InputSplitter
47 from IPython.core.inputsplitter import InputSplitter
48
48
49 isp = InputSplitter()
49 isp = InputSplitter()
50 # In practice, this input loop would be wrapped in an outside loop to read
50 # In practice, this input loop would be wrapped in an outside loop to read
51 # input indefinitely, until some exit/quit command was issued. Here we
51 # input indefinitely, until some exit/quit command was issued. Here we
52 # only illustrate the basic inner loop.
52 # only illustrate the basic inner loop.
53 while isp.push_accepts_more():
53 while isp.push_accepts_more():
54 indent = ' '*isp.indent_spaces
54 indent = ' '*isp.indent_spaces
55 prompt = '>>> ' + indent
55 prompt = '>>> ' + indent
56 line = indent + input_func(prompt)
56 line = indent + input_func(prompt)
57 isp.push(line)
57 isp.push(line)
58
58
59 # Here we just return input so we can use it in a test suite, but a real
59 # Here we just return input so we can use it in a test suite, but a real
60 # interpreter would instead send it for execution somewhere.
60 # interpreter would instead send it for execution somewhere.
61 src = isp.source_reset()
61 src = isp.source_reset()
62 #print 'Input source was:\n', src # dbg
62 #print 'Input source was:\n', src # dbg
63 return src
63 return src
64
64
65 #-----------------------------------------------------------------------------
65 #-----------------------------------------------------------------------------
66 # Test utilities, just for local use
66 # Test utilities, just for local use
67 #-----------------------------------------------------------------------------
67 #-----------------------------------------------------------------------------
68
68
69 def assemble(block):
69 def assemble(block):
70 """Assemble a block into multi-line sub-blocks."""
70 """Assemble a block into multi-line sub-blocks."""
71 return ['\n'.join(sub_block)+'\n' for sub_block in block]
71 return ['\n'.join(sub_block)+'\n' for sub_block in block]
72
72
73
73
74 def pseudo_input(lines):
74 def pseudo_input(lines):
75 """Return a function that acts like raw_input but feeds the input list."""
75 """Return a function that acts like raw_input but feeds the input list."""
76 ilines = iter(lines)
76 ilines = iter(lines)
77 def raw_in(prompt):
77 def raw_in(prompt):
78 try:
78 try:
79 return next(ilines)
79 return next(ilines)
80 except StopIteration:
80 except StopIteration:
81 return ''
81 return ''
82 return raw_in
82 return raw_in
83
83
84 #-----------------------------------------------------------------------------
84 #-----------------------------------------------------------------------------
85 # Tests
85 # Tests
86 #-----------------------------------------------------------------------------
86 #-----------------------------------------------------------------------------
87 def test_spaces():
87 def test_spaces():
88 tests = [('', 0),
88 tests = [('', 0),
89 (' ', 1),
89 (' ', 1),
90 ('\n', 0),
90 ('\n', 0),
91 (' \n', 1),
91 (' \n', 1),
92 ('x', 0),
92 ('x', 0),
93 (' x', 1),
93 (' x', 1),
94 (' x',2),
94 (' x',2),
95 (' x',4),
95 (' x',4),
96 # Note: tabs are counted as a single whitespace!
96 # Note: tabs are counted as a single whitespace!
97 ('\tx', 1),
97 ('\tx', 1),
98 ('\t x', 2),
98 ('\t x', 2),
99 ]
99 ]
100 tt.check_pairs(isp.num_ini_spaces, tests)
100 tt.check_pairs(isp.num_ini_spaces, tests)
101
101
102
102
103 def test_remove_comments():
103 def test_remove_comments():
104 tests = [('text', 'text'),
104 tests = [('text', 'text'),
105 ('text # comment', 'text '),
105 ('text # comment', 'text '),
106 ('text # comment\n', 'text \n'),
106 ('text # comment\n', 'text \n'),
107 ('text # comment \n', 'text \n'),
107 ('text # comment \n', 'text \n'),
108 ('line # c \nline\n','line \nline\n'),
108 ('line # c \nline\n','line \nline\n'),
109 ('line # c \nline#c2 \nline\nline #c\n\n',
109 ('line # c \nline#c2 \nline\nline #c\n\n',
110 'line \nline\nline\nline \n\n'),
110 'line \nline\nline\nline \n\n'),
111 ]
111 ]
112 tt.check_pairs(isp.remove_comments, tests)
112 tt.check_pairs(isp.remove_comments, tests)
113
113
114
114
115 def test_get_input_encoding():
115 def test_get_input_encoding():
116 encoding = isp.get_input_encoding()
116 encoding = isp.get_input_encoding()
117 nt.assert_true(isinstance(encoding, string_types))
117 nt.assert_true(isinstance(encoding, string_types))
118 # simple-minded check that at least encoding a simple string works with the
118 # simple-minded check that at least encoding a simple string works with the
119 # encoding we got.
119 # encoding we got.
120 nt.assert_equal(u'test'.encode(encoding), b'test')
120 nt.assert_equal(u'test'.encode(encoding), b'test')
121
121
122
122
123 class NoInputEncodingTestCase(unittest.TestCase):
123 class NoInputEncodingTestCase(unittest.TestCase):
124 def setUp(self):
124 def setUp(self):
125 self.old_stdin = sys.stdin
125 self.old_stdin = sys.stdin
126 class X: pass
126 class X: pass
127 fake_stdin = X()
127 fake_stdin = X()
128 sys.stdin = fake_stdin
128 sys.stdin = fake_stdin
129
129
130 def test(self):
130 def test(self):
131 # Verify that if sys.stdin has no 'encoding' attribute we do the right
131 # Verify that if sys.stdin has no 'encoding' attribute we do the right
132 # thing
132 # thing
133 enc = isp.get_input_encoding()
133 enc = isp.get_input_encoding()
134 self.assertEqual(enc, 'ascii')
134 self.assertEqual(enc, 'ascii')
135
135
136 def tearDown(self):
136 def tearDown(self):
137 sys.stdin = self.old_stdin
137 sys.stdin = self.old_stdin
138
138
139
139
140 class InputSplitterTestCase(unittest.TestCase):
140 class InputSplitterTestCase(unittest.TestCase):
141 def setUp(self):
141 def setUp(self):
142 self.isp = isp.InputSplitter()
142 self.isp = isp.InputSplitter()
143
143
144 def test_reset(self):
144 def test_reset(self):
145 isp = self.isp
145 isp = self.isp
146 isp.push('x=1')
146 isp.push('x=1')
147 isp.reset()
147 isp.reset()
148 self.assertEqual(isp._buffer, [])
148 self.assertEqual(isp._buffer, [])
149 self.assertEqual(isp.indent_spaces, 0)
149 self.assertEqual(isp.indent_spaces, 0)
150 self.assertEqual(isp.source, '')
150 self.assertEqual(isp.source, '')
151 self.assertEqual(isp.code, None)
151 self.assertEqual(isp.code, None)
152 self.assertEqual(isp._is_complete, False)
152 self.assertEqual(isp._is_complete, False)
153
153
154 def test_source(self):
154 def test_source(self):
155 self.isp._store('1')
155 self.isp._store('1')
156 self.isp._store('2')
156 self.isp._store('2')
157 self.assertEqual(self.isp.source, '1\n2\n')
157 self.assertEqual(self.isp.source, '1\n2\n')
158 self.assertTrue(len(self.isp._buffer)>0)
158 self.assertTrue(len(self.isp._buffer)>0)
159 self.assertEqual(self.isp.source_reset(), '1\n2\n')
159 self.assertEqual(self.isp.source_reset(), '1\n2\n')
160 self.assertEqual(self.isp._buffer, [])
160 self.assertEqual(self.isp._buffer, [])
161 self.assertEqual(self.isp.source, '')
161 self.assertEqual(self.isp.source, '')
162
162
163 def test_indent(self):
163 def test_indent(self):
164 isp = self.isp # shorthand
164 isp = self.isp # shorthand
165 isp.push('x=1')
165 isp.push('x=1')
166 self.assertEqual(isp.indent_spaces, 0)
166 self.assertEqual(isp.indent_spaces, 0)
167 isp.push('if 1:\n x=1')
167 isp.push('if 1:\n x=1')
168 self.assertEqual(isp.indent_spaces, 4)
168 self.assertEqual(isp.indent_spaces, 4)
169 isp.push('y=2\n')
169 isp.push('y=2\n')
170 self.assertEqual(isp.indent_spaces, 0)
170 self.assertEqual(isp.indent_spaces, 0)
171
171
172 def test_indent2(self):
172 def test_indent2(self):
173 isp = self.isp
173 isp = self.isp
174 isp.push('if 1:')
174 isp.push('if 1:')
175 self.assertEqual(isp.indent_spaces, 4)
175 self.assertEqual(isp.indent_spaces, 4)
176 isp.push(' x=1')
176 isp.push(' x=1')
177 self.assertEqual(isp.indent_spaces, 4)
177 self.assertEqual(isp.indent_spaces, 4)
178 # Blank lines shouldn't change the indent level
178 # Blank lines shouldn't change the indent level
179 isp.push(' '*2)
179 isp.push(' '*2)
180 self.assertEqual(isp.indent_spaces, 4)
180 self.assertEqual(isp.indent_spaces, 4)
181
181
182 def test_indent3(self):
182 def test_indent3(self):
183 isp = self.isp
183 isp = self.isp
184 # When a multiline statement contains parens or multiline strings, we
184 # When a multiline statement contains parens or multiline strings, we
185 # shouldn't get confused.
185 # shouldn't get confused.
186 isp.push("if 1:")
186 isp.push("if 1:")
187 isp.push(" x = (1+\n 2)")
187 isp.push(" x = (1+\n 2)")
188 self.assertEqual(isp.indent_spaces, 4)
188 self.assertEqual(isp.indent_spaces, 4)
189
189
190 def test_indent4(self):
190 def test_indent4(self):
191 isp = self.isp
191 isp = self.isp
192 # whitespace after ':' should not screw up indent level
192 # whitespace after ':' should not screw up indent level
193 isp.push('if 1: \n x=1')
193 isp.push('if 1: \n x=1')
194 self.assertEqual(isp.indent_spaces, 4)
194 self.assertEqual(isp.indent_spaces, 4)
195 isp.push('y=2\n')
195 isp.push('y=2\n')
196 self.assertEqual(isp.indent_spaces, 0)
196 self.assertEqual(isp.indent_spaces, 0)
197 isp.push('if 1:\t\n x=1')
197 isp.push('if 1:\t\n x=1')
198 self.assertEqual(isp.indent_spaces, 4)
198 self.assertEqual(isp.indent_spaces, 4)
199 isp.push('y=2\n')
199 isp.push('y=2\n')
200 self.assertEqual(isp.indent_spaces, 0)
200 self.assertEqual(isp.indent_spaces, 0)
201
201
202 def test_dedent_pass(self):
202 def test_dedent_pass(self):
203 isp = self.isp # shorthand
203 isp = self.isp # shorthand
204 # should NOT cause dedent
204 # should NOT cause dedent
205 isp.push('if 1:\n passes = 5')
205 isp.push('if 1:\n passes = 5')
206 self.assertEqual(isp.indent_spaces, 4)
206 self.assertEqual(isp.indent_spaces, 4)
207 isp.push('if 1:\n pass')
207 isp.push('if 1:\n pass')
208 self.assertEqual(isp.indent_spaces, 0)
208 self.assertEqual(isp.indent_spaces, 0)
209 isp.push('if 1:\n pass ')
209 isp.push('if 1:\n pass ')
210 self.assertEqual(isp.indent_spaces, 0)
210 self.assertEqual(isp.indent_spaces, 0)
211
211
212 def test_dedent_break(self):
212 def test_dedent_break(self):
213 isp = self.isp # shorthand
213 isp = self.isp # shorthand
214 # should NOT cause dedent
214 # should NOT cause dedent
215 isp.push('while 1:\n breaks = 5')
215 isp.push('while 1:\n breaks = 5')
216 self.assertEqual(isp.indent_spaces, 4)
216 self.assertEqual(isp.indent_spaces, 4)
217 isp.push('while 1:\n break')
217 isp.push('while 1:\n break')
218 self.assertEqual(isp.indent_spaces, 0)
218 self.assertEqual(isp.indent_spaces, 0)
219 isp.push('while 1:\n break ')
219 isp.push('while 1:\n break ')
220 self.assertEqual(isp.indent_spaces, 0)
220 self.assertEqual(isp.indent_spaces, 0)
221
221
222 def test_dedent_continue(self):
222 def test_dedent_continue(self):
223 isp = self.isp # shorthand
223 isp = self.isp # shorthand
224 # should NOT cause dedent
224 # should NOT cause dedent
225 isp.push('while 1:\n continues = 5')
225 isp.push('while 1:\n continues = 5')
226 self.assertEqual(isp.indent_spaces, 4)
226 self.assertEqual(isp.indent_spaces, 4)
227 isp.push('while 1:\n continue')
227 isp.push('while 1:\n continue')
228 self.assertEqual(isp.indent_spaces, 0)
228 self.assertEqual(isp.indent_spaces, 0)
229 isp.push('while 1:\n continue ')
229 isp.push('while 1:\n continue ')
230 self.assertEqual(isp.indent_spaces, 0)
230 self.assertEqual(isp.indent_spaces, 0)
231
231
232 def test_dedent_raise(self):
232 def test_dedent_raise(self):
233 isp = self.isp # shorthand
233 isp = self.isp # shorthand
234 # should NOT cause dedent
234 # should NOT cause dedent
235 isp.push('if 1:\n raised = 4')
235 isp.push('if 1:\n raised = 4')
236 self.assertEqual(isp.indent_spaces, 4)
236 self.assertEqual(isp.indent_spaces, 4)
237 isp.push('if 1:\n raise TypeError()')
237 isp.push('if 1:\n raise TypeError()')
238 self.assertEqual(isp.indent_spaces, 0)
238 self.assertEqual(isp.indent_spaces, 0)
239 isp.push('if 1:\n raise')
239 isp.push('if 1:\n raise')
240 self.assertEqual(isp.indent_spaces, 0)
240 self.assertEqual(isp.indent_spaces, 0)
241 isp.push('if 1:\n raise ')
241 isp.push('if 1:\n raise ')
242 self.assertEqual(isp.indent_spaces, 0)
242 self.assertEqual(isp.indent_spaces, 0)
243
243
244 def test_dedent_return(self):
244 def test_dedent_return(self):
245 isp = self.isp # shorthand
245 isp = self.isp # shorthand
246 # should NOT cause dedent
246 # should NOT cause dedent
247 isp.push('if 1:\n returning = 4')
247 isp.push('if 1:\n returning = 4')
248 self.assertEqual(isp.indent_spaces, 4)
248 self.assertEqual(isp.indent_spaces, 4)
249 isp.push('if 1:\n return 5 + 493')
249 isp.push('if 1:\n return 5 + 493')
250 self.assertEqual(isp.indent_spaces, 0)
250 self.assertEqual(isp.indent_spaces, 0)
251 isp.push('if 1:\n return')
251 isp.push('if 1:\n return')
252 self.assertEqual(isp.indent_spaces, 0)
252 self.assertEqual(isp.indent_spaces, 0)
253 isp.push('if 1:\n return ')
253 isp.push('if 1:\n return ')
254 self.assertEqual(isp.indent_spaces, 0)
254 self.assertEqual(isp.indent_spaces, 0)
255 isp.push('if 1:\n return(0)')
255 isp.push('if 1:\n return(0)')
256 self.assertEqual(isp.indent_spaces, 0)
256 self.assertEqual(isp.indent_spaces, 0)
257
257
258 def test_push(self):
258 def test_push(self):
259 isp = self.isp
259 isp = self.isp
260 self.assertTrue(isp.push('x=1'))
260 self.assertTrue(isp.push('x=1'))
261
261
262 def test_push2(self):
262 def test_push2(self):
263 isp = self.isp
263 isp = self.isp
264 self.assertFalse(isp.push('if 1:'))
264 self.assertFalse(isp.push('if 1:'))
265 for line in [' x=1', '# a comment', ' y=2']:
265 for line in [' x=1', '# a comment', ' y=2']:
266 print(line)
266 print(line)
267 self.assertTrue(isp.push(line))
267 self.assertTrue(isp.push(line))
268
268
269 def test_push3(self):
269 def test_push3(self):
270 isp = self.isp
270 isp = self.isp
271 isp.push('if True:')
271 isp.push('if True:')
272 isp.push(' a = 1')
272 isp.push(' a = 1')
273 self.assertFalse(isp.push('b = [1,'))
273 self.assertFalse(isp.push('b = [1,'))
274
274
275 def test_push_accepts_more(self):
275 def test_push_accepts_more(self):
276 isp = self.isp
276 isp = self.isp
277 isp.push('x=1')
277 isp.push('x=1')
278 self.assertFalse(isp.push_accepts_more())
278 self.assertFalse(isp.push_accepts_more())
279
279
280 def test_push_accepts_more2(self):
280 def test_push_accepts_more2(self):
281 isp = self.isp
281 isp = self.isp
282 isp.push('if 1:')
282 isp.push('if 1:')
283 self.assertTrue(isp.push_accepts_more())
283 self.assertTrue(isp.push_accepts_more())
284 isp.push(' x=1')
284 isp.push(' x=1')
285 self.assertTrue(isp.push_accepts_more())
285 self.assertTrue(isp.push_accepts_more())
286 isp.push('')
286 isp.push('')
287 self.assertFalse(isp.push_accepts_more())
287 self.assertFalse(isp.push_accepts_more())
288
288
289 def test_push_accepts_more3(self):
289 def test_push_accepts_more3(self):
290 isp = self.isp
290 isp = self.isp
291 isp.push("x = (2+\n3)")
291 isp.push("x = (2+\n3)")
292 self.assertFalse(isp.push_accepts_more())
292 self.assertFalse(isp.push_accepts_more())
293
293
294 def test_push_accepts_more4(self):
294 def test_push_accepts_more4(self):
295 isp = self.isp
295 isp = self.isp
296 # When a multiline statement contains parens or multiline strings, we
296 # When a multiline statement contains parens or multiline strings, we
297 # shouldn't get confused.
297 # shouldn't get confused.
298 # FIXME: we should be able to better handle de-dents in statements like
298 # FIXME: we should be able to better handle de-dents in statements like
299 # multiline strings and multiline expressions (continued with \ or
299 # multiline strings and multiline expressions (continued with \ or
300 # parens). Right now we aren't handling the indentation tracking quite
300 # parens). Right now we aren't handling the indentation tracking quite
301 # correctly with this, though in practice it may not be too much of a
301 # correctly with this, though in practice it may not be too much of a
302 # problem. We'll need to see.
302 # problem. We'll need to see.
303 isp.push("if 1:")
303 isp.push("if 1:")
304 isp.push(" x = (2+")
304 isp.push(" x = (2+")
305 isp.push(" 3)")
305 isp.push(" 3)")
306 self.assertTrue(isp.push_accepts_more())
306 self.assertTrue(isp.push_accepts_more())
307 isp.push(" y = 3")
307 isp.push(" y = 3")
308 self.assertTrue(isp.push_accepts_more())
308 self.assertTrue(isp.push_accepts_more())
309 isp.push('')
309 isp.push('')
310 self.assertFalse(isp.push_accepts_more())
310 self.assertFalse(isp.push_accepts_more())
311
311
312 def test_push_accepts_more5(self):
312 def test_push_accepts_more5(self):
313 isp = self.isp
313 isp = self.isp
314 isp.push('try:')
314 isp.push('try:')
315 isp.push(' a = 5')
315 isp.push(' a = 5')
316 isp.push('except:')
316 isp.push('except:')
317 isp.push(' raise')
317 isp.push(' raise')
318 # We want to be able to add an else: block at this point, so it should
318 # We want to be able to add an else: block at this point, so it should
319 # wait for a blank line.
319 # wait for a blank line.
320 self.assertTrue(isp.push_accepts_more())
320 self.assertTrue(isp.push_accepts_more())
321
321
322 def test_continuation(self):
322 def test_continuation(self):
323 isp = self.isp
323 isp = self.isp
324 isp.push("import os, \\")
324 isp.push("import os, \\")
325 self.assertTrue(isp.push_accepts_more())
325 self.assertTrue(isp.push_accepts_more())
326 isp.push("sys")
326 isp.push("sys")
327 self.assertFalse(isp.push_accepts_more())
327 self.assertFalse(isp.push_accepts_more())
328
328
329 def test_syntax_error(self):
329 def test_syntax_error(self):
330 isp = self.isp
330 isp = self.isp
331 # Syntax errors immediately produce a 'ready' block, so the invalid
331 # Syntax errors immediately produce a 'ready' block, so the invalid
332 # Python can be sent to the kernel for evaluation with possible ipython
332 # Python can be sent to the kernel for evaluation with possible ipython
333 # special-syntax conversion.
333 # special-syntax conversion.
334 isp.push('run foo')
334 isp.push('run foo')
335 self.assertFalse(isp.push_accepts_more())
335 self.assertFalse(isp.push_accepts_more())
336
336
337 def test_unicode(self):
337 def test_unicode(self):
338 self.isp.push(u"PΓ©rez")
338 self.isp.push(u"PΓ©rez")
339 self.isp.push(u'\xc3\xa9')
339 self.isp.push(u'\xc3\xa9')
340 self.isp.push(u"u'\xc3\xa9'")
340 self.isp.push(u"u'\xc3\xa9'")
341
341
342 def test_line_continuation(self):
342 def test_line_continuation(self):
343 """ Test issue #2108."""
343 """ Test issue #2108."""
344 isp = self.isp
344 isp = self.isp
345 # A blank line after a line continuation should not accept more
345 # A blank line after a line continuation should not accept more
346 isp.push("1 \\\n\n")
346 isp.push("1 \\\n\n")
347 self.assertFalse(isp.push_accepts_more())
347 self.assertFalse(isp.push_accepts_more())
348 # Whitespace after a \ is a SyntaxError. The only way to test that
348 # Whitespace after a \ is a SyntaxError. The only way to test that
349 # here is to test that push doesn't accept more (as with
349 # here is to test that push doesn't accept more (as with
350 # test_syntax_error() above).
350 # test_syntax_error() above).
351 isp.push(r"1 \ ")
351 isp.push(r"1 \ ")
352 self.assertFalse(isp.push_accepts_more())
352 self.assertFalse(isp.push_accepts_more())
353 # Even if the line is continuable (c.f. the regular Python
353 # Even if the line is continuable (c.f. the regular Python
354 # interpreter)
354 # interpreter)
355 isp.push(r"(1 \ ")
355 isp.push(r"(1 \ ")
356 self.assertFalse(isp.push_accepts_more())
356 self.assertFalse(isp.push_accepts_more())
357
357
358 class InteractiveLoopTestCase(unittest.TestCase):
358 class InteractiveLoopTestCase(unittest.TestCase):
359 """Tests for an interactive loop like a python shell.
359 """Tests for an interactive loop like a python shell.
360 """
360 """
361 def check_ns(self, lines, ns):
361 def check_ns(self, lines, ns):
362 """Validate that the given input lines produce the resulting namespace.
362 """Validate that the given input lines produce the resulting namespace.
363
363
364 Note: the input lines are given exactly as they would be typed in an
364 Note: the input lines are given exactly as they would be typed in an
365 auto-indenting environment, as mini_interactive_loop above already does
365 auto-indenting environment, as mini_interactive_loop above already does
366 auto-indenting and prepends spaces to the input.
366 auto-indenting and prepends spaces to the input.
367 """
367 """
368 src = mini_interactive_loop(pseudo_input(lines))
368 src = mini_interactive_loop(pseudo_input(lines))
369 test_ns = {}
369 test_ns = {}
370 exec(src, test_ns)
370 exec(src, test_ns)
371 # We can't check that the provided ns is identical to the test_ns,
371 # We can't check that the provided ns is identical to the test_ns,
372 # because Python fills test_ns with extra keys (copyright, etc). But
372 # because Python fills test_ns with extra keys (copyright, etc). But
373 # we can check that the given dict is *contained* in test_ns
373 # we can check that the given dict is *contained* in test_ns
374 for k,v in ns.iteritems():
374 for k,v in ns.iteritems():
375 self.assertEqual(test_ns[k], v)
375 self.assertEqual(test_ns[k], v)
376
376
377 def test_simple(self):
377 def test_simple(self):
378 self.check_ns(['x=1'], dict(x=1))
378 self.check_ns(['x=1'], dict(x=1))
379
379
380 def test_simple2(self):
380 def test_simple2(self):
381 self.check_ns(['if 1:', 'x=2'], dict(x=2))
381 self.check_ns(['if 1:', 'x=2'], dict(x=2))
382
382
383 def test_xy(self):
383 def test_xy(self):
384 self.check_ns(['x=1; y=2'], dict(x=1, y=2))
384 self.check_ns(['x=1; y=2'], dict(x=1, y=2))
385
385
386 def test_abc(self):
386 def test_abc(self):
387 self.check_ns(['if 1:','a=1','b=2','c=3'], dict(a=1, b=2, c=3))
387 self.check_ns(['if 1:','a=1','b=2','c=3'], dict(a=1, b=2, c=3))
388
388
389 def test_multi(self):
389 def test_multi(self):
390 self.check_ns(['x =(1+','1+','2)'], dict(x=4))
390 self.check_ns(['x =(1+','1+','2)'], dict(x=4))
391
391
392
392
393 class IPythonInputTestCase(InputSplitterTestCase):
393 class IPythonInputTestCase(InputSplitterTestCase):
394 """By just creating a new class whose .isp is a different instance, we
394 """By just creating a new class whose .isp is a different instance, we
395 re-run the same test battery on the new input splitter.
395 re-run the same test battery on the new input splitter.
396
396
397 In addition, this runs the tests over the syntax and syntax_ml dicts that
397 In addition, this runs the tests over the syntax and syntax_ml dicts that
398 were tested by individual functions, as part of the OO interface.
398 were tested by individual functions, as part of the OO interface.
399
399
400 It also makes some checks on the raw buffer storage.
400 It also makes some checks on the raw buffer storage.
401 """
401 """
402
402
403 def setUp(self):
403 def setUp(self):
404 self.isp = isp.IPythonInputSplitter()
404 self.isp = isp.IPythonInputSplitter()
405
405
406 def test_syntax(self):
406 def test_syntax(self):
407 """Call all single-line syntax tests from the main object"""
407 """Call all single-line syntax tests from the main object"""
408 isp = self.isp
408 isp = self.isp
409 for example in syntax.itervalues():
409 for example in syntax.itervalues():
410 for raw, out_t in example:
410 for raw, out_t in example:
411 if raw.startswith(' '):
411 if raw.startswith(' '):
412 continue
412 continue
413
413
414 isp.push(raw+'\n')
414 isp.push(raw+'\n')
415 out, out_raw = isp.source_raw_reset()
415 out, out_raw = isp.source_raw_reset()
416 self.assertEqual(out.rstrip(), out_t,
416 self.assertEqual(out.rstrip(), out_t,
417 tt.pair_fail_msg.format("inputsplitter",raw, out_t, out))
417 tt.pair_fail_msg.format("inputsplitter",raw, out_t, out))
418 self.assertEqual(out_raw.rstrip(), raw.rstrip())
418 self.assertEqual(out_raw.rstrip(), raw.rstrip())
419
419
420 def test_syntax_multiline(self):
420 def test_syntax_multiline(self):
421 isp = self.isp
421 isp = self.isp
422 for example in syntax_ml.itervalues():
422 for example in syntax_ml.itervalues():
423 for line_pairs in example:
423 for line_pairs in example:
424 out_t_parts = []
424 out_t_parts = []
425 raw_parts = []
425 raw_parts = []
426 for lraw, out_t_part in line_pairs:
426 for lraw, out_t_part in line_pairs:
427 if out_t_part is not None:
427 if out_t_part is not None:
428 out_t_parts.append(out_t_part)
428 out_t_parts.append(out_t_part)
429
429
430 if lraw is not None:
430 if lraw is not None:
431 isp.push(lraw)
431 isp.push(lraw)
432 raw_parts.append(lraw)
432 raw_parts.append(lraw)
433
433
434 out, out_raw = isp.source_raw_reset()
434 out, out_raw = isp.source_raw_reset()
435 out_t = '\n'.join(out_t_parts).rstrip()
435 out_t = '\n'.join(out_t_parts).rstrip()
436 raw = '\n'.join(raw_parts).rstrip()
436 raw = '\n'.join(raw_parts).rstrip()
437 self.assertEqual(out.rstrip(), out_t)
437 self.assertEqual(out.rstrip(), out_t)
438 self.assertEqual(out_raw.rstrip(), raw)
438 self.assertEqual(out_raw.rstrip(), raw)
439
439
440 def test_syntax_multiline_cell(self):
440 def test_syntax_multiline_cell(self):
441 isp = self.isp
441 isp = self.isp
442 for example in syntax_ml.itervalues():
442 for example in syntax_ml.itervalues():
443
443
444 out_t_parts = []
444 out_t_parts = []
445 for line_pairs in example:
445 for line_pairs in example:
446 raw = '\n'.join(r for r, _ in line_pairs if r is not None)
446 raw = '\n'.join(r for r, _ in line_pairs if r is not None)
447 out_t = '\n'.join(t for _,t in line_pairs if t is not None)
447 out_t = '\n'.join(t for _,t in line_pairs if t is not None)
448 out = isp.transform_cell(raw)
448 out = isp.transform_cell(raw)
449 # Match ignoring trailing whitespace
449 # Match ignoring trailing whitespace
450 self.assertEqual(out.rstrip(), out_t.rstrip())
450 self.assertEqual(out.rstrip(), out_t.rstrip())
451
451
452 def test_cellmagic_preempt(self):
452 def test_cellmagic_preempt(self):
453 isp = self.isp
453 isp = self.isp
454 for raw, name, line, cell in [
454 for raw, name, line, cell in [
455 ("%%cellm a\nIn[1]:", u'cellm', u'a', u'In[1]:'),
455 ("%%cellm a\nIn[1]:", u'cellm', u'a', u'In[1]:'),
456 ("%%cellm \nline\n>>>hi", u'cellm', u'', u'line\n>>>hi'),
456 ("%%cellm \nline\n>>>hi", u'cellm', u'', u'line\n>>>hi'),
457 (">>>%%cellm \nline\n>>>hi", u'cellm', u'', u'line\nhi'),
457 (">>>%%cellm \nline\n>>>hi", u'cellm', u'', u'line\nhi'),
458 ("%%cellm \n>>>hi", u'cellm', u'', u'hi'),
458 ("%%cellm \n>>>hi", u'cellm', u'', u'hi'),
459 ("%%cellm \nline1\nline2", u'cellm', u'', u'line1\nline2'),
459 ("%%cellm \nline1\nline2", u'cellm', u'', u'line1\nline2'),
460 ("%%cellm \nline1\\\\\nline2", u'cellm', u'', u'line1\\\\\nline2'),
460 ("%%cellm \nline1\\\\\nline2", u'cellm', u'', u'line1\\\\\nline2'),
461 ]:
461 ]:
462 expected = "get_ipython().run_cell_magic(%r, %r, %r)" % (
462 expected = "get_ipython().run_cell_magic(%r, %r, %r)" % (
463 name, line, cell
463 name, line, cell
464 )
464 )
465 out = isp.transform_cell(raw)
465 out = isp.transform_cell(raw)
466 self.assertEqual(out.rstrip(), expected.rstrip())
466 self.assertEqual(out.rstrip(), expected.rstrip())
467
467
468
468
469
469
470 #-----------------------------------------------------------------------------
470 #-----------------------------------------------------------------------------
471 # Main - use as a script, mostly for developer experiments
471 # Main - use as a script, mostly for developer experiments
472 #-----------------------------------------------------------------------------
472 #-----------------------------------------------------------------------------
473
473
474 if __name__ == '__main__':
474 if __name__ == '__main__':
475 # A simple demo for interactive experimentation. This code will not get
475 # A simple demo for interactive experimentation. This code will not get
476 # picked up by any test suite.
476 # picked up by any test suite.
477 from IPython.core.inputsplitter import InputSplitter, IPythonInputSplitter
477 from IPython.core.inputsplitter import InputSplitter, IPythonInputSplitter
478
478
479 # configure here the syntax to use, prompt and whether to autoindent
479 # configure here the syntax to use, prompt and whether to autoindent
480 #isp, start_prompt = InputSplitter(), '>>> '
480 #isp, start_prompt = InputSplitter(), '>>> '
481 isp, start_prompt = IPythonInputSplitter(), 'In> '
481 isp, start_prompt = IPythonInputSplitter(), 'In> '
482
482
483 autoindent = True
483 autoindent = True
484 #autoindent = False
484 #autoindent = False
485
485
486 try:
486 try:
487 while True:
487 while True:
488 prompt = start_prompt
488 prompt = start_prompt
489 while isp.push_accepts_more():
489 while isp.push_accepts_more():
490 indent = ' '*isp.indent_spaces
490 indent = ' '*isp.indent_spaces
491 if autoindent:
491 if autoindent:
492 line = indent + raw_input(prompt+indent)
492 line = indent + input(prompt+indent)
493 else:
493 else:
494 line = raw_input(prompt)
494 line = input(prompt)
495 isp.push(line)
495 isp.push(line)
496 prompt = '... '
496 prompt = '... '
497
497
498 # Here we just return input so we can use it in a test suite, but a
498 # Here we just return input so we can use it in a test suite, but a
499 # real interpreter would instead send it for execution somewhere.
499 # real interpreter would instead send it for execution somewhere.
500 #src = isp.source; raise EOFError # dbg
500 #src = isp.source; raise EOFError # dbg
501 src, raw = isp.source_raw_reset()
501 src, raw = isp.source_raw_reset()
502 print('Input source was:\n', src)
502 print('Input source was:\n', src)
503 print('Raw source was:\n', raw)
503 print('Raw source was:\n', raw)
504 except EOFError:
504 except EOFError:
505 print('Bye')
505 print('Bye')
506
506
507 # Tests for cell magics support
507 # Tests for cell magics support
508
508
509 def test_last_blank():
509 def test_last_blank():
510 nt.assert_false(isp.last_blank(''))
510 nt.assert_false(isp.last_blank(''))
511 nt.assert_false(isp.last_blank('abc'))
511 nt.assert_false(isp.last_blank('abc'))
512 nt.assert_false(isp.last_blank('abc\n'))
512 nt.assert_false(isp.last_blank('abc\n'))
513 nt.assert_false(isp.last_blank('abc\na'))
513 nt.assert_false(isp.last_blank('abc\na'))
514
514
515 nt.assert_true(isp.last_blank('\n'))
515 nt.assert_true(isp.last_blank('\n'))
516 nt.assert_true(isp.last_blank('\n '))
516 nt.assert_true(isp.last_blank('\n '))
517 nt.assert_true(isp.last_blank('abc\n '))
517 nt.assert_true(isp.last_blank('abc\n '))
518 nt.assert_true(isp.last_blank('abc\n\n'))
518 nt.assert_true(isp.last_blank('abc\n\n'))
519 nt.assert_true(isp.last_blank('abc\nd\n\n'))
519 nt.assert_true(isp.last_blank('abc\nd\n\n'))
520 nt.assert_true(isp.last_blank('abc\nd\ne\n\n'))
520 nt.assert_true(isp.last_blank('abc\nd\ne\n\n'))
521 nt.assert_true(isp.last_blank('abc \n \n \n\n'))
521 nt.assert_true(isp.last_blank('abc \n \n \n\n'))
522
522
523
523
524 def test_last_two_blanks():
524 def test_last_two_blanks():
525 nt.assert_false(isp.last_two_blanks(''))
525 nt.assert_false(isp.last_two_blanks(''))
526 nt.assert_false(isp.last_two_blanks('abc'))
526 nt.assert_false(isp.last_two_blanks('abc'))
527 nt.assert_false(isp.last_two_blanks('abc\n'))
527 nt.assert_false(isp.last_two_blanks('abc\n'))
528 nt.assert_false(isp.last_two_blanks('abc\n\na'))
528 nt.assert_false(isp.last_two_blanks('abc\n\na'))
529 nt.assert_false(isp.last_two_blanks('abc\n \n'))
529 nt.assert_false(isp.last_two_blanks('abc\n \n'))
530 nt.assert_false(isp.last_two_blanks('abc\n\n'))
530 nt.assert_false(isp.last_two_blanks('abc\n\n'))
531
531
532 nt.assert_true(isp.last_two_blanks('\n\n'))
532 nt.assert_true(isp.last_two_blanks('\n\n'))
533 nt.assert_true(isp.last_two_blanks('\n\n '))
533 nt.assert_true(isp.last_two_blanks('\n\n '))
534 nt.assert_true(isp.last_two_blanks('\n \n'))
534 nt.assert_true(isp.last_two_blanks('\n \n'))
535 nt.assert_true(isp.last_two_blanks('abc\n\n '))
535 nt.assert_true(isp.last_two_blanks('abc\n\n '))
536 nt.assert_true(isp.last_two_blanks('abc\n\n\n'))
536 nt.assert_true(isp.last_two_blanks('abc\n\n\n'))
537 nt.assert_true(isp.last_two_blanks('abc\n\n \n'))
537 nt.assert_true(isp.last_two_blanks('abc\n\n \n'))
538 nt.assert_true(isp.last_two_blanks('abc\n\n \n '))
538 nt.assert_true(isp.last_two_blanks('abc\n\n \n '))
539 nt.assert_true(isp.last_two_blanks('abc\n\n \n \n'))
539 nt.assert_true(isp.last_two_blanks('abc\n\n \n \n'))
540 nt.assert_true(isp.last_two_blanks('abc\nd\n\n\n'))
540 nt.assert_true(isp.last_two_blanks('abc\nd\n\n\n'))
541 nt.assert_true(isp.last_two_blanks('abc\nd\ne\nf\n\n\n'))
541 nt.assert_true(isp.last_two_blanks('abc\nd\ne\nf\n\n\n'))
542
542
543
543
544 class CellMagicsCommon(object):
544 class CellMagicsCommon(object):
545
545
546 def test_whole_cell(self):
546 def test_whole_cell(self):
547 src = "%%cellm line\nbody\n"
547 src = "%%cellm line\nbody\n"
548 sp = self.sp
548 sp = self.sp
549 sp.push(src)
549 sp.push(src)
550 out = sp.source_reset()
550 out = sp.source_reset()
551 ref = u"get_ipython().run_cell_magic({u}'cellm', {u}'line', {u}'body')\n"
551 ref = u"get_ipython().run_cell_magic({u}'cellm', {u}'line', {u}'body')\n"
552 nt.assert_equal(out, py3compat.u_format(ref))
552 nt.assert_equal(out, py3compat.u_format(ref))
553
553
554 def test_cellmagic_help(self):
554 def test_cellmagic_help(self):
555 self.sp.push('%%cellm?')
555 self.sp.push('%%cellm?')
556 nt.assert_false(self.sp.push_accepts_more())
556 nt.assert_false(self.sp.push_accepts_more())
557
557
558 def tearDown(self):
558 def tearDown(self):
559 self.sp.reset()
559 self.sp.reset()
560
560
561
561
562 class CellModeCellMagics(CellMagicsCommon, unittest.TestCase):
562 class CellModeCellMagics(CellMagicsCommon, unittest.TestCase):
563 sp = isp.IPythonInputSplitter(line_input_checker=False)
563 sp = isp.IPythonInputSplitter(line_input_checker=False)
564
564
565 def test_incremental(self):
565 def test_incremental(self):
566 sp = self.sp
566 sp = self.sp
567 sp.push('%%cellm firstline\n')
567 sp.push('%%cellm firstline\n')
568 nt.assert_true(sp.push_accepts_more()) #1
568 nt.assert_true(sp.push_accepts_more()) #1
569 sp.push('line2\n')
569 sp.push('line2\n')
570 nt.assert_true(sp.push_accepts_more()) #2
570 nt.assert_true(sp.push_accepts_more()) #2
571 sp.push('\n')
571 sp.push('\n')
572 # This should accept a blank line and carry on until the cell is reset
572 # This should accept a blank line and carry on until the cell is reset
573 nt.assert_true(sp.push_accepts_more()) #3
573 nt.assert_true(sp.push_accepts_more()) #3
574
574
575 class LineModeCellMagics(CellMagicsCommon, unittest.TestCase):
575 class LineModeCellMagics(CellMagicsCommon, unittest.TestCase):
576 sp = isp.IPythonInputSplitter(line_input_checker=True)
576 sp = isp.IPythonInputSplitter(line_input_checker=True)
577
577
578 def test_incremental(self):
578 def test_incremental(self):
579 sp = self.sp
579 sp = self.sp
580 sp.push('%%cellm line2\n')
580 sp.push('%%cellm line2\n')
581 nt.assert_true(sp.push_accepts_more()) #1
581 nt.assert_true(sp.push_accepts_more()) #1
582 sp.push('\n')
582 sp.push('\n')
583 # In this case, a blank line should end the cell magic
583 # In this case, a blank line should end the cell magic
584 nt.assert_false(sp.push_accepts_more()) #2
584 nt.assert_false(sp.push_accepts_more()) #2
@@ -1,582 +1,583 b''
1 """Module for interactive demos using IPython.
1 """Module for interactive demos using IPython.
2
2
3 This module implements a few classes for running Python scripts interactively
3 This module implements a few classes for running Python scripts interactively
4 in IPython for demonstrations. With very simple markup (a few tags in
4 in IPython for demonstrations. With very simple markup (a few tags in
5 comments), you can control points where the script stops executing and returns
5 comments), you can control points where the script stops executing and returns
6 control to IPython.
6 control to IPython.
7
7
8
8
9 Provided classes
9 Provided classes
10 ----------------
10 ----------------
11
11
12 The classes are (see their docstrings for further details):
12 The classes are (see their docstrings for further details):
13
13
14 - Demo: pure python demos
14 - Demo: pure python demos
15
15
16 - IPythonDemo: demos with input to be processed by IPython as if it had been
16 - IPythonDemo: demos with input to be processed by IPython as if it had been
17 typed interactively (so magics work, as well as any other special syntax you
17 typed interactively (so magics work, as well as any other special syntax you
18 may have added via input prefilters).
18 may have added via input prefilters).
19
19
20 - LineDemo: single-line version of the Demo class. These demos are executed
20 - LineDemo: single-line version of the Demo class. These demos are executed
21 one line at a time, and require no markup.
21 one line at a time, and require no markup.
22
22
23 - IPythonLineDemo: IPython version of the LineDemo class (the demo is
23 - IPythonLineDemo: IPython version of the LineDemo class (the demo is
24 executed a line at a time, but processed via IPython).
24 executed a line at a time, but processed via IPython).
25
25
26 - ClearMixin: mixin to make Demo classes with less visual clutter. It
26 - ClearMixin: mixin to make Demo classes with less visual clutter. It
27 declares an empty marquee and a pre_cmd that clears the screen before each
27 declares an empty marquee and a pre_cmd that clears the screen before each
28 block (see Subclassing below).
28 block (see Subclassing below).
29
29
30 - ClearDemo, ClearIPDemo: mixin-enabled versions of the Demo and IPythonDemo
30 - ClearDemo, ClearIPDemo: mixin-enabled versions of the Demo and IPythonDemo
31 classes.
31 classes.
32
32
33 Inheritance diagram:
33 Inheritance diagram:
34
34
35 .. inheritance-diagram:: IPython.lib.demo
35 .. inheritance-diagram:: IPython.lib.demo
36 :parts: 3
36 :parts: 3
37
37
38 Subclassing
38 Subclassing
39 -----------
39 -----------
40
40
41 The classes here all include a few methods meant to make customization by
41 The classes here all include a few methods meant to make customization by
42 subclassing more convenient. Their docstrings below have some more details:
42 subclassing more convenient. Their docstrings below have some more details:
43
43
44 - marquee(): generates a marquee to provide visible on-screen markers at each
44 - marquee(): generates a marquee to provide visible on-screen markers at each
45 block start and end.
45 block start and end.
46
46
47 - pre_cmd(): run right before the execution of each block.
47 - pre_cmd(): run right before the execution of each block.
48
48
49 - post_cmd(): run right after the execution of each block. If the block
49 - post_cmd(): run right after the execution of each block. If the block
50 raises an exception, this is NOT called.
50 raises an exception, this is NOT called.
51
51
52
52
53 Operation
53 Operation
54 ---------
54 ---------
55
55
56 The file is run in its own empty namespace (though you can pass it a string of
56 The file is run in its own empty namespace (though you can pass it a string of
57 arguments as if in a command line environment, and it will see those as
57 arguments as if in a command line environment, and it will see those as
58 sys.argv). But at each stop, the global IPython namespace is updated with the
58 sys.argv). But at each stop, the global IPython namespace is updated with the
59 current internal demo namespace, so you can work interactively with the data
59 current internal demo namespace, so you can work interactively with the data
60 accumulated so far.
60 accumulated so far.
61
61
62 By default, each block of code is printed (with syntax highlighting) before
62 By default, each block of code is printed (with syntax highlighting) before
63 executing it and you have to confirm execution. This is intended to show the
63 executing it and you have to confirm execution. This is intended to show the
64 code to an audience first so you can discuss it, and only proceed with
64 code to an audience first so you can discuss it, and only proceed with
65 execution once you agree. There are a few tags which allow you to modify this
65 execution once you agree. There are a few tags which allow you to modify this
66 behavior.
66 behavior.
67
67
68 The supported tags are:
68 The supported tags are:
69
69
70 # <demo> stop
70 # <demo> stop
71
71
72 Defines block boundaries, the points where IPython stops execution of the
72 Defines block boundaries, the points where IPython stops execution of the
73 file and returns to the interactive prompt.
73 file and returns to the interactive prompt.
74
74
75 You can optionally mark the stop tag with extra dashes before and after the
75 You can optionally mark the stop tag with extra dashes before and after the
76 word 'stop', to help visually distinguish the blocks in a text editor:
76 word 'stop', to help visually distinguish the blocks in a text editor:
77
77
78 # <demo> --- stop ---
78 # <demo> --- stop ---
79
79
80
80
81 # <demo> silent
81 # <demo> silent
82
82
83 Make a block execute silently (and hence automatically). Typically used in
83 Make a block execute silently (and hence automatically). Typically used in
84 cases where you have some boilerplate or initialization code which you need
84 cases where you have some boilerplate or initialization code which you need
85 executed but do not want to be seen in the demo.
85 executed but do not want to be seen in the demo.
86
86
87 # <demo> auto
87 # <demo> auto
88
88
89 Make a block execute automatically, but still being printed. Useful for
89 Make a block execute automatically, but still being printed. Useful for
90 simple code which does not warrant discussion, since it avoids the extra
90 simple code which does not warrant discussion, since it avoids the extra
91 manual confirmation.
91 manual confirmation.
92
92
93 # <demo> auto_all
93 # <demo> auto_all
94
94
95 This tag can _only_ be in the first block, and if given it overrides the
95 This tag can _only_ be in the first block, and if given it overrides the
96 individual auto tags to make the whole demo fully automatic (no block asks
96 individual auto tags to make the whole demo fully automatic (no block asks
97 for confirmation). It can also be given at creation time (or the attribute
97 for confirmation). It can also be given at creation time (or the attribute
98 set later) to override what's in the file.
98 set later) to override what's in the file.
99
99
100 While _any_ python file can be run as a Demo instance, if there are no stop
100 While _any_ python file can be run as a Demo instance, if there are no stop
101 tags the whole file will run in a single block (no different that calling
101 tags the whole file will run in a single block (no different that calling
102 first %pycat and then %run). The minimal markup to make this useful is to
102 first %pycat and then %run). The minimal markup to make this useful is to
103 place a set of stop tags; the other tags are only there to let you fine-tune
103 place a set of stop tags; the other tags are only there to let you fine-tune
104 the execution.
104 the execution.
105
105
106 This is probably best explained with the simple example file below. You can
106 This is probably best explained with the simple example file below. You can
107 copy this into a file named ex_demo.py, and try running it via::
107 copy this into a file named ex_demo.py, and try running it via::
108
108
109 from IPython.demo import Demo
109 from IPython.demo import Demo
110 d = Demo('ex_demo.py')
110 d = Demo('ex_demo.py')
111 d()
111 d()
112
112
113 Each time you call the demo object, it runs the next block. The demo object
113 Each time you call the demo object, it runs the next block. The demo object
114 has a few useful methods for navigation, like again(), edit(), jump(), seek()
114 has a few useful methods for navigation, like again(), edit(), jump(), seek()
115 and back(). It can be reset for a new run via reset() or reloaded from disk
115 and back(). It can be reset for a new run via reset() or reloaded from disk
116 (in case you've edited the source) via reload(). See their docstrings below.
116 (in case you've edited the source) via reload(). See their docstrings below.
117
117
118 Note: To make this simpler to explore, a file called "demo-exercizer.py" has
118 Note: To make this simpler to explore, a file called "demo-exercizer.py" has
119 been added to the "docs/examples/core" directory. Just cd to this directory in
119 been added to the "docs/examples/core" directory. Just cd to this directory in
120 an IPython session, and type::
120 an IPython session, and type::
121
121
122 %run demo-exercizer.py
122 %run demo-exercizer.py
123
123
124 and then follow the directions.
124 and then follow the directions.
125
125
126 Example
126 Example
127 -------
127 -------
128
128
129 The following is a very simple example of a valid demo file.
129 The following is a very simple example of a valid demo file.
130
130
131 ::
131 ::
132
132
133 #################### EXAMPLE DEMO <ex_demo.py> ###############################
133 #################### EXAMPLE DEMO <ex_demo.py> ###############################
134 '''A simple interactive demo to illustrate the use of IPython's Demo class.'''
134 '''A simple interactive demo to illustrate the use of IPython's Demo class.'''
135
135
136 print 'Hello, welcome to an interactive IPython demo.'
136 print 'Hello, welcome to an interactive IPython demo.'
137
137
138 # The mark below defines a block boundary, which is a point where IPython will
138 # The mark below defines a block boundary, which is a point where IPython will
139 # stop execution and return to the interactive prompt. The dashes are actually
139 # stop execution and return to the interactive prompt. The dashes are actually
140 # optional and used only as a visual aid to clearly separate blocks while
140 # optional and used only as a visual aid to clearly separate blocks while
141 # editing the demo code.
141 # editing the demo code.
142 # <demo> stop
142 # <demo> stop
143
143
144 x = 1
144 x = 1
145 y = 2
145 y = 2
146
146
147 # <demo> stop
147 # <demo> stop
148
148
149 # the mark below makes this block as silent
149 # the mark below makes this block as silent
150 # <demo> silent
150 # <demo> silent
151
151
152 print 'This is a silent block, which gets executed but not printed.'
152 print 'This is a silent block, which gets executed but not printed.'
153
153
154 # <demo> stop
154 # <demo> stop
155 # <demo> auto
155 # <demo> auto
156 print 'This is an automatic block.'
156 print 'This is an automatic block.'
157 print 'It is executed without asking for confirmation, but printed.'
157 print 'It is executed without asking for confirmation, but printed.'
158 z = x+y
158 z = x+y
159
159
160 print 'z=',x
160 print 'z=',x
161
161
162 # <demo> stop
162 # <demo> stop
163 # This is just another normal block.
163 # This is just another normal block.
164 print 'z is now:', z
164 print 'z is now:', z
165
165
166 print 'bye!'
166 print 'bye!'
167 ################### END EXAMPLE DEMO <ex_demo.py> ############################
167 ################### END EXAMPLE DEMO <ex_demo.py> ############################
168 """
168 """
169
169
170 from __future__ import unicode_literals
170 from __future__ import unicode_literals
171
171
172 #*****************************************************************************
172 #*****************************************************************************
173 # Copyright (C) 2005-2006 Fernando Perez. <Fernando.Perez@colorado.edu>
173 # Copyright (C) 2005-2006 Fernando Perez. <Fernando.Perez@colorado.edu>
174 #
174 #
175 # Distributed under the terms of the BSD License. The full license is in
175 # Distributed under the terms of the BSD License. The full license is in
176 # the file COPYING, distributed as part of this software.
176 # the file COPYING, distributed as part of this software.
177 #
177 #
178 #*****************************************************************************
178 #*****************************************************************************
179 from __future__ import print_function
179 from __future__ import print_function
180
180
181 import os
181 import os
182 import re
182 import re
183 import shlex
183 import shlex
184 import sys
184 import sys
185
185
186 from IPython.utils import io
186 from IPython.utils import io
187 from IPython.utils.text import marquee
187 from IPython.utils.text import marquee
188 from IPython.utils import openpy
188 from IPython.utils import openpy
189 from IPython.utils import py3compat
189 __all__ = ['Demo','IPythonDemo','LineDemo','IPythonLineDemo','DemoError']
190 __all__ = ['Demo','IPythonDemo','LineDemo','IPythonLineDemo','DemoError']
190
191
191 class DemoError(Exception): pass
192 class DemoError(Exception): pass
192
193
193 def re_mark(mark):
194 def re_mark(mark):
194 return re.compile(r'^\s*#\s+<demo>\s+%s\s*$' % mark,re.MULTILINE)
195 return re.compile(r'^\s*#\s+<demo>\s+%s\s*$' % mark,re.MULTILINE)
195
196
196 class Demo(object):
197 class Demo(object):
197
198
198 re_stop = re_mark('-*\s?stop\s?-*')
199 re_stop = re_mark('-*\s?stop\s?-*')
199 re_silent = re_mark('silent')
200 re_silent = re_mark('silent')
200 re_auto = re_mark('auto')
201 re_auto = re_mark('auto')
201 re_auto_all = re_mark('auto_all')
202 re_auto_all = re_mark('auto_all')
202
203
203 def __init__(self,src,title='',arg_str='',auto_all=None):
204 def __init__(self,src,title='',arg_str='',auto_all=None):
204 """Make a new demo object. To run the demo, simply call the object.
205 """Make a new demo object. To run the demo, simply call the object.
205
206
206 See the module docstring for full details and an example (you can use
207 See the module docstring for full details and an example (you can use
207 IPython.Demo? in IPython to see it).
208 IPython.Demo? in IPython to see it).
208
209
209 Inputs:
210 Inputs:
210
211
211 - src is either a file, or file-like object, or a
212 - src is either a file, or file-like object, or a
212 string that can be resolved to a filename.
213 string that can be resolved to a filename.
213
214
214 Optional inputs:
215 Optional inputs:
215
216
216 - title: a string to use as the demo name. Of most use when the demo
217 - title: a string to use as the demo name. Of most use when the demo
217 you are making comes from an object that has no filename, or if you
218 you are making comes from an object that has no filename, or if you
218 want an alternate denotation distinct from the filename.
219 want an alternate denotation distinct from the filename.
219
220
220 - arg_str(''): a string of arguments, internally converted to a list
221 - arg_str(''): a string of arguments, internally converted to a list
221 just like sys.argv, so the demo script can see a similar
222 just like sys.argv, so the demo script can see a similar
222 environment.
223 environment.
223
224
224 - auto_all(None): global flag to run all blocks automatically without
225 - auto_all(None): global flag to run all blocks automatically without
225 confirmation. This attribute overrides the block-level tags and
226 confirmation. This attribute overrides the block-level tags and
226 applies to the whole demo. It is an attribute of the object, and
227 applies to the whole demo. It is an attribute of the object, and
227 can be changed at runtime simply by reassigning it to a boolean
228 can be changed at runtime simply by reassigning it to a boolean
228 value.
229 value.
229 """
230 """
230 if hasattr(src, "read"):
231 if hasattr(src, "read"):
231 # It seems to be a file or a file-like object
232 # It seems to be a file or a file-like object
232 self.fname = "from a file-like object"
233 self.fname = "from a file-like object"
233 if title == '':
234 if title == '':
234 self.title = "from a file-like object"
235 self.title = "from a file-like object"
235 else:
236 else:
236 self.title = title
237 self.title = title
237 else:
238 else:
238 # Assume it's a string or something that can be converted to one
239 # Assume it's a string or something that can be converted to one
239 self.fname = src
240 self.fname = src
240 if title == '':
241 if title == '':
241 (filepath, filename) = os.path.split(src)
242 (filepath, filename) = os.path.split(src)
242 self.title = filename
243 self.title = filename
243 else:
244 else:
244 self.title = title
245 self.title = title
245 self.sys_argv = [src] + shlex.split(arg_str)
246 self.sys_argv = [src] + shlex.split(arg_str)
246 self.auto_all = auto_all
247 self.auto_all = auto_all
247 self.src = src
248 self.src = src
248
249
249 # get a few things from ipython. While it's a bit ugly design-wise,
250 # get a few things from ipython. While it's a bit ugly design-wise,
250 # it ensures that things like color scheme and the like are always in
251 # it ensures that things like color scheme and the like are always in
251 # sync with the ipython mode being used. This class is only meant to
252 # sync with the ipython mode being used. This class is only meant to
252 # be used inside ipython anyways, so it's OK.
253 # be used inside ipython anyways, so it's OK.
253 ip = get_ipython() # this is in builtins whenever IPython is running
254 ip = get_ipython() # this is in builtins whenever IPython is running
254 self.ip_ns = ip.user_ns
255 self.ip_ns = ip.user_ns
255 self.ip_colorize = ip.pycolorize
256 self.ip_colorize = ip.pycolorize
256 self.ip_showtb = ip.showtraceback
257 self.ip_showtb = ip.showtraceback
257 self.ip_run_cell = ip.run_cell
258 self.ip_run_cell = ip.run_cell
258 self.shell = ip
259 self.shell = ip
259
260
260 # load user data and initialize data structures
261 # load user data and initialize data structures
261 self.reload()
262 self.reload()
262
263
263 def fload(self):
264 def fload(self):
264 """Load file object."""
265 """Load file object."""
265 # read data and parse into blocks
266 # read data and parse into blocks
266 if hasattr(self, 'fobj') and self.fobj is not None:
267 if hasattr(self, 'fobj') and self.fobj is not None:
267 self.fobj.close()
268 self.fobj.close()
268 if hasattr(self.src, "read"):
269 if hasattr(self.src, "read"):
269 # It seems to be a file or a file-like object
270 # It seems to be a file or a file-like object
270 self.fobj = self.src
271 self.fobj = self.src
271 else:
272 else:
272 # Assume it's a string or something that can be converted to one
273 # Assume it's a string or something that can be converted to one
273 self.fobj = openpy.open(self.fname)
274 self.fobj = openpy.open(self.fname)
274
275
275 def reload(self):
276 def reload(self):
276 """Reload source from disk and initialize state."""
277 """Reload source from disk and initialize state."""
277 self.fload()
278 self.fload()
278
279
279 self.src = "".join(openpy.strip_encoding_cookie(self.fobj))
280 self.src = "".join(openpy.strip_encoding_cookie(self.fobj))
280 src_b = [b.strip() for b in self.re_stop.split(self.src) if b]
281 src_b = [b.strip() for b in self.re_stop.split(self.src) if b]
281 self._silent = [bool(self.re_silent.findall(b)) for b in src_b]
282 self._silent = [bool(self.re_silent.findall(b)) for b in src_b]
282 self._auto = [bool(self.re_auto.findall(b)) for b in src_b]
283 self._auto = [bool(self.re_auto.findall(b)) for b in src_b]
283
284
284 # if auto_all is not given (def. None), we read it from the file
285 # if auto_all is not given (def. None), we read it from the file
285 if self.auto_all is None:
286 if self.auto_all is None:
286 self.auto_all = bool(self.re_auto_all.findall(src_b[0]))
287 self.auto_all = bool(self.re_auto_all.findall(src_b[0]))
287 else:
288 else:
288 self.auto_all = bool(self.auto_all)
289 self.auto_all = bool(self.auto_all)
289
290
290 # Clean the sources from all markup so it doesn't get displayed when
291 # Clean the sources from all markup so it doesn't get displayed when
291 # running the demo
292 # running the demo
292 src_blocks = []
293 src_blocks = []
293 auto_strip = lambda s: self.re_auto.sub('',s)
294 auto_strip = lambda s: self.re_auto.sub('',s)
294 for i,b in enumerate(src_b):
295 for i,b in enumerate(src_b):
295 if self._auto[i]:
296 if self._auto[i]:
296 src_blocks.append(auto_strip(b))
297 src_blocks.append(auto_strip(b))
297 else:
298 else:
298 src_blocks.append(b)
299 src_blocks.append(b)
299 # remove the auto_all marker
300 # remove the auto_all marker
300 src_blocks[0] = self.re_auto_all.sub('',src_blocks[0])
301 src_blocks[0] = self.re_auto_all.sub('',src_blocks[0])
301
302
302 self.nblocks = len(src_blocks)
303 self.nblocks = len(src_blocks)
303 self.src_blocks = src_blocks
304 self.src_blocks = src_blocks
304
305
305 # also build syntax-highlighted source
306 # also build syntax-highlighted source
306 self.src_blocks_colored = map(self.ip_colorize,self.src_blocks)
307 self.src_blocks_colored = map(self.ip_colorize,self.src_blocks)
307
308
308 # ensure clean namespace and seek offset
309 # ensure clean namespace and seek offset
309 self.reset()
310 self.reset()
310
311
311 def reset(self):
312 def reset(self):
312 """Reset the namespace and seek pointer to restart the demo"""
313 """Reset the namespace and seek pointer to restart the demo"""
313 self.user_ns = {}
314 self.user_ns = {}
314 self.finished = False
315 self.finished = False
315 self.block_index = 0
316 self.block_index = 0
316
317
317 def _validate_index(self,index):
318 def _validate_index(self,index):
318 if index<0 or index>=self.nblocks:
319 if index<0 or index>=self.nblocks:
319 raise ValueError('invalid block index %s' % index)
320 raise ValueError('invalid block index %s' % index)
320
321
321 def _get_index(self,index):
322 def _get_index(self,index):
322 """Get the current block index, validating and checking status.
323 """Get the current block index, validating and checking status.
323
324
324 Returns None if the demo is finished"""
325 Returns None if the demo is finished"""
325
326
326 if index is None:
327 if index is None:
327 if self.finished:
328 if self.finished:
328 print('Demo finished. Use <demo_name>.reset() if you want to rerun it.', file=io.stdout)
329 print('Demo finished. Use <demo_name>.reset() if you want to rerun it.', file=io.stdout)
329 return None
330 return None
330 index = self.block_index
331 index = self.block_index
331 else:
332 else:
332 self._validate_index(index)
333 self._validate_index(index)
333 return index
334 return index
334
335
335 def seek(self,index):
336 def seek(self,index):
336 """Move the current seek pointer to the given block.
337 """Move the current seek pointer to the given block.
337
338
338 You can use negative indices to seek from the end, with identical
339 You can use negative indices to seek from the end, with identical
339 semantics to those of Python lists."""
340 semantics to those of Python lists."""
340 if index<0:
341 if index<0:
341 index = self.nblocks + index
342 index = self.nblocks + index
342 self._validate_index(index)
343 self._validate_index(index)
343 self.block_index = index
344 self.block_index = index
344 self.finished = False
345 self.finished = False
345
346
346 def back(self,num=1):
347 def back(self,num=1):
347 """Move the seek pointer back num blocks (default is 1)."""
348 """Move the seek pointer back num blocks (default is 1)."""
348 self.seek(self.block_index-num)
349 self.seek(self.block_index-num)
349
350
350 def jump(self,num=1):
351 def jump(self,num=1):
351 """Jump a given number of blocks relative to the current one.
352 """Jump a given number of blocks relative to the current one.
352
353
353 The offset can be positive or negative, defaults to 1."""
354 The offset can be positive or negative, defaults to 1."""
354 self.seek(self.block_index+num)
355 self.seek(self.block_index+num)
355
356
356 def again(self):
357 def again(self):
357 """Move the seek pointer back one block and re-execute."""
358 """Move the seek pointer back one block and re-execute."""
358 self.back(1)
359 self.back(1)
359 self()
360 self()
360
361
361 def edit(self,index=None):
362 def edit(self,index=None):
362 """Edit a block.
363 """Edit a block.
363
364
364 If no number is given, use the last block executed.
365 If no number is given, use the last block executed.
365
366
366 This edits the in-memory copy of the demo, it does NOT modify the
367 This edits the in-memory copy of the demo, it does NOT modify the
367 original source file. If you want to do that, simply open the file in
368 original source file. If you want to do that, simply open the file in
368 an editor and use reload() when you make changes to the file. This
369 an editor and use reload() when you make changes to the file. This
369 method is meant to let you change a block during a demonstration for
370 method is meant to let you change a block during a demonstration for
370 explanatory purposes, without damaging your original script."""
371 explanatory purposes, without damaging your original script."""
371
372
372 index = self._get_index(index)
373 index = self._get_index(index)
373 if index is None:
374 if index is None:
374 return
375 return
375 # decrease the index by one (unless we're at the very beginning), so
376 # decrease the index by one (unless we're at the very beginning), so
376 # that the default demo.edit() call opens up the sblock we've last run
377 # that the default demo.edit() call opens up the sblock we've last run
377 if index>0:
378 if index>0:
378 index -= 1
379 index -= 1
379
380
380 filename = self.shell.mktempfile(self.src_blocks[index])
381 filename = self.shell.mktempfile(self.src_blocks[index])
381 self.shell.hooks.editor(filename,1)
382 self.shell.hooks.editor(filename,1)
382 with open(filename, 'r') as f:
383 with open(filename, 'r') as f:
383 new_block = f.read()
384 new_block = f.read()
384 # update the source and colored block
385 # update the source and colored block
385 self.src_blocks[index] = new_block
386 self.src_blocks[index] = new_block
386 self.src_blocks_colored[index] = self.ip_colorize(new_block)
387 self.src_blocks_colored[index] = self.ip_colorize(new_block)
387 self.block_index = index
388 self.block_index = index
388 # call to run with the newly edited index
389 # call to run with the newly edited index
389 self()
390 self()
390
391
391 def show(self,index=None):
392 def show(self,index=None):
392 """Show a single block on screen"""
393 """Show a single block on screen"""
393
394
394 index = self._get_index(index)
395 index = self._get_index(index)
395 if index is None:
396 if index is None:
396 return
397 return
397
398
398 print(self.marquee('<%s> block # %s (%s remaining)' %
399 print(self.marquee('<%s> block # %s (%s remaining)' %
399 (self.title,index,self.nblocks-index-1)), file=io.stdout)
400 (self.title,index,self.nblocks-index-1)), file=io.stdout)
400 print((self.src_blocks_colored[index]), file=io.stdout)
401 print((self.src_blocks_colored[index]), file=io.stdout)
401 sys.stdout.flush()
402 sys.stdout.flush()
402
403
403 def show_all(self):
404 def show_all(self):
404 """Show entire demo on screen, block by block"""
405 """Show entire demo on screen, block by block"""
405
406
406 fname = self.title
407 fname = self.title
407 title = self.title
408 title = self.title
408 nblocks = self.nblocks
409 nblocks = self.nblocks
409 silent = self._silent
410 silent = self._silent
410 marquee = self.marquee
411 marquee = self.marquee
411 for index,block in enumerate(self.src_blocks_colored):
412 for index,block in enumerate(self.src_blocks_colored):
412 if silent[index]:
413 if silent[index]:
413 print(marquee('<%s> SILENT block # %s (%s remaining)' %
414 print(marquee('<%s> SILENT block # %s (%s remaining)' %
414 (title,index,nblocks-index-1)), file=io.stdout)
415 (title,index,nblocks-index-1)), file=io.stdout)
415 else:
416 else:
416 print(marquee('<%s> block # %s (%s remaining)' %
417 print(marquee('<%s> block # %s (%s remaining)' %
417 (title,index,nblocks-index-1)), file=io.stdout)
418 (title,index,nblocks-index-1)), file=io.stdout)
418 print(block, end=' ', file=io.stdout)
419 print(block, end=' ', file=io.stdout)
419 sys.stdout.flush()
420 sys.stdout.flush()
420
421
421 def run_cell(self,source):
422 def run_cell(self,source):
422 """Execute a string with one or more lines of code"""
423 """Execute a string with one or more lines of code"""
423
424
424 exec(source, self.user_ns)
425 exec(source, self.user_ns)
425
426
426 def __call__(self,index=None):
427 def __call__(self,index=None):
427 """run a block of the demo.
428 """run a block of the demo.
428
429
429 If index is given, it should be an integer >=1 and <= nblocks. This
430 If index is given, it should be an integer >=1 and <= nblocks. This
430 means that the calling convention is one off from typical Python
431 means that the calling convention is one off from typical Python
431 lists. The reason for the inconsistency is that the demo always
432 lists. The reason for the inconsistency is that the demo always
432 prints 'Block n/N, and N is the total, so it would be very odd to use
433 prints 'Block n/N, and N is the total, so it would be very odd to use
433 zero-indexing here."""
434 zero-indexing here."""
434
435
435 index = self._get_index(index)
436 index = self._get_index(index)
436 if index is None:
437 if index is None:
437 return
438 return
438 try:
439 try:
439 marquee = self.marquee
440 marquee = self.marquee
440 next_block = self.src_blocks[index]
441 next_block = self.src_blocks[index]
441 self.block_index += 1
442 self.block_index += 1
442 if self._silent[index]:
443 if self._silent[index]:
443 print(marquee('Executing silent block # %s (%s remaining)' %
444 print(marquee('Executing silent block # %s (%s remaining)' %
444 (index,self.nblocks-index-1)), file=io.stdout)
445 (index,self.nblocks-index-1)), file=io.stdout)
445 else:
446 else:
446 self.pre_cmd()
447 self.pre_cmd()
447 self.show(index)
448 self.show(index)
448 if self.auto_all or self._auto[index]:
449 if self.auto_all or self._auto[index]:
449 print(marquee('output:'), file=io.stdout)
450 print(marquee('output:'), file=io.stdout)
450 else:
451 else:
451 print(marquee('Press <q> to quit, <Enter> to execute...'), end=' ', file=io.stdout)
452 print(marquee('Press <q> to quit, <Enter> to execute...'), end=' ', file=io.stdout)
452 ans = raw_input().strip()
453 ans = py3compat.input().strip()
453 if ans:
454 if ans:
454 print(marquee('Block NOT executed'), file=io.stdout)
455 print(marquee('Block NOT executed'), file=io.stdout)
455 return
456 return
456 try:
457 try:
457 save_argv = sys.argv
458 save_argv = sys.argv
458 sys.argv = self.sys_argv
459 sys.argv = self.sys_argv
459 self.run_cell(next_block)
460 self.run_cell(next_block)
460 self.post_cmd()
461 self.post_cmd()
461 finally:
462 finally:
462 sys.argv = save_argv
463 sys.argv = save_argv
463
464
464 except:
465 except:
465 self.ip_showtb(filename=self.fname)
466 self.ip_showtb(filename=self.fname)
466 else:
467 else:
467 self.ip_ns.update(self.user_ns)
468 self.ip_ns.update(self.user_ns)
468
469
469 if self.block_index == self.nblocks:
470 if self.block_index == self.nblocks:
470 mq1 = self.marquee('END OF DEMO')
471 mq1 = self.marquee('END OF DEMO')
471 if mq1:
472 if mq1:
472 # avoid spurious print >>io.stdout,s if empty marquees are used
473 # avoid spurious print >>io.stdout,s if empty marquees are used
473 print(file=io.stdout)
474 print(file=io.stdout)
474 print(mq1, file=io.stdout)
475 print(mq1, file=io.stdout)
475 print(self.marquee('Use <demo_name>.reset() if you want to rerun it.'), file=io.stdout)
476 print(self.marquee('Use <demo_name>.reset() if you want to rerun it.'), file=io.stdout)
476 self.finished = True
477 self.finished = True
477
478
478 # These methods are meant to be overridden by subclasses who may wish to
479 # These methods are meant to be overridden by subclasses who may wish to
479 # customize the behavior of of their demos.
480 # customize the behavior of of their demos.
480 def marquee(self,txt='',width=78,mark='*'):
481 def marquee(self,txt='',width=78,mark='*'):
481 """Return the input string centered in a 'marquee'."""
482 """Return the input string centered in a 'marquee'."""
482 return marquee(txt,width,mark)
483 return marquee(txt,width,mark)
483
484
484 def pre_cmd(self):
485 def pre_cmd(self):
485 """Method called before executing each block."""
486 """Method called before executing each block."""
486 pass
487 pass
487
488
488 def post_cmd(self):
489 def post_cmd(self):
489 """Method called after executing each block."""
490 """Method called after executing each block."""
490 pass
491 pass
491
492
492
493
493 class IPythonDemo(Demo):
494 class IPythonDemo(Demo):
494 """Class for interactive demos with IPython's input processing applied.
495 """Class for interactive demos with IPython's input processing applied.
495
496
496 This subclasses Demo, but instead of executing each block by the Python
497 This subclasses Demo, but instead of executing each block by the Python
497 interpreter (via exec), it actually calls IPython on it, so that any input
498 interpreter (via exec), it actually calls IPython on it, so that any input
498 filters which may be in place are applied to the input block.
499 filters which may be in place are applied to the input block.
499
500
500 If you have an interactive environment which exposes special input
501 If you have an interactive environment which exposes special input
501 processing, you can use this class instead to write demo scripts which
502 processing, you can use this class instead to write demo scripts which
502 operate exactly as if you had typed them interactively. The default Demo
503 operate exactly as if you had typed them interactively. The default Demo
503 class requires the input to be valid, pure Python code.
504 class requires the input to be valid, pure Python code.
504 """
505 """
505
506
506 def run_cell(self,source):
507 def run_cell(self,source):
507 """Execute a string with one or more lines of code"""
508 """Execute a string with one or more lines of code"""
508
509
509 self.shell.run_cell(source)
510 self.shell.run_cell(source)
510
511
511 class LineDemo(Demo):
512 class LineDemo(Demo):
512 """Demo where each line is executed as a separate block.
513 """Demo where each line is executed as a separate block.
513
514
514 The input script should be valid Python code.
515 The input script should be valid Python code.
515
516
516 This class doesn't require any markup at all, and it's meant for simple
517 This class doesn't require any markup at all, and it's meant for simple
517 scripts (with no nesting or any kind of indentation) which consist of
518 scripts (with no nesting or any kind of indentation) which consist of
518 multiple lines of input to be executed, one at a time, as if they had been
519 multiple lines of input to be executed, one at a time, as if they had been
519 typed in the interactive prompt.
520 typed in the interactive prompt.
520
521
521 Note: the input can not have *any* indentation, which means that only
522 Note: the input can not have *any* indentation, which means that only
522 single-lines of input are accepted, not even function definitions are
523 single-lines of input are accepted, not even function definitions are
523 valid."""
524 valid."""
524
525
525 def reload(self):
526 def reload(self):
526 """Reload source from disk and initialize state."""
527 """Reload source from disk and initialize state."""
527 # read data and parse into blocks
528 # read data and parse into blocks
528 self.fload()
529 self.fload()
529 lines = self.fobj.readlines()
530 lines = self.fobj.readlines()
530 src_b = [l for l in lines if l.strip()]
531 src_b = [l for l in lines if l.strip()]
531 nblocks = len(src_b)
532 nblocks = len(src_b)
532 self.src = ''.join(lines)
533 self.src = ''.join(lines)
533 self._silent = [False]*nblocks
534 self._silent = [False]*nblocks
534 self._auto = [True]*nblocks
535 self._auto = [True]*nblocks
535 self.auto_all = True
536 self.auto_all = True
536 self.nblocks = nblocks
537 self.nblocks = nblocks
537 self.src_blocks = src_b
538 self.src_blocks = src_b
538
539
539 # also build syntax-highlighted source
540 # also build syntax-highlighted source
540 self.src_blocks_colored = map(self.ip_colorize,self.src_blocks)
541 self.src_blocks_colored = map(self.ip_colorize,self.src_blocks)
541
542
542 # ensure clean namespace and seek offset
543 # ensure clean namespace and seek offset
543 self.reset()
544 self.reset()
544
545
545
546
546 class IPythonLineDemo(IPythonDemo,LineDemo):
547 class IPythonLineDemo(IPythonDemo,LineDemo):
547 """Variant of the LineDemo class whose input is processed by IPython."""
548 """Variant of the LineDemo class whose input is processed by IPython."""
548 pass
549 pass
549
550
550
551
551 class ClearMixin(object):
552 class ClearMixin(object):
552 """Use this mixin to make Demo classes with less visual clutter.
553 """Use this mixin to make Demo classes with less visual clutter.
553
554
554 Demos using this mixin will clear the screen before every block and use
555 Demos using this mixin will clear the screen before every block and use
555 blank marquees.
556 blank marquees.
556
557
557 Note that in order for the methods defined here to actually override those
558 Note that in order for the methods defined here to actually override those
558 of the classes it's mixed with, it must go /first/ in the inheritance
559 of the classes it's mixed with, it must go /first/ in the inheritance
559 tree. For example:
560 tree. For example:
560
561
561 class ClearIPDemo(ClearMixin,IPythonDemo): pass
562 class ClearIPDemo(ClearMixin,IPythonDemo): pass
562
563
563 will provide an IPythonDemo class with the mixin's features.
564 will provide an IPythonDemo class with the mixin's features.
564 """
565 """
565
566
566 def marquee(self,txt='',width=78,mark='*'):
567 def marquee(self,txt='',width=78,mark='*'):
567 """Blank marquee that returns '' no matter what the input."""
568 """Blank marquee that returns '' no matter what the input."""
568 return ''
569 return ''
569
570
570 def pre_cmd(self):
571 def pre_cmd(self):
571 """Method called before executing each block.
572 """Method called before executing each block.
572
573
573 This one simply clears the screen."""
574 This one simply clears the screen."""
574 from IPython.utils.terminal import term_clear
575 from IPython.utils.terminal import term_clear
575 term_clear()
576 term_clear()
576
577
577 class ClearDemo(ClearMixin,Demo):
578 class ClearDemo(ClearMixin,Demo):
578 pass
579 pass
579
580
580
581
581 class ClearIPDemo(ClearMixin,IPythonDemo):
582 class ClearIPDemo(ClearMixin,IPythonDemo):
582 pass
583 pass
@@ -1,123 +1,124 b''
1 """ 'editor' hooks for common editors that work well with ipython
1 """ 'editor' hooks for common editors that work well with ipython
2
2
3 They should honor the line number argument, at least.
3 They should honor the line number argument, at least.
4
4
5 Contributions are *very* welcome.
5 Contributions are *very* welcome.
6 """
6 """
7 from __future__ import print_function
7 from __future__ import print_function
8
8
9 import os
9 import os
10 import pipes
10 import pipes
11 import subprocess
11 import subprocess
12
12
13 from IPython import get_ipython
13 from IPython import get_ipython
14 from IPython.core.error import TryNext
14 from IPython.core.error import TryNext
15 from IPython.utils import py3compat
15
16
16
17
17 def install_editor(template, wait=False):
18 def install_editor(template, wait=False):
18 """Installs the editor that is called by IPython for the %edit magic.
19 """Installs the editor that is called by IPython for the %edit magic.
19
20
20 This overrides the default editor, which is generally set by your EDITOR
21 This overrides the default editor, which is generally set by your EDITOR
21 environment variable or is notepad (windows) or vi (linux). By supplying a
22 environment variable or is notepad (windows) or vi (linux). By supplying a
22 template string `run_template`, you can control how the editor is invoked
23 template string `run_template`, you can control how the editor is invoked
23 by IPython -- (e.g. the format in which it accepts command line options)
24 by IPython -- (e.g. the format in which it accepts command line options)
24
25
25 Parameters
26 Parameters
26 ----------
27 ----------
27 template : basestring
28 template : basestring
28 run_template acts as a template for how your editor is invoked by
29 run_template acts as a template for how your editor is invoked by
29 the shell. It should contain '{filename}', which will be replaced on
30 the shell. It should contain '{filename}', which will be replaced on
30 invokation with the file name, and '{line}', $line by line number
31 invokation with the file name, and '{line}', $line by line number
31 (or 0) to invoke the file with.
32 (or 0) to invoke the file with.
32 wait : bool
33 wait : bool
33 If `wait` is true, wait until the user presses enter before returning,
34 If `wait` is true, wait until the user presses enter before returning,
34 to facilitate non-blocking editors that exit immediately after
35 to facilitate non-blocking editors that exit immediately after
35 the call.
36 the call.
36 """
37 """
37
38
38 # not all editors support $line, so we'll leave out this check
39 # not all editors support $line, so we'll leave out this check
39 # for substitution in ['$file', '$line']:
40 # for substitution in ['$file', '$line']:
40 # if not substitution in run_template:
41 # if not substitution in run_template:
41 # raise ValueError(('run_template should contain %s'
42 # raise ValueError(('run_template should contain %s'
42 # ' for string substitution. You supplied "%s"' % (substitution,
43 # ' for string substitution. You supplied "%s"' % (substitution,
43 # run_template)))
44 # run_template)))
44
45
45 def call_editor(self, filename, line=0):
46 def call_editor(self, filename, line=0):
46 if line is None:
47 if line is None:
47 line = 0
48 line = 0
48 cmd = template.format(filename=pipes.quote(filename), line=line)
49 cmd = template.format(filename=pipes.quote(filename), line=line)
49 print(">", cmd)
50 print(">", cmd)
50 proc = subprocess.Popen(cmd, shell=True)
51 proc = subprocess.Popen(cmd, shell=True)
51 if wait and proc.wait() != 0:
52 if wait and proc.wait() != 0:
52 raise TryNext()
53 raise TryNext()
53 if wait:
54 if wait:
54 raw_input("Press Enter when done editing:")
55 py3compat.input("Press Enter when done editing:")
55
56
56 get_ipython().set_hook('editor', call_editor)
57 get_ipython().set_hook('editor', call_editor)
57 get_ipython().editor = template
58 get_ipython().editor = template
58
59
59
60
60 # in these, exe is always the path/name of the executable. Useful
61 # in these, exe is always the path/name of the executable. Useful
61 # if you don't have the editor directory in your path
62 # if you don't have the editor directory in your path
62 def komodo(exe=u'komodo'):
63 def komodo(exe=u'komodo'):
63 """ Activestate Komodo [Edit] """
64 """ Activestate Komodo [Edit] """
64 install_editor(exe + u' -l {line} {filename}', wait=True)
65 install_editor(exe + u' -l {line} {filename}', wait=True)
65
66
66
67
67 def scite(exe=u"scite"):
68 def scite(exe=u"scite"):
68 """ SciTE or Sc1 """
69 """ SciTE or Sc1 """
69 install_editor(exe + u' {filename} -goto:{line}')
70 install_editor(exe + u' {filename} -goto:{line}')
70
71
71
72
72 def notepadplusplus(exe=u'notepad++'):
73 def notepadplusplus(exe=u'notepad++'):
73 """ Notepad++ http://notepad-plus.sourceforge.net """
74 """ Notepad++ http://notepad-plus.sourceforge.net """
74 install_editor(exe + u' -n{line} {filename}')
75 install_editor(exe + u' -n{line} {filename}')
75
76
76
77
77 def jed(exe=u'jed'):
78 def jed(exe=u'jed'):
78 """ JED, the lightweight emacsish editor """
79 """ JED, the lightweight emacsish editor """
79 install_editor(exe + u' +{line} {filename}')
80 install_editor(exe + u' +{line} {filename}')
80
81
81
82
82 def idle(exe=u'idle'):
83 def idle(exe=u'idle'):
83 """ Idle, the editor bundled with python
84 """ Idle, the editor bundled with python
84
85
85 Parameters
86 Parameters
86 ----------
87 ----------
87 exe : str, None
88 exe : str, None
88 If none, should be pretty smart about finding the executable.
89 If none, should be pretty smart about finding the executable.
89 """
90 """
90 if exe is None:
91 if exe is None:
91 import idlelib
92 import idlelib
92 p = os.path.dirname(idlelib.__filename__)
93 p = os.path.dirname(idlelib.__filename__)
93 # i'm not sure if this actually works. Is this idle.py script
94 # i'm not sure if this actually works. Is this idle.py script
94 # guarenteed to be executable?
95 # guarenteed to be executable?
95 exe = os.path.join(p, 'idle.py')
96 exe = os.path.join(p, 'idle.py')
96 install_editor(exe + u' {filename}')
97 install_editor(exe + u' {filename}')
97
98
98
99
99 def mate(exe=u'mate'):
100 def mate(exe=u'mate'):
100 """ TextMate, the missing editor"""
101 """ TextMate, the missing editor"""
101 # wait=True is not required since we're using the -w flag to mate
102 # wait=True is not required since we're using the -w flag to mate
102 install_editor(exe + u' -w -l {line} {filename}')
103 install_editor(exe + u' -w -l {line} {filename}')
103
104
104
105
105 # ##########################################
106 # ##########################################
106 # these are untested, report any problems
107 # these are untested, report any problems
107 # ##########################################
108 # ##########################################
108
109
109
110
110 def emacs(exe=u'emacs'):
111 def emacs(exe=u'emacs'):
111 install_editor(exe + u' +{line} {filename}')
112 install_editor(exe + u' +{line} {filename}')
112
113
113
114
114 def gnuclient(exe=u'gnuclient'):
115 def gnuclient(exe=u'gnuclient'):
115 install_editor(exe + u' -nw +{line} {filename}')
116 install_editor(exe + u' -nw +{line} {filename}')
116
117
117
118
118 def crimson_editor(exe=u'cedt.exe'):
119 def crimson_editor(exe=u'cedt.exe'):
119 install_editor(exe + u' /L:{line} {filename}')
120 install_editor(exe + u' /L:{line} {filename}')
120
121
121
122
122 def kate(exe=u'kate'):
123 def kate(exe=u'kate'):
123 install_editor(exe + u' -u -l {line} {filename}')
124 install_editor(exe + u' -u -l {line} {filename}')
@@ -1,492 +1,492 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """terminal client to the IPython kernel
2 """terminal client to the IPython kernel
3
3
4 """
4 """
5 #-----------------------------------------------------------------------------
5 #-----------------------------------------------------------------------------
6 # Copyright (C) 2013 The IPython Development Team
6 # Copyright (C) 2013 The IPython Development Team
7 #
7 #
8 # Distributed under the terms of the BSD License. The full license is in
8 # Distributed under the terms of the BSD License. The full license is in
9 # the file COPYING, distributed as part of this software.
9 # the file COPYING, distributed as part of this software.
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11
11
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13 # Imports
13 # Imports
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 from __future__ import print_function
15 from __future__ import print_function
16
16
17 import bdb
17 import bdb
18 import signal
18 import signal
19 import os
19 import os
20 import sys
20 import sys
21 import time
21 import time
22 import subprocess
22 import subprocess
23 from io import BytesIO
23 from io import BytesIO
24 import base64
24 import base64
25
25
26 try:
26 try:
27 from queue import Empty # Py 3
27 from queue import Empty # Py 3
28 except ImportError:
28 except ImportError:
29 from Queue import Empty # Py 2
29 from Queue import Empty # Py 2
30
30
31 from IPython.core import page
31 from IPython.core import page
32 from IPython.utils.warn import warn, error
32 from IPython.utils.warn import warn, error
33 from IPython.utils import io
33 from IPython.utils import io
34 from IPython.utils.py3compat import string_types
34 from IPython.utils.py3compat import string_types, input
35 from IPython.utils.traitlets import List, Enum, Any, Instance, Unicode, Float
35 from IPython.utils.traitlets import List, Enum, Any, Instance, Unicode, Float
36 from IPython.utils.tempdir import NamedFileInTemporaryDirectory
36 from IPython.utils.tempdir import NamedFileInTemporaryDirectory
37
37
38 from IPython.terminal.interactiveshell import TerminalInteractiveShell
38 from IPython.terminal.interactiveshell import TerminalInteractiveShell
39 from IPython.terminal.console.completer import ZMQCompleter
39 from IPython.terminal.console.completer import ZMQCompleter
40
40
41
41
42 class ZMQTerminalInteractiveShell(TerminalInteractiveShell):
42 class ZMQTerminalInteractiveShell(TerminalInteractiveShell):
43 """A subclass of TerminalInteractiveShell that uses the 0MQ kernel"""
43 """A subclass of TerminalInteractiveShell that uses the 0MQ kernel"""
44 _executing = False
44 _executing = False
45 _execution_state = Unicode('')
45 _execution_state = Unicode('')
46 kernel_timeout = Float(60, config=True,
46 kernel_timeout = Float(60, config=True,
47 help="""Timeout for giving up on a kernel (in seconds).
47 help="""Timeout for giving up on a kernel (in seconds).
48
48
49 On first connect and restart, the console tests whether the
49 On first connect and restart, the console tests whether the
50 kernel is running and responsive by sending kernel_info_requests.
50 kernel is running and responsive by sending kernel_info_requests.
51 This sets the timeout in seconds for how long the kernel can take
51 This sets the timeout in seconds for how long the kernel can take
52 before being presumed dead.
52 before being presumed dead.
53 """
53 """
54 )
54 )
55
55
56 image_handler = Enum(('PIL', 'stream', 'tempfile', 'callable'),
56 image_handler = Enum(('PIL', 'stream', 'tempfile', 'callable'),
57 config=True, help=
57 config=True, help=
58 """
58 """
59 Handler for image type output. This is useful, for example,
59 Handler for image type output. This is useful, for example,
60 when connecting to the kernel in which pylab inline backend is
60 when connecting to the kernel in which pylab inline backend is
61 activated. There are four handlers defined. 'PIL': Use
61 activated. There are four handlers defined. 'PIL': Use
62 Python Imaging Library to popup image; 'stream': Use an
62 Python Imaging Library to popup image; 'stream': Use an
63 external program to show the image. Image will be fed into
63 external program to show the image. Image will be fed into
64 the STDIN of the program. You will need to configure
64 the STDIN of the program. You will need to configure
65 `stream_image_handler`; 'tempfile': Use an external program to
65 `stream_image_handler`; 'tempfile': Use an external program to
66 show the image. Image will be saved in a temporally file and
66 show the image. Image will be saved in a temporally file and
67 the program is called with the temporally file. You will need
67 the program is called with the temporally file. You will need
68 to configure `tempfile_image_handler`; 'callable': You can set
68 to configure `tempfile_image_handler`; 'callable': You can set
69 any Python callable which is called with the image data. You
69 any Python callable which is called with the image data. You
70 will need to configure `callable_image_handler`.
70 will need to configure `callable_image_handler`.
71 """
71 """
72 )
72 )
73
73
74 stream_image_handler = List(config=True, help=
74 stream_image_handler = List(config=True, help=
75 """
75 """
76 Command to invoke an image viewer program when you are using
76 Command to invoke an image viewer program when you are using
77 'stream' image handler. This option is a list of string where
77 'stream' image handler. This option is a list of string where
78 the first element is the command itself and reminders are the
78 the first element is the command itself and reminders are the
79 options for the command. Raw image data is given as STDIN to
79 options for the command. Raw image data is given as STDIN to
80 the program.
80 the program.
81 """
81 """
82 )
82 )
83
83
84 tempfile_image_handler = List(config=True, help=
84 tempfile_image_handler = List(config=True, help=
85 """
85 """
86 Command to invoke an image viewer program when you are using
86 Command to invoke an image viewer program when you are using
87 'tempfile' image handler. This option is a list of string
87 'tempfile' image handler. This option is a list of string
88 where the first element is the command itself and reminders
88 where the first element is the command itself and reminders
89 are the options for the command. You can use {file} and
89 are the options for the command. You can use {file} and
90 {format} in the string to represent the location of the
90 {format} in the string to represent the location of the
91 generated image file and image format.
91 generated image file and image format.
92 """
92 """
93 )
93 )
94
94
95 callable_image_handler = Any(config=True, help=
95 callable_image_handler = Any(config=True, help=
96 """
96 """
97 Callable object called via 'callable' image handler with one
97 Callable object called via 'callable' image handler with one
98 argument, `data`, which is `msg["content"]["data"]` where
98 argument, `data`, which is `msg["content"]["data"]` where
99 `msg` is the message from iopub channel. For exmaple, you can
99 `msg` is the message from iopub channel. For exmaple, you can
100 find base64 encoded PNG data as `data['image/png']`.
100 find base64 encoded PNG data as `data['image/png']`.
101 """
101 """
102 )
102 )
103
103
104 mime_preference = List(
104 mime_preference = List(
105 default_value=['image/png', 'image/jpeg', 'image/svg+xml'],
105 default_value=['image/png', 'image/jpeg', 'image/svg+xml'],
106 config=True, allow_none=False, help=
106 config=True, allow_none=False, help=
107 """
107 """
108 Preferred object representation MIME type in order. First
108 Preferred object representation MIME type in order. First
109 matched MIME type will be used.
109 matched MIME type will be used.
110 """
110 """
111 )
111 )
112
112
113 manager = Instance('IPython.kernel.KernelManager')
113 manager = Instance('IPython.kernel.KernelManager')
114 client = Instance('IPython.kernel.KernelClient')
114 client = Instance('IPython.kernel.KernelClient')
115 def _client_changed(self, name, old, new):
115 def _client_changed(self, name, old, new):
116 self.session_id = new.session.session
116 self.session_id = new.session.session
117 session_id = Unicode()
117 session_id = Unicode()
118
118
119 def init_completer(self):
119 def init_completer(self):
120 """Initialize the completion machinery.
120 """Initialize the completion machinery.
121
121
122 This creates completion machinery that can be used by client code,
122 This creates completion machinery that can be used by client code,
123 either interactively in-process (typically triggered by the readline
123 either interactively in-process (typically triggered by the readline
124 library), programatically (such as in test suites) or out-of-prcess
124 library), programatically (such as in test suites) or out-of-prcess
125 (typically over the network by remote frontends).
125 (typically over the network by remote frontends).
126 """
126 """
127 from IPython.core.completerlib import (module_completer,
127 from IPython.core.completerlib import (module_completer,
128 magic_run_completer, cd_completer)
128 magic_run_completer, cd_completer)
129
129
130 self.Completer = ZMQCompleter(self, self.client, config=self.config)
130 self.Completer = ZMQCompleter(self, self.client, config=self.config)
131
131
132
132
133 self.set_hook('complete_command', module_completer, str_key = 'import')
133 self.set_hook('complete_command', module_completer, str_key = 'import')
134 self.set_hook('complete_command', module_completer, str_key = 'from')
134 self.set_hook('complete_command', module_completer, str_key = 'from')
135 self.set_hook('complete_command', magic_run_completer, str_key = '%run')
135 self.set_hook('complete_command', magic_run_completer, str_key = '%run')
136 self.set_hook('complete_command', cd_completer, str_key = '%cd')
136 self.set_hook('complete_command', cd_completer, str_key = '%cd')
137
137
138 # Only configure readline if we truly are using readline. IPython can
138 # Only configure readline if we truly are using readline. IPython can
139 # do tab-completion over the network, in GUIs, etc, where readline
139 # do tab-completion over the network, in GUIs, etc, where readline
140 # itself may be absent
140 # itself may be absent
141 if self.has_readline:
141 if self.has_readline:
142 self.set_readline_completer()
142 self.set_readline_completer()
143
143
144 def run_cell(self, cell, store_history=True):
144 def run_cell(self, cell, store_history=True):
145 """Run a complete IPython cell.
145 """Run a complete IPython cell.
146
146
147 Parameters
147 Parameters
148 ----------
148 ----------
149 cell : str
149 cell : str
150 The code (including IPython code such as %magic functions) to run.
150 The code (including IPython code such as %magic functions) to run.
151 store_history : bool
151 store_history : bool
152 If True, the raw and translated cell will be stored in IPython's
152 If True, the raw and translated cell will be stored in IPython's
153 history. For user code calling back into IPython's machinery, this
153 history. For user code calling back into IPython's machinery, this
154 should be set to False.
154 should be set to False.
155 """
155 """
156 if (not cell) or cell.isspace():
156 if (not cell) or cell.isspace():
157 return
157 return
158
158
159 if cell.strip() == 'exit':
159 if cell.strip() == 'exit':
160 # explicitly handle 'exit' command
160 # explicitly handle 'exit' command
161 return self.ask_exit()
161 return self.ask_exit()
162
162
163 # flush stale replies, which could have been ignored, due to missed heartbeats
163 # flush stale replies, which could have been ignored, due to missed heartbeats
164 while self.client.shell_channel.msg_ready():
164 while self.client.shell_channel.msg_ready():
165 self.client.shell_channel.get_msg()
165 self.client.shell_channel.get_msg()
166 # shell_channel.execute takes 'hidden', which is the inverse of store_hist
166 # shell_channel.execute takes 'hidden', which is the inverse of store_hist
167 msg_id = self.client.shell_channel.execute(cell, not store_history)
167 msg_id = self.client.shell_channel.execute(cell, not store_history)
168
168
169 # first thing is wait for any side effects (output, stdin, etc.)
169 # first thing is wait for any side effects (output, stdin, etc.)
170 self._executing = True
170 self._executing = True
171 self._execution_state = "busy"
171 self._execution_state = "busy"
172 while self._execution_state != 'idle' and self.client.is_alive():
172 while self._execution_state != 'idle' and self.client.is_alive():
173 try:
173 try:
174 self.handle_stdin_request(msg_id, timeout=0.05)
174 self.handle_stdin_request(msg_id, timeout=0.05)
175 except Empty:
175 except Empty:
176 # display intermediate print statements, etc.
176 # display intermediate print statements, etc.
177 self.handle_iopub(msg_id)
177 self.handle_iopub(msg_id)
178 pass
178 pass
179
179
180 # after all of that is done, wait for the execute reply
180 # after all of that is done, wait for the execute reply
181 while self.client.is_alive():
181 while self.client.is_alive():
182 try:
182 try:
183 self.handle_execute_reply(msg_id, timeout=0.05)
183 self.handle_execute_reply(msg_id, timeout=0.05)
184 except Empty:
184 except Empty:
185 pass
185 pass
186 else:
186 else:
187 break
187 break
188 self._executing = False
188 self._executing = False
189
189
190 #-----------------
190 #-----------------
191 # message handlers
191 # message handlers
192 #-----------------
192 #-----------------
193
193
194 def handle_execute_reply(self, msg_id, timeout=None):
194 def handle_execute_reply(self, msg_id, timeout=None):
195 msg = self.client.shell_channel.get_msg(block=False, timeout=timeout)
195 msg = self.client.shell_channel.get_msg(block=False, timeout=timeout)
196 if msg["parent_header"].get("msg_id", None) == msg_id:
196 if msg["parent_header"].get("msg_id", None) == msg_id:
197
197
198 self.handle_iopub(msg_id)
198 self.handle_iopub(msg_id)
199
199
200 content = msg["content"]
200 content = msg["content"]
201 status = content['status']
201 status = content['status']
202
202
203 if status == 'aborted':
203 if status == 'aborted':
204 self.write('Aborted\n')
204 self.write('Aborted\n')
205 return
205 return
206 elif status == 'ok':
206 elif status == 'ok':
207 # print execution payloads as well:
207 # print execution payloads as well:
208 for item in content["payload"]:
208 for item in content["payload"]:
209 text = item.get('text', None)
209 text = item.get('text', None)
210 if text:
210 if text:
211 page.page(text)
211 page.page(text)
212
212
213 elif status == 'error':
213 elif status == 'error':
214 for frame in content["traceback"]:
214 for frame in content["traceback"]:
215 print(frame, file=io.stderr)
215 print(frame, file=io.stderr)
216
216
217 self.execution_count = int(content["execution_count"] + 1)
217 self.execution_count = int(content["execution_count"] + 1)
218
218
219
219
220 def handle_iopub(self, msg_id):
220 def handle_iopub(self, msg_id):
221 """ Method to process subscribe channel's messages
221 """ Method to process subscribe channel's messages
222
222
223 This method consumes and processes messages on the IOPub channel,
223 This method consumes and processes messages on the IOPub channel,
224 such as stdout, stderr, pyout and status.
224 such as stdout, stderr, pyout and status.
225
225
226 It only displays output that is caused by the given msg_id
226 It only displays output that is caused by the given msg_id
227 """
227 """
228 while self.client.iopub_channel.msg_ready():
228 while self.client.iopub_channel.msg_ready():
229 sub_msg = self.client.iopub_channel.get_msg()
229 sub_msg = self.client.iopub_channel.get_msg()
230 msg_type = sub_msg['header']['msg_type']
230 msg_type = sub_msg['header']['msg_type']
231 parent = sub_msg["parent_header"]
231 parent = sub_msg["parent_header"]
232 if (not parent) or msg_id == parent['msg_id']:
232 if (not parent) or msg_id == parent['msg_id']:
233 if msg_type == 'status':
233 if msg_type == 'status':
234 state = self._execution_state = sub_msg["content"]["execution_state"]
234 state = self._execution_state = sub_msg["content"]["execution_state"]
235 # idle messages mean an individual sequence is complete,
235 # idle messages mean an individual sequence is complete,
236 # so break out of consumption to allow other things to take over.
236 # so break out of consumption to allow other things to take over.
237 if state == 'idle':
237 if state == 'idle':
238 break
238 break
239
239
240 elif msg_type == 'stream':
240 elif msg_type == 'stream':
241 if sub_msg["content"]["name"] == "stdout":
241 if sub_msg["content"]["name"] == "stdout":
242 print(sub_msg["content"]["data"], file=io.stdout, end="")
242 print(sub_msg["content"]["data"], file=io.stdout, end="")
243 io.stdout.flush()
243 io.stdout.flush()
244 elif sub_msg["content"]["name"] == "stderr" :
244 elif sub_msg["content"]["name"] == "stderr" :
245 print(sub_msg["content"]["data"], file=io.stderr, end="")
245 print(sub_msg["content"]["data"], file=io.stderr, end="")
246 io.stderr.flush()
246 io.stderr.flush()
247
247
248 elif msg_type == 'pyout':
248 elif msg_type == 'pyout':
249 self.execution_count = int(sub_msg["content"]["execution_count"])
249 self.execution_count = int(sub_msg["content"]["execution_count"])
250 format_dict = sub_msg["content"]["data"]
250 format_dict = sub_msg["content"]["data"]
251 self.handle_rich_data(format_dict)
251 self.handle_rich_data(format_dict)
252 # taken from DisplayHook.__call__:
252 # taken from DisplayHook.__call__:
253 hook = self.displayhook
253 hook = self.displayhook
254 hook.start_displayhook()
254 hook.start_displayhook()
255 hook.write_output_prompt()
255 hook.write_output_prompt()
256 hook.write_format_data(format_dict)
256 hook.write_format_data(format_dict)
257 hook.log_output(format_dict)
257 hook.log_output(format_dict)
258 hook.finish_displayhook()
258 hook.finish_displayhook()
259
259
260 elif msg_type == 'display_data':
260 elif msg_type == 'display_data':
261 self.handle_rich_data(sub_msg["content"]["data"])
261 self.handle_rich_data(sub_msg["content"]["data"])
262
262
263
263
264 _imagemime = {
264 _imagemime = {
265 'image/png': 'png',
265 'image/png': 'png',
266 'image/jpeg': 'jpeg',
266 'image/jpeg': 'jpeg',
267 'image/svg+xml': 'svg',
267 'image/svg+xml': 'svg',
268 }
268 }
269
269
270 def handle_rich_data(self, data):
270 def handle_rich_data(self, data):
271 for mime in self.mime_preference:
271 for mime in self.mime_preference:
272 if mime in data and mime in self._imagemime:
272 if mime in data and mime in self._imagemime:
273 self.handle_image(data, mime)
273 self.handle_image(data, mime)
274 return
274 return
275
275
276 def handle_image(self, data, mime):
276 def handle_image(self, data, mime):
277 handler = getattr(
277 handler = getattr(
278 self, 'handle_image_{0}'.format(self.image_handler), None)
278 self, 'handle_image_{0}'.format(self.image_handler), None)
279 if handler:
279 if handler:
280 handler(data, mime)
280 handler(data, mime)
281
281
282 def handle_image_PIL(self, data, mime):
282 def handle_image_PIL(self, data, mime):
283 if mime not in ('image/png', 'image/jpeg'):
283 if mime not in ('image/png', 'image/jpeg'):
284 return
284 return
285 import PIL.Image
285 import PIL.Image
286 raw = base64.decodestring(data[mime].encode('ascii'))
286 raw = base64.decodestring(data[mime].encode('ascii'))
287 img = PIL.Image.open(BytesIO(raw))
287 img = PIL.Image.open(BytesIO(raw))
288 img.show()
288 img.show()
289
289
290 def handle_image_stream(self, data, mime):
290 def handle_image_stream(self, data, mime):
291 raw = base64.decodestring(data[mime].encode('ascii'))
291 raw = base64.decodestring(data[mime].encode('ascii'))
292 imageformat = self._imagemime[mime]
292 imageformat = self._imagemime[mime]
293 fmt = dict(format=imageformat)
293 fmt = dict(format=imageformat)
294 args = [s.format(**fmt) for s in self.stream_image_handler]
294 args = [s.format(**fmt) for s in self.stream_image_handler]
295 with open(os.devnull, 'w') as devnull:
295 with open(os.devnull, 'w') as devnull:
296 proc = subprocess.Popen(
296 proc = subprocess.Popen(
297 args, stdin=subprocess.PIPE,
297 args, stdin=subprocess.PIPE,
298 stdout=devnull, stderr=devnull)
298 stdout=devnull, stderr=devnull)
299 proc.communicate(raw)
299 proc.communicate(raw)
300
300
301 def handle_image_tempfile(self, data, mime):
301 def handle_image_tempfile(self, data, mime):
302 raw = base64.decodestring(data[mime].encode('ascii'))
302 raw = base64.decodestring(data[mime].encode('ascii'))
303 imageformat = self._imagemime[mime]
303 imageformat = self._imagemime[mime]
304 filename = 'tmp.{0}'.format(imageformat)
304 filename = 'tmp.{0}'.format(imageformat)
305 with NamedFileInTemporaryDirectory(filename) as f, \
305 with NamedFileInTemporaryDirectory(filename) as f, \
306 open(os.devnull, 'w') as devnull:
306 open(os.devnull, 'w') as devnull:
307 f.write(raw)
307 f.write(raw)
308 f.flush()
308 f.flush()
309 fmt = dict(file=f.name, format=imageformat)
309 fmt = dict(file=f.name, format=imageformat)
310 args = [s.format(**fmt) for s in self.tempfile_image_handler]
310 args = [s.format(**fmt) for s in self.tempfile_image_handler]
311 subprocess.call(args, stdout=devnull, stderr=devnull)
311 subprocess.call(args, stdout=devnull, stderr=devnull)
312
312
313 def handle_image_callable(self, data, mime):
313 def handle_image_callable(self, data, mime):
314 self.callable_image_handler(data)
314 self.callable_image_handler(data)
315
315
316 def handle_stdin_request(self, msg_id, timeout=0.1):
316 def handle_stdin_request(self, msg_id, timeout=0.1):
317 """ Method to capture raw_input
317 """ Method to capture raw_input
318 """
318 """
319 msg_rep = self.client.stdin_channel.get_msg(timeout=timeout)
319 msg_rep = self.client.stdin_channel.get_msg(timeout=timeout)
320 # in case any iopub came while we were waiting:
320 # in case any iopub came while we were waiting:
321 self.handle_iopub(msg_id)
321 self.handle_iopub(msg_id)
322 if msg_id == msg_rep["parent_header"].get("msg_id"):
322 if msg_id == msg_rep["parent_header"].get("msg_id"):
323 # wrap SIGINT handler
323 # wrap SIGINT handler
324 real_handler = signal.getsignal(signal.SIGINT)
324 real_handler = signal.getsignal(signal.SIGINT)
325 def double_int(sig,frame):
325 def double_int(sig,frame):
326 # call real handler (forwards sigint to kernel),
326 # call real handler (forwards sigint to kernel),
327 # then raise local interrupt, stopping local raw_input
327 # then raise local interrupt, stopping local raw_input
328 real_handler(sig,frame)
328 real_handler(sig,frame)
329 raise KeyboardInterrupt
329 raise KeyboardInterrupt
330 signal.signal(signal.SIGINT, double_int)
330 signal.signal(signal.SIGINT, double_int)
331
331
332 try:
332 try:
333 raw_data = raw_input(msg_rep["content"]["prompt"])
333 raw_data = input(msg_rep["content"]["prompt"])
334 except EOFError:
334 except EOFError:
335 # turn EOFError into EOF character
335 # turn EOFError into EOF character
336 raw_data = '\x04'
336 raw_data = '\x04'
337 except KeyboardInterrupt:
337 except KeyboardInterrupt:
338 sys.stdout.write('\n')
338 sys.stdout.write('\n')
339 return
339 return
340 finally:
340 finally:
341 # restore SIGINT handler
341 # restore SIGINT handler
342 signal.signal(signal.SIGINT, real_handler)
342 signal.signal(signal.SIGINT, real_handler)
343
343
344 # only send stdin reply if there *was not* another request
344 # only send stdin reply if there *was not* another request
345 # or execution finished while we were reading.
345 # or execution finished while we were reading.
346 if not (self.client.stdin_channel.msg_ready() or self.client.shell_channel.msg_ready()):
346 if not (self.client.stdin_channel.msg_ready() or self.client.shell_channel.msg_ready()):
347 self.client.stdin_channel.input(raw_data)
347 self.client.stdin_channel.input(raw_data)
348
348
349 def mainloop(self, display_banner=False):
349 def mainloop(self, display_banner=False):
350 while True:
350 while True:
351 try:
351 try:
352 self.interact(display_banner=display_banner)
352 self.interact(display_banner=display_banner)
353 #self.interact_with_readline()
353 #self.interact_with_readline()
354 # XXX for testing of a readline-decoupled repl loop, call
354 # XXX for testing of a readline-decoupled repl loop, call
355 # interact_with_readline above
355 # interact_with_readline above
356 break
356 break
357 except KeyboardInterrupt:
357 except KeyboardInterrupt:
358 # this should not be necessary, but KeyboardInterrupt
358 # this should not be necessary, but KeyboardInterrupt
359 # handling seems rather unpredictable...
359 # handling seems rather unpredictable...
360 self.write("\nKeyboardInterrupt in interact()\n")
360 self.write("\nKeyboardInterrupt in interact()\n")
361
361
362 def wait_for_kernel(self, timeout=None):
362 def wait_for_kernel(self, timeout=None):
363 """method to wait for a kernel to be ready"""
363 """method to wait for a kernel to be ready"""
364 tic = time.time()
364 tic = time.time()
365 self.client.hb_channel.unpause()
365 self.client.hb_channel.unpause()
366 while True:
366 while True:
367 msg_id = self.client.kernel_info()
367 msg_id = self.client.kernel_info()
368 reply = None
368 reply = None
369 while True:
369 while True:
370 try:
370 try:
371 reply = self.client.get_shell_msg(timeout=1)
371 reply = self.client.get_shell_msg(timeout=1)
372 except Empty:
372 except Empty:
373 break
373 break
374 else:
374 else:
375 if reply['parent_header'].get('msg_id') == msg_id:
375 if reply['parent_header'].get('msg_id') == msg_id:
376 return True
376 return True
377 if timeout is not None \
377 if timeout is not None \
378 and (time.time() - tic) > timeout \
378 and (time.time() - tic) > timeout \
379 and not self.client.hb_channel.is_beating():
379 and not self.client.hb_channel.is_beating():
380 # heart failed
380 # heart failed
381 return False
381 return False
382 return True
382 return True
383
383
384 def interact(self, display_banner=None):
384 def interact(self, display_banner=None):
385 """Closely emulate the interactive Python console."""
385 """Closely emulate the interactive Python console."""
386
386
387 # batch run -> do not interact
387 # batch run -> do not interact
388 if self.exit_now:
388 if self.exit_now:
389 return
389 return
390
390
391 if display_banner is None:
391 if display_banner is None:
392 display_banner = self.display_banner
392 display_banner = self.display_banner
393
393
394 if isinstance(display_banner, string_types):
394 if isinstance(display_banner, string_types):
395 self.show_banner(display_banner)
395 self.show_banner(display_banner)
396 elif display_banner:
396 elif display_banner:
397 self.show_banner()
397 self.show_banner()
398
398
399 more = False
399 more = False
400
400
401 # run a non-empty no-op, so that we don't get a prompt until
401 # run a non-empty no-op, so that we don't get a prompt until
402 # we know the kernel is ready. This keeps the connection
402 # we know the kernel is ready. This keeps the connection
403 # message above the first prompt.
403 # message above the first prompt.
404 if not self.wait_for_kernel(self.kernel_timeout):
404 if not self.wait_for_kernel(self.kernel_timeout):
405 error("Kernel did not respond\n")
405 error("Kernel did not respond\n")
406 return
406 return
407
407
408 if self.has_readline:
408 if self.has_readline:
409 self.readline_startup_hook(self.pre_readline)
409 self.readline_startup_hook(self.pre_readline)
410 hlen_b4_cell = self.readline.get_current_history_length()
410 hlen_b4_cell = self.readline.get_current_history_length()
411 else:
411 else:
412 hlen_b4_cell = 0
412 hlen_b4_cell = 0
413 # exit_now is set by a call to %Exit or %Quit, through the
413 # exit_now is set by a call to %Exit or %Quit, through the
414 # ask_exit callback.
414 # ask_exit callback.
415
415
416 while not self.exit_now:
416 while not self.exit_now:
417 if not self.client.is_alive():
417 if not self.client.is_alive():
418 # kernel died, prompt for action or exit
418 # kernel died, prompt for action or exit
419
419
420 action = "restart" if self.manager else "wait for restart"
420 action = "restart" if self.manager else "wait for restart"
421 ans = self.ask_yes_no("kernel died, %s ([y]/n)?" % action, default='y')
421 ans = self.ask_yes_no("kernel died, %s ([y]/n)?" % action, default='y')
422 if ans:
422 if ans:
423 if self.manager:
423 if self.manager:
424 self.manager.restart_kernel(True)
424 self.manager.restart_kernel(True)
425 self.wait_for_kernel(self.kernel_timeout)
425 self.wait_for_kernel(self.kernel_timeout)
426 else:
426 else:
427 self.exit_now = True
427 self.exit_now = True
428 continue
428 continue
429 try:
429 try:
430 # protect prompt block from KeyboardInterrupt
430 # protect prompt block from KeyboardInterrupt
431 # when sitting on ctrl-C
431 # when sitting on ctrl-C
432 self.hooks.pre_prompt_hook()
432 self.hooks.pre_prompt_hook()
433 if more:
433 if more:
434 try:
434 try:
435 prompt = self.prompt_manager.render('in2')
435 prompt = self.prompt_manager.render('in2')
436 except Exception:
436 except Exception:
437 self.showtraceback()
437 self.showtraceback()
438 if self.autoindent:
438 if self.autoindent:
439 self.rl_do_indent = True
439 self.rl_do_indent = True
440
440
441 else:
441 else:
442 try:
442 try:
443 prompt = self.separate_in + self.prompt_manager.render('in')
443 prompt = self.separate_in + self.prompt_manager.render('in')
444 except Exception:
444 except Exception:
445 self.showtraceback()
445 self.showtraceback()
446
446
447 line = self.raw_input(prompt)
447 line = self.raw_input(prompt)
448 if self.exit_now:
448 if self.exit_now:
449 # quick exit on sys.std[in|out] close
449 # quick exit on sys.std[in|out] close
450 break
450 break
451 if self.autoindent:
451 if self.autoindent:
452 self.rl_do_indent = False
452 self.rl_do_indent = False
453
453
454 except KeyboardInterrupt:
454 except KeyboardInterrupt:
455 #double-guard against keyboardinterrupts during kbdint handling
455 #double-guard against keyboardinterrupts during kbdint handling
456 try:
456 try:
457 self.write('\nKeyboardInterrupt\n')
457 self.write('\nKeyboardInterrupt\n')
458 source_raw = self.input_splitter.source_raw_reset()[1]
458 source_raw = self.input_splitter.source_raw_reset()[1]
459 hlen_b4_cell = self._replace_rlhist_multiline(source_raw, hlen_b4_cell)
459 hlen_b4_cell = self._replace_rlhist_multiline(source_raw, hlen_b4_cell)
460 more = False
460 more = False
461 except KeyboardInterrupt:
461 except KeyboardInterrupt:
462 pass
462 pass
463 except EOFError:
463 except EOFError:
464 if self.autoindent:
464 if self.autoindent:
465 self.rl_do_indent = False
465 self.rl_do_indent = False
466 if self.has_readline:
466 if self.has_readline:
467 self.readline_startup_hook(None)
467 self.readline_startup_hook(None)
468 self.write('\n')
468 self.write('\n')
469 self.exit()
469 self.exit()
470 except bdb.BdbQuit:
470 except bdb.BdbQuit:
471 warn('The Python debugger has exited with a BdbQuit exception.\n'
471 warn('The Python debugger has exited with a BdbQuit exception.\n'
472 'Because of how pdb handles the stack, it is impossible\n'
472 'Because of how pdb handles the stack, it is impossible\n'
473 'for IPython to properly format this particular exception.\n'
473 'for IPython to properly format this particular exception.\n'
474 'IPython will resume normal operation.')
474 'IPython will resume normal operation.')
475 except:
475 except:
476 # exceptions here are VERY RARE, but they can be triggered
476 # exceptions here are VERY RARE, but they can be triggered
477 # asynchronously by signal handlers, for example.
477 # asynchronously by signal handlers, for example.
478 self.showtraceback()
478 self.showtraceback()
479 else:
479 else:
480 self.input_splitter.push(line)
480 self.input_splitter.push(line)
481 more = self.input_splitter.push_accepts_more()
481 more = self.input_splitter.push_accepts_more()
482 if (self.SyntaxTB.last_syntax_error and
482 if (self.SyntaxTB.last_syntax_error and
483 self.autoedit_syntax):
483 self.autoedit_syntax):
484 self.edit_syntax_error()
484 self.edit_syntax_error()
485 if not more:
485 if not more:
486 source_raw = self.input_splitter.source_raw_reset()[1]
486 source_raw = self.input_splitter.source_raw_reset()[1]
487 hlen_b4_cell = self._replace_rlhist_multiline(source_raw, hlen_b4_cell)
487 hlen_b4_cell = self._replace_rlhist_multiline(source_raw, hlen_b4_cell)
488 self.run_cell(source_raw)
488 self.run_cell(source_raw)
489
489
490
490
491 # Turn off the exit flag, so the mainloop can be restarted if desired
491 # Turn off the exit flag, so the mainloop can be restarted if desired
492 self.exit_now = False
492 self.exit_now = False
@@ -1,229 +1,229 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 IO related utilities.
3 IO related utilities.
4 """
4 """
5
5
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
7 # Copyright (C) 2008-2011 The IPython Development Team
7 # Copyright (C) 2008-2011 The IPython Development Team
8 #
8 #
9 # Distributed under the terms of the BSD License. The full license is in
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
10 # the file COPYING, distributed as part of this software.
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12 from __future__ import print_function
12 from __future__ import print_function
13
13
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 # Imports
15 # Imports
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17 import os
17 import os
18 import sys
18 import sys
19 import tempfile
19 import tempfile
20 from .capture import CapturedIO, capture_output
20 from .capture import CapturedIO, capture_output
21 from .py3compat import string_types
21 from .py3compat import string_types, input
22
22
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24 # Code
24 # Code
25 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
26
26
27
27
28 class IOStream:
28 class IOStream:
29
29
30 def __init__(self,stream, fallback=None):
30 def __init__(self,stream, fallback=None):
31 if not hasattr(stream,'write') or not hasattr(stream,'flush'):
31 if not hasattr(stream,'write') or not hasattr(stream,'flush'):
32 if fallback is not None:
32 if fallback is not None:
33 stream = fallback
33 stream = fallback
34 else:
34 else:
35 raise ValueError("fallback required, but not specified")
35 raise ValueError("fallback required, but not specified")
36 self.stream = stream
36 self.stream = stream
37 self._swrite = stream.write
37 self._swrite = stream.write
38
38
39 # clone all methods not overridden:
39 # clone all methods not overridden:
40 def clone(meth):
40 def clone(meth):
41 return not hasattr(self, meth) and not meth.startswith('_')
41 return not hasattr(self, meth) and not meth.startswith('_')
42 for meth in filter(clone, dir(stream)):
42 for meth in filter(clone, dir(stream)):
43 setattr(self, meth, getattr(stream, meth))
43 setattr(self, meth, getattr(stream, meth))
44
44
45 def write(self,data):
45 def write(self,data):
46 try:
46 try:
47 self._swrite(data)
47 self._swrite(data)
48 except:
48 except:
49 try:
49 try:
50 # print handles some unicode issues which may trip a plain
50 # print handles some unicode issues which may trip a plain
51 # write() call. Emulate write() by using an empty end
51 # write() call. Emulate write() by using an empty end
52 # argument.
52 # argument.
53 print(data, end='', file=self.stream)
53 print(data, end='', file=self.stream)
54 except:
54 except:
55 # if we get here, something is seriously broken.
55 # if we get here, something is seriously broken.
56 print('ERROR - failed to write data to stream:', self.stream,
56 print('ERROR - failed to write data to stream:', self.stream,
57 file=sys.stderr)
57 file=sys.stderr)
58
58
59 def writelines(self, lines):
59 def writelines(self, lines):
60 if isinstance(lines, string_types):
60 if isinstance(lines, string_types):
61 lines = [lines]
61 lines = [lines]
62 for line in lines:
62 for line in lines:
63 self.write(line)
63 self.write(line)
64
64
65 # This class used to have a writeln method, but regular files and streams
65 # This class used to have a writeln method, but regular files and streams
66 # in Python don't have this method. We need to keep this completely
66 # in Python don't have this method. We need to keep this completely
67 # compatible so we removed it.
67 # compatible so we removed it.
68
68
69 @property
69 @property
70 def closed(self):
70 def closed(self):
71 return self.stream.closed
71 return self.stream.closed
72
72
73 def close(self):
73 def close(self):
74 pass
74 pass
75
75
76 # setup stdin/stdout/stderr to sys.stdin/sys.stdout/sys.stderr
76 # setup stdin/stdout/stderr to sys.stdin/sys.stdout/sys.stderr
77 devnull = open(os.devnull, 'a')
77 devnull = open(os.devnull, 'a')
78 stdin = IOStream(sys.stdin, fallback=devnull)
78 stdin = IOStream(sys.stdin, fallback=devnull)
79 stdout = IOStream(sys.stdout, fallback=devnull)
79 stdout = IOStream(sys.stdout, fallback=devnull)
80 stderr = IOStream(sys.stderr, fallback=devnull)
80 stderr = IOStream(sys.stderr, fallback=devnull)
81
81
82 class IOTerm:
82 class IOTerm:
83 """ Term holds the file or file-like objects for handling I/O operations.
83 """ Term holds the file or file-like objects for handling I/O operations.
84
84
85 These are normally just sys.stdin, sys.stdout and sys.stderr but for
85 These are normally just sys.stdin, sys.stdout and sys.stderr but for
86 Windows they can can replaced to allow editing the strings before they are
86 Windows they can can replaced to allow editing the strings before they are
87 displayed."""
87 displayed."""
88
88
89 # In the future, having IPython channel all its I/O operations through
89 # In the future, having IPython channel all its I/O operations through
90 # this class will make it easier to embed it into other environments which
90 # this class will make it easier to embed it into other environments which
91 # are not a normal terminal (such as a GUI-based shell)
91 # are not a normal terminal (such as a GUI-based shell)
92 def __init__(self, stdin=None, stdout=None, stderr=None):
92 def __init__(self, stdin=None, stdout=None, stderr=None):
93 mymodule = sys.modules[__name__]
93 mymodule = sys.modules[__name__]
94 self.stdin = IOStream(stdin, mymodule.stdin)
94 self.stdin = IOStream(stdin, mymodule.stdin)
95 self.stdout = IOStream(stdout, mymodule.stdout)
95 self.stdout = IOStream(stdout, mymodule.stdout)
96 self.stderr = IOStream(stderr, mymodule.stderr)
96 self.stderr = IOStream(stderr, mymodule.stderr)
97
97
98
98
99 class Tee(object):
99 class Tee(object):
100 """A class to duplicate an output stream to stdout/err.
100 """A class to duplicate an output stream to stdout/err.
101
101
102 This works in a manner very similar to the Unix 'tee' command.
102 This works in a manner very similar to the Unix 'tee' command.
103
103
104 When the object is closed or deleted, it closes the original file given to
104 When the object is closed or deleted, it closes the original file given to
105 it for duplication.
105 it for duplication.
106 """
106 """
107 # Inspired by:
107 # Inspired by:
108 # http://mail.python.org/pipermail/python-list/2007-May/442737.html
108 # http://mail.python.org/pipermail/python-list/2007-May/442737.html
109
109
110 def __init__(self, file_or_name, mode="w", channel='stdout'):
110 def __init__(self, file_or_name, mode="w", channel='stdout'):
111 """Construct a new Tee object.
111 """Construct a new Tee object.
112
112
113 Parameters
113 Parameters
114 ----------
114 ----------
115 file_or_name : filename or open filehandle (writable)
115 file_or_name : filename or open filehandle (writable)
116 File that will be duplicated
116 File that will be duplicated
117
117
118 mode : optional, valid mode for open().
118 mode : optional, valid mode for open().
119 If a filename was give, open with this mode.
119 If a filename was give, open with this mode.
120
120
121 channel : str, one of ['stdout', 'stderr']
121 channel : str, one of ['stdout', 'stderr']
122 """
122 """
123 if channel not in ['stdout', 'stderr']:
123 if channel not in ['stdout', 'stderr']:
124 raise ValueError('Invalid channel spec %s' % channel)
124 raise ValueError('Invalid channel spec %s' % channel)
125
125
126 if hasattr(file_or_name, 'write') and hasattr(file_or_name, 'seek'):
126 if hasattr(file_or_name, 'write') and hasattr(file_or_name, 'seek'):
127 self.file = file_or_name
127 self.file = file_or_name
128 else:
128 else:
129 self.file = open(file_or_name, mode)
129 self.file = open(file_or_name, mode)
130 self.channel = channel
130 self.channel = channel
131 self.ostream = getattr(sys, channel)
131 self.ostream = getattr(sys, channel)
132 setattr(sys, channel, self)
132 setattr(sys, channel, self)
133 self._closed = False
133 self._closed = False
134
134
135 def close(self):
135 def close(self):
136 """Close the file and restore the channel."""
136 """Close the file and restore the channel."""
137 self.flush()
137 self.flush()
138 setattr(sys, self.channel, self.ostream)
138 setattr(sys, self.channel, self.ostream)
139 self.file.close()
139 self.file.close()
140 self._closed = True
140 self._closed = True
141
141
142 def write(self, data):
142 def write(self, data):
143 """Write data to both channels."""
143 """Write data to both channels."""
144 self.file.write(data)
144 self.file.write(data)
145 self.ostream.write(data)
145 self.ostream.write(data)
146 self.ostream.flush()
146 self.ostream.flush()
147
147
148 def flush(self):
148 def flush(self):
149 """Flush both channels."""
149 """Flush both channels."""
150 self.file.flush()
150 self.file.flush()
151 self.ostream.flush()
151 self.ostream.flush()
152
152
153 def __del__(self):
153 def __del__(self):
154 if not self._closed:
154 if not self._closed:
155 self.close()
155 self.close()
156
156
157
157
158 def ask_yes_no(prompt,default=None):
158 def ask_yes_no(prompt,default=None):
159 """Asks a question and returns a boolean (y/n) answer.
159 """Asks a question and returns a boolean (y/n) answer.
160
160
161 If default is given (one of 'y','n'), it is used if the user input is
161 If default is given (one of 'y','n'), it is used if the user input is
162 empty. Otherwise the question is repeated until an answer is given.
162 empty. Otherwise the question is repeated until an answer is given.
163
163
164 An EOF is treated as the default answer. If there is no default, an
164 An EOF is treated as the default answer. If there is no default, an
165 exception is raised to prevent infinite loops.
165 exception is raised to prevent infinite loops.
166
166
167 Valid answers are: y/yes/n/no (match is not case sensitive)."""
167 Valid answers are: y/yes/n/no (match is not case sensitive)."""
168
168
169 answers = {'y':True,'n':False,'yes':True,'no':False}
169 answers = {'y':True,'n':False,'yes':True,'no':False}
170 ans = None
170 ans = None
171 while ans not in answers.keys():
171 while ans not in answers.keys():
172 try:
172 try:
173 ans = raw_input(prompt+' ').lower()
173 ans = input(prompt+' ').lower()
174 if not ans: # response was an empty string
174 if not ans: # response was an empty string
175 ans = default
175 ans = default
176 except KeyboardInterrupt:
176 except KeyboardInterrupt:
177 pass
177 pass
178 except EOFError:
178 except EOFError:
179 if default in answers.keys():
179 if default in answers.keys():
180 ans = default
180 ans = default
181 print()
181 print()
182 else:
182 else:
183 raise
183 raise
184
184
185 return answers[ans]
185 return answers[ans]
186
186
187
187
188 def temp_pyfile(src, ext='.py'):
188 def temp_pyfile(src, ext='.py'):
189 """Make a temporary python file, return filename and filehandle.
189 """Make a temporary python file, return filename and filehandle.
190
190
191 Parameters
191 Parameters
192 ----------
192 ----------
193 src : string or list of strings (no need for ending newlines if list)
193 src : string or list of strings (no need for ending newlines if list)
194 Source code to be written to the file.
194 Source code to be written to the file.
195
195
196 ext : optional, string
196 ext : optional, string
197 Extension for the generated file.
197 Extension for the generated file.
198
198
199 Returns
199 Returns
200 -------
200 -------
201 (filename, open filehandle)
201 (filename, open filehandle)
202 It is the caller's responsibility to close the open file and unlink it.
202 It is the caller's responsibility to close the open file and unlink it.
203 """
203 """
204 fname = tempfile.mkstemp(ext)[1]
204 fname = tempfile.mkstemp(ext)[1]
205 f = open(fname,'w')
205 f = open(fname,'w')
206 f.write(src)
206 f.write(src)
207 f.flush()
207 f.flush()
208 return fname, f
208 return fname, f
209
209
210
210
211 def raw_print(*args, **kw):
211 def raw_print(*args, **kw):
212 """Raw print to sys.__stdout__, otherwise identical interface to print()."""
212 """Raw print to sys.__stdout__, otherwise identical interface to print()."""
213
213
214 print(*args, sep=kw.get('sep', ' '), end=kw.get('end', '\n'),
214 print(*args, sep=kw.get('sep', ' '), end=kw.get('end', '\n'),
215 file=sys.__stdout__)
215 file=sys.__stdout__)
216 sys.__stdout__.flush()
216 sys.__stdout__.flush()
217
217
218
218
219 def raw_print_err(*args, **kw):
219 def raw_print_err(*args, **kw):
220 """Raw print to sys.__stderr__, otherwise identical interface to print()."""
220 """Raw print to sys.__stderr__, otherwise identical interface to print()."""
221
221
222 print(*args, sep=kw.get('sep', ' '), end=kw.get('end', '\n'),
222 print(*args, sep=kw.get('sep', ' '), end=kw.get('end', '\n'),
223 file=sys.__stderr__)
223 file=sys.__stderr__)
224 sys.__stderr__.flush()
224 sys.__stderr__.flush()
225
225
226
226
227 # Short aliases for quick debugging, do NOT use these in production code.
227 # Short aliases for quick debugging, do NOT use these in production code.
228 rprint = raw_print
228 rprint = raw_print
229 rprinte = raw_print_err
229 rprinte = raw_print_err
General Comments 0
You need to be logged in to leave comments. Login now