##// END OF EJS Templates
Merge pull request #4492 from takluyver/docs-refresh...
Min RK -
r13534:e33abb35 merge
parent child Browse files
Show More
@@ -0,0 +1,74
1 from IPython.utils.text import indent, wrap_paragraphs
2
3 from IPython.terminal.ipapp import TerminalIPythonApp
4 from IPython.kernel.zmq.kernelapp import IPKernelApp
5 from IPython.html.notebookapp import NotebookApp
6 from IPython.qt.console.qtconsoleapp import IPythonQtConsoleApp
7
8 def document_config_options(classes):
9 lines = []
10 for cls in classes:
11 classname = cls.__name__
12 for k, trait in sorted(cls.class_traits(config=True).items()):
13 ttype = trait.__class__.__name__
14
15 termline = classname + '.' + trait.name
16
17 # Choices or type
18 if 'Enum' in ttype:
19 # include Enum choices
20 termline += ' : ' + '|'.join(repr(x) for x in trait.values)
21 else:
22 termline += ' : ' + ttype
23 lines.append(termline)
24
25 # Default value
26 try:
27 dv = trait.get_default_value()
28 dvr = repr(dv)
29 except Exception:
30 dvr = dv = None # ignore defaults we can't construct
31 if (dv is not None) and (dvr is not None):
32 if len(dvr) > 64:
33 dvr = dvr[:61]+'...'
34 # Double up backslashes, so they get to the rendered docs
35 dvr = dvr.replace('\\n', '\\\\n')
36 lines.append(' Default: ' + dvr)
37 lines.append('')
38
39 help = trait.get_metadata('help')
40 if help is not None:
41 help = '\n\n'.join(wrap_paragraphs(help, 76))
42 lines.append(indent(help, 4))
43 else:
44 lines.append(' No description')
45
46 lines.append('')
47 return '\n'.join(lines)
48
49 kernel_classes = IPKernelApp().classes
50
51 def write_doc(filename, title, classes, preamble=None):
52 configdoc = document_config_options(classes)
53 with open('source/config/options/%s.rst' % filename, 'w') as f:
54 f.write(title + '\n')
55 f.write(('=' * len(title)) + '\n')
56 f.write('\n')
57 if preamble is not None:
58 f.write(preamble + '\n\n')
59 f.write(configdoc)
60
61 if __name__ == '__main__':
62 write_doc('terminal', 'Terminal IPython options', TerminalIPythonApp().classes)
63 write_doc('kernel', 'IPython kernel options', kernel_classes,
64 preamble="These options can be used in :file:`ipython_notebook_config.py` "
65 "or in :file:`ipython_qtconsole_config.py`")
66 nbclasses = set(NotebookApp().classes) - set(kernel_classes)
67 write_doc('notebook', 'IPython notebook options', nbclasses,
68 preamble="Any of the :doc:`kernel` can also be used.")
69 qtclasses = set(IPythonQtConsoleApp().classes) - set(kernel_classes)
70 write_doc('qtconsole', 'IPython Qt console options', qtclasses,
71 preamble="Any of the :doc:`kernel` can also be used.")
72
73 with open('source/config/options/generated', 'w'):
74 pass No newline at end of file
@@ -0,0 +1,234
1 =======================
2 Specific config details
3 =======================
4
5 Prompts
6 =======
7
8 In the terminal, the format of the input and output prompts can be
9 customised. This does not currently affect other frontends.
10
11 The following codes in the prompt string will be substituted into the
12 prompt string:
13
14 ====== =================================== =====================================================
15 Short Long Notes
16 ====== =================================== =====================================================
17 %n,\\# {color.number}{count}{color.prompt} history counter with bolding
18 \\N {count} history counter without bolding
19 \\D {dots} series of dots the same width as the history counter
20 \\T {time} current time
21 \\w {cwd} current working directory
22 \\W {cwd_last} basename of CWD
23 \\Xn {cwd_x[n]} Show the last n terms of the CWD. n=0 means show all.
24 \\Yn {cwd_y[n]} Like \Xn, but show '~' for $HOME
25 \\h hostname, up to the first '.'
26 \\H full hostname
27 \\u username (from the $USER environment variable)
28 \\v IPython version
29 \\$ root symbol ("$" for normal user or "#" for root)
30 ``\\`` escaped '\\'
31 \\n newline
32 \\r carriage return
33 n/a {color.<Name>} set terminal colour - see below for list of names
34 ====== =================================== =====================================================
35
36 Available colour names are: Black, BlinkBlack, BlinkBlue, BlinkCyan,
37 BlinkGreen, BlinkLightGray, BlinkPurple, BlinkRed, BlinkYellow, Blue,
38 Brown, Cyan, DarkGray, Green, LightBlue, LightCyan, LightGray, LightGreen,
39 LightPurple, LightRed, Purple, Red, White, Yellow. The selected colour
40 scheme also defines the names *prompt* and *number*. Finally, the name
41 *normal* resets the terminal to its default colour.
42
43 So, this config::
44
45 c.PromptManager.in_template = "{color.LightGreen}{time}{color.Yellow} \u{color.normal}>>>"
46
47 will produce input prompts with the time in light green, your username
48 in yellow, and a ``>>>`` prompt in the default terminal colour.
49
50
51 .. _termcolour:
52
53 Terminal Colors
54 ===============
55
56 The default IPython configuration has most bells and whistles turned on
57 (they're pretty safe). But there's one that may cause problems on some
58 systems: the use of color on screen for displaying information. This is
59 very useful, since IPython can show prompts and exception tracebacks
60 with various colors, display syntax-highlighted source code, and in
61 general make it easier to visually parse information.
62
63 The following terminals seem to handle the color sequences fine:
64
65 * Linux main text console, KDE Konsole, Gnome Terminal, E-term,
66 rxvt, xterm.
67 * CDE terminal (tested under Solaris). This one boldfaces light colors.
68 * (X)Emacs buffers. See the :ref:`emacs` section for more details on
69 using IPython with (X)Emacs.
70 * A Windows (XP/2k) command prompt with pyreadline_.
71 * A Windows (XP/2k) CygWin shell. Although some users have reported
72 problems; it is not clear whether there is an issue for everyone
73 or only under specific configurations. If you have full color
74 support under cygwin, please post to the IPython mailing list so
75 this issue can be resolved for all users.
76
77 .. _pyreadline: https://code.launchpad.net/pyreadline
78
79 These have shown problems:
80
81 * Windows command prompt in WinXP/2k logged into a Linux machine via
82 telnet or ssh.
83 * Windows native command prompt in WinXP/2k, without Gary Bishop's
84 extensions. Once Gary's readline library is installed, the normal
85 WinXP/2k command prompt works perfectly.
86
87 Currently the following color schemes are available:
88
89 * NoColor: uses no color escapes at all (all escapes are empty '' ''
90 strings). This 'scheme' is thus fully safe to use in any terminal.
91 * Linux: works well in Linux console type environments: dark
92 background with light fonts. It uses bright colors for
93 information, so it is difficult to read if you have a light
94 colored background.
95 * LightBG: the basic colors are similar to those in the Linux scheme
96 but darker. It is easy to read in terminals with light backgrounds.
97
98 IPython uses colors for two main groups of things: prompts and
99 tracebacks which are directly printed to the terminal, and the object
100 introspection system which passes large sets of data through a pager.
101
102 If you are seeing garbage sequences in your terminal and no colour, you
103 may need to disable colours: run ``%colors NoColor`` inside IPython, or
104 add this to a config file::
105
106 c.InteractiveShell.colors = 'NoColor'
107
108 Colors in the pager
109 -------------------
110
111 On some systems, the default pager has problems with ANSI colour codes.
112 To configure your default pager to allow these:
113
114 1. Set the environment PAGER variable to ``less``.
115 2. Set the environment LESS variable to ``-r`` (plus any other options
116 you always want to pass to less by default). This tells less to
117 properly interpret control sequences, which is how color
118 information is given to your terminal.
119
120 .. _editors:
121
122 Editor configuration
123 ====================
124
125 IPython can integrate with text editors in a number of different ways:
126
127 * Editors (such as `(X)Emacs`_, vim_ and TextMate_) can
128 send code to IPython for execution.
129
130 * IPython's ``%edit`` magic command can open an editor of choice to edit
131 a code block.
132
133 The %edit command (and its alias %ed) will invoke the editor set in your
134 environment as :envvar:`EDITOR`. If this variable is not set, it will default
135 to vi under Linux/Unix and to notepad under Windows. You may want to set this
136 variable properly and to a lightweight editor which doesn't take too long to
137 start (that is, something other than a new instance of Emacs). This way you
138 can edit multi-line code quickly and with the power of a real editor right
139 inside IPython.
140
141 You can also control the editor by setting :attr:`TerminalInteractiveShell.editor`
142 in :file:`ipython_config.py`.
143
144 Vim
145 ---
146
147 Paul Ivanov's `vim-ipython <https://github.com/ivanov/vim-ipython>`_ provides
148 powerful IPython integration for vim.
149
150 .. _emacs:
151
152 (X)Emacs
153 --------
154
155 If you are a dedicated Emacs user, and want to use Emacs when IPython's
156 ``%edit`` magic command is called you should set up the Emacs server so that
157 new requests are handled by the original process. This means that almost no
158 time is spent in handling the request (assuming an Emacs process is already
159 running). For this to work, you need to set your EDITOR environment variable
160 to 'emacsclient'. The code below, supplied by Francois Pinard, can then be
161 used in your :file:`.emacs` file to enable the server:
162
163 .. code-block:: common-lisp
164
165 (defvar server-buffer-clients)
166 (when (and (fboundp 'server-start) (string-equal (getenv "TERM") 'xterm))
167 (server-start)
168 (defun fp-kill-server-with-buffer-routine ()
169 (and server-buffer-clients (server-done)))
170 (add-hook 'kill-buffer-hook 'fp-kill-server-with-buffer-routine))
171
172 Thanks to the work of Alexander Schmolck and Prabhu Ramachandran,
173 currently (X)Emacs and IPython get along very well in other ways.
174
175 .. note::
176
177 You will need to use a recent enough version of :file:`python-mode.el`,
178 along with the file :file:`ipython.el`. You can check that the version you
179 have of :file:`python-mode.el` is new enough by either looking at the
180 revision number in the file itself, or asking for it in (X)Emacs via ``M-x
181 py-version``. Versions 4.68 and newer contain the necessary fixes for
182 proper IPython support.
183
184 The file :file:`ipython.el` is included with the IPython distribution, in the
185 directory :file:`docs/emacs`. Once you put these files in your Emacs path, all
186 you need in your :file:`.emacs` file is:
187
188 .. code-block:: common-lisp
189
190 (require 'ipython)
191
192 This should give you full support for executing code snippets via
193 IPython, opening IPython as your Python shell via ``C-c !``, etc.
194
195 You can customize the arguments passed to the IPython instance at startup by
196 setting the ``py-python-command-args`` variable. For example, to start always
197 with ``matplotlib`` integration and hardcoded light-background colors, you can use:
198
199 .. code-block:: common-lisp
200
201 (setq py-python-command-args '("--matplotlib" "--colors" "LightBG"))
202
203 If you happen to get garbage instead of colored prompts as described in
204 the previous section, you may need to set also in your :file:`.emacs` file:
205
206 .. code-block:: common-lisp
207
208 (setq ansi-color-for-comint-mode t)
209
210 Notes on emacs support:
211
212 .. This looks hopelessly out of date - can someone update it?
213
214 * There is one caveat you should be aware of: you must start the IPython shell
215 before attempting to execute any code regions via ``C-c |``. Simply type
216 ``C-c !`` to start IPython before passing any code regions to the
217 interpreter, and you shouldn't experience any problems. This is due to a bug
218 in Python itself, which has been fixed for Python 2.3, but exists as of
219 Python 2.2.2 (reported as SF bug [ 737947 ]).
220
221 * The (X)Emacs support is maintained by Alexander Schmolck, so all
222 comments/requests should be directed to him through the IPython mailing
223 lists.
224
225 * This code is still somewhat experimental so it's a bit rough around the
226 edges (although in practice, it works quite well).
227
228 * Be aware that if you customized ``py-python-command`` previously, this value
229 will override what :file:`ipython.el` does (because loading the customization
230 variables comes later).
231
232 .. _`(X)Emacs`: http://www.gnu.org/software/emacs/
233 .. _TextMate: http://macromates.com/
234 .. _vim: http://www.vim.org/
@@ -0,0 +1,156
1 =====================================
2 Introduction to IPython configuration
3 =====================================
4
5 .. _setting_config:
6
7 Setting configurable options
8 ============================
9
10 Many of IPython's classes have configurable attributes (see
11 :doc:`options/index` for the list). These can be
12 configured in several ways.
13
14 Python config files
15 -------------------
16
17 To create the blank config files, run::
18
19 ipython profile create [profilename]
20
21 If you leave out the profile name, the files will be created for the
22 ``default`` profile (see :ref:`profiles`). These will typically be
23 located in :file:`~/.ipython/profile_default/`, and will be named
24 :file:`ipython_config.py`, :file:`ipython_notebook_config.py`, etc.
25 The settings in :file:`ipython_config.py` apply to all IPython commands.
26
27 The files typically start by getting the root config object::
28
29 c = get_config()
30
31 You can then configure class attributes like this::
32
33 c.InteractiveShell.automagic = False
34
35 Be careful with spelling--incorrect names will simply be ignored, with
36 no error.
37
38 To add to a collection which may have already been defined elsewhere,
39 you can use methods like those found on lists, dicts and sets: append,
40 extend, :meth:`~IPython.config.loader.LazyConfigValue.prepend` (like
41 extend, but at the front), add and update (which works both for dicts
42 and sets)::
43
44 c.InteractiveShellApp.extensions.append('rmagic')
45
46 .. versionadded:: 2.0
47 list, dict and set methods for config values
48
49 Example config file
50 ```````````````````
51
52 ::
53
54 # sample ipython_config.py
55 c = get_config()
56
57 c.TerminalIPythonApp.display_banner = True
58 c.InteractiveShellApp.log_level = 20
59 c.InteractiveShellApp.extensions = [
60 'myextension'
61 ]
62 c.InteractiveShellApp.exec_lines = [
63 'import numpy',
64 'import scipy'
65 ]
66 c.InteractiveShellApp.exec_files = [
67 'mycode.py',
68 'fancy.ipy'
69 ]
70 c.InteractiveShell.autoindent = True
71 c.InteractiveShell.colors = 'LightBG'
72 c.InteractiveShell.confirm_exit = False
73 c.InteractiveShell.deep_reload = True
74 c.InteractiveShell.editor = 'nano'
75 c.InteractiveShell.xmode = 'Context'
76
77 c.PromptManager.in_template = 'In [\#]: '
78 c.PromptManager.in2_template = ' .\D.: '
79 c.PromptManager.out_template = 'Out[\#]: '
80 c.PromptManager.justify = True
81
82 c.PrefilterManager.multi_line_specials = True
83
84 c.AliasManager.user_aliases = [
85 ('la', 'ls -al')
86 ]
87
88
89 Command line arguments
90 ----------------------
91
92 Every configurable value can be set from the command line, using this
93 syntax::
94
95 ipython --ClassName.attribute=value
96
97 Many frequently used options have short aliases and flags, such as
98 ``--matplotlib`` (to integrate with a matplotlib GUI event loop) or
99 ``--pdb`` (automatic post-mortem debugging of exceptions).
100
101 To see all of these abbreviated options, run::
102
103 ipython --help
104 ipython notebook --help
105 # etc.
106
107 Options specified at the command line, in either format, override
108 options set in a configuration file.
109
110 The config magic
111 ----------------
112
113 You can also modify config from inside IPython, using a magic command::
114
115 %config IPCompleter.greedy = True
116
117 At present, this only affects the current session - changes you make to
118 config are not saved anywhere. Also, some options are only read when
119 IPython starts, so they can't be changed like this.
120
121 .. _profiles:
122
123 Profiles
124 ========
125
126 IPython can use multiple profiles, with separate configuration and
127 history. By default, if you don't specify a profile, IPython always runs
128 in the ``default`` profile. To use a new profile::
129
130 ipython profile create foo # create the profile foo
131 ipython --profile=foo # start IPython using the new profile
132
133 Profiles are typically stored in :ref:`ipythondir`, but you can also keep
134 a profile in the current working directory, for example to distribute it
135 with a project. To find a profile directory on the filesystem::
136
137 ipython locate profile foo
138
139 .. _ipythondir:
140
141 The IPython directory
142 =====================
143
144 IPython stores its files---config, command history and extensions---in
145 the directory :file:`~/.ipython/` by default.
146
147 .. envvar:: IPYTHONDIR
148
149 If set, this environment variable should be the path to a directory,
150 which IPython will use for user data. IPython will create it if it
151 does not exist.
152
153 .. option:: --ipython-dir=<path>
154
155 This command line option can also be used to override the default
156 IPython directory.
@@ -0,0 +1,14
1 ===============
2 IPython options
3 ===============
4
5 Any of the options listed here can be set in config files, at the
6 command line, or from inside IPython. See :ref:`setting_config` for
7 details.
8
9 .. toctree::
10
11 terminal
12 kernel
13 notebook
14 qtconsole
@@ -1,16 +1,17
1 MANIFEST
1 MANIFEST
2 build
2 build
3 dist
3 dist
4 _build
4 _build
5 docs/man/*.gz
5 docs/man/*.gz
6 docs/source/api/generated
6 docs/source/api/generated
7 docs/source/config/options
7 docs/gh-pages
8 docs/gh-pages
8 IPython/html/notebook/static/mathjax
9 IPython/html/notebook/static/mathjax
9 *.py[co]
10 *.py[co]
10 __pycache__
11 __pycache__
11 build
12 build
12 *.egg-info
13 *.egg-info
13 *~
14 *~
14 *.bak
15 *.bak
15 .ipynb_checkpoints
16 .ipynb_checkpoints
16 .tox
17 .tox
@@ -1,410 +1,410
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 A mixin for :class:`~IPython.core.application.Application` classes that
3 A mixin for :class:`~IPython.core.application.Application` classes that
4 launch InteractiveShell instances, load extensions, etc.
4 launch InteractiveShell instances, load extensions, etc.
5
5
6 Authors
6 Authors
7 -------
7 -------
8
8
9 * Min Ragan-Kelley
9 * Min Ragan-Kelley
10 """
10 """
11
11
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13 # Copyright (C) 2008-2011 The IPython Development Team
13 # Copyright (C) 2008-2011 The IPython Development Team
14 #
14 #
15 # Distributed under the terms of the BSD License. The full license is in
15 # Distributed under the terms of the BSD License. The full license is in
16 # the file COPYING, distributed as part of this software.
16 # the file COPYING, distributed as part of this software.
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18
18
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20 # Imports
20 # Imports
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22
22
23 from __future__ import absolute_import
23 from __future__ import absolute_import
24 from __future__ import print_function
24 from __future__ import print_function
25
25
26 import glob
26 import glob
27 import os
27 import os
28 import sys
28 import sys
29
29
30 from IPython.config.application import boolean_flag
30 from IPython.config.application import boolean_flag
31 from IPython.config.configurable import Configurable
31 from IPython.config.configurable import Configurable
32 from IPython.config.loader import Config
32 from IPython.config.loader import Config
33 from IPython.core import pylabtools
33 from IPython.core import pylabtools
34 from IPython.utils import py3compat
34 from IPython.utils import py3compat
35 from IPython.utils.contexts import preserve_keys
35 from IPython.utils.contexts import preserve_keys
36 from IPython.utils.path import filefind
36 from IPython.utils.path import filefind
37 from IPython.utils.traitlets import (
37 from IPython.utils.traitlets import (
38 Unicode, Instance, List, Bool, CaselessStrEnum, Dict
38 Unicode, Instance, List, Bool, CaselessStrEnum, Dict
39 )
39 )
40 from IPython.lib.inputhook import guis
40 from IPython.lib.inputhook import guis
41
41
42 #-----------------------------------------------------------------------------
42 #-----------------------------------------------------------------------------
43 # Aliases and Flags
43 # Aliases and Flags
44 #-----------------------------------------------------------------------------
44 #-----------------------------------------------------------------------------
45
45
46 gui_keys = tuple(sorted([ key for key in guis if key is not None ]))
46 gui_keys = tuple(sorted([ key for key in guis if key is not None ]))
47
47
48 backend_keys = sorted(pylabtools.backends.keys())
48 backend_keys = sorted(pylabtools.backends.keys())
49 backend_keys.insert(0, 'auto')
49 backend_keys.insert(0, 'auto')
50
50
51 shell_flags = {}
51 shell_flags = {}
52
52
53 addflag = lambda *args: shell_flags.update(boolean_flag(*args))
53 addflag = lambda *args: shell_flags.update(boolean_flag(*args))
54 addflag('autoindent', 'InteractiveShell.autoindent',
54 addflag('autoindent', 'InteractiveShell.autoindent',
55 'Turn on autoindenting.', 'Turn off autoindenting.'
55 'Turn on autoindenting.', 'Turn off autoindenting.'
56 )
56 )
57 addflag('automagic', 'InteractiveShell.automagic',
57 addflag('automagic', 'InteractiveShell.automagic',
58 """Turn on the auto calling of magic commands. Type %%magic at the
58 """Turn on the auto calling of magic commands. Type %%magic at the
59 IPython prompt for more information.""",
59 IPython prompt for more information.""",
60 'Turn off the auto calling of magic commands.'
60 'Turn off the auto calling of magic commands.'
61 )
61 )
62 addflag('pdb', 'InteractiveShell.pdb',
62 addflag('pdb', 'InteractiveShell.pdb',
63 "Enable auto calling the pdb debugger after every exception.",
63 "Enable auto calling the pdb debugger after every exception.",
64 "Disable auto calling the pdb debugger after every exception."
64 "Disable auto calling the pdb debugger after every exception."
65 )
65 )
66 # pydb flag doesn't do any config, as core.debugger switches on import,
66 # pydb flag doesn't do any config, as core.debugger switches on import,
67 # which is before parsing. This just allows the flag to be passed.
67 # which is before parsing. This just allows the flag to be passed.
68 shell_flags.update(dict(
68 shell_flags.update(dict(
69 pydb = ({},
69 pydb = ({},
70 """Use the third party 'pydb' package as debugger, instead of pdb.
70 """Use the third party 'pydb' package as debugger, instead of pdb.
71 Requires that pydb is installed."""
71 Requires that pydb is installed."""
72 )
72 )
73 ))
73 ))
74 addflag('pprint', 'PlainTextFormatter.pprint',
74 addflag('pprint', 'PlainTextFormatter.pprint',
75 "Enable auto pretty printing of results.",
75 "Enable auto pretty printing of results.",
76 "Disable auto pretty printing of results."
76 "Disable auto pretty printing of results."
77 )
77 )
78 addflag('color-info', 'InteractiveShell.color_info',
78 addflag('color-info', 'InteractiveShell.color_info',
79 """IPython can display information about objects via a set of func-
79 """IPython can display information about objects via a set of func-
80 tions, and optionally can use colors for this, syntax highlighting
80 tions, and optionally can use colors for this, syntax highlighting
81 source code and various other elements. However, because this
81 source code and various other elements. However, because this
82 information is passed through a pager (like 'less') and many pagers get
82 information is passed through a pager (like 'less') and many pagers get
83 confused with color codes, this option is off by default. You can test
83 confused with color codes, this option is off by default. You can test
84 it and turn it on permanently in your ipython_config.py file if it
84 it and turn it on permanently in your ipython_config.py file if it
85 works for you. Test it and turn it on permanently if it works with
85 works for you. Test it and turn it on permanently if it works with
86 your system. The magic function %%color_info allows you to toggle this
86 your system. The magic function %%color_info allows you to toggle this
87 interactively for testing.""",
87 interactively for testing.""",
88 "Disable using colors for info related things."
88 "Disable using colors for info related things."
89 )
89 )
90 addflag('deep-reload', 'InteractiveShell.deep_reload',
90 addflag('deep-reload', 'InteractiveShell.deep_reload',
91 """Enable deep (recursive) reloading by default. IPython can use the
91 """Enable deep (recursive) reloading by default. IPython can use the
92 deep_reload module which reloads changes in modules recursively (it
92 deep_reload module which reloads changes in modules recursively (it
93 replaces the reload() function, so you don't need to change anything to
93 replaces the reload() function, so you don't need to change anything to
94 use it). deep_reload() forces a full reload of modules whose code may
94 use it). deep_reload() forces a full reload of modules whose code may
95 have changed, which the default reload() function does not. When
95 have changed, which the default reload() function does not. When
96 deep_reload is off, IPython will use the normal reload(), but
96 deep_reload is off, IPython will use the normal reload(), but
97 deep_reload will still be available as dreload(). This feature is off
97 deep_reload will still be available as dreload(). This feature is off
98 by default [which means that you have both normal reload() and
98 by default [which means that you have both normal reload() and
99 dreload()].""",
99 dreload()].""",
100 "Disable deep (recursive) reloading by default."
100 "Disable deep (recursive) reloading by default."
101 )
101 )
102 nosep_config = Config()
102 nosep_config = Config()
103 nosep_config.InteractiveShell.separate_in = ''
103 nosep_config.InteractiveShell.separate_in = ''
104 nosep_config.InteractiveShell.separate_out = ''
104 nosep_config.InteractiveShell.separate_out = ''
105 nosep_config.InteractiveShell.separate_out2 = ''
105 nosep_config.InteractiveShell.separate_out2 = ''
106
106
107 shell_flags['nosep']=(nosep_config, "Eliminate all spacing between prompts.")
107 shell_flags['nosep']=(nosep_config, "Eliminate all spacing between prompts.")
108 shell_flags['pylab'] = (
108 shell_flags['pylab'] = (
109 {'InteractiveShellApp' : {'pylab' : 'auto'}},
109 {'InteractiveShellApp' : {'pylab' : 'auto'}},
110 """Pre-load matplotlib and numpy for interactive use with
110 """Pre-load matplotlib and numpy for interactive use with
111 the default matplotlib backend."""
111 the default matplotlib backend."""
112 )
112 )
113 shell_flags['matplotlib'] = (
113 shell_flags['matplotlib'] = (
114 {'InteractiveShellApp' : {'matplotlib' : 'auto'}},
114 {'InteractiveShellApp' : {'matplotlib' : 'auto'}},
115 """Configure matplotlib for interactive use with
115 """Configure matplotlib for interactive use with
116 the default matplotlib backend."""
116 the default matplotlib backend."""
117 )
117 )
118
118
119 # it's possible we don't want short aliases for *all* of these:
119 # it's possible we don't want short aliases for *all* of these:
120 shell_aliases = dict(
120 shell_aliases = dict(
121 autocall='InteractiveShell.autocall',
121 autocall='InteractiveShell.autocall',
122 colors='InteractiveShell.colors',
122 colors='InteractiveShell.colors',
123 logfile='InteractiveShell.logfile',
123 logfile='InteractiveShell.logfile',
124 logappend='InteractiveShell.logappend',
124 logappend='InteractiveShell.logappend',
125 c='InteractiveShellApp.code_to_run',
125 c='InteractiveShellApp.code_to_run',
126 m='InteractiveShellApp.module_to_run',
126 m='InteractiveShellApp.module_to_run',
127 ext='InteractiveShellApp.extra_extension',
127 ext='InteractiveShellApp.extra_extension',
128 gui='InteractiveShellApp.gui',
128 gui='InteractiveShellApp.gui',
129 pylab='InteractiveShellApp.pylab',
129 pylab='InteractiveShellApp.pylab',
130 matplotlib='InteractiveShellApp.matplotlib',
130 matplotlib='InteractiveShellApp.matplotlib',
131 )
131 )
132 shell_aliases['cache-size'] = 'InteractiveShell.cache_size'
132 shell_aliases['cache-size'] = 'InteractiveShell.cache_size'
133
133
134 #-----------------------------------------------------------------------------
134 #-----------------------------------------------------------------------------
135 # Main classes and functions
135 # Main classes and functions
136 #-----------------------------------------------------------------------------
136 #-----------------------------------------------------------------------------
137
137
138 class InteractiveShellApp(Configurable):
138 class InteractiveShellApp(Configurable):
139 """A Mixin for applications that start InteractiveShell instances.
139 """A Mixin for applications that start InteractiveShell instances.
140
140
141 Provides configurables for loading extensions and executing files
141 Provides configurables for loading extensions and executing files
142 as part of configuring a Shell environment.
142 as part of configuring a Shell environment.
143
143
144 The following methods should be called by the :meth:`initialize` method
144 The following methods should be called by the :meth:`initialize` method
145 of the subclass:
145 of the subclass:
146
146
147 - :meth:`init_path`
147 - :meth:`init_path`
148 - :meth:`init_shell` (to be implemented by the subclass)
148 - :meth:`init_shell` (to be implemented by the subclass)
149 - :meth:`init_gui_pylab`
149 - :meth:`init_gui_pylab`
150 - :meth:`init_extensions`
150 - :meth:`init_extensions`
151 - :meth:`init_code`
151 - :meth:`init_code`
152 """
152 """
153 extensions = List(Unicode, config=True,
153 extensions = List(Unicode, config=True,
154 help="A list of dotted module names of IPython extensions to load."
154 help="A list of dotted module names of IPython extensions to load."
155 )
155 )
156 extra_extension = Unicode('', config=True,
156 extra_extension = Unicode('', config=True,
157 help="dotted module name of an IPython extension to load."
157 help="dotted module name of an IPython extension to load."
158 )
158 )
159 def _extra_extension_changed(self, name, old, new):
159 def _extra_extension_changed(self, name, old, new):
160 if new:
160 if new:
161 # add to self.extensions
161 # add to self.extensions
162 self.extensions.append(new)
162 self.extensions.append(new)
163
163
164 # Extensions that are always loaded (not configurable)
164 # Extensions that are always loaded (not configurable)
165 default_extensions = List(Unicode, [u'storemagic'], config=False)
165 default_extensions = List(Unicode, [u'storemagic'], config=False)
166
166
167 exec_files = List(Unicode, config=True,
167 exec_files = List(Unicode, config=True,
168 help="""List of files to run at IPython startup."""
168 help="""List of files to run at IPython startup."""
169 )
169 )
170 file_to_run = Unicode('', config=True,
170 file_to_run = Unicode('', config=True,
171 help="""A file to be run""")
171 help="""A file to be run""")
172
172
173 exec_lines = List(Unicode, config=True,
173 exec_lines = List(Unicode, config=True,
174 help="""lines of code to run at IPython startup."""
174 help="""lines of code to run at IPython startup."""
175 )
175 )
176 code_to_run = Unicode('', config=True,
176 code_to_run = Unicode('', config=True,
177 help="Execute the given command string."
177 help="Execute the given command string."
178 )
178 )
179 module_to_run = Unicode('', config=True,
179 module_to_run = Unicode('', config=True,
180 help="Run the module as a script."
180 help="Run the module as a script."
181 )
181 )
182 gui = CaselessStrEnum(gui_keys, config=True,
182 gui = CaselessStrEnum(gui_keys, config=True,
183 help="Enable GUI event loop integration with any of {0}.".format(gui_keys)
183 help="Enable GUI event loop integration with any of {0}.".format(gui_keys)
184 )
184 )
185 matplotlib = CaselessStrEnum(backend_keys,
185 matplotlib = CaselessStrEnum(backend_keys,
186 config=True,
186 config=True,
187 help="""Configure matplotlib for interactive use with
187 help="""Configure matplotlib for interactive use with
188 the default matplotlib backend."""
188 the default matplotlib backend."""
189 )
189 )
190 pylab = CaselessStrEnum(backend_keys,
190 pylab = CaselessStrEnum(backend_keys,
191 config=True,
191 config=True,
192 help="""Pre-load matplotlib and numpy for interactive use,
192 help="""Pre-load matplotlib and numpy for interactive use,
193 selecting a particular matplotlib backend and loop integration.
193 selecting a particular matplotlib backend and loop integration.
194 """
194 """
195 )
195 )
196 pylab_import_all = Bool(True, config=True,
196 pylab_import_all = Bool(True, config=True,
197 help="""If true, IPython will populate the user namespace with numpy, pylab, etc.
197 help="""If true, IPython will populate the user namespace with numpy, pylab, etc.
198 and an 'import *' is done from numpy and pylab, when using pylab mode.
198 and an ``import *`` is done from numpy and pylab, when using pylab mode.
199
199
200 When False, pylab mode should not import any names into the user namespace.
200 When False, pylab mode should not import any names into the user namespace.
201 """
201 """
202 )
202 )
203 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
203 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
204
204
205 user_ns = Instance(dict, args=None, allow_none=True)
205 user_ns = Instance(dict, args=None, allow_none=True)
206 def _user_ns_changed(self, name, old, new):
206 def _user_ns_changed(self, name, old, new):
207 if self.shell is not None:
207 if self.shell is not None:
208 self.shell.user_ns = new
208 self.shell.user_ns = new
209 self.shell.init_user_ns()
209 self.shell.init_user_ns()
210
210
211 def init_path(self):
211 def init_path(self):
212 """Add current working directory, '', to sys.path"""
212 """Add current working directory, '', to sys.path"""
213 if sys.path[0] != '':
213 if sys.path[0] != '':
214 sys.path.insert(0, '')
214 sys.path.insert(0, '')
215
215
216 def init_shell(self):
216 def init_shell(self):
217 raise NotImplementedError("Override in subclasses")
217 raise NotImplementedError("Override in subclasses")
218
218
219 def init_gui_pylab(self):
219 def init_gui_pylab(self):
220 """Enable GUI event loop integration, taking pylab into account."""
220 """Enable GUI event loop integration, taking pylab into account."""
221 enable = False
221 enable = False
222 shell = self.shell
222 shell = self.shell
223 if self.pylab:
223 if self.pylab:
224 enable = lambda key: shell.enable_pylab(key, import_all=self.pylab_import_all)
224 enable = lambda key: shell.enable_pylab(key, import_all=self.pylab_import_all)
225 key = self.pylab
225 key = self.pylab
226 elif self.matplotlib:
226 elif self.matplotlib:
227 enable = shell.enable_matplotlib
227 enable = shell.enable_matplotlib
228 key = self.matplotlib
228 key = self.matplotlib
229 elif self.gui:
229 elif self.gui:
230 enable = shell.enable_gui
230 enable = shell.enable_gui
231 key = self.gui
231 key = self.gui
232
232
233 if not enable:
233 if not enable:
234 return
234 return
235
235
236 try:
236 try:
237 r = enable(key)
237 r = enable(key)
238 except ImportError:
238 except ImportError:
239 self.log.warn("Eventloop or matplotlib integration failed. Is matplotlib installed?")
239 self.log.warn("Eventloop or matplotlib integration failed. Is matplotlib installed?")
240 self.shell.showtraceback()
240 self.shell.showtraceback()
241 return
241 return
242 except Exception:
242 except Exception:
243 self.log.warn("GUI event loop or pylab initialization failed")
243 self.log.warn("GUI event loop or pylab initialization failed")
244 self.shell.showtraceback()
244 self.shell.showtraceback()
245 return
245 return
246
246
247 if isinstance(r, tuple):
247 if isinstance(r, tuple):
248 gui, backend = r[:2]
248 gui, backend = r[:2]
249 self.log.info("Enabling GUI event loop integration, "
249 self.log.info("Enabling GUI event loop integration, "
250 "eventloop=%s, matplotlib=%s", gui, backend)
250 "eventloop=%s, matplotlib=%s", gui, backend)
251 if key == "auto":
251 if key == "auto":
252 print("Using matplotlib backend: %s" % backend)
252 print("Using matplotlib backend: %s" % backend)
253 else:
253 else:
254 gui = r
254 gui = r
255 self.log.info("Enabling GUI event loop integration, "
255 self.log.info("Enabling GUI event loop integration, "
256 "eventloop=%s", gui)
256 "eventloop=%s", gui)
257
257
258 def init_extensions(self):
258 def init_extensions(self):
259 """Load all IPython extensions in IPythonApp.extensions.
259 """Load all IPython extensions in IPythonApp.extensions.
260
260
261 This uses the :meth:`ExtensionManager.load_extensions` to load all
261 This uses the :meth:`ExtensionManager.load_extensions` to load all
262 the extensions listed in ``self.extensions``.
262 the extensions listed in ``self.extensions``.
263 """
263 """
264 try:
264 try:
265 self.log.debug("Loading IPython extensions...")
265 self.log.debug("Loading IPython extensions...")
266 extensions = self.default_extensions + self.extensions
266 extensions = self.default_extensions + self.extensions
267 for ext in extensions:
267 for ext in extensions:
268 try:
268 try:
269 self.log.info("Loading IPython extension: %s" % ext)
269 self.log.info("Loading IPython extension: %s" % ext)
270 self.shell.extension_manager.load_extension(ext)
270 self.shell.extension_manager.load_extension(ext)
271 except:
271 except:
272 self.log.warn("Error in loading extension: %s" % ext +
272 self.log.warn("Error in loading extension: %s" % ext +
273 "\nCheck your config files in %s" % self.profile_dir.location
273 "\nCheck your config files in %s" % self.profile_dir.location
274 )
274 )
275 self.shell.showtraceback()
275 self.shell.showtraceback()
276 except:
276 except:
277 self.log.warn("Unknown error in loading extensions:")
277 self.log.warn("Unknown error in loading extensions:")
278 self.shell.showtraceback()
278 self.shell.showtraceback()
279
279
280 def init_code(self):
280 def init_code(self):
281 """run the pre-flight code, specified via exec_lines"""
281 """run the pre-flight code, specified via exec_lines"""
282 self._run_startup_files()
282 self._run_startup_files()
283 self._run_exec_lines()
283 self._run_exec_lines()
284 self._run_exec_files()
284 self._run_exec_files()
285 self._run_cmd_line_code()
285 self._run_cmd_line_code()
286 self._run_module()
286 self._run_module()
287
287
288 # flush output, so itwon't be attached to the first cell
288 # flush output, so itwon't be attached to the first cell
289 sys.stdout.flush()
289 sys.stdout.flush()
290 sys.stderr.flush()
290 sys.stderr.flush()
291
291
292 # Hide variables defined here from %who etc.
292 # Hide variables defined here from %who etc.
293 self.shell.user_ns_hidden.update(self.shell.user_ns)
293 self.shell.user_ns_hidden.update(self.shell.user_ns)
294
294
295 def _run_exec_lines(self):
295 def _run_exec_lines(self):
296 """Run lines of code in IPythonApp.exec_lines in the user's namespace."""
296 """Run lines of code in IPythonApp.exec_lines in the user's namespace."""
297 if not self.exec_lines:
297 if not self.exec_lines:
298 return
298 return
299 try:
299 try:
300 self.log.debug("Running code from IPythonApp.exec_lines...")
300 self.log.debug("Running code from IPythonApp.exec_lines...")
301 for line in self.exec_lines:
301 for line in self.exec_lines:
302 try:
302 try:
303 self.log.info("Running code in user namespace: %s" %
303 self.log.info("Running code in user namespace: %s" %
304 line)
304 line)
305 self.shell.run_cell(line, store_history=False)
305 self.shell.run_cell(line, store_history=False)
306 except:
306 except:
307 self.log.warn("Error in executing line in user "
307 self.log.warn("Error in executing line in user "
308 "namespace: %s" % line)
308 "namespace: %s" % line)
309 self.shell.showtraceback()
309 self.shell.showtraceback()
310 except:
310 except:
311 self.log.warn("Unknown error in handling IPythonApp.exec_lines:")
311 self.log.warn("Unknown error in handling IPythonApp.exec_lines:")
312 self.shell.showtraceback()
312 self.shell.showtraceback()
313
313
314 def _exec_file(self, fname):
314 def _exec_file(self, fname):
315 try:
315 try:
316 full_filename = filefind(fname, [u'.', self.ipython_dir])
316 full_filename = filefind(fname, [u'.', self.ipython_dir])
317 except IOError as e:
317 except IOError as e:
318 self.log.warn("File not found: %r"%fname)
318 self.log.warn("File not found: %r"%fname)
319 return
319 return
320 # Make sure that the running script gets a proper sys.argv as if it
320 # Make sure that the running script gets a proper sys.argv as if it
321 # were run from a system shell.
321 # were run from a system shell.
322 save_argv = sys.argv
322 save_argv = sys.argv
323 sys.argv = [full_filename] + self.extra_args[1:]
323 sys.argv = [full_filename] + self.extra_args[1:]
324 # protect sys.argv from potential unicode strings on Python 2:
324 # protect sys.argv from potential unicode strings on Python 2:
325 if not py3compat.PY3:
325 if not py3compat.PY3:
326 sys.argv = [ py3compat.cast_bytes(a) for a in sys.argv ]
326 sys.argv = [ py3compat.cast_bytes(a) for a in sys.argv ]
327 try:
327 try:
328 if os.path.isfile(full_filename):
328 if os.path.isfile(full_filename):
329 self.log.info("Running file in user namespace: %s" %
329 self.log.info("Running file in user namespace: %s" %
330 full_filename)
330 full_filename)
331 # Ensure that __file__ is always defined to match Python
331 # Ensure that __file__ is always defined to match Python
332 # behavior.
332 # behavior.
333 with preserve_keys(self.shell.user_ns, '__file__'):
333 with preserve_keys(self.shell.user_ns, '__file__'):
334 self.shell.user_ns['__file__'] = fname
334 self.shell.user_ns['__file__'] = fname
335 if full_filename.endswith('.ipy'):
335 if full_filename.endswith('.ipy'):
336 self.shell.safe_execfile_ipy(full_filename)
336 self.shell.safe_execfile_ipy(full_filename)
337 else:
337 else:
338 # default to python, even without extension
338 # default to python, even without extension
339 self.shell.safe_execfile(full_filename,
339 self.shell.safe_execfile(full_filename,
340 self.shell.user_ns)
340 self.shell.user_ns)
341 finally:
341 finally:
342 sys.argv = save_argv
342 sys.argv = save_argv
343
343
344 def _run_startup_files(self):
344 def _run_startup_files(self):
345 """Run files from profile startup directory"""
345 """Run files from profile startup directory"""
346 startup_dir = self.profile_dir.startup_dir
346 startup_dir = self.profile_dir.startup_dir
347 startup_files = []
347 startup_files = []
348 if os.environ.get('PYTHONSTARTUP', False):
348 if os.environ.get('PYTHONSTARTUP', False):
349 startup_files.append(os.environ['PYTHONSTARTUP'])
349 startup_files.append(os.environ['PYTHONSTARTUP'])
350 startup_files += glob.glob(os.path.join(startup_dir, '*.py'))
350 startup_files += glob.glob(os.path.join(startup_dir, '*.py'))
351 startup_files += glob.glob(os.path.join(startup_dir, '*.ipy'))
351 startup_files += glob.glob(os.path.join(startup_dir, '*.ipy'))
352 if not startup_files:
352 if not startup_files:
353 return
353 return
354
354
355 self.log.debug("Running startup files from %s...", startup_dir)
355 self.log.debug("Running startup files from %s...", startup_dir)
356 try:
356 try:
357 for fname in sorted(startup_files):
357 for fname in sorted(startup_files):
358 self._exec_file(fname)
358 self._exec_file(fname)
359 except:
359 except:
360 self.log.warn("Unknown error in handling startup files:")
360 self.log.warn("Unknown error in handling startup files:")
361 self.shell.showtraceback()
361 self.shell.showtraceback()
362
362
363 def _run_exec_files(self):
363 def _run_exec_files(self):
364 """Run files from IPythonApp.exec_files"""
364 """Run files from IPythonApp.exec_files"""
365 if not self.exec_files:
365 if not self.exec_files:
366 return
366 return
367
367
368 self.log.debug("Running files in IPythonApp.exec_files...")
368 self.log.debug("Running files in IPythonApp.exec_files...")
369 try:
369 try:
370 for fname in self.exec_files:
370 for fname in self.exec_files:
371 self._exec_file(fname)
371 self._exec_file(fname)
372 except:
372 except:
373 self.log.warn("Unknown error in handling IPythonApp.exec_files:")
373 self.log.warn("Unknown error in handling IPythonApp.exec_files:")
374 self.shell.showtraceback()
374 self.shell.showtraceback()
375
375
376 def _run_cmd_line_code(self):
376 def _run_cmd_line_code(self):
377 """Run code or file specified at the command-line"""
377 """Run code or file specified at the command-line"""
378 if self.code_to_run:
378 if self.code_to_run:
379 line = self.code_to_run
379 line = self.code_to_run
380 try:
380 try:
381 self.log.info("Running code given at command line (c=): %s" %
381 self.log.info("Running code given at command line (c=): %s" %
382 line)
382 line)
383 self.shell.run_cell(line, store_history=False)
383 self.shell.run_cell(line, store_history=False)
384 except:
384 except:
385 self.log.warn("Error in executing line in user namespace: %s" %
385 self.log.warn("Error in executing line in user namespace: %s" %
386 line)
386 line)
387 self.shell.showtraceback()
387 self.shell.showtraceback()
388
388
389 # Like Python itself, ignore the second if the first of these is present
389 # Like Python itself, ignore the second if the first of these is present
390 elif self.file_to_run:
390 elif self.file_to_run:
391 fname = self.file_to_run
391 fname = self.file_to_run
392 try:
392 try:
393 self._exec_file(fname)
393 self._exec_file(fname)
394 except:
394 except:
395 self.log.warn("Error in executing file in user namespace: %s" %
395 self.log.warn("Error in executing file in user namespace: %s" %
396 fname)
396 fname)
397 self.shell.showtraceback()
397 self.shell.showtraceback()
398
398
399 def _run_module(self):
399 def _run_module(self):
400 """Run module specified at the command-line."""
400 """Run module specified at the command-line."""
401 if self.module_to_run:
401 if self.module_to_run:
402 # Make sure that the module gets a proper sys.argv as if it were
402 # Make sure that the module gets a proper sys.argv as if it were
403 # run using `python -m`.
403 # run using `python -m`.
404 save_argv = sys.argv
404 save_argv = sys.argv
405 sys.argv = [sys.executable] + self.extra_args
405 sys.argv = [sys.executable] + self.extra_args
406 try:
406 try:
407 self.shell.safe_run_module(self.module_to_run,
407 self.shell.safe_run_module(self.module_to_run,
408 self.shell.user_ns)
408 self.shell.user_ns)
409 finally:
409 finally:
410 sys.argv = save_argv
410 sys.argv = save_argv
@@ -1,2109 +1,2112
1 """ An abstract base class for console-type widgets.
1 """ An abstract base class for console-type widgets.
2 """
2 """
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Imports
4 # Imports
5 #-----------------------------------------------------------------------------
5 #-----------------------------------------------------------------------------
6
6
7 # Standard library imports
7 # Standard library imports
8 import os.path
8 import os.path
9 import re
9 import re
10 import sys
10 import sys
11 from textwrap import dedent
11 from textwrap import dedent
12 import time
12 import time
13 from unicodedata import category
13 from unicodedata import category
14 import webbrowser
14 import webbrowser
15
15
16 # System library imports
16 # System library imports
17 from IPython.external.qt import QtCore, QtGui
17 from IPython.external.qt import QtCore, QtGui
18
18
19 # Local imports
19 # Local imports
20 from IPython.config.configurable import LoggingConfigurable
20 from IPython.config.configurable import LoggingConfigurable
21 from IPython.core.inputsplitter import ESC_SEQUENCES
21 from IPython.core.inputsplitter import ESC_SEQUENCES
22 from IPython.qt.rich_text import HtmlExporter
22 from IPython.qt.rich_text import HtmlExporter
23 from IPython.qt.util import MetaQObjectHasTraits, get_font
23 from IPython.qt.util import MetaQObjectHasTraits, get_font
24 from IPython.utils.text import columnize
24 from IPython.utils.text import columnize
25 from IPython.utils.traitlets import Bool, Enum, Integer, Unicode
25 from IPython.utils.traitlets import Bool, Enum, Integer, Unicode
26 from .ansi_code_processor import QtAnsiCodeProcessor
26 from .ansi_code_processor import QtAnsiCodeProcessor
27 from .completion_widget import CompletionWidget
27 from .completion_widget import CompletionWidget
28 from .completion_html import CompletionHtml
28 from .completion_html import CompletionHtml
29 from .completion_plain import CompletionPlain
29 from .completion_plain import CompletionPlain
30 from .kill_ring import QtKillRing
30 from .kill_ring import QtKillRing
31
31
32
32
33 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
34 # Functions
34 # Functions
35 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
36
36
37 ESCAPE_CHARS = ''.join(ESC_SEQUENCES)
37 ESCAPE_CHARS = ''.join(ESC_SEQUENCES)
38 ESCAPE_RE = re.compile("^["+ESCAPE_CHARS+"]+")
38 ESCAPE_RE = re.compile("^["+ESCAPE_CHARS+"]+")
39
39
40 def commonprefix(items):
40 def commonprefix(items):
41 """Get common prefix for completions
41 """Get common prefix for completions
42
42
43 Return the longest common prefix of a list of strings, but with special
43 Return the longest common prefix of a list of strings, but with special
44 treatment of escape characters that might precede commands in IPython,
44 treatment of escape characters that might precede commands in IPython,
45 such as %magic functions. Used in tab completion.
45 such as %magic functions. Used in tab completion.
46
46
47 For a more general function, see os.path.commonprefix
47 For a more general function, see os.path.commonprefix
48 """
48 """
49 # the last item will always have the least leading % symbol
49 # the last item will always have the least leading % symbol
50 # min / max are first/last in alphabetical order
50 # min / max are first/last in alphabetical order
51 first_match = ESCAPE_RE.match(min(items))
51 first_match = ESCAPE_RE.match(min(items))
52 last_match = ESCAPE_RE.match(max(items))
52 last_match = ESCAPE_RE.match(max(items))
53 # common suffix is (common prefix of reversed items) reversed
53 # common suffix is (common prefix of reversed items) reversed
54 if first_match and last_match:
54 if first_match and last_match:
55 prefix = os.path.commonprefix((first_match.group(0)[::-1], last_match.group(0)[::-1]))[::-1]
55 prefix = os.path.commonprefix((first_match.group(0)[::-1], last_match.group(0)[::-1]))[::-1]
56 else:
56 else:
57 prefix = ''
57 prefix = ''
58
58
59 items = [s.lstrip(ESCAPE_CHARS) for s in items]
59 items = [s.lstrip(ESCAPE_CHARS) for s in items]
60 return prefix+os.path.commonprefix(items)
60 return prefix+os.path.commonprefix(items)
61
61
62 def is_letter_or_number(char):
62 def is_letter_or_number(char):
63 """ Returns whether the specified unicode character is a letter or a number.
63 """ Returns whether the specified unicode character is a letter or a number.
64 """
64 """
65 cat = category(char)
65 cat = category(char)
66 return cat.startswith('L') or cat.startswith('N')
66 return cat.startswith('L') or cat.startswith('N')
67
67
68 #-----------------------------------------------------------------------------
68 #-----------------------------------------------------------------------------
69 # Classes
69 # Classes
70 #-----------------------------------------------------------------------------
70 #-----------------------------------------------------------------------------
71
71
72 class ConsoleWidget(MetaQObjectHasTraits('NewBase', (LoggingConfigurable, QtGui.QWidget), {})):
72 class ConsoleWidget(MetaQObjectHasTraits('NewBase', (LoggingConfigurable, QtGui.QWidget), {})):
73 """ An abstract base class for console-type widgets. This class has
73 """ An abstract base class for console-type widgets. This class has
74 functionality for:
74 functionality for:
75
75
76 * Maintaining a prompt and editing region
76 * Maintaining a prompt and editing region
77 * Providing the traditional Unix-style console keyboard shortcuts
77 * Providing the traditional Unix-style console keyboard shortcuts
78 * Performing tab completion
78 * Performing tab completion
79 * Paging text
79 * Paging text
80 * Handling ANSI escape codes
80 * Handling ANSI escape codes
81
81
82 ConsoleWidget also provides a number of utility methods that will be
82 ConsoleWidget also provides a number of utility methods that will be
83 convenient to implementors of a console-style widget.
83 convenient to implementors of a console-style widget.
84 """
84 """
85
85
86 #------ Configuration ------------------------------------------------------
86 #------ Configuration ------------------------------------------------------
87
87
88 ansi_codes = Bool(True, config=True,
88 ansi_codes = Bool(True, config=True,
89 help="Whether to process ANSI escape codes."
89 help="Whether to process ANSI escape codes."
90 )
90 )
91 buffer_size = Integer(500, config=True,
91 buffer_size = Integer(500, config=True,
92 help="""
92 help="""
93 The maximum number of lines of text before truncation. Specifying a
93 The maximum number of lines of text before truncation. Specifying a
94 non-positive number disables text truncation (not recommended).
94 non-positive number disables text truncation (not recommended).
95 """
95 """
96 )
96 )
97 execute_on_complete_input = Bool(True, config=True,
97 execute_on_complete_input = Bool(True, config=True,
98 help="""Whether to automatically execute on syntactically complete input.
98 help="""Whether to automatically execute on syntactically complete input.
99
99
100 If False, Shift-Enter is required to submit each execution.
100 If False, Shift-Enter is required to submit each execution.
101 Disabling this is mainly useful for non-Python kernels,
101 Disabling this is mainly useful for non-Python kernels,
102 where the completion check would be wrong.
102 where the completion check would be wrong.
103 """
103 """
104 )
104 )
105 gui_completion = Enum(['plain', 'droplist', 'ncurses'], config=True,
105 gui_completion = Enum(['plain', 'droplist', 'ncurses'], config=True,
106 default_value = 'ncurses',
106 default_value = 'ncurses',
107 help="""
107 help="""
108 The type of completer to use. Valid values are:
108 The type of completer to use. Valid values are:
109
109
110 'plain' : Show the available completion as a text list
110 'plain' : Show the available completion as a text list
111 Below the editing area.
111 Below the editing area.
112 'droplist': Show the completion in a drop down list navigable
112 'droplist': Show the completion in a drop down list navigable
113 by the arrow keys, and from which you can select
113 by the arrow keys, and from which you can select
114 completion by pressing Return.
114 completion by pressing Return.
115 'ncurses' : Show the completion as a text list which is navigable by
115 'ncurses' : Show the completion as a text list which is navigable by
116 `tab` and arrow keys.
116 `tab` and arrow keys.
117 """
117 """
118 )
118 )
119 # NOTE: this value can only be specified during initialization.
119 # NOTE: this value can only be specified during initialization.
120 kind = Enum(['plain', 'rich'], default_value='plain', config=True,
120 kind = Enum(['plain', 'rich'], default_value='plain', config=True,
121 help="""
121 help="""
122 The type of underlying text widget to use. Valid values are 'plain',
122 The type of underlying text widget to use. Valid values are 'plain',
123 which specifies a QPlainTextEdit, and 'rich', which specifies a
123 which specifies a QPlainTextEdit, and 'rich', which specifies a
124 QTextEdit.
124 QTextEdit.
125 """
125 """
126 )
126 )
127 # NOTE: this value can only be specified during initialization.
127 # NOTE: this value can only be specified during initialization.
128 paging = Enum(['inside', 'hsplit', 'vsplit', 'custom', 'none'],
128 paging = Enum(['inside', 'hsplit', 'vsplit', 'custom', 'none'],
129 default_value='inside', config=True,
129 default_value='inside', config=True,
130 help="""
130 help="""
131 The type of paging to use. Valid values are:
131 The type of paging to use. Valid values are:
132
132
133 'inside' : The widget pages like a traditional terminal.
133 'inside'
134 'hsplit' : When paging is requested, the widget is split
134 The widget pages like a traditional terminal.
135 horizontally. The top pane contains the console, and the
135 'hsplit'
136 bottom pane contains the paged text.
136 When paging is requested, the widget is split horizontally. The top
137 'vsplit' : Similar to 'hsplit', except that a vertical splitter
137 pane contains the console, and the bottom pane contains the paged text.
138 used.
138 'vsplit'
139 'custom' : No action is taken by the widget beyond emitting a
139 Similar to 'hsplit', except that a vertical splitter is used.
140 'custom_page_requested(str)' signal.
140 'custom'
141 'none' : The text is written directly to the console.
141 No action is taken by the widget beyond emitting a
142 'custom_page_requested(str)' signal.
143 'none'
144 The text is written directly to the console.
142 """)
145 """)
143
146
144 font_family = Unicode(config=True,
147 font_family = Unicode(config=True,
145 help="""The font family to use for the console.
148 help="""The font family to use for the console.
146 On OSX this defaults to Monaco, on Windows the default is
149 On OSX this defaults to Monaco, on Windows the default is
147 Consolas with fallback of Courier, and on other platforms
150 Consolas with fallback of Courier, and on other platforms
148 the default is Monospace.
151 the default is Monospace.
149 """)
152 """)
150 def _font_family_default(self):
153 def _font_family_default(self):
151 if sys.platform == 'win32':
154 if sys.platform == 'win32':
152 # Consolas ships with Vista/Win7, fallback to Courier if needed
155 # Consolas ships with Vista/Win7, fallback to Courier if needed
153 return 'Consolas'
156 return 'Consolas'
154 elif sys.platform == 'darwin':
157 elif sys.platform == 'darwin':
155 # OSX always has Monaco, no need for a fallback
158 # OSX always has Monaco, no need for a fallback
156 return 'Monaco'
159 return 'Monaco'
157 else:
160 else:
158 # Monospace should always exist, no need for a fallback
161 # Monospace should always exist, no need for a fallback
159 return 'Monospace'
162 return 'Monospace'
160
163
161 font_size = Integer(config=True,
164 font_size = Integer(config=True,
162 help="""The font size. If unconfigured, Qt will be entrusted
165 help="""The font size. If unconfigured, Qt will be entrusted
163 with the size of the font.
166 with the size of the font.
164 """)
167 """)
165
168
166 width = Integer(81, config=True,
169 width = Integer(81, config=True,
167 help="""The width of the console at start time in number
170 help="""The width of the console at start time in number
168 of characters (will double with `hsplit` paging)
171 of characters (will double with `hsplit` paging)
169 """)
172 """)
170
173
171 height = Integer(25, config=True,
174 height = Integer(25, config=True,
172 help="""The height of the console at start time in number
175 help="""The height of the console at start time in number
173 of characters (will double with `vsplit` paging)
176 of characters (will double with `vsplit` paging)
174 """)
177 """)
175
178
176 # Whether to override ShortcutEvents for the keybindings defined by this
179 # Whether to override ShortcutEvents for the keybindings defined by this
177 # widget (Ctrl+n, Ctrl+a, etc). Enable this if you want this widget to take
180 # widget (Ctrl+n, Ctrl+a, etc). Enable this if you want this widget to take
178 # priority (when it has focus) over, e.g., window-level menu shortcuts.
181 # priority (when it has focus) over, e.g., window-level menu shortcuts.
179 override_shortcuts = Bool(False)
182 override_shortcuts = Bool(False)
180
183
181 # ------ Custom Qt Widgets -------------------------------------------------
184 # ------ Custom Qt Widgets -------------------------------------------------
182
185
183 # For other projects to easily override the Qt widgets used by the console
186 # For other projects to easily override the Qt widgets used by the console
184 # (e.g. Spyder)
187 # (e.g. Spyder)
185 custom_control = None
188 custom_control = None
186 custom_page_control = None
189 custom_page_control = None
187
190
188 #------ Signals ------------------------------------------------------------
191 #------ Signals ------------------------------------------------------------
189
192
190 # Signals that indicate ConsoleWidget state.
193 # Signals that indicate ConsoleWidget state.
191 copy_available = QtCore.Signal(bool)
194 copy_available = QtCore.Signal(bool)
192 redo_available = QtCore.Signal(bool)
195 redo_available = QtCore.Signal(bool)
193 undo_available = QtCore.Signal(bool)
196 undo_available = QtCore.Signal(bool)
194
197
195 # Signal emitted when paging is needed and the paging style has been
198 # Signal emitted when paging is needed and the paging style has been
196 # specified as 'custom'.
199 # specified as 'custom'.
197 custom_page_requested = QtCore.Signal(object)
200 custom_page_requested = QtCore.Signal(object)
198
201
199 # Signal emitted when the font is changed.
202 # Signal emitted when the font is changed.
200 font_changed = QtCore.Signal(QtGui.QFont)
203 font_changed = QtCore.Signal(QtGui.QFont)
201
204
202 #------ Protected class variables ------------------------------------------
205 #------ Protected class variables ------------------------------------------
203
206
204 # control handles
207 # control handles
205 _control = None
208 _control = None
206 _page_control = None
209 _page_control = None
207 _splitter = None
210 _splitter = None
208
211
209 # When the control key is down, these keys are mapped.
212 # When the control key is down, these keys are mapped.
210 _ctrl_down_remap = { QtCore.Qt.Key_B : QtCore.Qt.Key_Left,
213 _ctrl_down_remap = { QtCore.Qt.Key_B : QtCore.Qt.Key_Left,
211 QtCore.Qt.Key_F : QtCore.Qt.Key_Right,
214 QtCore.Qt.Key_F : QtCore.Qt.Key_Right,
212 QtCore.Qt.Key_A : QtCore.Qt.Key_Home,
215 QtCore.Qt.Key_A : QtCore.Qt.Key_Home,
213 QtCore.Qt.Key_P : QtCore.Qt.Key_Up,
216 QtCore.Qt.Key_P : QtCore.Qt.Key_Up,
214 QtCore.Qt.Key_N : QtCore.Qt.Key_Down,
217 QtCore.Qt.Key_N : QtCore.Qt.Key_Down,
215 QtCore.Qt.Key_H : QtCore.Qt.Key_Backspace, }
218 QtCore.Qt.Key_H : QtCore.Qt.Key_Backspace, }
216 if not sys.platform == 'darwin':
219 if not sys.platform == 'darwin':
217 # On OS X, Ctrl-E already does the right thing, whereas End moves the
220 # On OS X, Ctrl-E already does the right thing, whereas End moves the
218 # cursor to the bottom of the buffer.
221 # cursor to the bottom of the buffer.
219 _ctrl_down_remap[QtCore.Qt.Key_E] = QtCore.Qt.Key_End
222 _ctrl_down_remap[QtCore.Qt.Key_E] = QtCore.Qt.Key_End
220
223
221 # The shortcuts defined by this widget. We need to keep track of these to
224 # The shortcuts defined by this widget. We need to keep track of these to
222 # support 'override_shortcuts' above.
225 # support 'override_shortcuts' above.
223 _shortcuts = set(_ctrl_down_remap.keys()) | \
226 _shortcuts = set(_ctrl_down_remap.keys()) | \
224 { QtCore.Qt.Key_C, QtCore.Qt.Key_G, QtCore.Qt.Key_O,
227 { QtCore.Qt.Key_C, QtCore.Qt.Key_G, QtCore.Qt.Key_O,
225 QtCore.Qt.Key_V }
228 QtCore.Qt.Key_V }
226
229
227 _temp_buffer_filled = False
230 _temp_buffer_filled = False
228
231
229 #---------------------------------------------------------------------------
232 #---------------------------------------------------------------------------
230 # 'QObject' interface
233 # 'QObject' interface
231 #---------------------------------------------------------------------------
234 #---------------------------------------------------------------------------
232
235
233 def __init__(self, parent=None, **kw):
236 def __init__(self, parent=None, **kw):
234 """ Create a ConsoleWidget.
237 """ Create a ConsoleWidget.
235
238
236 Parameters:
239 Parameters:
237 -----------
240 -----------
238 parent : QWidget, optional [default None]
241 parent : QWidget, optional [default None]
239 The parent for this widget.
242 The parent for this widget.
240 """
243 """
241 QtGui.QWidget.__init__(self, parent)
244 QtGui.QWidget.__init__(self, parent)
242 LoggingConfigurable.__init__(self, **kw)
245 LoggingConfigurable.__init__(self, **kw)
243
246
244 # While scrolling the pager on Mac OS X, it tears badly. The
247 # While scrolling the pager on Mac OS X, it tears badly. The
245 # NativeGesture is platform and perhaps build-specific hence
248 # NativeGesture is platform and perhaps build-specific hence
246 # we take adequate precautions here.
249 # we take adequate precautions here.
247 self._pager_scroll_events = [QtCore.QEvent.Wheel]
250 self._pager_scroll_events = [QtCore.QEvent.Wheel]
248 if hasattr(QtCore.QEvent, 'NativeGesture'):
251 if hasattr(QtCore.QEvent, 'NativeGesture'):
249 self._pager_scroll_events.append(QtCore.QEvent.NativeGesture)
252 self._pager_scroll_events.append(QtCore.QEvent.NativeGesture)
250
253
251 # Create the layout and underlying text widget.
254 # Create the layout and underlying text widget.
252 layout = QtGui.QStackedLayout(self)
255 layout = QtGui.QStackedLayout(self)
253 layout.setContentsMargins(0, 0, 0, 0)
256 layout.setContentsMargins(0, 0, 0, 0)
254 self._control = self._create_control()
257 self._control = self._create_control()
255 if self.paging in ('hsplit', 'vsplit'):
258 if self.paging in ('hsplit', 'vsplit'):
256 self._splitter = QtGui.QSplitter()
259 self._splitter = QtGui.QSplitter()
257 if self.paging == 'hsplit':
260 if self.paging == 'hsplit':
258 self._splitter.setOrientation(QtCore.Qt.Horizontal)
261 self._splitter.setOrientation(QtCore.Qt.Horizontal)
259 else:
262 else:
260 self._splitter.setOrientation(QtCore.Qt.Vertical)
263 self._splitter.setOrientation(QtCore.Qt.Vertical)
261 self._splitter.addWidget(self._control)
264 self._splitter.addWidget(self._control)
262 layout.addWidget(self._splitter)
265 layout.addWidget(self._splitter)
263 else:
266 else:
264 layout.addWidget(self._control)
267 layout.addWidget(self._control)
265
268
266 # Create the paging widget, if necessary.
269 # Create the paging widget, if necessary.
267 if self.paging in ('inside', 'hsplit', 'vsplit'):
270 if self.paging in ('inside', 'hsplit', 'vsplit'):
268 self._page_control = self._create_page_control()
271 self._page_control = self._create_page_control()
269 if self._splitter:
272 if self._splitter:
270 self._page_control.hide()
273 self._page_control.hide()
271 self._splitter.addWidget(self._page_control)
274 self._splitter.addWidget(self._page_control)
272 else:
275 else:
273 layout.addWidget(self._page_control)
276 layout.addWidget(self._page_control)
274
277
275 # Initialize protected variables. Some variables contain useful state
278 # Initialize protected variables. Some variables contain useful state
276 # information for subclasses; they should be considered read-only.
279 # information for subclasses; they should be considered read-only.
277 self._append_before_prompt_pos = 0
280 self._append_before_prompt_pos = 0
278 self._ansi_processor = QtAnsiCodeProcessor()
281 self._ansi_processor = QtAnsiCodeProcessor()
279 if self.gui_completion == 'ncurses':
282 if self.gui_completion == 'ncurses':
280 self._completion_widget = CompletionHtml(self)
283 self._completion_widget = CompletionHtml(self)
281 elif self.gui_completion == 'droplist':
284 elif self.gui_completion == 'droplist':
282 self._completion_widget = CompletionWidget(self)
285 self._completion_widget = CompletionWidget(self)
283 elif self.gui_completion == 'plain':
286 elif self.gui_completion == 'plain':
284 self._completion_widget = CompletionPlain(self)
287 self._completion_widget = CompletionPlain(self)
285
288
286 self._continuation_prompt = '> '
289 self._continuation_prompt = '> '
287 self._continuation_prompt_html = None
290 self._continuation_prompt_html = None
288 self._executing = False
291 self._executing = False
289 self._filter_resize = False
292 self._filter_resize = False
290 self._html_exporter = HtmlExporter(self._control)
293 self._html_exporter = HtmlExporter(self._control)
291 self._input_buffer_executing = ''
294 self._input_buffer_executing = ''
292 self._input_buffer_pending = ''
295 self._input_buffer_pending = ''
293 self._kill_ring = QtKillRing(self._control)
296 self._kill_ring = QtKillRing(self._control)
294 self._prompt = ''
297 self._prompt = ''
295 self._prompt_html = None
298 self._prompt_html = None
296 self._prompt_pos = 0
299 self._prompt_pos = 0
297 self._prompt_sep = ''
300 self._prompt_sep = ''
298 self._reading = False
301 self._reading = False
299 self._reading_callback = None
302 self._reading_callback = None
300 self._tab_width = 8
303 self._tab_width = 8
301
304
302 # List of strings pending to be appended as plain text in the widget.
305 # List of strings pending to be appended as plain text in the widget.
303 # The text is not immediately inserted when available to not
306 # The text is not immediately inserted when available to not
304 # choke the Qt event loop with paint events for the widget in
307 # choke the Qt event loop with paint events for the widget in
305 # case of lots of output from kernel.
308 # case of lots of output from kernel.
306 self._pending_insert_text = []
309 self._pending_insert_text = []
307
310
308 # Timer to flush the pending stream messages. The interval is adjusted
311 # Timer to flush the pending stream messages. The interval is adjusted
309 # later based on actual time taken for flushing a screen (buffer_size)
312 # later based on actual time taken for flushing a screen (buffer_size)
310 # of output text.
313 # of output text.
311 self._pending_text_flush_interval = QtCore.QTimer(self._control)
314 self._pending_text_flush_interval = QtCore.QTimer(self._control)
312 self._pending_text_flush_interval.setInterval(100)
315 self._pending_text_flush_interval.setInterval(100)
313 self._pending_text_flush_interval.setSingleShot(True)
316 self._pending_text_flush_interval.setSingleShot(True)
314 self._pending_text_flush_interval.timeout.connect(
317 self._pending_text_flush_interval.timeout.connect(
315 self._flush_pending_stream)
318 self._flush_pending_stream)
316
319
317 # Set a monospaced font.
320 # Set a monospaced font.
318 self.reset_font()
321 self.reset_font()
319
322
320 # Configure actions.
323 # Configure actions.
321 action = QtGui.QAction('Print', None)
324 action = QtGui.QAction('Print', None)
322 action.setEnabled(True)
325 action.setEnabled(True)
323 printkey = QtGui.QKeySequence(QtGui.QKeySequence.Print)
326 printkey = QtGui.QKeySequence(QtGui.QKeySequence.Print)
324 if printkey.matches("Ctrl+P") and sys.platform != 'darwin':
327 if printkey.matches("Ctrl+P") and sys.platform != 'darwin':
325 # Only override the default if there is a collision.
328 # Only override the default if there is a collision.
326 # Qt ctrl = cmd on OSX, so the match gets a false positive on OSX.
329 # Qt ctrl = cmd on OSX, so the match gets a false positive on OSX.
327 printkey = "Ctrl+Shift+P"
330 printkey = "Ctrl+Shift+P"
328 action.setShortcut(printkey)
331 action.setShortcut(printkey)
329 action.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut)
332 action.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut)
330 action.triggered.connect(self.print_)
333 action.triggered.connect(self.print_)
331 self.addAction(action)
334 self.addAction(action)
332 self.print_action = action
335 self.print_action = action
333
336
334 action = QtGui.QAction('Save as HTML/XML', None)
337 action = QtGui.QAction('Save as HTML/XML', None)
335 action.setShortcut(QtGui.QKeySequence.Save)
338 action.setShortcut(QtGui.QKeySequence.Save)
336 action.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut)
339 action.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut)
337 action.triggered.connect(self.export_html)
340 action.triggered.connect(self.export_html)
338 self.addAction(action)
341 self.addAction(action)
339 self.export_action = action
342 self.export_action = action
340
343
341 action = QtGui.QAction('Select All', None)
344 action = QtGui.QAction('Select All', None)
342 action.setEnabled(True)
345 action.setEnabled(True)
343 selectall = QtGui.QKeySequence(QtGui.QKeySequence.SelectAll)
346 selectall = QtGui.QKeySequence(QtGui.QKeySequence.SelectAll)
344 if selectall.matches("Ctrl+A") and sys.platform != 'darwin':
347 if selectall.matches("Ctrl+A") and sys.platform != 'darwin':
345 # Only override the default if there is a collision.
348 # Only override the default if there is a collision.
346 # Qt ctrl = cmd on OSX, so the match gets a false positive on OSX.
349 # Qt ctrl = cmd on OSX, so the match gets a false positive on OSX.
347 selectall = "Ctrl+Shift+A"
350 selectall = "Ctrl+Shift+A"
348 action.setShortcut(selectall)
351 action.setShortcut(selectall)
349 action.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut)
352 action.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut)
350 action.triggered.connect(self.select_all)
353 action.triggered.connect(self.select_all)
351 self.addAction(action)
354 self.addAction(action)
352 self.select_all_action = action
355 self.select_all_action = action
353
356
354 self.increase_font_size = QtGui.QAction("Bigger Font",
357 self.increase_font_size = QtGui.QAction("Bigger Font",
355 self,
358 self,
356 shortcut=QtGui.QKeySequence.ZoomIn,
359 shortcut=QtGui.QKeySequence.ZoomIn,
357 shortcutContext=QtCore.Qt.WidgetWithChildrenShortcut,
360 shortcutContext=QtCore.Qt.WidgetWithChildrenShortcut,
358 statusTip="Increase the font size by one point",
361 statusTip="Increase the font size by one point",
359 triggered=self._increase_font_size)
362 triggered=self._increase_font_size)
360 self.addAction(self.increase_font_size)
363 self.addAction(self.increase_font_size)
361
364
362 self.decrease_font_size = QtGui.QAction("Smaller Font",
365 self.decrease_font_size = QtGui.QAction("Smaller Font",
363 self,
366 self,
364 shortcut=QtGui.QKeySequence.ZoomOut,
367 shortcut=QtGui.QKeySequence.ZoomOut,
365 shortcutContext=QtCore.Qt.WidgetWithChildrenShortcut,
368 shortcutContext=QtCore.Qt.WidgetWithChildrenShortcut,
366 statusTip="Decrease the font size by one point",
369 statusTip="Decrease the font size by one point",
367 triggered=self._decrease_font_size)
370 triggered=self._decrease_font_size)
368 self.addAction(self.decrease_font_size)
371 self.addAction(self.decrease_font_size)
369
372
370 self.reset_font_size = QtGui.QAction("Normal Font",
373 self.reset_font_size = QtGui.QAction("Normal Font",
371 self,
374 self,
372 shortcut="Ctrl+0",
375 shortcut="Ctrl+0",
373 shortcutContext=QtCore.Qt.WidgetWithChildrenShortcut,
376 shortcutContext=QtCore.Qt.WidgetWithChildrenShortcut,
374 statusTip="Restore the Normal font size",
377 statusTip="Restore the Normal font size",
375 triggered=self.reset_font)
378 triggered=self.reset_font)
376 self.addAction(self.reset_font_size)
379 self.addAction(self.reset_font_size)
377
380
378 # Accept drag and drop events here. Drops were already turned off
381 # Accept drag and drop events here. Drops were already turned off
379 # in self._control when that widget was created.
382 # in self._control when that widget was created.
380 self.setAcceptDrops(True)
383 self.setAcceptDrops(True)
381
384
382 #---------------------------------------------------------------------------
385 #---------------------------------------------------------------------------
383 # Drag and drop support
386 # Drag and drop support
384 #---------------------------------------------------------------------------
387 #---------------------------------------------------------------------------
385
388
386 def dragEnterEvent(self, e):
389 def dragEnterEvent(self, e):
387 if e.mimeData().hasUrls():
390 if e.mimeData().hasUrls():
388 # The link action should indicate to that the drop will insert
391 # The link action should indicate to that the drop will insert
389 # the file anme.
392 # the file anme.
390 e.setDropAction(QtCore.Qt.LinkAction)
393 e.setDropAction(QtCore.Qt.LinkAction)
391 e.accept()
394 e.accept()
392 elif e.mimeData().hasText():
395 elif e.mimeData().hasText():
393 # By changing the action to copy we don't need to worry about
396 # By changing the action to copy we don't need to worry about
394 # the user accidentally moving text around in the widget.
397 # the user accidentally moving text around in the widget.
395 e.setDropAction(QtCore.Qt.CopyAction)
398 e.setDropAction(QtCore.Qt.CopyAction)
396 e.accept()
399 e.accept()
397
400
398 def dragMoveEvent(self, e):
401 def dragMoveEvent(self, e):
399 if e.mimeData().hasUrls():
402 if e.mimeData().hasUrls():
400 pass
403 pass
401 elif e.mimeData().hasText():
404 elif e.mimeData().hasText():
402 cursor = self._control.cursorForPosition(e.pos())
405 cursor = self._control.cursorForPosition(e.pos())
403 if self._in_buffer(cursor.position()):
406 if self._in_buffer(cursor.position()):
404 e.setDropAction(QtCore.Qt.CopyAction)
407 e.setDropAction(QtCore.Qt.CopyAction)
405 self._control.setTextCursor(cursor)
408 self._control.setTextCursor(cursor)
406 else:
409 else:
407 e.setDropAction(QtCore.Qt.IgnoreAction)
410 e.setDropAction(QtCore.Qt.IgnoreAction)
408 e.accept()
411 e.accept()
409
412
410 def dropEvent(self, e):
413 def dropEvent(self, e):
411 if e.mimeData().hasUrls():
414 if e.mimeData().hasUrls():
412 self._keep_cursor_in_buffer()
415 self._keep_cursor_in_buffer()
413 cursor = self._control.textCursor()
416 cursor = self._control.textCursor()
414 filenames = [url.toLocalFile() for url in e.mimeData().urls()]
417 filenames = [url.toLocalFile() for url in e.mimeData().urls()]
415 text = ', '.join("'" + f.replace("'", "'\"'\"'") + "'"
418 text = ', '.join("'" + f.replace("'", "'\"'\"'") + "'"
416 for f in filenames)
419 for f in filenames)
417 self._insert_plain_text_into_buffer(cursor, text)
420 self._insert_plain_text_into_buffer(cursor, text)
418 elif e.mimeData().hasText():
421 elif e.mimeData().hasText():
419 cursor = self._control.cursorForPosition(e.pos())
422 cursor = self._control.cursorForPosition(e.pos())
420 if self._in_buffer(cursor.position()):
423 if self._in_buffer(cursor.position()):
421 text = e.mimeData().text()
424 text = e.mimeData().text()
422 self._insert_plain_text_into_buffer(cursor, text)
425 self._insert_plain_text_into_buffer(cursor, text)
423
426
424 def eventFilter(self, obj, event):
427 def eventFilter(self, obj, event):
425 """ Reimplemented to ensure a console-like behavior in the underlying
428 """ Reimplemented to ensure a console-like behavior in the underlying
426 text widgets.
429 text widgets.
427 """
430 """
428 etype = event.type()
431 etype = event.type()
429 if etype == QtCore.QEvent.KeyPress:
432 if etype == QtCore.QEvent.KeyPress:
430
433
431 # Re-map keys for all filtered widgets.
434 # Re-map keys for all filtered widgets.
432 key = event.key()
435 key = event.key()
433 if self._control_key_down(event.modifiers()) and \
436 if self._control_key_down(event.modifiers()) and \
434 key in self._ctrl_down_remap:
437 key in self._ctrl_down_remap:
435 new_event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress,
438 new_event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress,
436 self._ctrl_down_remap[key],
439 self._ctrl_down_remap[key],
437 QtCore.Qt.NoModifier)
440 QtCore.Qt.NoModifier)
438 QtGui.qApp.sendEvent(obj, new_event)
441 QtGui.qApp.sendEvent(obj, new_event)
439 return True
442 return True
440
443
441 elif obj == self._control:
444 elif obj == self._control:
442 return self._event_filter_console_keypress(event)
445 return self._event_filter_console_keypress(event)
443
446
444 elif obj == self._page_control:
447 elif obj == self._page_control:
445 return self._event_filter_page_keypress(event)
448 return self._event_filter_page_keypress(event)
446
449
447 # Make middle-click paste safe.
450 # Make middle-click paste safe.
448 elif etype == QtCore.QEvent.MouseButtonRelease and \
451 elif etype == QtCore.QEvent.MouseButtonRelease and \
449 event.button() == QtCore.Qt.MidButton and \
452 event.button() == QtCore.Qt.MidButton and \
450 obj == self._control.viewport():
453 obj == self._control.viewport():
451 cursor = self._control.cursorForPosition(event.pos())
454 cursor = self._control.cursorForPosition(event.pos())
452 self._control.setTextCursor(cursor)
455 self._control.setTextCursor(cursor)
453 self.paste(QtGui.QClipboard.Selection)
456 self.paste(QtGui.QClipboard.Selection)
454 return True
457 return True
455
458
456 # Manually adjust the scrollbars *after* a resize event is dispatched.
459 # Manually adjust the scrollbars *after* a resize event is dispatched.
457 elif etype == QtCore.QEvent.Resize and not self._filter_resize:
460 elif etype == QtCore.QEvent.Resize and not self._filter_resize:
458 self._filter_resize = True
461 self._filter_resize = True
459 QtGui.qApp.sendEvent(obj, event)
462 QtGui.qApp.sendEvent(obj, event)
460 self._adjust_scrollbars()
463 self._adjust_scrollbars()
461 self._filter_resize = False
464 self._filter_resize = False
462 return True
465 return True
463
466
464 # Override shortcuts for all filtered widgets.
467 # Override shortcuts for all filtered widgets.
465 elif etype == QtCore.QEvent.ShortcutOverride and \
468 elif etype == QtCore.QEvent.ShortcutOverride and \
466 self.override_shortcuts and \
469 self.override_shortcuts and \
467 self._control_key_down(event.modifiers()) and \
470 self._control_key_down(event.modifiers()) and \
468 event.key() in self._shortcuts:
471 event.key() in self._shortcuts:
469 event.accept()
472 event.accept()
470
473
471 # Handle scrolling of the vsplit pager. This hack attempts to solve
474 # Handle scrolling of the vsplit pager. This hack attempts to solve
472 # problems with tearing of the help text inside the pager window. This
475 # problems with tearing of the help text inside the pager window. This
473 # happens only on Mac OS X with both PySide and PyQt. This fix isn't
476 # happens only on Mac OS X with both PySide and PyQt. This fix isn't
474 # perfect but makes the pager more usable.
477 # perfect but makes the pager more usable.
475 elif etype in self._pager_scroll_events and \
478 elif etype in self._pager_scroll_events and \
476 obj == self._page_control:
479 obj == self._page_control:
477 self._page_control.repaint()
480 self._page_control.repaint()
478 return True
481 return True
479
482
480 elif etype == QtCore.QEvent.MouseMove:
483 elif etype == QtCore.QEvent.MouseMove:
481 anchor = self._control.anchorAt(event.pos())
484 anchor = self._control.anchorAt(event.pos())
482 QtGui.QToolTip.showText(event.globalPos(), anchor)
485 QtGui.QToolTip.showText(event.globalPos(), anchor)
483
486
484 return super(ConsoleWidget, self).eventFilter(obj, event)
487 return super(ConsoleWidget, self).eventFilter(obj, event)
485
488
486 #---------------------------------------------------------------------------
489 #---------------------------------------------------------------------------
487 # 'QWidget' interface
490 # 'QWidget' interface
488 #---------------------------------------------------------------------------
491 #---------------------------------------------------------------------------
489
492
490 def sizeHint(self):
493 def sizeHint(self):
491 """ Reimplemented to suggest a size that is 80 characters wide and
494 """ Reimplemented to suggest a size that is 80 characters wide and
492 25 lines high.
495 25 lines high.
493 """
496 """
494 font_metrics = QtGui.QFontMetrics(self.font)
497 font_metrics = QtGui.QFontMetrics(self.font)
495 margin = (self._control.frameWidth() +
498 margin = (self._control.frameWidth() +
496 self._control.document().documentMargin()) * 2
499 self._control.document().documentMargin()) * 2
497 style = self.style()
500 style = self.style()
498 splitwidth = style.pixelMetric(QtGui.QStyle.PM_SplitterWidth)
501 splitwidth = style.pixelMetric(QtGui.QStyle.PM_SplitterWidth)
499
502
500 # Note 1: Despite my best efforts to take the various margins into
503 # Note 1: Despite my best efforts to take the various margins into
501 # account, the width is still coming out a bit too small, so we include
504 # account, the width is still coming out a bit too small, so we include
502 # a fudge factor of one character here.
505 # a fudge factor of one character here.
503 # Note 2: QFontMetrics.maxWidth is not used here or anywhere else due
506 # Note 2: QFontMetrics.maxWidth is not used here or anywhere else due
504 # to a Qt bug on certain Mac OS systems where it returns 0.
507 # to a Qt bug on certain Mac OS systems where it returns 0.
505 width = font_metrics.width(' ') * self.width + margin
508 width = font_metrics.width(' ') * self.width + margin
506 width += style.pixelMetric(QtGui.QStyle.PM_ScrollBarExtent)
509 width += style.pixelMetric(QtGui.QStyle.PM_ScrollBarExtent)
507 if self.paging == 'hsplit':
510 if self.paging == 'hsplit':
508 width = width * 2 + splitwidth
511 width = width * 2 + splitwidth
509
512
510 height = font_metrics.height() * self.height + margin
513 height = font_metrics.height() * self.height + margin
511 if self.paging == 'vsplit':
514 if self.paging == 'vsplit':
512 height = height * 2 + splitwidth
515 height = height * 2 + splitwidth
513
516
514 return QtCore.QSize(width, height)
517 return QtCore.QSize(width, height)
515
518
516 #---------------------------------------------------------------------------
519 #---------------------------------------------------------------------------
517 # 'ConsoleWidget' public interface
520 # 'ConsoleWidget' public interface
518 #---------------------------------------------------------------------------
521 #---------------------------------------------------------------------------
519
522
520 def can_copy(self):
523 def can_copy(self):
521 """ Returns whether text can be copied to the clipboard.
524 """ Returns whether text can be copied to the clipboard.
522 """
525 """
523 return self._control.textCursor().hasSelection()
526 return self._control.textCursor().hasSelection()
524
527
525 def can_cut(self):
528 def can_cut(self):
526 """ Returns whether text can be cut to the clipboard.
529 """ Returns whether text can be cut to the clipboard.
527 """
530 """
528 cursor = self._control.textCursor()
531 cursor = self._control.textCursor()
529 return (cursor.hasSelection() and
532 return (cursor.hasSelection() and
530 self._in_buffer(cursor.anchor()) and
533 self._in_buffer(cursor.anchor()) and
531 self._in_buffer(cursor.position()))
534 self._in_buffer(cursor.position()))
532
535
533 def can_paste(self):
536 def can_paste(self):
534 """ Returns whether text can be pasted from the clipboard.
537 """ Returns whether text can be pasted from the clipboard.
535 """
538 """
536 if self._control.textInteractionFlags() & QtCore.Qt.TextEditable:
539 if self._control.textInteractionFlags() & QtCore.Qt.TextEditable:
537 return bool(QtGui.QApplication.clipboard().text())
540 return bool(QtGui.QApplication.clipboard().text())
538 return False
541 return False
539
542
540 def clear(self, keep_input=True):
543 def clear(self, keep_input=True):
541 """ Clear the console.
544 """ Clear the console.
542
545
543 Parameters:
546 Parameters:
544 -----------
547 -----------
545 keep_input : bool, optional (default True)
548 keep_input : bool, optional (default True)
546 If set, restores the old input buffer if a new prompt is written.
549 If set, restores the old input buffer if a new prompt is written.
547 """
550 """
548 if self._executing:
551 if self._executing:
549 self._control.clear()
552 self._control.clear()
550 else:
553 else:
551 if keep_input:
554 if keep_input:
552 input_buffer = self.input_buffer
555 input_buffer = self.input_buffer
553 self._control.clear()
556 self._control.clear()
554 self._show_prompt()
557 self._show_prompt()
555 if keep_input:
558 if keep_input:
556 self.input_buffer = input_buffer
559 self.input_buffer = input_buffer
557
560
558 def copy(self):
561 def copy(self):
559 """ Copy the currently selected text to the clipboard.
562 """ Copy the currently selected text to the clipboard.
560 """
563 """
561 self.layout().currentWidget().copy()
564 self.layout().currentWidget().copy()
562
565
563 def copy_anchor(self, anchor):
566 def copy_anchor(self, anchor):
564 """ Copy anchor text to the clipboard
567 """ Copy anchor text to the clipboard
565 """
568 """
566 QtGui.QApplication.clipboard().setText(anchor)
569 QtGui.QApplication.clipboard().setText(anchor)
567
570
568 def cut(self):
571 def cut(self):
569 """ Copy the currently selected text to the clipboard and delete it
572 """ Copy the currently selected text to the clipboard and delete it
570 if it's inside the input buffer.
573 if it's inside the input buffer.
571 """
574 """
572 self.copy()
575 self.copy()
573 if self.can_cut():
576 if self.can_cut():
574 self._control.textCursor().removeSelectedText()
577 self._control.textCursor().removeSelectedText()
575
578
576 def execute(self, source=None, hidden=False, interactive=False):
579 def execute(self, source=None, hidden=False, interactive=False):
577 """ Executes source or the input buffer, possibly prompting for more
580 """ Executes source or the input buffer, possibly prompting for more
578 input.
581 input.
579
582
580 Parameters:
583 Parameters:
581 -----------
584 -----------
582 source : str, optional
585 source : str, optional
583
586
584 The source to execute. If not specified, the input buffer will be
587 The source to execute. If not specified, the input buffer will be
585 used. If specified and 'hidden' is False, the input buffer will be
588 used. If specified and 'hidden' is False, the input buffer will be
586 replaced with the source before execution.
589 replaced with the source before execution.
587
590
588 hidden : bool, optional (default False)
591 hidden : bool, optional (default False)
589
592
590 If set, no output will be shown and the prompt will not be modified.
593 If set, no output will be shown and the prompt will not be modified.
591 In other words, it will be completely invisible to the user that
594 In other words, it will be completely invisible to the user that
592 an execution has occurred.
595 an execution has occurred.
593
596
594 interactive : bool, optional (default False)
597 interactive : bool, optional (default False)
595
598
596 Whether the console is to treat the source as having been manually
599 Whether the console is to treat the source as having been manually
597 entered by the user. The effect of this parameter depends on the
600 entered by the user. The effect of this parameter depends on the
598 subclass implementation.
601 subclass implementation.
599
602
600 Raises:
603 Raises:
601 -------
604 -------
602 RuntimeError
605 RuntimeError
603 If incomplete input is given and 'hidden' is True. In this case,
606 If incomplete input is given and 'hidden' is True. In this case,
604 it is not possible to prompt for more input.
607 it is not possible to prompt for more input.
605
608
606 Returns:
609 Returns:
607 --------
610 --------
608 A boolean indicating whether the source was executed.
611 A boolean indicating whether the source was executed.
609 """
612 """
610 # WARNING: The order in which things happen here is very particular, in
613 # WARNING: The order in which things happen here is very particular, in
611 # large part because our syntax highlighting is fragile. If you change
614 # large part because our syntax highlighting is fragile. If you change
612 # something, test carefully!
615 # something, test carefully!
613
616
614 # Decide what to execute.
617 # Decide what to execute.
615 if source is None:
618 if source is None:
616 source = self.input_buffer
619 source = self.input_buffer
617 if not hidden:
620 if not hidden:
618 # A newline is appended later, but it should be considered part
621 # A newline is appended later, but it should be considered part
619 # of the input buffer.
622 # of the input buffer.
620 source += '\n'
623 source += '\n'
621 elif not hidden:
624 elif not hidden:
622 self.input_buffer = source
625 self.input_buffer = source
623
626
624 # Execute the source or show a continuation prompt if it is incomplete.
627 # Execute the source or show a continuation prompt if it is incomplete.
625 if self.execute_on_complete_input:
628 if self.execute_on_complete_input:
626 complete = self._is_complete(source, interactive)
629 complete = self._is_complete(source, interactive)
627 else:
630 else:
628 complete = not interactive
631 complete = not interactive
629 if hidden:
632 if hidden:
630 if complete or not self.execute_on_complete_input:
633 if complete or not self.execute_on_complete_input:
631 self._execute(source, hidden)
634 self._execute(source, hidden)
632 else:
635 else:
633 error = 'Incomplete noninteractive input: "%s"'
636 error = 'Incomplete noninteractive input: "%s"'
634 raise RuntimeError(error % source)
637 raise RuntimeError(error % source)
635 else:
638 else:
636 if complete:
639 if complete:
637 self._append_plain_text('\n')
640 self._append_plain_text('\n')
638 self._input_buffer_executing = self.input_buffer
641 self._input_buffer_executing = self.input_buffer
639 self._executing = True
642 self._executing = True
640 self._prompt_finished()
643 self._prompt_finished()
641
644
642 # The maximum block count is only in effect during execution.
645 # The maximum block count is only in effect during execution.
643 # This ensures that _prompt_pos does not become invalid due to
646 # This ensures that _prompt_pos does not become invalid due to
644 # text truncation.
647 # text truncation.
645 self._control.document().setMaximumBlockCount(self.buffer_size)
648 self._control.document().setMaximumBlockCount(self.buffer_size)
646
649
647 # Setting a positive maximum block count will automatically
650 # Setting a positive maximum block count will automatically
648 # disable the undo/redo history, but just to be safe:
651 # disable the undo/redo history, but just to be safe:
649 self._control.setUndoRedoEnabled(False)
652 self._control.setUndoRedoEnabled(False)
650
653
651 # Perform actual execution.
654 # Perform actual execution.
652 self._execute(source, hidden)
655 self._execute(source, hidden)
653
656
654 else:
657 else:
655 # Do this inside an edit block so continuation prompts are
658 # Do this inside an edit block so continuation prompts are
656 # removed seamlessly via undo/redo.
659 # removed seamlessly via undo/redo.
657 cursor = self._get_end_cursor()
660 cursor = self._get_end_cursor()
658 cursor.beginEditBlock()
661 cursor.beginEditBlock()
659 cursor.insertText('\n')
662 cursor.insertText('\n')
660 self._insert_continuation_prompt(cursor)
663 self._insert_continuation_prompt(cursor)
661 cursor.endEditBlock()
664 cursor.endEditBlock()
662
665
663 # Do not do this inside the edit block. It works as expected
666 # Do not do this inside the edit block. It works as expected
664 # when using a QPlainTextEdit control, but does not have an
667 # when using a QPlainTextEdit control, but does not have an
665 # effect when using a QTextEdit. I believe this is a Qt bug.
668 # effect when using a QTextEdit. I believe this is a Qt bug.
666 self._control.moveCursor(QtGui.QTextCursor.End)
669 self._control.moveCursor(QtGui.QTextCursor.End)
667
670
668 return complete
671 return complete
669
672
670 def export_html(self):
673 def export_html(self):
671 """ Shows a dialog to export HTML/XML in various formats.
674 """ Shows a dialog to export HTML/XML in various formats.
672 """
675 """
673 self._html_exporter.export()
676 self._html_exporter.export()
674
677
675 def _get_input_buffer(self, force=False):
678 def _get_input_buffer(self, force=False):
676 """ The text that the user has entered entered at the current prompt.
679 """ The text that the user has entered entered at the current prompt.
677
680
678 If the console is currently executing, the text that is executing will
681 If the console is currently executing, the text that is executing will
679 always be returned.
682 always be returned.
680 """
683 """
681 # If we're executing, the input buffer may not even exist anymore due to
684 # If we're executing, the input buffer may not even exist anymore due to
682 # the limit imposed by 'buffer_size'. Therefore, we store it.
685 # the limit imposed by 'buffer_size'. Therefore, we store it.
683 if self._executing and not force:
686 if self._executing and not force:
684 return self._input_buffer_executing
687 return self._input_buffer_executing
685
688
686 cursor = self._get_end_cursor()
689 cursor = self._get_end_cursor()
687 cursor.setPosition(self._prompt_pos, QtGui.QTextCursor.KeepAnchor)
690 cursor.setPosition(self._prompt_pos, QtGui.QTextCursor.KeepAnchor)
688 input_buffer = cursor.selection().toPlainText()
691 input_buffer = cursor.selection().toPlainText()
689
692
690 # Strip out continuation prompts.
693 # Strip out continuation prompts.
691 return input_buffer.replace('\n' + self._continuation_prompt, '\n')
694 return input_buffer.replace('\n' + self._continuation_prompt, '\n')
692
695
693 def _set_input_buffer(self, string):
696 def _set_input_buffer(self, string):
694 """ Sets the text in the input buffer.
697 """ Sets the text in the input buffer.
695
698
696 If the console is currently executing, this call has no *immediate*
699 If the console is currently executing, this call has no *immediate*
697 effect. When the execution is finished, the input buffer will be updated
700 effect. When the execution is finished, the input buffer will be updated
698 appropriately.
701 appropriately.
699 """
702 """
700 # If we're executing, store the text for later.
703 # If we're executing, store the text for later.
701 if self._executing:
704 if self._executing:
702 self._input_buffer_pending = string
705 self._input_buffer_pending = string
703 return
706 return
704
707
705 # Remove old text.
708 # Remove old text.
706 cursor = self._get_end_cursor()
709 cursor = self._get_end_cursor()
707 cursor.beginEditBlock()
710 cursor.beginEditBlock()
708 cursor.setPosition(self._prompt_pos, QtGui.QTextCursor.KeepAnchor)
711 cursor.setPosition(self._prompt_pos, QtGui.QTextCursor.KeepAnchor)
709 cursor.removeSelectedText()
712 cursor.removeSelectedText()
710
713
711 # Insert new text with continuation prompts.
714 # Insert new text with continuation prompts.
712 self._insert_plain_text_into_buffer(self._get_prompt_cursor(), string)
715 self._insert_plain_text_into_buffer(self._get_prompt_cursor(), string)
713 cursor.endEditBlock()
716 cursor.endEditBlock()
714 self._control.moveCursor(QtGui.QTextCursor.End)
717 self._control.moveCursor(QtGui.QTextCursor.End)
715
718
716 input_buffer = property(_get_input_buffer, _set_input_buffer)
719 input_buffer = property(_get_input_buffer, _set_input_buffer)
717
720
718 def _get_font(self):
721 def _get_font(self):
719 """ The base font being used by the ConsoleWidget.
722 """ The base font being used by the ConsoleWidget.
720 """
723 """
721 return self._control.document().defaultFont()
724 return self._control.document().defaultFont()
722
725
723 def _set_font(self, font):
726 def _set_font(self, font):
724 """ Sets the base font for the ConsoleWidget to the specified QFont.
727 """ Sets the base font for the ConsoleWidget to the specified QFont.
725 """
728 """
726 font_metrics = QtGui.QFontMetrics(font)
729 font_metrics = QtGui.QFontMetrics(font)
727 self._control.setTabStopWidth(self.tab_width * font_metrics.width(' '))
730 self._control.setTabStopWidth(self.tab_width * font_metrics.width(' '))
728
731
729 self._completion_widget.setFont(font)
732 self._completion_widget.setFont(font)
730 self._control.document().setDefaultFont(font)
733 self._control.document().setDefaultFont(font)
731 if self._page_control:
734 if self._page_control:
732 self._page_control.document().setDefaultFont(font)
735 self._page_control.document().setDefaultFont(font)
733
736
734 self.font_changed.emit(font)
737 self.font_changed.emit(font)
735
738
736 font = property(_get_font, _set_font)
739 font = property(_get_font, _set_font)
737
740
738 def open_anchor(self, anchor):
741 def open_anchor(self, anchor):
739 """ Open selected anchor in the default webbrowser
742 """ Open selected anchor in the default webbrowser
740 """
743 """
741 webbrowser.open( anchor )
744 webbrowser.open( anchor )
742
745
743 def paste(self, mode=QtGui.QClipboard.Clipboard):
746 def paste(self, mode=QtGui.QClipboard.Clipboard):
744 """ Paste the contents of the clipboard into the input region.
747 """ Paste the contents of the clipboard into the input region.
745
748
746 Parameters:
749 Parameters:
747 -----------
750 -----------
748 mode : QClipboard::Mode, optional [default QClipboard::Clipboard]
751 mode : QClipboard::Mode, optional [default QClipboard::Clipboard]
749
752
750 Controls which part of the system clipboard is used. This can be
753 Controls which part of the system clipboard is used. This can be
751 used to access the selection clipboard in X11 and the Find buffer
754 used to access the selection clipboard in X11 and the Find buffer
752 in Mac OS. By default, the regular clipboard is used.
755 in Mac OS. By default, the regular clipboard is used.
753 """
756 """
754 if self._control.textInteractionFlags() & QtCore.Qt.TextEditable:
757 if self._control.textInteractionFlags() & QtCore.Qt.TextEditable:
755 # Make sure the paste is safe.
758 # Make sure the paste is safe.
756 self._keep_cursor_in_buffer()
759 self._keep_cursor_in_buffer()
757 cursor = self._control.textCursor()
760 cursor = self._control.textCursor()
758
761
759 # Remove any trailing newline, which confuses the GUI and forces the
762 # Remove any trailing newline, which confuses the GUI and forces the
760 # user to backspace.
763 # user to backspace.
761 text = QtGui.QApplication.clipboard().text(mode).rstrip()
764 text = QtGui.QApplication.clipboard().text(mode).rstrip()
762 self._insert_plain_text_into_buffer(cursor, dedent(text))
765 self._insert_plain_text_into_buffer(cursor, dedent(text))
763
766
764 def print_(self, printer = None):
767 def print_(self, printer = None):
765 """ Print the contents of the ConsoleWidget to the specified QPrinter.
768 """ Print the contents of the ConsoleWidget to the specified QPrinter.
766 """
769 """
767 if (not printer):
770 if (not printer):
768 printer = QtGui.QPrinter()
771 printer = QtGui.QPrinter()
769 if(QtGui.QPrintDialog(printer).exec_() != QtGui.QDialog.Accepted):
772 if(QtGui.QPrintDialog(printer).exec_() != QtGui.QDialog.Accepted):
770 return
773 return
771 self._control.print_(printer)
774 self._control.print_(printer)
772
775
773 def prompt_to_top(self):
776 def prompt_to_top(self):
774 """ Moves the prompt to the top of the viewport.
777 """ Moves the prompt to the top of the viewport.
775 """
778 """
776 if not self._executing:
779 if not self._executing:
777 prompt_cursor = self._get_prompt_cursor()
780 prompt_cursor = self._get_prompt_cursor()
778 if self._get_cursor().blockNumber() < prompt_cursor.blockNumber():
781 if self._get_cursor().blockNumber() < prompt_cursor.blockNumber():
779 self._set_cursor(prompt_cursor)
782 self._set_cursor(prompt_cursor)
780 self._set_top_cursor(prompt_cursor)
783 self._set_top_cursor(prompt_cursor)
781
784
782 def redo(self):
785 def redo(self):
783 """ Redo the last operation. If there is no operation to redo, nothing
786 """ Redo the last operation. If there is no operation to redo, nothing
784 happens.
787 happens.
785 """
788 """
786 self._control.redo()
789 self._control.redo()
787
790
788 def reset_font(self):
791 def reset_font(self):
789 """ Sets the font to the default fixed-width font for this platform.
792 """ Sets the font to the default fixed-width font for this platform.
790 """
793 """
791 if sys.platform == 'win32':
794 if sys.platform == 'win32':
792 # Consolas ships with Vista/Win7, fallback to Courier if needed
795 # Consolas ships with Vista/Win7, fallback to Courier if needed
793 fallback = 'Courier'
796 fallback = 'Courier'
794 elif sys.platform == 'darwin':
797 elif sys.platform == 'darwin':
795 # OSX always has Monaco
798 # OSX always has Monaco
796 fallback = 'Monaco'
799 fallback = 'Monaco'
797 else:
800 else:
798 # Monospace should always exist
801 # Monospace should always exist
799 fallback = 'Monospace'
802 fallback = 'Monospace'
800 font = get_font(self.font_family, fallback)
803 font = get_font(self.font_family, fallback)
801 if self.font_size:
804 if self.font_size:
802 font.setPointSize(self.font_size)
805 font.setPointSize(self.font_size)
803 else:
806 else:
804 font.setPointSize(QtGui.qApp.font().pointSize())
807 font.setPointSize(QtGui.qApp.font().pointSize())
805 font.setStyleHint(QtGui.QFont.TypeWriter)
808 font.setStyleHint(QtGui.QFont.TypeWriter)
806 self._set_font(font)
809 self._set_font(font)
807
810
808 def change_font_size(self, delta):
811 def change_font_size(self, delta):
809 """Change the font size by the specified amount (in points).
812 """Change the font size by the specified amount (in points).
810 """
813 """
811 font = self.font
814 font = self.font
812 size = max(font.pointSize() + delta, 1) # minimum 1 point
815 size = max(font.pointSize() + delta, 1) # minimum 1 point
813 font.setPointSize(size)
816 font.setPointSize(size)
814 self._set_font(font)
817 self._set_font(font)
815
818
816 def _increase_font_size(self):
819 def _increase_font_size(self):
817 self.change_font_size(1)
820 self.change_font_size(1)
818
821
819 def _decrease_font_size(self):
822 def _decrease_font_size(self):
820 self.change_font_size(-1)
823 self.change_font_size(-1)
821
824
822 def select_all(self):
825 def select_all(self):
823 """ Selects all the text in the buffer.
826 """ Selects all the text in the buffer.
824 """
827 """
825 self._control.selectAll()
828 self._control.selectAll()
826
829
827 def _get_tab_width(self):
830 def _get_tab_width(self):
828 """ The width (in terms of space characters) for tab characters.
831 """ The width (in terms of space characters) for tab characters.
829 """
832 """
830 return self._tab_width
833 return self._tab_width
831
834
832 def _set_tab_width(self, tab_width):
835 def _set_tab_width(self, tab_width):
833 """ Sets the width (in terms of space characters) for tab characters.
836 """ Sets the width (in terms of space characters) for tab characters.
834 """
837 """
835 font_metrics = QtGui.QFontMetrics(self.font)
838 font_metrics = QtGui.QFontMetrics(self.font)
836 self._control.setTabStopWidth(tab_width * font_metrics.width(' '))
839 self._control.setTabStopWidth(tab_width * font_metrics.width(' '))
837
840
838 self._tab_width = tab_width
841 self._tab_width = tab_width
839
842
840 tab_width = property(_get_tab_width, _set_tab_width)
843 tab_width = property(_get_tab_width, _set_tab_width)
841
844
842 def undo(self):
845 def undo(self):
843 """ Undo the last operation. If there is no operation to undo, nothing
846 """ Undo the last operation. If there is no operation to undo, nothing
844 happens.
847 happens.
845 """
848 """
846 self._control.undo()
849 self._control.undo()
847
850
848 #---------------------------------------------------------------------------
851 #---------------------------------------------------------------------------
849 # 'ConsoleWidget' abstract interface
852 # 'ConsoleWidget' abstract interface
850 #---------------------------------------------------------------------------
853 #---------------------------------------------------------------------------
851
854
852 def _is_complete(self, source, interactive):
855 def _is_complete(self, source, interactive):
853 """ Returns whether 'source' can be executed. When triggered by an
856 """ Returns whether 'source' can be executed. When triggered by an
854 Enter/Return key press, 'interactive' is True; otherwise, it is
857 Enter/Return key press, 'interactive' is True; otherwise, it is
855 False.
858 False.
856 """
859 """
857 raise NotImplementedError
860 raise NotImplementedError
858
861
859 def _execute(self, source, hidden):
862 def _execute(self, source, hidden):
860 """ Execute 'source'. If 'hidden', do not show any output.
863 """ Execute 'source'. If 'hidden', do not show any output.
861 """
864 """
862 raise NotImplementedError
865 raise NotImplementedError
863
866
864 def _prompt_started_hook(self):
867 def _prompt_started_hook(self):
865 """ Called immediately after a new prompt is displayed.
868 """ Called immediately after a new prompt is displayed.
866 """
869 """
867 pass
870 pass
868
871
869 def _prompt_finished_hook(self):
872 def _prompt_finished_hook(self):
870 """ Called immediately after a prompt is finished, i.e. when some input
873 """ Called immediately after a prompt is finished, i.e. when some input
871 will be processed and a new prompt displayed.
874 will be processed and a new prompt displayed.
872 """
875 """
873 pass
876 pass
874
877
875 def _up_pressed(self, shift_modifier):
878 def _up_pressed(self, shift_modifier):
876 """ Called when the up key is pressed. Returns whether to continue
879 """ Called when the up key is pressed. Returns whether to continue
877 processing the event.
880 processing the event.
878 """
881 """
879 return True
882 return True
880
883
881 def _down_pressed(self, shift_modifier):
884 def _down_pressed(self, shift_modifier):
882 """ Called when the down key is pressed. Returns whether to continue
885 """ Called when the down key is pressed. Returns whether to continue
883 processing the event.
886 processing the event.
884 """
887 """
885 return True
888 return True
886
889
887 def _tab_pressed(self):
890 def _tab_pressed(self):
888 """ Called when the tab key is pressed. Returns whether to continue
891 """ Called when the tab key is pressed. Returns whether to continue
889 processing the event.
892 processing the event.
890 """
893 """
891 return False
894 return False
892
895
893 #--------------------------------------------------------------------------
896 #--------------------------------------------------------------------------
894 # 'ConsoleWidget' protected interface
897 # 'ConsoleWidget' protected interface
895 #--------------------------------------------------------------------------
898 #--------------------------------------------------------------------------
896
899
897 def _append_custom(self, insert, input, before_prompt=False, *args, **kwargs):
900 def _append_custom(self, insert, input, before_prompt=False, *args, **kwargs):
898 """ A low-level method for appending content to the end of the buffer.
901 """ A low-level method for appending content to the end of the buffer.
899
902
900 If 'before_prompt' is enabled, the content will be inserted before the
903 If 'before_prompt' is enabled, the content will be inserted before the
901 current prompt, if there is one.
904 current prompt, if there is one.
902 """
905 """
903 # Determine where to insert the content.
906 # Determine where to insert the content.
904 cursor = self._control.textCursor()
907 cursor = self._control.textCursor()
905 if before_prompt and (self._reading or not self._executing):
908 if before_prompt and (self._reading or not self._executing):
906 self._flush_pending_stream()
909 self._flush_pending_stream()
907 cursor.setPosition(self._append_before_prompt_pos)
910 cursor.setPosition(self._append_before_prompt_pos)
908 else:
911 else:
909 if insert != self._insert_plain_text:
912 if insert != self._insert_plain_text:
910 self._flush_pending_stream()
913 self._flush_pending_stream()
911 cursor.movePosition(QtGui.QTextCursor.End)
914 cursor.movePosition(QtGui.QTextCursor.End)
912 start_pos = cursor.position()
915 start_pos = cursor.position()
913
916
914 # Perform the insertion.
917 # Perform the insertion.
915 result = insert(cursor, input, *args, **kwargs)
918 result = insert(cursor, input, *args, **kwargs)
916
919
917 # Adjust the prompt position if we have inserted before it. This is safe
920 # Adjust the prompt position if we have inserted before it. This is safe
918 # because buffer truncation is disabled when not executing.
921 # because buffer truncation is disabled when not executing.
919 if before_prompt and not self._executing:
922 if before_prompt and not self._executing:
920 diff = cursor.position() - start_pos
923 diff = cursor.position() - start_pos
921 self._append_before_prompt_pos += diff
924 self._append_before_prompt_pos += diff
922 self._prompt_pos += diff
925 self._prompt_pos += diff
923
926
924 return result
927 return result
925
928
926 def _append_block(self, block_format=None, before_prompt=False):
929 def _append_block(self, block_format=None, before_prompt=False):
927 """ Appends an new QTextBlock to the end of the console buffer.
930 """ Appends an new QTextBlock to the end of the console buffer.
928 """
931 """
929 self._append_custom(self._insert_block, block_format, before_prompt)
932 self._append_custom(self._insert_block, block_format, before_prompt)
930
933
931 def _append_html(self, html, before_prompt=False):
934 def _append_html(self, html, before_prompt=False):
932 """ Appends HTML at the end of the console buffer.
935 """ Appends HTML at the end of the console buffer.
933 """
936 """
934 self._append_custom(self._insert_html, html, before_prompt)
937 self._append_custom(self._insert_html, html, before_prompt)
935
938
936 def _append_html_fetching_plain_text(self, html, before_prompt=False):
939 def _append_html_fetching_plain_text(self, html, before_prompt=False):
937 """ Appends HTML, then returns the plain text version of it.
940 """ Appends HTML, then returns the plain text version of it.
938 """
941 """
939 return self._append_custom(self._insert_html_fetching_plain_text,
942 return self._append_custom(self._insert_html_fetching_plain_text,
940 html, before_prompt)
943 html, before_prompt)
941
944
942 def _append_plain_text(self, text, before_prompt=False):
945 def _append_plain_text(self, text, before_prompt=False):
943 """ Appends plain text, processing ANSI codes if enabled.
946 """ Appends plain text, processing ANSI codes if enabled.
944 """
947 """
945 self._append_custom(self._insert_plain_text, text, before_prompt)
948 self._append_custom(self._insert_plain_text, text, before_prompt)
946
949
947 def _cancel_completion(self):
950 def _cancel_completion(self):
948 """ If text completion is progress, cancel it.
951 """ If text completion is progress, cancel it.
949 """
952 """
950 self._completion_widget.cancel_completion()
953 self._completion_widget.cancel_completion()
951
954
952 def _clear_temporary_buffer(self):
955 def _clear_temporary_buffer(self):
953 """ Clears the "temporary text" buffer, i.e. all the text following
956 """ Clears the "temporary text" buffer, i.e. all the text following
954 the prompt region.
957 the prompt region.
955 """
958 """
956 # Select and remove all text below the input buffer.
959 # Select and remove all text below the input buffer.
957 cursor = self._get_prompt_cursor()
960 cursor = self._get_prompt_cursor()
958 prompt = self._continuation_prompt.lstrip()
961 prompt = self._continuation_prompt.lstrip()
959 if(self._temp_buffer_filled):
962 if(self._temp_buffer_filled):
960 self._temp_buffer_filled = False
963 self._temp_buffer_filled = False
961 while cursor.movePosition(QtGui.QTextCursor.NextBlock):
964 while cursor.movePosition(QtGui.QTextCursor.NextBlock):
962 temp_cursor = QtGui.QTextCursor(cursor)
965 temp_cursor = QtGui.QTextCursor(cursor)
963 temp_cursor.select(QtGui.QTextCursor.BlockUnderCursor)
966 temp_cursor.select(QtGui.QTextCursor.BlockUnderCursor)
964 text = temp_cursor.selection().toPlainText().lstrip()
967 text = temp_cursor.selection().toPlainText().lstrip()
965 if not text.startswith(prompt):
968 if not text.startswith(prompt):
966 break
969 break
967 else:
970 else:
968 # We've reached the end of the input buffer and no text follows.
971 # We've reached the end of the input buffer and no text follows.
969 return
972 return
970 cursor.movePosition(QtGui.QTextCursor.Left) # Grab the newline.
973 cursor.movePosition(QtGui.QTextCursor.Left) # Grab the newline.
971 cursor.movePosition(QtGui.QTextCursor.End,
974 cursor.movePosition(QtGui.QTextCursor.End,
972 QtGui.QTextCursor.KeepAnchor)
975 QtGui.QTextCursor.KeepAnchor)
973 cursor.removeSelectedText()
976 cursor.removeSelectedText()
974
977
975 # After doing this, we have no choice but to clear the undo/redo
978 # After doing this, we have no choice but to clear the undo/redo
976 # history. Otherwise, the text is not "temporary" at all, because it
979 # history. Otherwise, the text is not "temporary" at all, because it
977 # can be recalled with undo/redo. Unfortunately, Qt does not expose
980 # can be recalled with undo/redo. Unfortunately, Qt does not expose
978 # fine-grained control to the undo/redo system.
981 # fine-grained control to the undo/redo system.
979 if self._control.isUndoRedoEnabled():
982 if self._control.isUndoRedoEnabled():
980 self._control.setUndoRedoEnabled(False)
983 self._control.setUndoRedoEnabled(False)
981 self._control.setUndoRedoEnabled(True)
984 self._control.setUndoRedoEnabled(True)
982
985
983 def _complete_with_items(self, cursor, items):
986 def _complete_with_items(self, cursor, items):
984 """ Performs completion with 'items' at the specified cursor location.
987 """ Performs completion with 'items' at the specified cursor location.
985 """
988 """
986 self._cancel_completion()
989 self._cancel_completion()
987
990
988 if len(items) == 1:
991 if len(items) == 1:
989 cursor.setPosition(self._control.textCursor().position(),
992 cursor.setPosition(self._control.textCursor().position(),
990 QtGui.QTextCursor.KeepAnchor)
993 QtGui.QTextCursor.KeepAnchor)
991 cursor.insertText(items[0])
994 cursor.insertText(items[0])
992
995
993 elif len(items) > 1:
996 elif len(items) > 1:
994 current_pos = self._control.textCursor().position()
997 current_pos = self._control.textCursor().position()
995 prefix = commonprefix(items)
998 prefix = commonprefix(items)
996 if prefix:
999 if prefix:
997 cursor.setPosition(current_pos, QtGui.QTextCursor.KeepAnchor)
1000 cursor.setPosition(current_pos, QtGui.QTextCursor.KeepAnchor)
998 cursor.insertText(prefix)
1001 cursor.insertText(prefix)
999 current_pos = cursor.position()
1002 current_pos = cursor.position()
1000
1003
1001 cursor.movePosition(QtGui.QTextCursor.Left, n=len(prefix))
1004 cursor.movePosition(QtGui.QTextCursor.Left, n=len(prefix))
1002 self._completion_widget.show_items(cursor, items)
1005 self._completion_widget.show_items(cursor, items)
1003
1006
1004
1007
1005 def _fill_temporary_buffer(self, cursor, text, html=False):
1008 def _fill_temporary_buffer(self, cursor, text, html=False):
1006 """fill the area below the active editting zone with text"""
1009 """fill the area below the active editting zone with text"""
1007
1010
1008 current_pos = self._control.textCursor().position()
1011 current_pos = self._control.textCursor().position()
1009
1012
1010 cursor.beginEditBlock()
1013 cursor.beginEditBlock()
1011 self._append_plain_text('\n')
1014 self._append_plain_text('\n')
1012 self._page(text, html=html)
1015 self._page(text, html=html)
1013 cursor.endEditBlock()
1016 cursor.endEditBlock()
1014
1017
1015 cursor.setPosition(current_pos)
1018 cursor.setPosition(current_pos)
1016 self._control.moveCursor(QtGui.QTextCursor.End)
1019 self._control.moveCursor(QtGui.QTextCursor.End)
1017 self._control.setTextCursor(cursor)
1020 self._control.setTextCursor(cursor)
1018
1021
1019 self._temp_buffer_filled = True
1022 self._temp_buffer_filled = True
1020
1023
1021
1024
1022 def _context_menu_make(self, pos):
1025 def _context_menu_make(self, pos):
1023 """ Creates a context menu for the given QPoint (in widget coordinates).
1026 """ Creates a context menu for the given QPoint (in widget coordinates).
1024 """
1027 """
1025 menu = QtGui.QMenu(self)
1028 menu = QtGui.QMenu(self)
1026
1029
1027 self.cut_action = menu.addAction('Cut', self.cut)
1030 self.cut_action = menu.addAction('Cut', self.cut)
1028 self.cut_action.setEnabled(self.can_cut())
1031 self.cut_action.setEnabled(self.can_cut())
1029 self.cut_action.setShortcut(QtGui.QKeySequence.Cut)
1032 self.cut_action.setShortcut(QtGui.QKeySequence.Cut)
1030
1033
1031 self.copy_action = menu.addAction('Copy', self.copy)
1034 self.copy_action = menu.addAction('Copy', self.copy)
1032 self.copy_action.setEnabled(self.can_copy())
1035 self.copy_action.setEnabled(self.can_copy())
1033 self.copy_action.setShortcut(QtGui.QKeySequence.Copy)
1036 self.copy_action.setShortcut(QtGui.QKeySequence.Copy)
1034
1037
1035 self.paste_action = menu.addAction('Paste', self.paste)
1038 self.paste_action = menu.addAction('Paste', self.paste)
1036 self.paste_action.setEnabled(self.can_paste())
1039 self.paste_action.setEnabled(self.can_paste())
1037 self.paste_action.setShortcut(QtGui.QKeySequence.Paste)
1040 self.paste_action.setShortcut(QtGui.QKeySequence.Paste)
1038
1041
1039 anchor = self._control.anchorAt(pos)
1042 anchor = self._control.anchorAt(pos)
1040 if anchor:
1043 if anchor:
1041 menu.addSeparator()
1044 menu.addSeparator()
1042 self.copy_link_action = menu.addAction(
1045 self.copy_link_action = menu.addAction(
1043 'Copy Link Address', lambda: self.copy_anchor(anchor=anchor))
1046 'Copy Link Address', lambda: self.copy_anchor(anchor=anchor))
1044 self.open_link_action = menu.addAction(
1047 self.open_link_action = menu.addAction(
1045 'Open Link', lambda: self.open_anchor(anchor=anchor))
1048 'Open Link', lambda: self.open_anchor(anchor=anchor))
1046
1049
1047 menu.addSeparator()
1050 menu.addSeparator()
1048 menu.addAction(self.select_all_action)
1051 menu.addAction(self.select_all_action)
1049
1052
1050 menu.addSeparator()
1053 menu.addSeparator()
1051 menu.addAction(self.export_action)
1054 menu.addAction(self.export_action)
1052 menu.addAction(self.print_action)
1055 menu.addAction(self.print_action)
1053
1056
1054 return menu
1057 return menu
1055
1058
1056 def _control_key_down(self, modifiers, include_command=False):
1059 def _control_key_down(self, modifiers, include_command=False):
1057 """ Given a KeyboardModifiers flags object, return whether the Control
1060 """ Given a KeyboardModifiers flags object, return whether the Control
1058 key is down.
1061 key is down.
1059
1062
1060 Parameters:
1063 Parameters:
1061 -----------
1064 -----------
1062 include_command : bool, optional (default True)
1065 include_command : bool, optional (default True)
1063 Whether to treat the Command key as a (mutually exclusive) synonym
1066 Whether to treat the Command key as a (mutually exclusive) synonym
1064 for Control when in Mac OS.
1067 for Control when in Mac OS.
1065 """
1068 """
1066 # Note that on Mac OS, ControlModifier corresponds to the Command key
1069 # Note that on Mac OS, ControlModifier corresponds to the Command key
1067 # while MetaModifier corresponds to the Control key.
1070 # while MetaModifier corresponds to the Control key.
1068 if sys.platform == 'darwin':
1071 if sys.platform == 'darwin':
1069 down = include_command and (modifiers & QtCore.Qt.ControlModifier)
1072 down = include_command and (modifiers & QtCore.Qt.ControlModifier)
1070 return bool(down) ^ bool(modifiers & QtCore.Qt.MetaModifier)
1073 return bool(down) ^ bool(modifiers & QtCore.Qt.MetaModifier)
1071 else:
1074 else:
1072 return bool(modifiers & QtCore.Qt.ControlModifier)
1075 return bool(modifiers & QtCore.Qt.ControlModifier)
1073
1076
1074 def _create_control(self):
1077 def _create_control(self):
1075 """ Creates and connects the underlying text widget.
1078 """ Creates and connects the underlying text widget.
1076 """
1079 """
1077 # Create the underlying control.
1080 # Create the underlying control.
1078 if self.custom_control:
1081 if self.custom_control:
1079 control = self.custom_control()
1082 control = self.custom_control()
1080 elif self.kind == 'plain':
1083 elif self.kind == 'plain':
1081 control = QtGui.QPlainTextEdit()
1084 control = QtGui.QPlainTextEdit()
1082 elif self.kind == 'rich':
1085 elif self.kind == 'rich':
1083 control = QtGui.QTextEdit()
1086 control = QtGui.QTextEdit()
1084 control.setAcceptRichText(False)
1087 control.setAcceptRichText(False)
1085 control.setMouseTracking(True)
1088 control.setMouseTracking(True)
1086
1089
1087 # Prevent the widget from handling drops, as we already provide
1090 # Prevent the widget from handling drops, as we already provide
1088 # the logic in this class.
1091 # the logic in this class.
1089 control.setAcceptDrops(False)
1092 control.setAcceptDrops(False)
1090
1093
1091 # Install event filters. The filter on the viewport is needed for
1094 # Install event filters. The filter on the viewport is needed for
1092 # mouse events.
1095 # mouse events.
1093 control.installEventFilter(self)
1096 control.installEventFilter(self)
1094 control.viewport().installEventFilter(self)
1097 control.viewport().installEventFilter(self)
1095
1098
1096 # Connect signals.
1099 # Connect signals.
1097 control.customContextMenuRequested.connect(
1100 control.customContextMenuRequested.connect(
1098 self._custom_context_menu_requested)
1101 self._custom_context_menu_requested)
1099 control.copyAvailable.connect(self.copy_available)
1102 control.copyAvailable.connect(self.copy_available)
1100 control.redoAvailable.connect(self.redo_available)
1103 control.redoAvailable.connect(self.redo_available)
1101 control.undoAvailable.connect(self.undo_available)
1104 control.undoAvailable.connect(self.undo_available)
1102
1105
1103 # Hijack the document size change signal to prevent Qt from adjusting
1106 # Hijack the document size change signal to prevent Qt from adjusting
1104 # the viewport's scrollbar. We are relying on an implementation detail
1107 # the viewport's scrollbar. We are relying on an implementation detail
1105 # of Q(Plain)TextEdit here, which is potentially dangerous, but without
1108 # of Q(Plain)TextEdit here, which is potentially dangerous, but without
1106 # this functionality we cannot create a nice terminal interface.
1109 # this functionality we cannot create a nice terminal interface.
1107 layout = control.document().documentLayout()
1110 layout = control.document().documentLayout()
1108 layout.documentSizeChanged.disconnect()
1111 layout.documentSizeChanged.disconnect()
1109 layout.documentSizeChanged.connect(self._adjust_scrollbars)
1112 layout.documentSizeChanged.connect(self._adjust_scrollbars)
1110
1113
1111 # Configure the control.
1114 # Configure the control.
1112 control.setAttribute(QtCore.Qt.WA_InputMethodEnabled, True)
1115 control.setAttribute(QtCore.Qt.WA_InputMethodEnabled, True)
1113 control.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
1116 control.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
1114 control.setReadOnly(True)
1117 control.setReadOnly(True)
1115 control.setUndoRedoEnabled(False)
1118 control.setUndoRedoEnabled(False)
1116 control.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
1119 control.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
1117 return control
1120 return control
1118
1121
1119 def _create_page_control(self):
1122 def _create_page_control(self):
1120 """ Creates and connects the underlying paging widget.
1123 """ Creates and connects the underlying paging widget.
1121 """
1124 """
1122 if self.custom_page_control:
1125 if self.custom_page_control:
1123 control = self.custom_page_control()
1126 control = self.custom_page_control()
1124 elif self.kind == 'plain':
1127 elif self.kind == 'plain':
1125 control = QtGui.QPlainTextEdit()
1128 control = QtGui.QPlainTextEdit()
1126 elif self.kind == 'rich':
1129 elif self.kind == 'rich':
1127 control = QtGui.QTextEdit()
1130 control = QtGui.QTextEdit()
1128 control.installEventFilter(self)
1131 control.installEventFilter(self)
1129 viewport = control.viewport()
1132 viewport = control.viewport()
1130 viewport.installEventFilter(self)
1133 viewport.installEventFilter(self)
1131 control.setReadOnly(True)
1134 control.setReadOnly(True)
1132 control.setUndoRedoEnabled(False)
1135 control.setUndoRedoEnabled(False)
1133 control.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
1136 control.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
1134 return control
1137 return control
1135
1138
1136 def _event_filter_console_keypress(self, event):
1139 def _event_filter_console_keypress(self, event):
1137 """ Filter key events for the underlying text widget to create a
1140 """ Filter key events for the underlying text widget to create a
1138 console-like interface.
1141 console-like interface.
1139 """
1142 """
1140 intercepted = False
1143 intercepted = False
1141 cursor = self._control.textCursor()
1144 cursor = self._control.textCursor()
1142 position = cursor.position()
1145 position = cursor.position()
1143 key = event.key()
1146 key = event.key()
1144 ctrl_down = self._control_key_down(event.modifiers())
1147 ctrl_down = self._control_key_down(event.modifiers())
1145 alt_down = event.modifiers() & QtCore.Qt.AltModifier
1148 alt_down = event.modifiers() & QtCore.Qt.AltModifier
1146 shift_down = event.modifiers() & QtCore.Qt.ShiftModifier
1149 shift_down = event.modifiers() & QtCore.Qt.ShiftModifier
1147
1150
1148 #------ Special sequences ----------------------------------------------
1151 #------ Special sequences ----------------------------------------------
1149
1152
1150 if event.matches(QtGui.QKeySequence.Copy):
1153 if event.matches(QtGui.QKeySequence.Copy):
1151 self.copy()
1154 self.copy()
1152 intercepted = True
1155 intercepted = True
1153
1156
1154 elif event.matches(QtGui.QKeySequence.Cut):
1157 elif event.matches(QtGui.QKeySequence.Cut):
1155 self.cut()
1158 self.cut()
1156 intercepted = True
1159 intercepted = True
1157
1160
1158 elif event.matches(QtGui.QKeySequence.Paste):
1161 elif event.matches(QtGui.QKeySequence.Paste):
1159 self.paste()
1162 self.paste()
1160 intercepted = True
1163 intercepted = True
1161
1164
1162 #------ Special modifier logic -----------------------------------------
1165 #------ Special modifier logic -----------------------------------------
1163
1166
1164 elif key in (QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter):
1167 elif key in (QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter):
1165 intercepted = True
1168 intercepted = True
1166
1169
1167 # Special handling when tab completing in text mode.
1170 # Special handling when tab completing in text mode.
1168 self._cancel_completion()
1171 self._cancel_completion()
1169
1172
1170 if self._in_buffer(position):
1173 if self._in_buffer(position):
1171 # Special handling when a reading a line of raw input.
1174 # Special handling when a reading a line of raw input.
1172 if self._reading:
1175 if self._reading:
1173 self._append_plain_text('\n')
1176 self._append_plain_text('\n')
1174 self._reading = False
1177 self._reading = False
1175 if self._reading_callback:
1178 if self._reading_callback:
1176 self._reading_callback()
1179 self._reading_callback()
1177
1180
1178 # If the input buffer is a single line or there is only
1181 # If the input buffer is a single line or there is only
1179 # whitespace after the cursor, execute. Otherwise, split the
1182 # whitespace after the cursor, execute. Otherwise, split the
1180 # line with a continuation prompt.
1183 # line with a continuation prompt.
1181 elif not self._executing:
1184 elif not self._executing:
1182 cursor.movePosition(QtGui.QTextCursor.End,
1185 cursor.movePosition(QtGui.QTextCursor.End,
1183 QtGui.QTextCursor.KeepAnchor)
1186 QtGui.QTextCursor.KeepAnchor)
1184 at_end = len(cursor.selectedText().strip()) == 0
1187 at_end = len(cursor.selectedText().strip()) == 0
1185 single_line = (self._get_end_cursor().blockNumber() ==
1188 single_line = (self._get_end_cursor().blockNumber() ==
1186 self._get_prompt_cursor().blockNumber())
1189 self._get_prompt_cursor().blockNumber())
1187 if (at_end or shift_down or single_line) and not ctrl_down:
1190 if (at_end or shift_down or single_line) and not ctrl_down:
1188 self.execute(interactive = not shift_down)
1191 self.execute(interactive = not shift_down)
1189 else:
1192 else:
1190 # Do this inside an edit block for clean undo/redo.
1193 # Do this inside an edit block for clean undo/redo.
1191 cursor.beginEditBlock()
1194 cursor.beginEditBlock()
1192 cursor.setPosition(position)
1195 cursor.setPosition(position)
1193 cursor.insertText('\n')
1196 cursor.insertText('\n')
1194 self._insert_continuation_prompt(cursor)
1197 self._insert_continuation_prompt(cursor)
1195 cursor.endEditBlock()
1198 cursor.endEditBlock()
1196
1199
1197 # Ensure that the whole input buffer is visible.
1200 # Ensure that the whole input buffer is visible.
1198 # FIXME: This will not be usable if the input buffer is
1201 # FIXME: This will not be usable if the input buffer is
1199 # taller than the console widget.
1202 # taller than the console widget.
1200 self._control.moveCursor(QtGui.QTextCursor.End)
1203 self._control.moveCursor(QtGui.QTextCursor.End)
1201 self._control.setTextCursor(cursor)
1204 self._control.setTextCursor(cursor)
1202
1205
1203 #------ Control/Cmd modifier -------------------------------------------
1206 #------ Control/Cmd modifier -------------------------------------------
1204
1207
1205 elif ctrl_down:
1208 elif ctrl_down:
1206 if key == QtCore.Qt.Key_G:
1209 if key == QtCore.Qt.Key_G:
1207 self._keyboard_quit()
1210 self._keyboard_quit()
1208 intercepted = True
1211 intercepted = True
1209
1212
1210 elif key == QtCore.Qt.Key_K:
1213 elif key == QtCore.Qt.Key_K:
1211 if self._in_buffer(position):
1214 if self._in_buffer(position):
1212 cursor.clearSelection()
1215 cursor.clearSelection()
1213 cursor.movePosition(QtGui.QTextCursor.EndOfLine,
1216 cursor.movePosition(QtGui.QTextCursor.EndOfLine,
1214 QtGui.QTextCursor.KeepAnchor)
1217 QtGui.QTextCursor.KeepAnchor)
1215 if not cursor.hasSelection():
1218 if not cursor.hasSelection():
1216 # Line deletion (remove continuation prompt)
1219 # Line deletion (remove continuation prompt)
1217 cursor.movePosition(QtGui.QTextCursor.NextBlock,
1220 cursor.movePosition(QtGui.QTextCursor.NextBlock,
1218 QtGui.QTextCursor.KeepAnchor)
1221 QtGui.QTextCursor.KeepAnchor)
1219 cursor.movePosition(QtGui.QTextCursor.Right,
1222 cursor.movePosition(QtGui.QTextCursor.Right,
1220 QtGui.QTextCursor.KeepAnchor,
1223 QtGui.QTextCursor.KeepAnchor,
1221 len(self._continuation_prompt))
1224 len(self._continuation_prompt))
1222 self._kill_ring.kill_cursor(cursor)
1225 self._kill_ring.kill_cursor(cursor)
1223 self._set_cursor(cursor)
1226 self._set_cursor(cursor)
1224 intercepted = True
1227 intercepted = True
1225
1228
1226 elif key == QtCore.Qt.Key_L:
1229 elif key == QtCore.Qt.Key_L:
1227 self.prompt_to_top()
1230 self.prompt_to_top()
1228 intercepted = True
1231 intercepted = True
1229
1232
1230 elif key == QtCore.Qt.Key_O:
1233 elif key == QtCore.Qt.Key_O:
1231 if self._page_control and self._page_control.isVisible():
1234 if self._page_control and self._page_control.isVisible():
1232 self._page_control.setFocus()
1235 self._page_control.setFocus()
1233 intercepted = True
1236 intercepted = True
1234
1237
1235 elif key == QtCore.Qt.Key_U:
1238 elif key == QtCore.Qt.Key_U:
1236 if self._in_buffer(position):
1239 if self._in_buffer(position):
1237 cursor.clearSelection()
1240 cursor.clearSelection()
1238 start_line = cursor.blockNumber()
1241 start_line = cursor.blockNumber()
1239 if start_line == self._get_prompt_cursor().blockNumber():
1242 if start_line == self._get_prompt_cursor().blockNumber():
1240 offset = len(self._prompt)
1243 offset = len(self._prompt)
1241 else:
1244 else:
1242 offset = len(self._continuation_prompt)
1245 offset = len(self._continuation_prompt)
1243 cursor.movePosition(QtGui.QTextCursor.StartOfBlock,
1246 cursor.movePosition(QtGui.QTextCursor.StartOfBlock,
1244 QtGui.QTextCursor.KeepAnchor)
1247 QtGui.QTextCursor.KeepAnchor)
1245 cursor.movePosition(QtGui.QTextCursor.Right,
1248 cursor.movePosition(QtGui.QTextCursor.Right,
1246 QtGui.QTextCursor.KeepAnchor, offset)
1249 QtGui.QTextCursor.KeepAnchor, offset)
1247 self._kill_ring.kill_cursor(cursor)
1250 self._kill_ring.kill_cursor(cursor)
1248 self._set_cursor(cursor)
1251 self._set_cursor(cursor)
1249 intercepted = True
1252 intercepted = True
1250
1253
1251 elif key == QtCore.Qt.Key_Y:
1254 elif key == QtCore.Qt.Key_Y:
1252 self._keep_cursor_in_buffer()
1255 self._keep_cursor_in_buffer()
1253 self._kill_ring.yank()
1256 self._kill_ring.yank()
1254 intercepted = True
1257 intercepted = True
1255
1258
1256 elif key in (QtCore.Qt.Key_Backspace, QtCore.Qt.Key_Delete):
1259 elif key in (QtCore.Qt.Key_Backspace, QtCore.Qt.Key_Delete):
1257 if key == QtCore.Qt.Key_Backspace:
1260 if key == QtCore.Qt.Key_Backspace:
1258 cursor = self._get_word_start_cursor(position)
1261 cursor = self._get_word_start_cursor(position)
1259 else: # key == QtCore.Qt.Key_Delete
1262 else: # key == QtCore.Qt.Key_Delete
1260 cursor = self._get_word_end_cursor(position)
1263 cursor = self._get_word_end_cursor(position)
1261 cursor.setPosition(position, QtGui.QTextCursor.KeepAnchor)
1264 cursor.setPosition(position, QtGui.QTextCursor.KeepAnchor)
1262 self._kill_ring.kill_cursor(cursor)
1265 self._kill_ring.kill_cursor(cursor)
1263 intercepted = True
1266 intercepted = True
1264
1267
1265 elif key == QtCore.Qt.Key_D:
1268 elif key == QtCore.Qt.Key_D:
1266 if len(self.input_buffer) == 0:
1269 if len(self.input_buffer) == 0:
1267 self.exit_requested.emit(self)
1270 self.exit_requested.emit(self)
1268 else:
1271 else:
1269 new_event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress,
1272 new_event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress,
1270 QtCore.Qt.Key_Delete,
1273 QtCore.Qt.Key_Delete,
1271 QtCore.Qt.NoModifier)
1274 QtCore.Qt.NoModifier)
1272 QtGui.qApp.sendEvent(self._control, new_event)
1275 QtGui.qApp.sendEvent(self._control, new_event)
1273 intercepted = True
1276 intercepted = True
1274
1277
1275 #------ Alt modifier ---------------------------------------------------
1278 #------ Alt modifier ---------------------------------------------------
1276
1279
1277 elif alt_down:
1280 elif alt_down:
1278 if key == QtCore.Qt.Key_B:
1281 if key == QtCore.Qt.Key_B:
1279 self._set_cursor(self._get_word_start_cursor(position))
1282 self._set_cursor(self._get_word_start_cursor(position))
1280 intercepted = True
1283 intercepted = True
1281
1284
1282 elif key == QtCore.Qt.Key_F:
1285 elif key == QtCore.Qt.Key_F:
1283 self._set_cursor(self._get_word_end_cursor(position))
1286 self._set_cursor(self._get_word_end_cursor(position))
1284 intercepted = True
1287 intercepted = True
1285
1288
1286 elif key == QtCore.Qt.Key_Y:
1289 elif key == QtCore.Qt.Key_Y:
1287 self._kill_ring.rotate()
1290 self._kill_ring.rotate()
1288 intercepted = True
1291 intercepted = True
1289
1292
1290 elif key == QtCore.Qt.Key_Backspace:
1293 elif key == QtCore.Qt.Key_Backspace:
1291 cursor = self._get_word_start_cursor(position)
1294 cursor = self._get_word_start_cursor(position)
1292 cursor.setPosition(position, QtGui.QTextCursor.KeepAnchor)
1295 cursor.setPosition(position, QtGui.QTextCursor.KeepAnchor)
1293 self._kill_ring.kill_cursor(cursor)
1296 self._kill_ring.kill_cursor(cursor)
1294 intercepted = True
1297 intercepted = True
1295
1298
1296 elif key == QtCore.Qt.Key_D:
1299 elif key == QtCore.Qt.Key_D:
1297 cursor = self._get_word_end_cursor(position)
1300 cursor = self._get_word_end_cursor(position)
1298 cursor.setPosition(position, QtGui.QTextCursor.KeepAnchor)
1301 cursor.setPosition(position, QtGui.QTextCursor.KeepAnchor)
1299 self._kill_ring.kill_cursor(cursor)
1302 self._kill_ring.kill_cursor(cursor)
1300 intercepted = True
1303 intercepted = True
1301
1304
1302 elif key == QtCore.Qt.Key_Delete:
1305 elif key == QtCore.Qt.Key_Delete:
1303 intercepted = True
1306 intercepted = True
1304
1307
1305 elif key == QtCore.Qt.Key_Greater:
1308 elif key == QtCore.Qt.Key_Greater:
1306 self._control.moveCursor(QtGui.QTextCursor.End)
1309 self._control.moveCursor(QtGui.QTextCursor.End)
1307 intercepted = True
1310 intercepted = True
1308
1311
1309 elif key == QtCore.Qt.Key_Less:
1312 elif key == QtCore.Qt.Key_Less:
1310 self._control.setTextCursor(self._get_prompt_cursor())
1313 self._control.setTextCursor(self._get_prompt_cursor())
1311 intercepted = True
1314 intercepted = True
1312
1315
1313 #------ No modifiers ---------------------------------------------------
1316 #------ No modifiers ---------------------------------------------------
1314
1317
1315 else:
1318 else:
1316 if shift_down:
1319 if shift_down:
1317 anchormode = QtGui.QTextCursor.KeepAnchor
1320 anchormode = QtGui.QTextCursor.KeepAnchor
1318 else:
1321 else:
1319 anchormode = QtGui.QTextCursor.MoveAnchor
1322 anchormode = QtGui.QTextCursor.MoveAnchor
1320
1323
1321 if key == QtCore.Qt.Key_Escape:
1324 if key == QtCore.Qt.Key_Escape:
1322 self._keyboard_quit()
1325 self._keyboard_quit()
1323 intercepted = True
1326 intercepted = True
1324
1327
1325 elif key == QtCore.Qt.Key_Up:
1328 elif key == QtCore.Qt.Key_Up:
1326 if self._reading or not self._up_pressed(shift_down):
1329 if self._reading or not self._up_pressed(shift_down):
1327 intercepted = True
1330 intercepted = True
1328 else:
1331 else:
1329 prompt_line = self._get_prompt_cursor().blockNumber()
1332 prompt_line = self._get_prompt_cursor().blockNumber()
1330 intercepted = cursor.blockNumber() <= prompt_line
1333 intercepted = cursor.blockNumber() <= prompt_line
1331
1334
1332 elif key == QtCore.Qt.Key_Down:
1335 elif key == QtCore.Qt.Key_Down:
1333 if self._reading or not self._down_pressed(shift_down):
1336 if self._reading or not self._down_pressed(shift_down):
1334 intercepted = True
1337 intercepted = True
1335 else:
1338 else:
1336 end_line = self._get_end_cursor().blockNumber()
1339 end_line = self._get_end_cursor().blockNumber()
1337 intercepted = cursor.blockNumber() == end_line
1340 intercepted = cursor.blockNumber() == end_line
1338
1341
1339 elif key == QtCore.Qt.Key_Tab:
1342 elif key == QtCore.Qt.Key_Tab:
1340 if not self._reading:
1343 if not self._reading:
1341 if self._tab_pressed():
1344 if self._tab_pressed():
1342 # real tab-key, insert four spaces
1345 # real tab-key, insert four spaces
1343 cursor.insertText(' '*4)
1346 cursor.insertText(' '*4)
1344 intercepted = True
1347 intercepted = True
1345
1348
1346 elif key == QtCore.Qt.Key_Left:
1349 elif key == QtCore.Qt.Key_Left:
1347
1350
1348 # Move to the previous line
1351 # Move to the previous line
1349 line, col = cursor.blockNumber(), cursor.columnNumber()
1352 line, col = cursor.blockNumber(), cursor.columnNumber()
1350 if line > self._get_prompt_cursor().blockNumber() and \
1353 if line > self._get_prompt_cursor().blockNumber() and \
1351 col == len(self._continuation_prompt):
1354 col == len(self._continuation_prompt):
1352 self._control.moveCursor(QtGui.QTextCursor.PreviousBlock,
1355 self._control.moveCursor(QtGui.QTextCursor.PreviousBlock,
1353 mode=anchormode)
1356 mode=anchormode)
1354 self._control.moveCursor(QtGui.QTextCursor.EndOfBlock,
1357 self._control.moveCursor(QtGui.QTextCursor.EndOfBlock,
1355 mode=anchormode)
1358 mode=anchormode)
1356 intercepted = True
1359 intercepted = True
1357
1360
1358 # Regular left movement
1361 # Regular left movement
1359 else:
1362 else:
1360 intercepted = not self._in_buffer(position - 1)
1363 intercepted = not self._in_buffer(position - 1)
1361
1364
1362 elif key == QtCore.Qt.Key_Right:
1365 elif key == QtCore.Qt.Key_Right:
1363 original_block_number = cursor.blockNumber()
1366 original_block_number = cursor.blockNumber()
1364 cursor.movePosition(QtGui.QTextCursor.Right,
1367 cursor.movePosition(QtGui.QTextCursor.Right,
1365 mode=anchormode)
1368 mode=anchormode)
1366 if cursor.blockNumber() != original_block_number:
1369 if cursor.blockNumber() != original_block_number:
1367 cursor.movePosition(QtGui.QTextCursor.Right,
1370 cursor.movePosition(QtGui.QTextCursor.Right,
1368 n=len(self._continuation_prompt),
1371 n=len(self._continuation_prompt),
1369 mode=anchormode)
1372 mode=anchormode)
1370 self._set_cursor(cursor)
1373 self._set_cursor(cursor)
1371 intercepted = True
1374 intercepted = True
1372
1375
1373 elif key == QtCore.Qt.Key_Home:
1376 elif key == QtCore.Qt.Key_Home:
1374 start_line = cursor.blockNumber()
1377 start_line = cursor.blockNumber()
1375 if start_line == self._get_prompt_cursor().blockNumber():
1378 if start_line == self._get_prompt_cursor().blockNumber():
1376 start_pos = self._prompt_pos
1379 start_pos = self._prompt_pos
1377 else:
1380 else:
1378 cursor.movePosition(QtGui.QTextCursor.StartOfBlock,
1381 cursor.movePosition(QtGui.QTextCursor.StartOfBlock,
1379 QtGui.QTextCursor.KeepAnchor)
1382 QtGui.QTextCursor.KeepAnchor)
1380 start_pos = cursor.position()
1383 start_pos = cursor.position()
1381 start_pos += len(self._continuation_prompt)
1384 start_pos += len(self._continuation_prompt)
1382 cursor.setPosition(position)
1385 cursor.setPosition(position)
1383 if shift_down and self._in_buffer(position):
1386 if shift_down and self._in_buffer(position):
1384 cursor.setPosition(start_pos, QtGui.QTextCursor.KeepAnchor)
1387 cursor.setPosition(start_pos, QtGui.QTextCursor.KeepAnchor)
1385 else:
1388 else:
1386 cursor.setPosition(start_pos)
1389 cursor.setPosition(start_pos)
1387 self._set_cursor(cursor)
1390 self._set_cursor(cursor)
1388 intercepted = True
1391 intercepted = True
1389
1392
1390 elif key == QtCore.Qt.Key_Backspace:
1393 elif key == QtCore.Qt.Key_Backspace:
1391
1394
1392 # Line deletion (remove continuation prompt)
1395 # Line deletion (remove continuation prompt)
1393 line, col = cursor.blockNumber(), cursor.columnNumber()
1396 line, col = cursor.blockNumber(), cursor.columnNumber()
1394 if not self._reading and \
1397 if not self._reading and \
1395 col == len(self._continuation_prompt) and \
1398 col == len(self._continuation_prompt) and \
1396 line > self._get_prompt_cursor().blockNumber():
1399 line > self._get_prompt_cursor().blockNumber():
1397 cursor.beginEditBlock()
1400 cursor.beginEditBlock()
1398 cursor.movePosition(QtGui.QTextCursor.StartOfBlock,
1401 cursor.movePosition(QtGui.QTextCursor.StartOfBlock,
1399 QtGui.QTextCursor.KeepAnchor)
1402 QtGui.QTextCursor.KeepAnchor)
1400 cursor.removeSelectedText()
1403 cursor.removeSelectedText()
1401 cursor.deletePreviousChar()
1404 cursor.deletePreviousChar()
1402 cursor.endEditBlock()
1405 cursor.endEditBlock()
1403 intercepted = True
1406 intercepted = True
1404
1407
1405 # Regular backwards deletion
1408 # Regular backwards deletion
1406 else:
1409 else:
1407 anchor = cursor.anchor()
1410 anchor = cursor.anchor()
1408 if anchor == position:
1411 if anchor == position:
1409 intercepted = not self._in_buffer(position - 1)
1412 intercepted = not self._in_buffer(position - 1)
1410 else:
1413 else:
1411 intercepted = not self._in_buffer(min(anchor, position))
1414 intercepted = not self._in_buffer(min(anchor, position))
1412
1415
1413 elif key == QtCore.Qt.Key_Delete:
1416 elif key == QtCore.Qt.Key_Delete:
1414
1417
1415 # Line deletion (remove continuation prompt)
1418 # Line deletion (remove continuation prompt)
1416 if not self._reading and self._in_buffer(position) and \
1419 if not self._reading and self._in_buffer(position) and \
1417 cursor.atBlockEnd() and not cursor.hasSelection():
1420 cursor.atBlockEnd() and not cursor.hasSelection():
1418 cursor.movePosition(QtGui.QTextCursor.NextBlock,
1421 cursor.movePosition(QtGui.QTextCursor.NextBlock,
1419 QtGui.QTextCursor.KeepAnchor)
1422 QtGui.QTextCursor.KeepAnchor)
1420 cursor.movePosition(QtGui.QTextCursor.Right,
1423 cursor.movePosition(QtGui.QTextCursor.Right,
1421 QtGui.QTextCursor.KeepAnchor,
1424 QtGui.QTextCursor.KeepAnchor,
1422 len(self._continuation_prompt))
1425 len(self._continuation_prompt))
1423 cursor.removeSelectedText()
1426 cursor.removeSelectedText()
1424 intercepted = True
1427 intercepted = True
1425
1428
1426 # Regular forwards deletion:
1429 # Regular forwards deletion:
1427 else:
1430 else:
1428 anchor = cursor.anchor()
1431 anchor = cursor.anchor()
1429 intercepted = (not self._in_buffer(anchor) or
1432 intercepted = (not self._in_buffer(anchor) or
1430 not self._in_buffer(position))
1433 not self._in_buffer(position))
1431
1434
1432 # Don't move the cursor if Control/Cmd is pressed to allow copy-paste
1435 # Don't move the cursor if Control/Cmd is pressed to allow copy-paste
1433 # using the keyboard in any part of the buffer. Also, permit scrolling
1436 # using the keyboard in any part of the buffer. Also, permit scrolling
1434 # with Page Up/Down keys. Finally, if we're executing, don't move the
1437 # with Page Up/Down keys. Finally, if we're executing, don't move the
1435 # cursor (if even this made sense, we can't guarantee that the prompt
1438 # cursor (if even this made sense, we can't guarantee that the prompt
1436 # position is still valid due to text truncation).
1439 # position is still valid due to text truncation).
1437 if not (self._control_key_down(event.modifiers(), include_command=True)
1440 if not (self._control_key_down(event.modifiers(), include_command=True)
1438 or key in (QtCore.Qt.Key_PageUp, QtCore.Qt.Key_PageDown)
1441 or key in (QtCore.Qt.Key_PageUp, QtCore.Qt.Key_PageDown)
1439 or (self._executing and not self._reading)):
1442 or (self._executing and not self._reading)):
1440 self._keep_cursor_in_buffer()
1443 self._keep_cursor_in_buffer()
1441
1444
1442 return intercepted
1445 return intercepted
1443
1446
1444 def _event_filter_page_keypress(self, event):
1447 def _event_filter_page_keypress(self, event):
1445 """ Filter key events for the paging widget to create console-like
1448 """ Filter key events for the paging widget to create console-like
1446 interface.
1449 interface.
1447 """
1450 """
1448 key = event.key()
1451 key = event.key()
1449 ctrl_down = self._control_key_down(event.modifiers())
1452 ctrl_down = self._control_key_down(event.modifiers())
1450 alt_down = event.modifiers() & QtCore.Qt.AltModifier
1453 alt_down = event.modifiers() & QtCore.Qt.AltModifier
1451
1454
1452 if ctrl_down:
1455 if ctrl_down:
1453 if key == QtCore.Qt.Key_O:
1456 if key == QtCore.Qt.Key_O:
1454 self._control.setFocus()
1457 self._control.setFocus()
1455 intercept = True
1458 intercept = True
1456
1459
1457 elif alt_down:
1460 elif alt_down:
1458 if key == QtCore.Qt.Key_Greater:
1461 if key == QtCore.Qt.Key_Greater:
1459 self._page_control.moveCursor(QtGui.QTextCursor.End)
1462 self._page_control.moveCursor(QtGui.QTextCursor.End)
1460 intercepted = True
1463 intercepted = True
1461
1464
1462 elif key == QtCore.Qt.Key_Less:
1465 elif key == QtCore.Qt.Key_Less:
1463 self._page_control.moveCursor(QtGui.QTextCursor.Start)
1466 self._page_control.moveCursor(QtGui.QTextCursor.Start)
1464 intercepted = True
1467 intercepted = True
1465
1468
1466 elif key in (QtCore.Qt.Key_Q, QtCore.Qt.Key_Escape):
1469 elif key in (QtCore.Qt.Key_Q, QtCore.Qt.Key_Escape):
1467 if self._splitter:
1470 if self._splitter:
1468 self._page_control.hide()
1471 self._page_control.hide()
1469 self._control.setFocus()
1472 self._control.setFocus()
1470 else:
1473 else:
1471 self.layout().setCurrentWidget(self._control)
1474 self.layout().setCurrentWidget(self._control)
1472 return True
1475 return True
1473
1476
1474 elif key in (QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return,
1477 elif key in (QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return,
1475 QtCore.Qt.Key_Tab):
1478 QtCore.Qt.Key_Tab):
1476 new_event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress,
1479 new_event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress,
1477 QtCore.Qt.Key_PageDown,
1480 QtCore.Qt.Key_PageDown,
1478 QtCore.Qt.NoModifier)
1481 QtCore.Qt.NoModifier)
1479 QtGui.qApp.sendEvent(self._page_control, new_event)
1482 QtGui.qApp.sendEvent(self._page_control, new_event)
1480 return True
1483 return True
1481
1484
1482 elif key == QtCore.Qt.Key_Backspace:
1485 elif key == QtCore.Qt.Key_Backspace:
1483 new_event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress,
1486 new_event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress,
1484 QtCore.Qt.Key_PageUp,
1487 QtCore.Qt.Key_PageUp,
1485 QtCore.Qt.NoModifier)
1488 QtCore.Qt.NoModifier)
1486 QtGui.qApp.sendEvent(self._page_control, new_event)
1489 QtGui.qApp.sendEvent(self._page_control, new_event)
1487 return True
1490 return True
1488
1491
1489 return False
1492 return False
1490
1493
1491 def _flush_pending_stream(self):
1494 def _flush_pending_stream(self):
1492 """ Flush out pending text into the widget. """
1495 """ Flush out pending text into the widget. """
1493 text = self._pending_insert_text
1496 text = self._pending_insert_text
1494 self._pending_insert_text = []
1497 self._pending_insert_text = []
1495 buffer_size = self._control.document().maximumBlockCount()
1498 buffer_size = self._control.document().maximumBlockCount()
1496 if buffer_size > 0:
1499 if buffer_size > 0:
1497 text = self._get_last_lines_from_list(text, buffer_size)
1500 text = self._get_last_lines_from_list(text, buffer_size)
1498 text = ''.join(text)
1501 text = ''.join(text)
1499 t = time.time()
1502 t = time.time()
1500 self._insert_plain_text(self._get_end_cursor(), text, flush=True)
1503 self._insert_plain_text(self._get_end_cursor(), text, flush=True)
1501 # Set the flush interval to equal the maximum time to update text.
1504 # Set the flush interval to equal the maximum time to update text.
1502 self._pending_text_flush_interval.setInterval(max(100,
1505 self._pending_text_flush_interval.setInterval(max(100,
1503 (time.time()-t)*1000))
1506 (time.time()-t)*1000))
1504
1507
1505 def _format_as_columns(self, items, separator=' '):
1508 def _format_as_columns(self, items, separator=' '):
1506 """ Transform a list of strings into a single string with columns.
1509 """ Transform a list of strings into a single string with columns.
1507
1510
1508 Parameters
1511 Parameters
1509 ----------
1512 ----------
1510 items : sequence of strings
1513 items : sequence of strings
1511 The strings to process.
1514 The strings to process.
1512
1515
1513 separator : str, optional [default is two spaces]
1516 separator : str, optional [default is two spaces]
1514 The string that separates columns.
1517 The string that separates columns.
1515
1518
1516 Returns
1519 Returns
1517 -------
1520 -------
1518 The formatted string.
1521 The formatted string.
1519 """
1522 """
1520 # Calculate the number of characters available.
1523 # Calculate the number of characters available.
1521 width = self._control.viewport().width()
1524 width = self._control.viewport().width()
1522 char_width = QtGui.QFontMetrics(self.font).width(' ')
1525 char_width = QtGui.QFontMetrics(self.font).width(' ')
1523 displaywidth = max(10, (width / char_width) - 1)
1526 displaywidth = max(10, (width / char_width) - 1)
1524
1527
1525 return columnize(items, separator, displaywidth)
1528 return columnize(items, separator, displaywidth)
1526
1529
1527 def _get_block_plain_text(self, block):
1530 def _get_block_plain_text(self, block):
1528 """ Given a QTextBlock, return its unformatted text.
1531 """ Given a QTextBlock, return its unformatted text.
1529 """
1532 """
1530 cursor = QtGui.QTextCursor(block)
1533 cursor = QtGui.QTextCursor(block)
1531 cursor.movePosition(QtGui.QTextCursor.StartOfBlock)
1534 cursor.movePosition(QtGui.QTextCursor.StartOfBlock)
1532 cursor.movePosition(QtGui.QTextCursor.EndOfBlock,
1535 cursor.movePosition(QtGui.QTextCursor.EndOfBlock,
1533 QtGui.QTextCursor.KeepAnchor)
1536 QtGui.QTextCursor.KeepAnchor)
1534 return cursor.selection().toPlainText()
1537 return cursor.selection().toPlainText()
1535
1538
1536 def _get_cursor(self):
1539 def _get_cursor(self):
1537 """ Convenience method that returns a cursor for the current position.
1540 """ Convenience method that returns a cursor for the current position.
1538 """
1541 """
1539 return self._control.textCursor()
1542 return self._control.textCursor()
1540
1543
1541 def _get_end_cursor(self):
1544 def _get_end_cursor(self):
1542 """ Convenience method that returns a cursor for the last character.
1545 """ Convenience method that returns a cursor for the last character.
1543 """
1546 """
1544 cursor = self._control.textCursor()
1547 cursor = self._control.textCursor()
1545 cursor.movePosition(QtGui.QTextCursor.End)
1548 cursor.movePosition(QtGui.QTextCursor.End)
1546 return cursor
1549 return cursor
1547
1550
1548 def _get_input_buffer_cursor_column(self):
1551 def _get_input_buffer_cursor_column(self):
1549 """ Returns the column of the cursor in the input buffer, excluding the
1552 """ Returns the column of the cursor in the input buffer, excluding the
1550 contribution by the prompt, or -1 if there is no such column.
1553 contribution by the prompt, or -1 if there is no such column.
1551 """
1554 """
1552 prompt = self._get_input_buffer_cursor_prompt()
1555 prompt = self._get_input_buffer_cursor_prompt()
1553 if prompt is None:
1556 if prompt is None:
1554 return -1
1557 return -1
1555 else:
1558 else:
1556 cursor = self._control.textCursor()
1559 cursor = self._control.textCursor()
1557 return cursor.columnNumber() - len(prompt)
1560 return cursor.columnNumber() - len(prompt)
1558
1561
1559 def _get_input_buffer_cursor_line(self):
1562 def _get_input_buffer_cursor_line(self):
1560 """ Returns the text of the line of the input buffer that contains the
1563 """ Returns the text of the line of the input buffer that contains the
1561 cursor, or None if there is no such line.
1564 cursor, or None if there is no such line.
1562 """
1565 """
1563 prompt = self._get_input_buffer_cursor_prompt()
1566 prompt = self._get_input_buffer_cursor_prompt()
1564 if prompt is None:
1567 if prompt is None:
1565 return None
1568 return None
1566 else:
1569 else:
1567 cursor = self._control.textCursor()
1570 cursor = self._control.textCursor()
1568 text = self._get_block_plain_text(cursor.block())
1571 text = self._get_block_plain_text(cursor.block())
1569 return text[len(prompt):]
1572 return text[len(prompt):]
1570
1573
1571 def _get_input_buffer_cursor_prompt(self):
1574 def _get_input_buffer_cursor_prompt(self):
1572 """ Returns the (plain text) prompt for line of the input buffer that
1575 """ Returns the (plain text) prompt for line of the input buffer that
1573 contains the cursor, or None if there is no such line.
1576 contains the cursor, or None if there is no such line.
1574 """
1577 """
1575 if self._executing:
1578 if self._executing:
1576 return None
1579 return None
1577 cursor = self._control.textCursor()
1580 cursor = self._control.textCursor()
1578 if cursor.position() >= self._prompt_pos:
1581 if cursor.position() >= self._prompt_pos:
1579 if cursor.blockNumber() == self._get_prompt_cursor().blockNumber():
1582 if cursor.blockNumber() == self._get_prompt_cursor().blockNumber():
1580 return self._prompt
1583 return self._prompt
1581 else:
1584 else:
1582 return self._continuation_prompt
1585 return self._continuation_prompt
1583 else:
1586 else:
1584 return None
1587 return None
1585
1588
1586 def _get_last_lines(self, text, num_lines, return_count=False):
1589 def _get_last_lines(self, text, num_lines, return_count=False):
1587 """ Return last specified number of lines of text (like `tail -n`).
1590 """ Return last specified number of lines of text (like `tail -n`).
1588 If return_count is True, returns a tuple of clipped text and the
1591 If return_count is True, returns a tuple of clipped text and the
1589 number of lines in the clipped text.
1592 number of lines in the clipped text.
1590 """
1593 """
1591 pos = len(text)
1594 pos = len(text)
1592 if pos < num_lines:
1595 if pos < num_lines:
1593 if return_count:
1596 if return_count:
1594 return text, text.count('\n') if return_count else text
1597 return text, text.count('\n') if return_count else text
1595 else:
1598 else:
1596 return text
1599 return text
1597 i = 0
1600 i = 0
1598 while i < num_lines:
1601 while i < num_lines:
1599 pos = text.rfind('\n', None, pos)
1602 pos = text.rfind('\n', None, pos)
1600 if pos == -1:
1603 if pos == -1:
1601 pos = None
1604 pos = None
1602 break
1605 break
1603 i += 1
1606 i += 1
1604 if return_count:
1607 if return_count:
1605 return text[pos:], i
1608 return text[pos:], i
1606 else:
1609 else:
1607 return text[pos:]
1610 return text[pos:]
1608
1611
1609 def _get_last_lines_from_list(self, text_list, num_lines):
1612 def _get_last_lines_from_list(self, text_list, num_lines):
1610 """ Return the list of text clipped to last specified lines.
1613 """ Return the list of text clipped to last specified lines.
1611 """
1614 """
1612 ret = []
1615 ret = []
1613 lines_pending = num_lines
1616 lines_pending = num_lines
1614 for text in reversed(text_list):
1617 for text in reversed(text_list):
1615 text, lines_added = self._get_last_lines(text, lines_pending,
1618 text, lines_added = self._get_last_lines(text, lines_pending,
1616 return_count=True)
1619 return_count=True)
1617 ret.append(text)
1620 ret.append(text)
1618 lines_pending -= lines_added
1621 lines_pending -= lines_added
1619 if lines_pending <= 0:
1622 if lines_pending <= 0:
1620 break
1623 break
1621 return ret[::-1]
1624 return ret[::-1]
1622
1625
1623 def _get_prompt_cursor(self):
1626 def _get_prompt_cursor(self):
1624 """ Convenience method that returns a cursor for the prompt position.
1627 """ Convenience method that returns a cursor for the prompt position.
1625 """
1628 """
1626 cursor = self._control.textCursor()
1629 cursor = self._control.textCursor()
1627 cursor.setPosition(self._prompt_pos)
1630 cursor.setPosition(self._prompt_pos)
1628 return cursor
1631 return cursor
1629
1632
1630 def _get_selection_cursor(self, start, end):
1633 def _get_selection_cursor(self, start, end):
1631 """ Convenience method that returns a cursor with text selected between
1634 """ Convenience method that returns a cursor with text selected between
1632 the positions 'start' and 'end'.
1635 the positions 'start' and 'end'.
1633 """
1636 """
1634 cursor = self._control.textCursor()
1637 cursor = self._control.textCursor()
1635 cursor.setPosition(start)
1638 cursor.setPosition(start)
1636 cursor.setPosition(end, QtGui.QTextCursor.KeepAnchor)
1639 cursor.setPosition(end, QtGui.QTextCursor.KeepAnchor)
1637 return cursor
1640 return cursor
1638
1641
1639 def _get_word_start_cursor(self, position):
1642 def _get_word_start_cursor(self, position):
1640 """ Find the start of the word to the left the given position. If a
1643 """ Find the start of the word to the left the given position. If a
1641 sequence of non-word characters precedes the first word, skip over
1644 sequence of non-word characters precedes the first word, skip over
1642 them. (This emulates the behavior of bash, emacs, etc.)
1645 them. (This emulates the behavior of bash, emacs, etc.)
1643 """
1646 """
1644 document = self._control.document()
1647 document = self._control.document()
1645 position -= 1
1648 position -= 1
1646 while position >= self._prompt_pos and \
1649 while position >= self._prompt_pos and \
1647 not is_letter_or_number(document.characterAt(position)):
1650 not is_letter_or_number(document.characterAt(position)):
1648 position -= 1
1651 position -= 1
1649 while position >= self._prompt_pos and \
1652 while position >= self._prompt_pos and \
1650 is_letter_or_number(document.characterAt(position)):
1653 is_letter_or_number(document.characterAt(position)):
1651 position -= 1
1654 position -= 1
1652 cursor = self._control.textCursor()
1655 cursor = self._control.textCursor()
1653 cursor.setPosition(position + 1)
1656 cursor.setPosition(position + 1)
1654 return cursor
1657 return cursor
1655
1658
1656 def _get_word_end_cursor(self, position):
1659 def _get_word_end_cursor(self, position):
1657 """ Find the end of the word to the right the given position. If a
1660 """ Find the end of the word to the right the given position. If a
1658 sequence of non-word characters precedes the first word, skip over
1661 sequence of non-word characters precedes the first word, skip over
1659 them. (This emulates the behavior of bash, emacs, etc.)
1662 them. (This emulates the behavior of bash, emacs, etc.)
1660 """
1663 """
1661 document = self._control.document()
1664 document = self._control.document()
1662 end = self._get_end_cursor().position()
1665 end = self._get_end_cursor().position()
1663 while position < end and \
1666 while position < end and \
1664 not is_letter_or_number(document.characterAt(position)):
1667 not is_letter_or_number(document.characterAt(position)):
1665 position += 1
1668 position += 1
1666 while position < end and \
1669 while position < end and \
1667 is_letter_or_number(document.characterAt(position)):
1670 is_letter_or_number(document.characterAt(position)):
1668 position += 1
1671 position += 1
1669 cursor = self._control.textCursor()
1672 cursor = self._control.textCursor()
1670 cursor.setPosition(position)
1673 cursor.setPosition(position)
1671 return cursor
1674 return cursor
1672
1675
1673 def _insert_continuation_prompt(self, cursor):
1676 def _insert_continuation_prompt(self, cursor):
1674 """ Inserts new continuation prompt using the specified cursor.
1677 """ Inserts new continuation prompt using the specified cursor.
1675 """
1678 """
1676 if self._continuation_prompt_html is None:
1679 if self._continuation_prompt_html is None:
1677 self._insert_plain_text(cursor, self._continuation_prompt)
1680 self._insert_plain_text(cursor, self._continuation_prompt)
1678 else:
1681 else:
1679 self._continuation_prompt = self._insert_html_fetching_plain_text(
1682 self._continuation_prompt = self._insert_html_fetching_plain_text(
1680 cursor, self._continuation_prompt_html)
1683 cursor, self._continuation_prompt_html)
1681
1684
1682 def _insert_block(self, cursor, block_format=None):
1685 def _insert_block(self, cursor, block_format=None):
1683 """ Inserts an empty QTextBlock using the specified cursor.
1686 """ Inserts an empty QTextBlock using the specified cursor.
1684 """
1687 """
1685 if block_format is None:
1688 if block_format is None:
1686 block_format = QtGui.QTextBlockFormat()
1689 block_format = QtGui.QTextBlockFormat()
1687 cursor.insertBlock(block_format)
1690 cursor.insertBlock(block_format)
1688
1691
1689 def _insert_html(self, cursor, html):
1692 def _insert_html(self, cursor, html):
1690 """ Inserts HTML using the specified cursor in such a way that future
1693 """ Inserts HTML using the specified cursor in such a way that future
1691 formatting is unaffected.
1694 formatting is unaffected.
1692 """
1695 """
1693 cursor.beginEditBlock()
1696 cursor.beginEditBlock()
1694 cursor.insertHtml(html)
1697 cursor.insertHtml(html)
1695
1698
1696 # After inserting HTML, the text document "remembers" it's in "html
1699 # After inserting HTML, the text document "remembers" it's in "html
1697 # mode", which means that subsequent calls adding plain text will result
1700 # mode", which means that subsequent calls adding plain text will result
1698 # in unwanted formatting, lost tab characters, etc. The following code
1701 # in unwanted formatting, lost tab characters, etc. The following code
1699 # hacks around this behavior, which I consider to be a bug in Qt, by
1702 # hacks around this behavior, which I consider to be a bug in Qt, by
1700 # (crudely) resetting the document's style state.
1703 # (crudely) resetting the document's style state.
1701 cursor.movePosition(QtGui.QTextCursor.Left,
1704 cursor.movePosition(QtGui.QTextCursor.Left,
1702 QtGui.QTextCursor.KeepAnchor)
1705 QtGui.QTextCursor.KeepAnchor)
1703 if cursor.selection().toPlainText() == ' ':
1706 if cursor.selection().toPlainText() == ' ':
1704 cursor.removeSelectedText()
1707 cursor.removeSelectedText()
1705 else:
1708 else:
1706 cursor.movePosition(QtGui.QTextCursor.Right)
1709 cursor.movePosition(QtGui.QTextCursor.Right)
1707 cursor.insertText(' ', QtGui.QTextCharFormat())
1710 cursor.insertText(' ', QtGui.QTextCharFormat())
1708 cursor.endEditBlock()
1711 cursor.endEditBlock()
1709
1712
1710 def _insert_html_fetching_plain_text(self, cursor, html):
1713 def _insert_html_fetching_plain_text(self, cursor, html):
1711 """ Inserts HTML using the specified cursor, then returns its plain text
1714 """ Inserts HTML using the specified cursor, then returns its plain text
1712 version.
1715 version.
1713 """
1716 """
1714 cursor.beginEditBlock()
1717 cursor.beginEditBlock()
1715 cursor.removeSelectedText()
1718 cursor.removeSelectedText()
1716
1719
1717 start = cursor.position()
1720 start = cursor.position()
1718 self._insert_html(cursor, html)
1721 self._insert_html(cursor, html)
1719 end = cursor.position()
1722 end = cursor.position()
1720 cursor.setPosition(start, QtGui.QTextCursor.KeepAnchor)
1723 cursor.setPosition(start, QtGui.QTextCursor.KeepAnchor)
1721 text = cursor.selection().toPlainText()
1724 text = cursor.selection().toPlainText()
1722
1725
1723 cursor.setPosition(end)
1726 cursor.setPosition(end)
1724 cursor.endEditBlock()
1727 cursor.endEditBlock()
1725 return text
1728 return text
1726
1729
1727 def _insert_plain_text(self, cursor, text, flush=False):
1730 def _insert_plain_text(self, cursor, text, flush=False):
1728 """ Inserts plain text using the specified cursor, processing ANSI codes
1731 """ Inserts plain text using the specified cursor, processing ANSI codes
1729 if enabled.
1732 if enabled.
1730 """
1733 """
1731 # maximumBlockCount() can be different from self.buffer_size in
1734 # maximumBlockCount() can be different from self.buffer_size in
1732 # case input prompt is active.
1735 # case input prompt is active.
1733 buffer_size = self._control.document().maximumBlockCount()
1736 buffer_size = self._control.document().maximumBlockCount()
1734
1737
1735 if self._executing and not flush and \
1738 if self._executing and not flush and \
1736 self._pending_text_flush_interval.isActive():
1739 self._pending_text_flush_interval.isActive():
1737 self._pending_insert_text.append(text)
1740 self._pending_insert_text.append(text)
1738 if buffer_size > 0:
1741 if buffer_size > 0:
1739 self._pending_insert_text = self._get_last_lines_from_list(
1742 self._pending_insert_text = self._get_last_lines_from_list(
1740 self._pending_insert_text, buffer_size)
1743 self._pending_insert_text, buffer_size)
1741 return
1744 return
1742
1745
1743 if self._executing and not self._pending_text_flush_interval.isActive():
1746 if self._executing and not self._pending_text_flush_interval.isActive():
1744 self._pending_text_flush_interval.start()
1747 self._pending_text_flush_interval.start()
1745
1748
1746 # Clip the text to last `buffer_size` lines.
1749 # Clip the text to last `buffer_size` lines.
1747 if buffer_size > 0:
1750 if buffer_size > 0:
1748 text = self._get_last_lines(text, buffer_size)
1751 text = self._get_last_lines(text, buffer_size)
1749
1752
1750 cursor.beginEditBlock()
1753 cursor.beginEditBlock()
1751 if self.ansi_codes:
1754 if self.ansi_codes:
1752 for substring in self._ansi_processor.split_string(text):
1755 for substring in self._ansi_processor.split_string(text):
1753 for act in self._ansi_processor.actions:
1756 for act in self._ansi_processor.actions:
1754
1757
1755 # Unlike real terminal emulators, we don't distinguish
1758 # Unlike real terminal emulators, we don't distinguish
1756 # between the screen and the scrollback buffer. A screen
1759 # between the screen and the scrollback buffer. A screen
1757 # erase request clears everything.
1760 # erase request clears everything.
1758 if act.action == 'erase' and act.area == 'screen':
1761 if act.action == 'erase' and act.area == 'screen':
1759 cursor.select(QtGui.QTextCursor.Document)
1762 cursor.select(QtGui.QTextCursor.Document)
1760 cursor.removeSelectedText()
1763 cursor.removeSelectedText()
1761
1764
1762 # Simulate a form feed by scrolling just past the last line.
1765 # Simulate a form feed by scrolling just past the last line.
1763 elif act.action == 'scroll' and act.unit == 'page':
1766 elif act.action == 'scroll' and act.unit == 'page':
1764 cursor.insertText('\n')
1767 cursor.insertText('\n')
1765 cursor.endEditBlock()
1768 cursor.endEditBlock()
1766 self._set_top_cursor(cursor)
1769 self._set_top_cursor(cursor)
1767 cursor.joinPreviousEditBlock()
1770 cursor.joinPreviousEditBlock()
1768 cursor.deletePreviousChar()
1771 cursor.deletePreviousChar()
1769
1772
1770 elif act.action == 'carriage-return':
1773 elif act.action == 'carriage-return':
1771 cursor.movePosition(
1774 cursor.movePosition(
1772 cursor.StartOfLine, cursor.KeepAnchor)
1775 cursor.StartOfLine, cursor.KeepAnchor)
1773
1776
1774 elif act.action == 'beep':
1777 elif act.action == 'beep':
1775 QtGui.qApp.beep()
1778 QtGui.qApp.beep()
1776
1779
1777 elif act.action == 'backspace':
1780 elif act.action == 'backspace':
1778 if not cursor.atBlockStart():
1781 if not cursor.atBlockStart():
1779 cursor.movePosition(
1782 cursor.movePosition(
1780 cursor.PreviousCharacter, cursor.KeepAnchor)
1783 cursor.PreviousCharacter, cursor.KeepAnchor)
1781
1784
1782 elif act.action == 'newline':
1785 elif act.action == 'newline':
1783 cursor.movePosition(cursor.EndOfLine)
1786 cursor.movePosition(cursor.EndOfLine)
1784
1787
1785 format = self._ansi_processor.get_format()
1788 format = self._ansi_processor.get_format()
1786
1789
1787 selection = cursor.selectedText()
1790 selection = cursor.selectedText()
1788 if len(selection) == 0:
1791 if len(selection) == 0:
1789 cursor.insertText(substring, format)
1792 cursor.insertText(substring, format)
1790 elif substring is not None:
1793 elif substring is not None:
1791 # BS and CR are treated as a change in print
1794 # BS and CR are treated as a change in print
1792 # position, rather than a backwards character
1795 # position, rather than a backwards character
1793 # deletion for output equivalence with (I)Python
1796 # deletion for output equivalence with (I)Python
1794 # terminal.
1797 # terminal.
1795 if len(substring) >= len(selection):
1798 if len(substring) >= len(selection):
1796 cursor.insertText(substring, format)
1799 cursor.insertText(substring, format)
1797 else:
1800 else:
1798 old_text = selection[len(substring):]
1801 old_text = selection[len(substring):]
1799 cursor.insertText(substring + old_text, format)
1802 cursor.insertText(substring + old_text, format)
1800 cursor.movePosition(cursor.PreviousCharacter,
1803 cursor.movePosition(cursor.PreviousCharacter,
1801 cursor.KeepAnchor, len(old_text))
1804 cursor.KeepAnchor, len(old_text))
1802 else:
1805 else:
1803 cursor.insertText(text)
1806 cursor.insertText(text)
1804 cursor.endEditBlock()
1807 cursor.endEditBlock()
1805
1808
1806 def _insert_plain_text_into_buffer(self, cursor, text):
1809 def _insert_plain_text_into_buffer(self, cursor, text):
1807 """ Inserts text into the input buffer using the specified cursor (which
1810 """ Inserts text into the input buffer using the specified cursor (which
1808 must be in the input buffer), ensuring that continuation prompts are
1811 must be in the input buffer), ensuring that continuation prompts are
1809 inserted as necessary.
1812 inserted as necessary.
1810 """
1813 """
1811 lines = text.splitlines(True)
1814 lines = text.splitlines(True)
1812 if lines:
1815 if lines:
1813 cursor.beginEditBlock()
1816 cursor.beginEditBlock()
1814 cursor.insertText(lines[0])
1817 cursor.insertText(lines[0])
1815 for line in lines[1:]:
1818 for line in lines[1:]:
1816 if self._continuation_prompt_html is None:
1819 if self._continuation_prompt_html is None:
1817 cursor.insertText(self._continuation_prompt)
1820 cursor.insertText(self._continuation_prompt)
1818 else:
1821 else:
1819 self._continuation_prompt = \
1822 self._continuation_prompt = \
1820 self._insert_html_fetching_plain_text(
1823 self._insert_html_fetching_plain_text(
1821 cursor, self._continuation_prompt_html)
1824 cursor, self._continuation_prompt_html)
1822 cursor.insertText(line)
1825 cursor.insertText(line)
1823 cursor.endEditBlock()
1826 cursor.endEditBlock()
1824
1827
1825 def _in_buffer(self, position=None):
1828 def _in_buffer(self, position=None):
1826 """ Returns whether the current cursor (or, if specified, a position) is
1829 """ Returns whether the current cursor (or, if specified, a position) is
1827 inside the editing region.
1830 inside the editing region.
1828 """
1831 """
1829 cursor = self._control.textCursor()
1832 cursor = self._control.textCursor()
1830 if position is None:
1833 if position is None:
1831 position = cursor.position()
1834 position = cursor.position()
1832 else:
1835 else:
1833 cursor.setPosition(position)
1836 cursor.setPosition(position)
1834 line = cursor.blockNumber()
1837 line = cursor.blockNumber()
1835 prompt_line = self._get_prompt_cursor().blockNumber()
1838 prompt_line = self._get_prompt_cursor().blockNumber()
1836 if line == prompt_line:
1839 if line == prompt_line:
1837 return position >= self._prompt_pos
1840 return position >= self._prompt_pos
1838 elif line > prompt_line:
1841 elif line > prompt_line:
1839 cursor.movePosition(QtGui.QTextCursor.StartOfBlock)
1842 cursor.movePosition(QtGui.QTextCursor.StartOfBlock)
1840 prompt_pos = cursor.position() + len(self._continuation_prompt)
1843 prompt_pos = cursor.position() + len(self._continuation_prompt)
1841 return position >= prompt_pos
1844 return position >= prompt_pos
1842 return False
1845 return False
1843
1846
1844 def _keep_cursor_in_buffer(self):
1847 def _keep_cursor_in_buffer(self):
1845 """ Ensures that the cursor is inside the editing region. Returns
1848 """ Ensures that the cursor is inside the editing region. Returns
1846 whether the cursor was moved.
1849 whether the cursor was moved.
1847 """
1850 """
1848 moved = not self._in_buffer()
1851 moved = not self._in_buffer()
1849 if moved:
1852 if moved:
1850 cursor = self._control.textCursor()
1853 cursor = self._control.textCursor()
1851 cursor.movePosition(QtGui.QTextCursor.End)
1854 cursor.movePosition(QtGui.QTextCursor.End)
1852 self._control.setTextCursor(cursor)
1855 self._control.setTextCursor(cursor)
1853 return moved
1856 return moved
1854
1857
1855 def _keyboard_quit(self):
1858 def _keyboard_quit(self):
1856 """ Cancels the current editing task ala Ctrl-G in Emacs.
1859 """ Cancels the current editing task ala Ctrl-G in Emacs.
1857 """
1860 """
1858 if self._temp_buffer_filled :
1861 if self._temp_buffer_filled :
1859 self._cancel_completion()
1862 self._cancel_completion()
1860 self._clear_temporary_buffer()
1863 self._clear_temporary_buffer()
1861 else:
1864 else:
1862 self.input_buffer = ''
1865 self.input_buffer = ''
1863
1866
1864 def _page(self, text, html=False):
1867 def _page(self, text, html=False):
1865 """ Displays text using the pager if it exceeds the height of the
1868 """ Displays text using the pager if it exceeds the height of the
1866 viewport.
1869 viewport.
1867
1870
1868 Parameters:
1871 Parameters:
1869 -----------
1872 -----------
1870 html : bool, optional (default False)
1873 html : bool, optional (default False)
1871 If set, the text will be interpreted as HTML instead of plain text.
1874 If set, the text will be interpreted as HTML instead of plain text.
1872 """
1875 """
1873 line_height = QtGui.QFontMetrics(self.font).height()
1876 line_height = QtGui.QFontMetrics(self.font).height()
1874 minlines = self._control.viewport().height() / line_height
1877 minlines = self._control.viewport().height() / line_height
1875 if self.paging != 'none' and \
1878 if self.paging != 'none' and \
1876 re.match("(?:[^\n]*\n){%i}" % minlines, text):
1879 re.match("(?:[^\n]*\n){%i}" % minlines, text):
1877 if self.paging == 'custom':
1880 if self.paging == 'custom':
1878 self.custom_page_requested.emit(text)
1881 self.custom_page_requested.emit(text)
1879 else:
1882 else:
1880 self._page_control.clear()
1883 self._page_control.clear()
1881 cursor = self._page_control.textCursor()
1884 cursor = self._page_control.textCursor()
1882 if html:
1885 if html:
1883 self._insert_html(cursor, text)
1886 self._insert_html(cursor, text)
1884 else:
1887 else:
1885 self._insert_plain_text(cursor, text)
1888 self._insert_plain_text(cursor, text)
1886 self._page_control.moveCursor(QtGui.QTextCursor.Start)
1889 self._page_control.moveCursor(QtGui.QTextCursor.Start)
1887
1890
1888 self._page_control.viewport().resize(self._control.size())
1891 self._page_control.viewport().resize(self._control.size())
1889 if self._splitter:
1892 if self._splitter:
1890 self._page_control.show()
1893 self._page_control.show()
1891 self._page_control.setFocus()
1894 self._page_control.setFocus()
1892 else:
1895 else:
1893 self.layout().setCurrentWidget(self._page_control)
1896 self.layout().setCurrentWidget(self._page_control)
1894 elif html:
1897 elif html:
1895 self._append_html(text)
1898 self._append_html(text)
1896 else:
1899 else:
1897 self._append_plain_text(text)
1900 self._append_plain_text(text)
1898
1901
1899 def _set_paging(self, paging):
1902 def _set_paging(self, paging):
1900 """
1903 """
1901 Change the pager to `paging` style.
1904 Change the pager to `paging` style.
1902
1905
1903 XXX: currently, this is limited to switching between 'hsplit' and
1906 XXX: currently, this is limited to switching between 'hsplit' and
1904 'vsplit'.
1907 'vsplit'.
1905
1908
1906 Parameters:
1909 Parameters:
1907 -----------
1910 -----------
1908 paging : string
1911 paging : string
1909 Either "hsplit", "vsplit", or "inside"
1912 Either "hsplit", "vsplit", or "inside"
1910 """
1913 """
1911 if self._splitter is None:
1914 if self._splitter is None:
1912 raise NotImplementedError("""can only switch if --paging=hsplit or
1915 raise NotImplementedError("""can only switch if --paging=hsplit or
1913 --paging=vsplit is used.""")
1916 --paging=vsplit is used.""")
1914 if paging == 'hsplit':
1917 if paging == 'hsplit':
1915 self._splitter.setOrientation(QtCore.Qt.Horizontal)
1918 self._splitter.setOrientation(QtCore.Qt.Horizontal)
1916 elif paging == 'vsplit':
1919 elif paging == 'vsplit':
1917 self._splitter.setOrientation(QtCore.Qt.Vertical)
1920 self._splitter.setOrientation(QtCore.Qt.Vertical)
1918 elif paging == 'inside':
1921 elif paging == 'inside':
1919 raise NotImplementedError("""switching to 'inside' paging not
1922 raise NotImplementedError("""switching to 'inside' paging not
1920 supported yet.""")
1923 supported yet.""")
1921 else:
1924 else:
1922 raise ValueError("unknown paging method '%s'" % paging)
1925 raise ValueError("unknown paging method '%s'" % paging)
1923 self.paging = paging
1926 self.paging = paging
1924
1927
1925 def _prompt_finished(self):
1928 def _prompt_finished(self):
1926 """ Called immediately after a prompt is finished, i.e. when some input
1929 """ Called immediately after a prompt is finished, i.e. when some input
1927 will be processed and a new prompt displayed.
1930 will be processed and a new prompt displayed.
1928 """
1931 """
1929 self._control.setReadOnly(True)
1932 self._control.setReadOnly(True)
1930 self._prompt_finished_hook()
1933 self._prompt_finished_hook()
1931
1934
1932 def _prompt_started(self):
1935 def _prompt_started(self):
1933 """ Called immediately after a new prompt is displayed.
1936 """ Called immediately after a new prompt is displayed.
1934 """
1937 """
1935 # Temporarily disable the maximum block count to permit undo/redo and
1938 # Temporarily disable the maximum block count to permit undo/redo and
1936 # to ensure that the prompt position does not change due to truncation.
1939 # to ensure that the prompt position does not change due to truncation.
1937 self._control.document().setMaximumBlockCount(0)
1940 self._control.document().setMaximumBlockCount(0)
1938 self._control.setUndoRedoEnabled(True)
1941 self._control.setUndoRedoEnabled(True)
1939
1942
1940 # Work around bug in QPlainTextEdit: input method is not re-enabled
1943 # Work around bug in QPlainTextEdit: input method is not re-enabled
1941 # when read-only is disabled.
1944 # when read-only is disabled.
1942 self._control.setReadOnly(False)
1945 self._control.setReadOnly(False)
1943 self._control.setAttribute(QtCore.Qt.WA_InputMethodEnabled, True)
1946 self._control.setAttribute(QtCore.Qt.WA_InputMethodEnabled, True)
1944
1947
1945 if not self._reading:
1948 if not self._reading:
1946 self._executing = False
1949 self._executing = False
1947 self._prompt_started_hook()
1950 self._prompt_started_hook()
1948
1951
1949 # If the input buffer has changed while executing, load it.
1952 # If the input buffer has changed while executing, load it.
1950 if self._input_buffer_pending:
1953 if self._input_buffer_pending:
1951 self.input_buffer = self._input_buffer_pending
1954 self.input_buffer = self._input_buffer_pending
1952 self._input_buffer_pending = ''
1955 self._input_buffer_pending = ''
1953
1956
1954 self._control.moveCursor(QtGui.QTextCursor.End)
1957 self._control.moveCursor(QtGui.QTextCursor.End)
1955
1958
1956 def _readline(self, prompt='', callback=None):
1959 def _readline(self, prompt='', callback=None):
1957 """ Reads one line of input from the user.
1960 """ Reads one line of input from the user.
1958
1961
1959 Parameters
1962 Parameters
1960 ----------
1963 ----------
1961 prompt : str, optional
1964 prompt : str, optional
1962 The prompt to print before reading the line.
1965 The prompt to print before reading the line.
1963
1966
1964 callback : callable, optional
1967 callback : callable, optional
1965 A callback to execute with the read line. If not specified, input is
1968 A callback to execute with the read line. If not specified, input is
1966 read *synchronously* and this method does not return until it has
1969 read *synchronously* and this method does not return until it has
1967 been read.
1970 been read.
1968
1971
1969 Returns
1972 Returns
1970 -------
1973 -------
1971 If a callback is specified, returns nothing. Otherwise, returns the
1974 If a callback is specified, returns nothing. Otherwise, returns the
1972 input string with the trailing newline stripped.
1975 input string with the trailing newline stripped.
1973 """
1976 """
1974 if self._reading:
1977 if self._reading:
1975 raise RuntimeError('Cannot read a line. Widget is already reading.')
1978 raise RuntimeError('Cannot read a line. Widget is already reading.')
1976
1979
1977 if not callback and not self.isVisible():
1980 if not callback and not self.isVisible():
1978 # If the user cannot see the widget, this function cannot return.
1981 # If the user cannot see the widget, this function cannot return.
1979 raise RuntimeError('Cannot synchronously read a line if the widget '
1982 raise RuntimeError('Cannot synchronously read a line if the widget '
1980 'is not visible!')
1983 'is not visible!')
1981
1984
1982 self._reading = True
1985 self._reading = True
1983 self._show_prompt(prompt, newline=False)
1986 self._show_prompt(prompt, newline=False)
1984
1987
1985 if callback is None:
1988 if callback is None:
1986 self._reading_callback = None
1989 self._reading_callback = None
1987 while self._reading:
1990 while self._reading:
1988 QtCore.QCoreApplication.processEvents()
1991 QtCore.QCoreApplication.processEvents()
1989 return self._get_input_buffer(force=True).rstrip('\n')
1992 return self._get_input_buffer(force=True).rstrip('\n')
1990
1993
1991 else:
1994 else:
1992 self._reading_callback = lambda: \
1995 self._reading_callback = lambda: \
1993 callback(self._get_input_buffer(force=True).rstrip('\n'))
1996 callback(self._get_input_buffer(force=True).rstrip('\n'))
1994
1997
1995 def _set_continuation_prompt(self, prompt, html=False):
1998 def _set_continuation_prompt(self, prompt, html=False):
1996 """ Sets the continuation prompt.
1999 """ Sets the continuation prompt.
1997
2000
1998 Parameters
2001 Parameters
1999 ----------
2002 ----------
2000 prompt : str
2003 prompt : str
2001 The prompt to show when more input is needed.
2004 The prompt to show when more input is needed.
2002
2005
2003 html : bool, optional (default False)
2006 html : bool, optional (default False)
2004 If set, the prompt will be inserted as formatted HTML. Otherwise,
2007 If set, the prompt will be inserted as formatted HTML. Otherwise,
2005 the prompt will be treated as plain text, though ANSI color codes
2008 the prompt will be treated as plain text, though ANSI color codes
2006 will be handled.
2009 will be handled.
2007 """
2010 """
2008 if html:
2011 if html:
2009 self._continuation_prompt_html = prompt
2012 self._continuation_prompt_html = prompt
2010 else:
2013 else:
2011 self._continuation_prompt = prompt
2014 self._continuation_prompt = prompt
2012 self._continuation_prompt_html = None
2015 self._continuation_prompt_html = None
2013
2016
2014 def _set_cursor(self, cursor):
2017 def _set_cursor(self, cursor):
2015 """ Convenience method to set the current cursor.
2018 """ Convenience method to set the current cursor.
2016 """
2019 """
2017 self._control.setTextCursor(cursor)
2020 self._control.setTextCursor(cursor)
2018
2021
2019 def _set_top_cursor(self, cursor):
2022 def _set_top_cursor(self, cursor):
2020 """ Scrolls the viewport so that the specified cursor is at the top.
2023 """ Scrolls the viewport so that the specified cursor is at the top.
2021 """
2024 """
2022 scrollbar = self._control.verticalScrollBar()
2025 scrollbar = self._control.verticalScrollBar()
2023 scrollbar.setValue(scrollbar.maximum())
2026 scrollbar.setValue(scrollbar.maximum())
2024 original_cursor = self._control.textCursor()
2027 original_cursor = self._control.textCursor()
2025 self._control.setTextCursor(cursor)
2028 self._control.setTextCursor(cursor)
2026 self._control.ensureCursorVisible()
2029 self._control.ensureCursorVisible()
2027 self._control.setTextCursor(original_cursor)
2030 self._control.setTextCursor(original_cursor)
2028
2031
2029 def _show_prompt(self, prompt=None, html=False, newline=True):
2032 def _show_prompt(self, prompt=None, html=False, newline=True):
2030 """ Writes a new prompt at the end of the buffer.
2033 """ Writes a new prompt at the end of the buffer.
2031
2034
2032 Parameters
2035 Parameters
2033 ----------
2036 ----------
2034 prompt : str, optional
2037 prompt : str, optional
2035 The prompt to show. If not specified, the previous prompt is used.
2038 The prompt to show. If not specified, the previous prompt is used.
2036
2039
2037 html : bool, optional (default False)
2040 html : bool, optional (default False)
2038 Only relevant when a prompt is specified. If set, the prompt will
2041 Only relevant when a prompt is specified. If set, the prompt will
2039 be inserted as formatted HTML. Otherwise, the prompt will be treated
2042 be inserted as formatted HTML. Otherwise, the prompt will be treated
2040 as plain text, though ANSI color codes will be handled.
2043 as plain text, though ANSI color codes will be handled.
2041
2044
2042 newline : bool, optional (default True)
2045 newline : bool, optional (default True)
2043 If set, a new line will be written before showing the prompt if
2046 If set, a new line will be written before showing the prompt if
2044 there is not already a newline at the end of the buffer.
2047 there is not already a newline at the end of the buffer.
2045 """
2048 """
2046 # Save the current end position to support _append*(before_prompt=True).
2049 # Save the current end position to support _append*(before_prompt=True).
2047 cursor = self._get_end_cursor()
2050 cursor = self._get_end_cursor()
2048 self._append_before_prompt_pos = cursor.position()
2051 self._append_before_prompt_pos = cursor.position()
2049
2052
2050 # Insert a preliminary newline, if necessary.
2053 # Insert a preliminary newline, if necessary.
2051 if newline and cursor.position() > 0:
2054 if newline and cursor.position() > 0:
2052 cursor.movePosition(QtGui.QTextCursor.Left,
2055 cursor.movePosition(QtGui.QTextCursor.Left,
2053 QtGui.QTextCursor.KeepAnchor)
2056 QtGui.QTextCursor.KeepAnchor)
2054 if cursor.selection().toPlainText() != '\n':
2057 if cursor.selection().toPlainText() != '\n':
2055 self._append_block()
2058 self._append_block()
2056
2059
2057 # Write the prompt.
2060 # Write the prompt.
2058 self._append_plain_text(self._prompt_sep)
2061 self._append_plain_text(self._prompt_sep)
2059 if prompt is None:
2062 if prompt is None:
2060 if self._prompt_html is None:
2063 if self._prompt_html is None:
2061 self._append_plain_text(self._prompt)
2064 self._append_plain_text(self._prompt)
2062 else:
2065 else:
2063 self._append_html(self._prompt_html)
2066 self._append_html(self._prompt_html)
2064 else:
2067 else:
2065 if html:
2068 if html:
2066 self._prompt = self._append_html_fetching_plain_text(prompt)
2069 self._prompt = self._append_html_fetching_plain_text(prompt)
2067 self._prompt_html = prompt
2070 self._prompt_html = prompt
2068 else:
2071 else:
2069 self._append_plain_text(prompt)
2072 self._append_plain_text(prompt)
2070 self._prompt = prompt
2073 self._prompt = prompt
2071 self._prompt_html = None
2074 self._prompt_html = None
2072
2075
2073 self._flush_pending_stream()
2076 self._flush_pending_stream()
2074 self._prompt_pos = self._get_end_cursor().position()
2077 self._prompt_pos = self._get_end_cursor().position()
2075 self._prompt_started()
2078 self._prompt_started()
2076
2079
2077 #------ Signal handlers ----------------------------------------------------
2080 #------ Signal handlers ----------------------------------------------------
2078
2081
2079 def _adjust_scrollbars(self):
2082 def _adjust_scrollbars(self):
2080 """ Expands the vertical scrollbar beyond the range set by Qt.
2083 """ Expands the vertical scrollbar beyond the range set by Qt.
2081 """
2084 """
2082 # This code is adapted from _q_adjustScrollbars in qplaintextedit.cpp
2085 # This code is adapted from _q_adjustScrollbars in qplaintextedit.cpp
2083 # and qtextedit.cpp.
2086 # and qtextedit.cpp.
2084 document = self._control.document()
2087 document = self._control.document()
2085 scrollbar = self._control.verticalScrollBar()
2088 scrollbar = self._control.verticalScrollBar()
2086 viewport_height = self._control.viewport().height()
2089 viewport_height = self._control.viewport().height()
2087 if isinstance(self._control, QtGui.QPlainTextEdit):
2090 if isinstance(self._control, QtGui.QPlainTextEdit):
2088 maximum = max(0, document.lineCount() - 1)
2091 maximum = max(0, document.lineCount() - 1)
2089 step = viewport_height / self._control.fontMetrics().lineSpacing()
2092 step = viewport_height / self._control.fontMetrics().lineSpacing()
2090 else:
2093 else:
2091 # QTextEdit does not do line-based layout and blocks will not in
2094 # QTextEdit does not do line-based layout and blocks will not in
2092 # general have the same height. Therefore it does not make sense to
2095 # general have the same height. Therefore it does not make sense to
2093 # attempt to scroll in line height increments.
2096 # attempt to scroll in line height increments.
2094 maximum = document.size().height()
2097 maximum = document.size().height()
2095 step = viewport_height
2098 step = viewport_height
2096 diff = maximum - scrollbar.maximum()
2099 diff = maximum - scrollbar.maximum()
2097 scrollbar.setRange(0, maximum)
2100 scrollbar.setRange(0, maximum)
2098 scrollbar.setPageStep(step)
2101 scrollbar.setPageStep(step)
2099
2102
2100 # Compensate for undesirable scrolling that occurs automatically due to
2103 # Compensate for undesirable scrolling that occurs automatically due to
2101 # maximumBlockCount() text truncation.
2104 # maximumBlockCount() text truncation.
2102 if diff < 0 and document.blockCount() == document.maximumBlockCount():
2105 if diff < 0 and document.blockCount() == document.maximumBlockCount():
2103 scrollbar.setValue(scrollbar.value() + diff)
2106 scrollbar.setValue(scrollbar.value() + diff)
2104
2107
2105 def _custom_context_menu_requested(self, pos):
2108 def _custom_context_menu_requested(self, pos):
2106 """ Shows a context menu at the given QPoint (in widget coordinates).
2109 """ Shows a context menu at the given QPoint (in widget coordinates).
2107 """
2110 """
2108 menu = self._context_menu_make(pos)
2111 menu = self._context_menu_make(pos)
2109 menu.exec_(self._control.mapToGlobal(pos))
2112 menu.exec_(self._control.mapToGlobal(pos))
@@ -1,144 +1,151
1 # Makefile for Sphinx documentation
1 # Makefile for Sphinx documentation
2 #
2 #
3
3
4 # You can set these variables from the command line.
4 # You can set these variables from the command line.
5 SPHINXOPTS =
5 SPHINXOPTS =
6 SPHINXBUILD = sphinx-build
6 SPHINXBUILD = sphinx-build
7 PAPER =
7 PAPER =
8 SRCDIR = source
8 SRCDIR = source
9 BUILDDIR = build
9 BUILDDIR = build
10
10
11 # Internal variables.
11 # Internal variables.
12 PAPEROPT_a4 = -D latex_paper_size=a4
12 PAPEROPT_a4 = -D latex_paper_size=a4
13 PAPEROPT_letter = -D latex_paper_size=letter
13 PAPEROPT_letter = -D latex_paper_size=letter
14 ALLSPHINXOPTS = -d build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) $(SRCDIR)
14 ALLSPHINXOPTS = -d build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) $(SRCDIR)
15
15
16 .PHONY: help clean html web pickle htmlhelp latex changes linkcheck api
16 .PHONY: help clean html web pickle htmlhelp latex changes linkcheck api
17
17
18 default: html
18 default: html
19
19
20 help:
20 help:
21 @echo "Please use \`make <target>' where <target> is one of"
21 @echo "Please use \`make <target>' where <target> is one of"
22 @echo " html standalone HTML files"
22 @echo " html standalone HTML files"
23 @echo " html_noapi same as above, without the time consuming API docs"
23 @echo " html_noapi same as above, without the time consuming API docs"
24 @echo " pickle pickle files (usable by e.g. sphinx-web)"
24 @echo " pickle pickle files (usable by e.g. sphinx-web)"
25 @echo " htmlhelp HTML files and a HTML help project"
25 @echo " htmlhelp HTML files and a HTML help project"
26 @echo " latex LaTeX files, you can set PAPER=a4 or PAPER=letter"
26 @echo " latex LaTeX files, you can set PAPER=a4 or PAPER=letter"
27 @echo " texinfo Texinfo files"
27 @echo " texinfo Texinfo files"
28 @echo " info Texinfo files and run them through makeinfo"
28 @echo " info Texinfo files and run them through makeinfo"
29 @echo " changes an overview over all changed/added/deprecated items"
29 @echo " changes an overview over all changed/added/deprecated items"
30 @echo " linkcheck check all external links for integrity (takes a long time)"
30 @echo " linkcheck check all external links for integrity (takes a long time)"
31 @echo
31 @echo
32 @echo "Compound utility targets:"
32 @echo "Compound utility targets:"
33 @echo "pdf latex and then runs the PDF generation"
33 @echo "pdf latex and then runs the PDF generation"
34 @echo "all html and pdf"
34 @echo "all html and pdf"
35 @echo "dist all, and then puts the results in dist/"
35 @echo "dist all, and then puts the results in dist/"
36 @echo "gitwash-update update git workflow from source repo"
36 @echo "gitwash-update update git workflow from source repo"
37
37
38 clean_api:
38 clean_api:
39 -rm -rf $(SRCDIR)/api/generated
39 -rm -rf $(SRCDIR)/api/generated
40
40
41 clean: clean_api
41 clean: clean_api
42 -rm -rf build/* dist/*
42 -rm -rf build/* dist/*
43 -rm -rf $(SRCDIR)/config/options/generated
43
44
44 pdf: latex
45 pdf: latex
45 cd build/latex && make all-pdf
46 cd build/latex && make all-pdf
46
47
47 all: html pdf
48 all: html pdf
48
49
49 # For final distribution, only build HTML (our pdf is now so large as to be
50 # For final distribution, only build HTML (our pdf is now so large as to be
50 # unusable, takes forever to build and just bloats the downloads). We leave
51 # unusable, takes forever to build and just bloats the downloads). We leave
51 # them hardlinked at the top-level so users find them easily, though the
52 # them hardlinked at the top-level so users find them easily, though the
52 # original build/html dir is left in-place (useful to reload builds while
53 # original build/html dir is left in-place (useful to reload builds while
53 # testing).
54 # testing).
54 dist: html
55 dist: html
55 rm -rf html
56 rm -rf html
56 cp -al build/html .
57 cp -al build/html .
57 @echo "Build finished. Final docs are in html/"
58 @echo "Build finished. Final docs are in html/"
58
59
59 html: api
60 html: api autoconfig
60 html_noapi: clean_api
61 html_noapi: clean_api autoconfig
61
62
62 html html_noapi:
63 html html_noapi:
63 mkdir -p build/html build/doctrees
64 mkdir -p build/html build/doctrees
64 $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) build/html
65 $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) build/html
65 @echo
66 @echo
66 @echo "Build finished. The HTML pages are in build/html."
67 @echo "Build finished. The HTML pages are in build/html."
67
68
69 autoconfig: source/config/options/generated
70
71 source/config/options/generated:
72 python autogen_config.py
73 @echo "Created docs for config options"
74
68 api: source/api/generated/gen.txt
75 api: source/api/generated/gen.txt
69
76
70 source/api/generated/gen.txt:
77 source/api/generated/gen.txt:
71 python autogen_api.py
78 python autogen_api.py
72 @echo "Build API docs finished."
79 @echo "Build API docs finished."
73
80
74 pickle:
81 pickle:
75 mkdir -p build/pickle build/doctrees
82 mkdir -p build/pickle build/doctrees
76 $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) build/pickle
83 $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) build/pickle
77 @echo
84 @echo
78 @echo "Build finished; now you can process the pickle files or run"
85 @echo "Build finished; now you can process the pickle files or run"
79 @echo " sphinx-web build/pickle"
86 @echo " sphinx-web build/pickle"
80 @echo "to start the sphinx-web server."
87 @echo "to start the sphinx-web server."
81
88
82 web: pickle
89 web: pickle
83
90
84 htmlhelp:
91 htmlhelp:
85 mkdir -p build/htmlhelp build/doctrees
92 mkdir -p build/htmlhelp build/doctrees
86 $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) build/htmlhelp
93 $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) build/htmlhelp
87 @echo
94 @echo
88 @echo "Build finished; now you can run HTML Help Workshop with the" \
95 @echo "Build finished; now you can run HTML Help Workshop with the" \
89 ".hhp project file in build/htmlhelp."
96 ".hhp project file in build/htmlhelp."
90
97
91 qthelp:
98 qthelp:
92 mkdir -p build/qthelp
99 mkdir -p build/qthelp
93 $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) build/qthelp
100 $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) build/qthelp
94 @echo
101 @echo
95 @echo "Build finished; now you can run "qcollectiongenerator" with the" \
102 @echo "Build finished; now you can run "qcollectiongenerator" with the" \
96 ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
103 ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
97 @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/IPython.qhcp"
104 @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/IPython.qhcp"
98 @echo "To view the help file:"
105 @echo "To view the help file:"
99 @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/IPython.qhc"
106 @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/IPython.qhc"
100
107
101 latex: api
108 latex: api autoconfig
102 mkdir -p build/latex build/doctrees
109 mkdir -p build/latex build/doctrees
103 $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) build/latex
110 $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) build/latex
104 @echo
111 @echo
105 @echo "Build finished; the LaTeX files are in build/latex."
112 @echo "Build finished; the LaTeX files are in build/latex."
106 @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
113 @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
107 "run these through (pdf)latex."
114 "run these through (pdf)latex."
108
115
109 changes:
116 changes:
110 mkdir -p build/changes build/doctrees
117 mkdir -p build/changes build/doctrees
111 $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) build/changes
118 $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) build/changes
112 @echo
119 @echo
113 @echo "The overview file is in build/changes."
120 @echo "The overview file is in build/changes."
114
121
115 linkcheck:
122 linkcheck:
116 mkdir -p build/linkcheck build/doctrees
123 mkdir -p build/linkcheck build/doctrees
117 $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) build/linkcheck
124 $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) build/linkcheck
118 @echo
125 @echo
119 @echo "Link check complete; look for any errors in the above output " \
126 @echo "Link check complete; look for any errors in the above output " \
120 "or in build/linkcheck/output.rst."
127 "or in build/linkcheck/output.rst."
121
128
122 gitwash-update:
129 gitwash-update:
123 python ../tools/gitwash_dumper.py source/development ipython
130 python ../tools/gitwash_dumper.py source/development ipython
124
131
125 nightly: dist
132 nightly: dist
126 rsync -avH --delete dist/ ipython:www/doc/nightly
133 rsync -avH --delete dist/ ipython:www/doc/nightly
127
134
128 gh-pages: clean html
135 gh-pages: clean html
129 python gh-pages.py
136 python gh-pages.py
130
137
131 texinfo:
138 texinfo:
132 mkdir -p $(BUILDDIR)/texinfo
139 mkdir -p $(BUILDDIR)/texinfo
133 $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
140 $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
134 @echo
141 @echo
135 @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
142 @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
136 @echo "Run \`make' in that directory to run these through makeinfo" \
143 @echo "Run \`make' in that directory to run these through makeinfo" \
137 "(use \`make info' here to do that automatically)."
144 "(use \`make info' here to do that automatically)."
138
145
139 info:
146 info:
140 mkdir -p $(BUILDDIR)/texinfo
147 mkdir -p $(BUILDDIR)/texinfo
141 $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
148 $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
142 @echo "Running Texinfo files through makeinfo..."
149 @echo "Running Texinfo files through makeinfo..."
143 make -C $(BUILDDIR)/texinfo info
150 make -C $(BUILDDIR)/texinfo info
144 @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
151 @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
@@ -1,510 +1,521
1 /**
1 /**
2 * Alternate Sphinx design
2 * Alternate Sphinx design
3 * Originally created by Armin Ronacher for Werkzeug, adapted by Georg Brandl.
3 * Originally created by Armin Ronacher for Werkzeug, adapted by Georg Brandl.
4 */
4 */
5
5
6 body {
6 body {
7 font-family: 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva', 'Verdana', sans-serif;
7 font-family: 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva', 'Verdana', sans-serif;
8 font-size: 14px;
8 font-size: 14px;
9 letter-spacing: -0.01em;
9 letter-spacing: -0.01em;
10 line-height: 150%;
10 line-height: 150%;
11 text-align: center;
11 text-align: center;
12 /*background-color: #AFC1C4; */
12 /*background-color: #AFC1C4; */
13 background-color: #BFD1D4;
13 background-color: #BFD1D4;
14 color: black;
14 color: black;
15 padding: 0;
15 padding: 0;
16 border: 1px solid #aaa;
16 border: 1px solid #aaa;
17
17
18 margin: 0px 80px 0px 80px;
18 margin: 0px 80px 0px 80px;
19 min-width: 740px;
19 min-width: 740px;
20 }
20 }
21
21
22 a {
22 a {
23 color: #CA7900;
23 color: #CA7900;
24 text-decoration: none;
24 text-decoration: none;
25 }
25 }
26
26
27 a:hover {
27 a:hover {
28 color: #2491CF;
28 color: #2491CF;
29 }
29 }
30
30
31 pre {
31 pre {
32 font-family: 'Consolas', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
32 font-family: 'Consolas', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
33 font-size: 0.95em;
33 font-size: 0.95em;
34 letter-spacing: 0.015em;
34 letter-spacing: 0.015em;
35 padding: 0.5em;
35 padding: 0.5em;
36 border: 1px solid #ccc;
36 border: 1px solid #ccc;
37 background-color: #f8f8f8;
37 background-color: #f8f8f8;
38 }
38 }
39
39
40 td.linenos pre {
40 td.linenos pre {
41 padding: 0.5em 0;
41 padding: 0.5em 0;
42 border: 0;
42 border: 0;
43 background-color: transparent;
43 background-color: transparent;
44 color: #aaa;
44 color: #aaa;
45 }
45 }
46
46
47 table.highlighttable {
47 table.highlighttable {
48 margin-left: 0.5em;
48 margin-left: 0.5em;
49 }
49 }
50
50
51 table.highlighttable td {
51 table.highlighttable td {
52 padding: 0 0.5em 0 0.5em;
52 padding: 0 0.5em 0 0.5em;
53 }
53 }
54
54
55 cite, code, tt {
55 cite, code, tt {
56 font-family: 'Consolas', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
56 font-family: 'Consolas', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
57 font-size: 0.95em;
57 font-size: 0.95em;
58 letter-spacing: 0.01em;
58 letter-spacing: 0.01em;
59 }
59 }
60
60
61 hr {
61 hr {
62 border: 1px solid #abc;
62 border: 1px solid #abc;
63 margin: 2em;
63 margin: 2em;
64 }
64 }
65
65
66 tt {
66 tt {
67 background-color: #f2f2f2;
67 background-color: #f2f2f2;
68 border-bottom: 1px solid #ddd;
68 border-bottom: 1px solid #ddd;
69 color: #333;
69 color: #333;
70 }
70 }
71
71
72 tt.descname {
72 tt.descname {
73 background-color: transparent;
73 background-color: transparent;
74 font-weight: bold;
74 font-weight: bold;
75 font-size: 1.2em;
75 font-size: 1.2em;
76 border: 0;
76 border: 0;
77 }
77 }
78
78
79 tt.descclassname {
79 tt.descclassname {
80 background-color: transparent;
80 background-color: transparent;
81 border: 0;
81 border: 0;
82 }
82 }
83
83
84 tt.xref {
84 tt.xref {
85 background-color: transparent;
85 background-color: transparent;
86 font-weight: bold;
86 font-weight: bold;
87 border: 0;
87 border: 0;
88 }
88 }
89
89
90 a tt {
90 a tt {
91 background-color: transparent;
91 background-color: transparent;
92 font-weight: bold;
92 font-weight: bold;
93 border: 0;
93 border: 0;
94 color: #CA7900;
94 color: #CA7900;
95 }
95 }
96
96
97 a tt:hover {
97 a tt:hover {
98 color: #2491CF;
98 color: #2491CF;
99 }
99 }
100
100
101 dl {
101 dl {
102 margin-bottom: 15px;
102 margin-bottom: 15px;
103 }
103 }
104
104
105 dd p {
105 dd p {
106 margin-top: 0px;
106 margin-top: 0px;
107 }
107 }
108
108
109 dd ul, dd table {
109 dd ul, dd table {
110 margin-bottom: 10px;
110 margin-bottom: 10px;
111 }
111 }
112
112
113 dd {
113 dd {
114 margin-top: 3px;
114 margin-top: 3px;
115 margin-bottom: 10px;
115 margin-bottom: 10px;
116 margin-left: 30px;
116 margin-left: 30px;
117 }
117 }
118
118
119 .refcount {
119 .refcount {
120 color: #060;
120 color: #060;
121 }
121 }
122
122
123 dt {
124 font-weight: bold;
125 padding-left: 0.5em;
126 }
127
123 dt:target,
128 dt:target,
124 .highlight {
129 .highlight {
125 background-color: #fbe54e;
130 background-color: #fbe54e;
126 }
131 }
127
132
128 dl.class, dl.function {
133 dl.class, dl.function {
129 border-top: 2px solid #888;
134 border-top: 2px solid #888;
130 }
135 }
131
136
132 dl.method, dl.attribute {
137 dl.method, dl.attribute {
133 border-top: 1px solid #aaa;
138 border-top: 1px solid #aaa;
134 }
139 }
135
140
136 dl.glossary dt {
141 dl.glossary dt {
137 font-weight: bold;
142 font-weight: bold;
138 font-size: 1.1em;
143 font-size: 1.1em;
139 }
144 }
140
145
141 pre {
146 pre {
142 line-height: 120%;
147 line-height: 120%;
143 }
148 }
144
149
145 pre a {
150 pre a {
146 color: inherit;
151 color: inherit;
147 text-decoration: underline;
152 text-decoration: underline;
148 }
153 }
149
154
150 .first {
155 .first {
151 margin-top: 0 !important;
156 margin-top: 0 !important;
152 }
157 }
153
158
154 div.document {
159 div.document {
155 background-color: white;
160 background-color: white;
156 text-align: left;
161 text-align: left;
157 background-image: url(contents.png);
162 background-image: url(contents.png);
158 background-repeat: repeat-x;
163 background-repeat: repeat-x;
159 }
164 }
160
165
161 /*
166 /*
162 div.documentwrapper {
167 div.documentwrapper {
163 width: 100%;
168 width: 100%;
164 }
169 }
165 */
170 */
166
171
167 div.clearer {
172 div.clearer {
168 clear: both;
173 clear: both;
169 }
174 }
170
175
171 div.related h3 {
176 div.related h3 {
172 display: none;
177 display: none;
173 }
178 }
174
179
175 div.related ul {
180 div.related ul {
176 background-image: url(navigation.png);
181 background-image: url(navigation.png);
177 height: 2em;
182 height: 2em;
178 list-style: none;
183 list-style: none;
179 border-top: 1px solid #ddd;
184 border-top: 1px solid #ddd;
180 border-bottom: 1px solid #ddd;
185 border-bottom: 1px solid #ddd;
181 margin: 0;
186 margin: 0;
182 padding-left: 10px;
187 padding-left: 10px;
183 }
188 }
184
189
185 div.related ul li {
190 div.related ul li {
186 margin: 0;
191 margin: 0;
187 padding: 0;
192 padding: 0;
188 height: 2em;
193 height: 2em;
189 float: left;
194 float: left;
190 }
195 }
191
196
192 div.related ul li.right {
197 div.related ul li.right {
193 float: right;
198 float: right;
194 margin-right: 5px;
199 margin-right: 5px;
195 }
200 }
196
201
197 div.related ul li a {
202 div.related ul li a {
198 margin: 0;
203 margin: 0;
199 padding: 0 5px 0 5px;
204 padding: 0 5px 0 5px;
200 line-height: 1.75em;
205 line-height: 1.75em;
201 color: #EE9816;
206 color: #EE9816;
202 }
207 }
203
208
204 div.related ul li a:hover {
209 div.related ul li a:hover {
205 color: #3CA8E7;
210 color: #3CA8E7;
206 }
211 }
207
212
208 div.body {
213 div.body {
209 margin: 0;
214 margin: 0;
210 padding: 0.5em 20px 20px 20px;
215 padding: 0.5em 20px 20px 20px;
211 }
216 }
212
217
213 div.bodywrapper {
218 div.bodywrapper {
214 margin: 0 240px 0 0;
219 margin: 0 240px 0 0;
215 border-right: 1px solid #ccc;
220 border-right: 1px solid #ccc;
216 }
221 }
217
222
218 div.body a {
223 div.body a {
219 text-decoration: underline;
224 text-decoration: underline;
220 }
225 }
221
226
222 div.sphinxsidebar {
227 div.sphinxsidebar {
223 margin: 0;
228 margin: 0;
224 padding: 0.5em 15px 15px 0;
229 padding: 0.5em 15px 15px 0;
225 width: 210px;
230 width: 210px;
226 float: right;
231 float: right;
227 text-align: left;
232 text-align: left;
228 /* margin-left: -100%; */
233 /* margin-left: -100%; */
229 }
234 }
230
235
231 div.sphinxsidebar h4, div.sphinxsidebar h3 {
236 div.sphinxsidebar h4, div.sphinxsidebar h3 {
232 margin: 1em 0 0.5em 0;
237 margin: 1em 0 0.5em 0;
233 font-size: 0.9em;
238 font-size: 0.9em;
234 padding: 0.1em 0 0.1em 0.5em;
239 padding: 0.1em 0 0.1em 0.5em;
235 color: white;
240 color: white;
236 border: 1px solid #86989B;
241 border: 1px solid #86989B;
237 background-color: #AFC1C4;
242 background-color: #AFC1C4;
238 }
243 }
239
244
240 div.sphinxsidebar ul {
245 div.sphinxsidebar ul {
241 padding-left: 1.5em;
246 padding-left: 1.5em;
242 margin-top: 7px;
247 margin-top: 7px;
243 list-style: none;
248 list-style: none;
244 padding: 0;
249 padding: 0;
245 line-height: 130%;
250 line-height: 130%;
246 }
251 }
247
252
248 div.sphinxsidebar ul ul {
253 div.sphinxsidebar ul ul {
249 list-style: square;
254 list-style: square;
250 margin-left: 20px;
255 margin-left: 20px;
251 }
256 }
252
257
253 p {
258 p {
254 margin: 0.8em 0 0.5em 0;
259 margin: 0.8em 0 0.5em 0;
255 }
260 }
256
261
257 p.rubric {
262 p.rubric {
258 font-weight: bold;
263 font-weight: bold;
259 }
264 }
260
265
261 h1 {
266 h1 {
262 margin: 0;
267 margin: 0;
263 padding: 0.7em 0 0.3em 0;
268 padding: 0.7em 0 0.3em 0;
264 font-size: 1.5em;
269 font-size: 1.5em;
265 color: #11557C;
270 color: #11557C;
266 }
271 }
267
272
268 h2 {
273 h2 {
269 margin: 1.3em 0 0.2em 0;
274 margin: 1.3em 0 0.2em 0;
270 font-size: 1.35em;
275 font-size: 1.35em;
271 padding: 0;
276 padding: 0;
272 }
277 }
273
278
274 h3 {
279 h3 {
275 margin: 1em 0 -0.3em 0;
280 margin: 1em 0 -0.3em 0;
276 font-size: 1.2em;
281 font-size: 1.2em;
277 }
282 }
278
283
279 h1 a, h2 a, h3 a, h4 a, h5 a, h6 a {
284 h1 a, h2 a, h3 a, h4 a, h5 a, h6 a {
280 color: black!important;
285 color: black!important;
281 }
286 }
282
287
283 h1 a.anchor, h2 a.anchor, h3 a.anchor, h4 a.anchor, h5 a.anchor, h6 a.anchor {
288 h1 a.anchor, h2 a.anchor, h3 a.anchor, h4 a.anchor, h5 a.anchor, h6 a.anchor {
284 display: none;
289 display: none;
285 margin: 0 0 0 0.3em;
290 margin: 0 0 0 0.3em;
286 padding: 0 0.2em 0 0.2em;
291 padding: 0 0.2em 0 0.2em;
287 color: #aaa!important;
292 color: #aaa!important;
288 }
293 }
289
294
290 h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor,
295 h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor,
291 h5:hover a.anchor, h6:hover a.anchor {
296 h5:hover a.anchor, h6:hover a.anchor {
292 display: inline;
297 display: inline;
293 }
298 }
294
299
295 h1 a.anchor:hover, h2 a.anchor:hover, h3 a.anchor:hover, h4 a.anchor:hover,
300 h1 a.anchor:hover, h2 a.anchor:hover, h3 a.anchor:hover, h4 a.anchor:hover,
296 h5 a.anchor:hover, h6 a.anchor:hover {
301 h5 a.anchor:hover, h6 a.anchor:hover {
297 color: #777;
302 color: #777;
298 background-color: #eee;
303 background-color: #eee;
299 }
304 }
300
305
301 table {
306 table {
302 border-collapse: collapse;
307 border-collapse: collapse;
303 margin: 0 -0.5em 0 -0.5em;
308 margin: 0 -0.5em 0 -0.5em;
304 }
309 }
305
310
306 table td, table th {
311 table td, table th {
307 padding: 0.2em 0.5em 0.2em 0.5em;
312 padding: 0.2em 0.5em 0.2em 0.5em;
308 }
313 }
309
314
310 div.footer {
315 div.footer {
311 background-color: #E3EFF1;
316 background-color: #E3EFF1;
312 color: #86989B;
317 color: #86989B;
313 padding: 3px 8px 3px 0;
318 padding: 3px 8px 3px 0;
314 clear: both;
319 clear: both;
315 font-size: 0.8em;
320 font-size: 0.8em;
316 text-align: right;
321 text-align: right;
317 }
322 }
318
323
319 div.footer a {
324 div.footer a {
320 color: #86989B;
325 color: #86989B;
321 text-decoration: underline;
326 text-decoration: underline;
322 }
327 }
323
328
324 div.pagination {
329 div.pagination {
325 margin-top: 2em;
330 margin-top: 2em;
326 padding-top: 0.5em;
331 padding-top: 0.5em;
327 border-top: 1px solid black;
332 border-top: 1px solid black;
328 text-align: center;
333 text-align: center;
329 }
334 }
330
335
331 div.sphinxsidebar ul.toc {
336 div.sphinxsidebar ul.toc {
332 margin: 1em 0 1em 0;
337 margin: 1em 0 1em 0;
333 padding: 0 0 0 0.5em;
338 padding: 0 0 0 0.5em;
334 list-style: none;
339 list-style: none;
335 }
340 }
336
341
337 div.sphinxsidebar ul.toc li {
342 div.sphinxsidebar ul.toc li {
338 margin: 0.5em 0 0.5em 0;
343 margin: 0.5em 0 0.5em 0;
339 font-size: 0.9em;
344 font-size: 0.9em;
340 line-height: 130%;
345 line-height: 130%;
341 }
346 }
342
347
343 div.sphinxsidebar ul.toc li p {
348 div.sphinxsidebar ul.toc li p {
344 margin: 0;
349 margin: 0;
345 padding: 0;
350 padding: 0;
346 }
351 }
347
352
348 div.sphinxsidebar ul.toc ul {
353 div.sphinxsidebar ul.toc ul {
349 margin: 0.2em 0 0.2em 0;
354 margin: 0.2em 0 0.2em 0;
350 padding: 0 0 0 1.8em;
355 padding: 0 0 0 1.8em;
351 }
356 }
352
357
353 div.sphinxsidebar ul.toc ul li {
358 div.sphinxsidebar ul.toc ul li {
354 padding: 0;
359 padding: 0;
355 }
360 }
356
361
357 div.admonition, div.warning {
362 div.admonition, div.warning {
358 font-size: 0.9em;
363 font-size: 0.9em;
359 margin: 1em 0 0 0;
364 margin: 1em 0 0 0;
360 border: 1px solid #86989B;
365 border: 1px solid #86989B;
361 background-color: #f7f7f7;
366 background-color: #f7f7f7;
362 }
367 }
363
368
364 div.admonition p, div.warning p {
369 div.admonition p, div.warning p {
365 margin: 0.5em 1em 0.5em 1em;
370 margin: 0.5em 1em 0.5em 1em;
366 padding: 0;
371 padding: 0;
367 }
372 }
368
373
369 div.admonition pre, div.warning pre {
374 div.admonition pre, div.warning pre {
370 margin: 0.4em 1em 0.4em 1em;
375 margin: 0.4em 1em 0.4em 1em;
371 }
376 }
372
377
373 div.admonition p.admonition-title,
378 div.admonition p.admonition-title,
374 div.warning p.admonition-title {
379 div.warning p.admonition-title {
375 margin: 0;
380 margin: 0;
376 padding: 0.1em 0 0.1em 0.5em;
381 padding: 0.1em 0 0.1em 0.5em;
377 color: white;
382 color: white;
378 border-bottom: 1px solid #86989B;
383 border-bottom: 1px solid #86989B;
379 font-weight: bold;
384 font-weight: bold;
380 background-color: #AFC1C4;
385 background-color: #AFC1C4;
381 }
386 }
382
387
383 div.warning {
388 div.warning {
384 border: 1px solid #940000;
389 border: 1px solid #940000;
385 }
390 }
386
391
387 div.warning p.admonition-title {
392 div.warning p.admonition-title {
388 background-color: #CF0000;
393 background-color: #CF0000;
389 border-bottom-color: #940000;
394 border-bottom-color: #940000;
390 }
395 }
391
396
392 div.admonition ul, div.admonition ol,
397 div.admonition ul, div.admonition ol,
393 div.warning ul, div.warning ol {
398 div.warning ul, div.warning ol {
394 margin: 0.1em 0.5em 0.5em 3em;
399 margin: 0.1em 0.5em 0.5em 3em;
395 padding: 0;
400 padding: 0;
396 }
401 }
397
402
398 div.versioninfo {
403 div.versioninfo {
399 margin: 1em 0 0 0;
404 margin: 1em 0 0 0;
400 border: 1px solid #ccc;
405 border: 1px solid #ccc;
401 background-color: #DDEAF0;
406 background-color: #DDEAF0;
402 padding: 8px;
407 padding: 8px;
403 line-height: 1.3em;
408 line-height: 1.3em;
404 font-size: 0.9em;
409 font-size: 0.9em;
405 }
410 }
406
411
407
412
408 a.headerlink {
413 a.headerlink {
409 color: #c60f0f!important;
414 color: #c60f0f!important;
410 font-size: 1em;
415 font-size: 1em;
411 margin-left: 6px;
416 margin-left: 6px;
412 padding: 0 4px 0 4px;
417 padding: 0 4px 0 4px;
413 text-decoration: none!important;
418 text-decoration: none!important;
414 visibility: hidden;
419 visibility: hidden;
415 }
420 }
416
421
417 h1:hover > a.headerlink,
422 h1:hover > a.headerlink,
418 h2:hover > a.headerlink,
423 h2:hover > a.headerlink,
419 h3:hover > a.headerlink,
424 h3:hover > a.headerlink,
420 h4:hover > a.headerlink,
425 h4:hover > a.headerlink,
421 h5:hover > a.headerlink,
426 h5:hover > a.headerlink,
422 h6:hover > a.headerlink,
427 h6:hover > a.headerlink,
423 dt:hover > a.headerlink {
428 dt:hover > a.headerlink {
424 visibility: visible;
429 visibility: visible;
425 }
430 }
426
431
427 a.headerlink:hover {
432 a.headerlink:hover {
428 background-color: #ccc;
433 background-color: #ccc;
429 color: white!important;
434 color: white!important;
430 }
435 }
431
436
432 table.indextable td {
437 table.indextable td {
433 text-align: left;
438 text-align: left;
434 vertical-align: top;
439 vertical-align: top;
435 }
440 }
436
441
437 table.indextable dl, table.indextable dd {
442 table.indextable dl, table.indextable dd {
438 margin-top: 0;
443 margin-top: 0;
439 margin-bottom: 0;
444 margin-bottom: 0;
440 }
445 }
441
446
442 table.indextable tr.pcap {
447 table.indextable tr.pcap {
443 height: 10px;
448 height: 10px;
444 }
449 }
445
450
446 table.indextable tr.cap {
451 table.indextable tr.cap {
447 margin-top: 10px;
452 margin-top: 10px;
448 background-color: #f2f2f2;
453 background-color: #f2f2f2;
449 }
454 }
450
455
451 img.toggler {
456 img.toggler {
452 margin-right: 3px;
457 margin-right: 3px;
453 margin-top: 3px;
458 margin-top: 3px;
454 cursor: pointer;
459 cursor: pointer;
455 }
460 }
456
461
457 img.inheritance {
462 img.inheritance {
458 border: 0px
463 border: 0px
459 }
464 }
460
465
461 form.pfform {
466 form.pfform {
462 margin: 10px 0 20px 0;
467 margin: 10px 0 20px 0;
463 }
468 }
464
469
465 table.contentstable {
470 table.contentstable {
466 width: 90%;
471 width: 90%;
467 }
472 }
468
473
469 table.contentstable p.biglink {
474 table.contentstable p.biglink {
470 line-height: 150%;
475 line-height: 150%;
471 }
476 }
472
477
473 a.biglink {
478 a.biglink {
474 font-size: 1.3em;
479 font-size: 1.3em;
475 }
480 }
476
481
477 span.linkdescr {
482 span.linkdescr {
478 font-style: italic;
483 font-style: italic;
479 padding-top: 5px;
484 padding-top: 5px;
480 font-size: 90%;
485 font-size: 90%;
481 }
486 }
482
487
488 .search input[name=q] {
489 max-width: 100%;
490 box-sizing: border-box;
491 -moz-box-sizing: border-box;
492 }
493
483 ul.search {
494 ul.search {
484 margin: 10px 0 0 20px;
495 margin: 10px 0 0 20px;
485 padding: 0;
496 padding: 0;
486 }
497 }
487
498
488 ul.search li {
499 ul.search li {
489 padding: 5px 0 5px 20px;
500 padding: 5px 0 5px 20px;
490 background-image: url(file.png);
501 background-image: url(file.png);
491 background-repeat: no-repeat;
502 background-repeat: no-repeat;
492 background-position: 0 7px;
503 background-position: 0 7px;
493 }
504 }
494
505
495 ul.search li a {
506 ul.search li a {
496 font-weight: bold;
507 font-weight: bold;
497 }
508 }
498
509
499 ul.search li div.context {
510 ul.search li div.context {
500 color: #888;
511 color: #888;
501 margin: 2px 0 0 30px;
512 margin: 2px 0 0 30px;
502 text-align: left;
513 text-align: left;
503 }
514 }
504
515
505 ul.keywordmatches li.goodmatch a {
516 ul.keywordmatches li.goodmatch a {
506 font-weight: bold;
517 font-weight: bold;
507 }
518 }
508 div.figure {
519 div.figure {
509 text-align: center;
520 text-align: center;
510 }
521 }
@@ -1,16 +1,30
1 .. _config_index:
1 .. _config_index:
2
2
3 ===============================
3 ===============================
4 Configuration and customization
4 Configuration and customization
5 ===============================
5 ===============================
6
6
7 Configuring IPython
8 -------------------
9
10 .. toctree::
11 :maxdepth: 2
12
13 intro
14 options/index
15 details
16
17 .. seealso::
18
19 :doc:`/development/config`
20 Technical details of the config system.
21
22 Extending and integrating with IPython
23 --------------------------------------
24
7 .. toctree::
25 .. toctree::
8 :maxdepth: 2
26 :maxdepth: 2
9
27
10 overview
11 extensions/index
28 extensions/index
12 ipython
13 integrating
29 integrating
14 editors
15 inputtransforms
30 inputtransforms
16 old
@@ -1,529 +1,524
1 .. _config_overview:
1 .. _config_overview:
2
2
3 ============================================
3 ============================================
4 Overview of the IPython configuration system
4 Overview of the IPython configuration system
5 ============================================
5 ============================================
6
6
7 This section describes the IPython configuration system.
7 This section describes the IPython configuration system.
8
8
9 The following discussion is for users who want to configure
10 IPython to their liking. Developers who want to know how they can
11 enable their objects to take advantage of the configuration system
12 should consult the :ref:`developer guide <developer_guide>`
13
14 The main concepts
9 The main concepts
15 =================
10 =================
16
11
17 There are a number of abstractions that the IPython configuration system uses.
12 There are a number of abstractions that the IPython configuration system uses.
18 Each of these abstractions is represented by a Python class.
13 Each of these abstractions is represented by a Python class.
19
14
20 Configuration object: :class:`~IPython.config.loader.Config`
15 Configuration object: :class:`~IPython.config.loader.Config`
21 A configuration object is a simple dictionary-like class that holds
16 A configuration object is a simple dictionary-like class that holds
22 configuration attributes and sub-configuration objects. These classes
17 configuration attributes and sub-configuration objects. These classes
23 support dotted attribute style access (``Foo.bar``) in addition to the
18 support dotted attribute style access (``Foo.bar``) in addition to the
24 regular dictionary style access (``Foo['bar']``). Configuration objects
19 regular dictionary style access (``Foo['bar']``). Configuration objects
25 are smart. They know how to merge themselves with other configuration
20 are smart. They know how to merge themselves with other configuration
26 objects and they automatically create sub-configuration objects.
21 objects and they automatically create sub-configuration objects.
27
22
28 Application: :class:`~IPython.config.application.Application`
23 Application: :class:`~IPython.config.application.Application`
29 An application is a process that does a specific job. The most obvious
24 An application is a process that does a specific job. The most obvious
30 application is the :command:`ipython` command line program. Each
25 application is the :command:`ipython` command line program. Each
31 application reads *one or more* configuration files and a single set of
26 application reads *one or more* configuration files and a single set of
32 command line options
27 command line options
33 and then produces a master configuration object for the application. This
28 and then produces a master configuration object for the application. This
34 configuration object is then passed to the configurable objects that the
29 configuration object is then passed to the configurable objects that the
35 application creates. These configurable objects implement the actual logic
30 application creates. These configurable objects implement the actual logic
36 of the application and know how to configure themselves given the
31 of the application and know how to configure themselves given the
37 configuration object.
32 configuration object.
38
33
39 Applications always have a `log` attribute that is a configured Logger.
34 Applications always have a `log` attribute that is a configured Logger.
40 This allows centralized logging configuration per-application.
35 This allows centralized logging configuration per-application.
41
36
42 Configurable: :class:`~IPython.config.configurable.Configurable`
37 Configurable: :class:`~IPython.config.configurable.Configurable`
43 A configurable is a regular Python class that serves as a base class for
38 A configurable is a regular Python class that serves as a base class for
44 all main classes in an application. The
39 all main classes in an application. The
45 :class:`~IPython.config.configurable.Configurable` base class is
40 :class:`~IPython.config.configurable.Configurable` base class is
46 lightweight and only does one things.
41 lightweight and only does one things.
47
42
48 This :class:`~IPython.config.configurable.Configurable` is a subclass
43 This :class:`~IPython.config.configurable.Configurable` is a subclass
49 of :class:`~IPython.utils.traitlets.HasTraits` that knows how to configure
44 of :class:`~IPython.utils.traitlets.HasTraits` that knows how to configure
50 itself. Class level traits with the metadata ``config=True`` become
45 itself. Class level traits with the metadata ``config=True`` become
51 values that can be configured from the command line and configuration
46 values that can be configured from the command line and configuration
52 files.
47 files.
53
48
54 Developers create :class:`~IPython.config.configurable.Configurable`
49 Developers create :class:`~IPython.config.configurable.Configurable`
55 subclasses that implement all of the logic in the application. Each of
50 subclasses that implement all of the logic in the application. Each of
56 these subclasses has its own configuration information that controls how
51 these subclasses has its own configuration information that controls how
57 instances are created.
52 instances are created.
58
53
59 Singletons: :class:`~IPython.config.configurable.SingletonConfigurable`
54 Singletons: :class:`~IPython.config.configurable.SingletonConfigurable`
60 Any object for which there is a single canonical instance. These are
55 Any object for which there is a single canonical instance. These are
61 just like Configurables, except they have a class method
56 just like Configurables, except they have a class method
62 :meth:`~IPython.config.configurable.SingletonConfigurable.instance`,
57 :meth:`~IPython.config.configurable.SingletonConfigurable.instance`,
63 that returns the current active instance (or creates one if it
58 that returns the current active instance (or creates one if it
64 does not exist). Examples of singletons include
59 does not exist). Examples of singletons include
65 :class:`~IPython.config.application.Application`s and
60 :class:`~IPython.config.application.Application`s and
66 :class:`~IPython.core.interactiveshell.InteractiveShell`. This lets
61 :class:`~IPython.core.interactiveshell.InteractiveShell`. This lets
67 objects easily connect to the current running Application without passing
62 objects easily connect to the current running Application without passing
68 objects around everywhere. For instance, to get the current running
63 objects around everywhere. For instance, to get the current running
69 Application instance, simply do: ``app = Application.instance()``.
64 Application instance, simply do: ``app = Application.instance()``.
70
65
71
66
72 .. note::
67 .. note::
73
68
74 Singletons are not strictly enforced - you can have many instances
69 Singletons are not strictly enforced - you can have many instances
75 of a given singleton class, but the :meth:`instance` method will always
70 of a given singleton class, but the :meth:`instance` method will always
76 return the same one.
71 return the same one.
77
72
78 Having described these main concepts, we can now state the main idea in our
73 Having described these main concepts, we can now state the main idea in our
79 configuration system: *"configuration" allows the default values of class
74 configuration system: *"configuration" allows the default values of class
80 attributes to be controlled on a class by class basis*. Thus all instances of
75 attributes to be controlled on a class by class basis*. Thus all instances of
81 a given class are configured in the same way. Furthermore, if two instances
76 a given class are configured in the same way. Furthermore, if two instances
82 need to be configured differently, they need to be instances of two different
77 need to be configured differently, they need to be instances of two different
83 classes. While this model may seem a bit restrictive, we have found that it
78 classes. While this model may seem a bit restrictive, we have found that it
84 expresses most things that need to be configured extremely well. However, it
79 expresses most things that need to be configured extremely well. However, it
85 is possible to create two instances of the same class that have different
80 is possible to create two instances of the same class that have different
86 trait values. This is done by overriding the configuration.
81 trait values. This is done by overriding the configuration.
87
82
88 Now, we show what our configuration objects and files look like.
83 Now, we show what our configuration objects and files look like.
89
84
90 Configuration objects and files
85 Configuration objects and files
91 ===============================
86 ===============================
92
87
93 A configuration file is simply a pure Python file that sets the attributes
88 A configuration file is simply a pure Python file that sets the attributes
94 of a global, pre-created configuration object. This configuration object is a
89 of a global, pre-created configuration object. This configuration object is a
95 :class:`~IPython.config.loader.Config` instance. While in a configuration
90 :class:`~IPython.config.loader.Config` instance. While in a configuration
96 file, to get a reference to this object, simply call the :func:`get_config`
91 file, to get a reference to this object, simply call the :func:`get_config`
97 function. We inject this function into the global namespace that the
92 function. We inject this function into the global namespace that the
98 configuration file is executed in.
93 configuration file is executed in.
99
94
100 Here is an example of a super simple configuration file that does nothing::
95 Here is an example of a super simple configuration file that does nothing::
101
96
102 c = get_config()
97 c = get_config()
103
98
104 Once you get a reference to the configuration object, you simply set
99 Once you get a reference to the configuration object, you simply set
105 attributes on it. All you have to know is:
100 attributes on it. All you have to know is:
106
101
107 * The name of each attribute.
102 * The name of each attribute.
108 * The type of each attribute.
103 * The type of each attribute.
109
104
110 The answers to these two questions are provided by the various
105 The answers to these two questions are provided by the various
111 :class:`~IPython.config.configurable.Configurable` subclasses that an
106 :class:`~IPython.config.configurable.Configurable` subclasses that an
112 application uses. Let's look at how this would work for a simple configurable
107 application uses. Let's look at how this would work for a simple configurable
113 subclass::
108 subclass::
114
109
115 # Sample configurable:
110 # Sample configurable:
116 from IPython.config.configurable import Configurable
111 from IPython.config.configurable import Configurable
117 from IPython.utils.traitlets import Int, Float, Unicode, Bool
112 from IPython.utils.traitlets import Int, Float, Unicode, Bool
118
113
119 class MyClass(Configurable):
114 class MyClass(Configurable):
120 name = Unicode(u'defaultname', config=True)
115 name = Unicode(u'defaultname', config=True)
121 ranking = Int(0, config=True)
116 ranking = Int(0, config=True)
122 value = Float(99.0)
117 value = Float(99.0)
123 # The rest of the class implementation would go here..
118 # The rest of the class implementation would go here..
124
119
125 In this example, we see that :class:`MyClass` has three attributes, two
120 In this example, we see that :class:`MyClass` has three attributes, two
126 of whom (``name``, ``ranking``) can be configured. All of the attributes
121 of whom (``name``, ``ranking``) can be configured. All of the attributes
127 are given types and default values. If a :class:`MyClass` is instantiated,
122 are given types and default values. If a :class:`MyClass` is instantiated,
128 but not configured, these default values will be used. But let's see how
123 but not configured, these default values will be used. But let's see how
129 to configure this class in a configuration file::
124 to configure this class in a configuration file::
130
125
131 # Sample config file
126 # Sample config file
132 c = get_config()
127 c = get_config()
133
128
134 c.MyClass.name = 'coolname'
129 c.MyClass.name = 'coolname'
135 c.MyClass.ranking = 10
130 c.MyClass.ranking = 10
136
131
137 After this configuration file is loaded, the values set in it will override
132 After this configuration file is loaded, the values set in it will override
138 the class defaults anytime a :class:`MyClass` is created. Furthermore,
133 the class defaults anytime a :class:`MyClass` is created. Furthermore,
139 these attributes will be type checked and validated anytime they are set.
134 these attributes will be type checked and validated anytime they are set.
140 This type checking is handled by the :mod:`IPython.utils.traitlets` module,
135 This type checking is handled by the :mod:`IPython.utils.traitlets` module,
141 which provides the :class:`Unicode`, :class:`Int` and :class:`Float` types.
136 which provides the :class:`Unicode`, :class:`Int` and :class:`Float` types.
142 In addition to these traitlets, the :mod:`IPython.utils.traitlets` provides
137 In addition to these traitlets, the :mod:`IPython.utils.traitlets` provides
143 traitlets for a number of other types.
138 traitlets for a number of other types.
144
139
145 .. note::
140 .. note::
146
141
147 Underneath the hood, the :class:`Configurable` base class is a subclass of
142 Underneath the hood, the :class:`Configurable` base class is a subclass of
148 :class:`IPython.utils.traitlets.HasTraits`. The
143 :class:`IPython.utils.traitlets.HasTraits`. The
149 :mod:`IPython.utils.traitlets` module is a lightweight version of
144 :mod:`IPython.utils.traitlets` module is a lightweight version of
150 :mod:`enthought.traits`. Our implementation is a pure Python subset
145 :mod:`enthought.traits`. Our implementation is a pure Python subset
151 (mostly API compatible) of :mod:`enthought.traits` that does not have any
146 (mostly API compatible) of :mod:`enthought.traits` that does not have any
152 of the automatic GUI generation capabilities. Our plan is to achieve 100%
147 of the automatic GUI generation capabilities. Our plan is to achieve 100%
153 API compatibility to enable the actual :mod:`enthought.traits` to
148 API compatibility to enable the actual :mod:`enthought.traits` to
154 eventually be used instead. Currently, we cannot use
149 eventually be used instead. Currently, we cannot use
155 :mod:`enthought.traits` as we are committed to the core of IPython being
150 :mod:`enthought.traits` as we are committed to the core of IPython being
156 pure Python.
151 pure Python.
157
152
158 It should be very clear at this point what the naming convention is for
153 It should be very clear at this point what the naming convention is for
159 configuration attributes::
154 configuration attributes::
160
155
161 c.ClassName.attribute_name = attribute_value
156 c.ClassName.attribute_name = attribute_value
162
157
163 Here, ``ClassName`` is the name of the class whose configuration attribute you
158 Here, ``ClassName`` is the name of the class whose configuration attribute you
164 want to set, ``attribute_name`` is the name of the attribute you want to set
159 want to set, ``attribute_name`` is the name of the attribute you want to set
165 and ``attribute_value`` the the value you want it to have. The ``ClassName``
160 and ``attribute_value`` the the value you want it to have. The ``ClassName``
166 attribute of ``c`` is not the actual class, but instead is another
161 attribute of ``c`` is not the actual class, but instead is another
167 :class:`~IPython.config.loader.Config` instance.
162 :class:`~IPython.config.loader.Config` instance.
168
163
169 .. note::
164 .. note::
170
165
171 The careful reader may wonder how the ``ClassName`` (``MyClass`` in
166 The careful reader may wonder how the ``ClassName`` (``MyClass`` in
172 the above example) attribute of the configuration object ``c`` gets
167 the above example) attribute of the configuration object ``c`` gets
173 created. These attributes are created on the fly by the
168 created. These attributes are created on the fly by the
174 :class:`~IPython.config.loader.Config` instance, using a simple naming
169 :class:`~IPython.config.loader.Config` instance, using a simple naming
175 convention. Any attribute of a :class:`~IPython.config.loader.Config`
170 convention. Any attribute of a :class:`~IPython.config.loader.Config`
176 instance whose name begins with an uppercase character is assumed to be a
171 instance whose name begins with an uppercase character is assumed to be a
177 sub-configuration and a new empty :class:`~IPython.config.loader.Config`
172 sub-configuration and a new empty :class:`~IPython.config.loader.Config`
178 instance is dynamically created for that attribute. This allows deeply
173 instance is dynamically created for that attribute. This allows deeply
179 hierarchical information created easily (``c.Foo.Bar.value``) on the fly.
174 hierarchical information created easily (``c.Foo.Bar.value``) on the fly.
180
175
181 Configuration files inheritance
176 Configuration files inheritance
182 ===============================
177 ===============================
183
178
184 Let's say you want to have different configuration files for various purposes.
179 Let's say you want to have different configuration files for various purposes.
185 Our configuration system makes it easy for one configuration file to inherit
180 Our configuration system makes it easy for one configuration file to inherit
186 the information in another configuration file. The :func:`load_subconfig`
181 the information in another configuration file. The :func:`load_subconfig`
187 command can be used in a configuration file for this purpose. Here is a simple
182 command can be used in a configuration file for this purpose. Here is a simple
188 example that loads all of the values from the file :file:`base_config.py`::
183 example that loads all of the values from the file :file:`base_config.py`::
189
184
190 # base_config.py
185 # base_config.py
191 c = get_config()
186 c = get_config()
192 c.MyClass.name = 'coolname'
187 c.MyClass.name = 'coolname'
193 c.MyClass.ranking = 100
188 c.MyClass.ranking = 100
194
189
195 into the configuration file :file:`main_config.py`::
190 into the configuration file :file:`main_config.py`::
196
191
197 # main_config.py
192 # main_config.py
198 c = get_config()
193 c = get_config()
199
194
200 # Load everything from base_config.py
195 # Load everything from base_config.py
201 load_subconfig('base_config.py')
196 load_subconfig('base_config.py')
202
197
203 # Now override one of the values
198 # Now override one of the values
204 c.MyClass.name = 'bettername'
199 c.MyClass.name = 'bettername'
205
200
206 In a situation like this the :func:`load_subconfig` makes sure that the
201 In a situation like this the :func:`load_subconfig` makes sure that the
207 search path for sub-configuration files is inherited from that of the parent.
202 search path for sub-configuration files is inherited from that of the parent.
208 Thus, you can typically put the two in the same directory and everything will
203 Thus, you can typically put the two in the same directory and everything will
209 just work.
204 just work.
210
205
211 You can also load configuration files by profile, for instance:
206 You can also load configuration files by profile, for instance:
212
207
213 .. sourcecode:: python
208 .. sourcecode:: python
214
209
215 load_subconfig('ipython_config.py', profile='default')
210 load_subconfig('ipython_config.py', profile='default')
216
211
217 to inherit your default configuration as a starting point.
212 to inherit your default configuration as a starting point.
218
213
219
214
220 Class based configuration inheritance
215 Class based configuration inheritance
221 =====================================
216 =====================================
222
217
223 There is another aspect of configuration where inheritance comes into play.
218 There is another aspect of configuration where inheritance comes into play.
224 Sometimes, your classes will have an inheritance hierarchy that you want
219 Sometimes, your classes will have an inheritance hierarchy that you want
225 to be reflected in the configuration system. Here is a simple example::
220 to be reflected in the configuration system. Here is a simple example::
226
221
227 from IPython.config.configurable import Configurable
222 from IPython.config.configurable import Configurable
228 from IPython.utils.traitlets import Int, Float, Unicode, Bool
223 from IPython.utils.traitlets import Int, Float, Unicode, Bool
229
224
230 class Foo(Configurable):
225 class Foo(Configurable):
231 name = Unicode(u'fooname', config=True)
226 name = Unicode(u'fooname', config=True)
232 value = Float(100.0, config=True)
227 value = Float(100.0, config=True)
233
228
234 class Bar(Foo):
229 class Bar(Foo):
235 name = Unicode(u'barname', config=True)
230 name = Unicode(u'barname', config=True)
236 othervalue = Int(0, config=True)
231 othervalue = Int(0, config=True)
237
232
238 Now, we can create a configuration file to configure instances of :class:`Foo`
233 Now, we can create a configuration file to configure instances of :class:`Foo`
239 and :class:`Bar`::
234 and :class:`Bar`::
240
235
241 # config file
236 # config file
242 c = get_config()
237 c = get_config()
243
238
244 c.Foo.name = u'bestname'
239 c.Foo.name = u'bestname'
245 c.Bar.othervalue = 10
240 c.Bar.othervalue = 10
246
241
247 This class hierarchy and configuration file accomplishes the following:
242 This class hierarchy and configuration file accomplishes the following:
248
243
249 * The default value for :attr:`Foo.name` and :attr:`Bar.name` will be
244 * The default value for :attr:`Foo.name` and :attr:`Bar.name` will be
250 'bestname'. Because :class:`Bar` is a :class:`Foo` subclass it also
245 'bestname'. Because :class:`Bar` is a :class:`Foo` subclass it also
251 picks up the configuration information for :class:`Foo`.
246 picks up the configuration information for :class:`Foo`.
252 * The default value for :attr:`Foo.value` and :attr:`Bar.value` will be
247 * The default value for :attr:`Foo.value` and :attr:`Bar.value` will be
253 ``100.0``, which is the value specified as the class default.
248 ``100.0``, which is the value specified as the class default.
254 * The default value for :attr:`Bar.othervalue` will be 10 as set in the
249 * The default value for :attr:`Bar.othervalue` will be 10 as set in the
255 configuration file. Because :class:`Foo` is the parent of :class:`Bar`
250 configuration file. Because :class:`Foo` is the parent of :class:`Bar`
256 it doesn't know anything about the :attr:`othervalue` attribute.
251 it doesn't know anything about the :attr:`othervalue` attribute.
257
252
258
253
259 .. _ipython_dir:
254 .. _ipython_dir:
260
255
261 Configuration file location
256 Configuration file location
262 ===========================
257 ===========================
263
258
264 So where should you put your configuration files? IPython uses "profiles" for
259 So where should you put your configuration files? IPython uses "profiles" for
265 configuration, and by default, all profiles will be stored in the so called
260 configuration, and by default, all profiles will be stored in the so called
266 "IPython directory". The location of this directory is determined by the
261 "IPython directory". The location of this directory is determined by the
267 following algorithm:
262 following algorithm:
268
263
269 * If the ``ipython-dir`` command line flag is given, its value is used.
264 * If the ``ipython-dir`` command line flag is given, its value is used.
270
265
271 * If not, the value returned by :func:`IPython.utils.path.get_ipython_dir`
266 * If not, the value returned by :func:`IPython.utils.path.get_ipython_dir`
272 is used. This function will first look at the :envvar:`IPYTHONDIR`
267 is used. This function will first look at the :envvar:`IPYTHONDIR`
273 environment variable and then default to :file:`~/.ipython`.
268 environment variable and then default to :file:`~/.ipython`.
274 Historical support for the :envvar:`IPYTHON_DIR` environment variable will
269 Historical support for the :envvar:`IPYTHON_DIR` environment variable will
275 be removed in a future release.
270 be removed in a future release.
276
271
277 For most users, the configuration directory will be :file:`~/.ipython`.
272 For most users, the configuration directory will be :file:`~/.ipython`.
278
273
279 Previous versions of IPython on Linux would use the XDG config directory,
274 Previous versions of IPython on Linux would use the XDG config directory,
280 creating :file:`~/.config/ipython` by default. We have decided to go
275 creating :file:`~/.config/ipython` by default. We have decided to go
281 back to :file:`~/.ipython` for consistency among systems. IPython will
276 back to :file:`~/.ipython` for consistency among systems. IPython will
282 issue a warning if it finds the XDG location, and will move it to the new
277 issue a warning if it finds the XDG location, and will move it to the new
283 location if there isn't already a directory there.
278 location if there isn't already a directory there.
284
279
285 Once the location of the IPython directory has been determined, you need to know
280 Once the location of the IPython directory has been determined, you need to know
286 which profile you are using. For users with a single configuration, this will
281 which profile you are using. For users with a single configuration, this will
287 simply be 'default', and will be located in
282 simply be 'default', and will be located in
288 :file:`<IPYTHONDIR>/profile_default`.
283 :file:`<IPYTHONDIR>/profile_default`.
289
284
290 The next thing you need to know is what to call your configuration file. The
285 The next thing you need to know is what to call your configuration file. The
291 basic idea is that each application has its own default configuration filename.
286 basic idea is that each application has its own default configuration filename.
292 The default named used by the :command:`ipython` command line program is
287 The default named used by the :command:`ipython` command line program is
293 :file:`ipython_config.py`, and *all* IPython applications will use this file.
288 :file:`ipython_config.py`, and *all* IPython applications will use this file.
294 Other applications, such as the parallel :command:`ipcluster` scripts or the
289 Other applications, such as the parallel :command:`ipcluster` scripts or the
295 QtConsole will load their own config files *after* :file:`ipython_config.py`. To
290 QtConsole will load their own config files *after* :file:`ipython_config.py`. To
296 load a particular configuration file instead of the default, the name can be
291 load a particular configuration file instead of the default, the name can be
297 overridden by the ``config_file`` command line flag.
292 overridden by the ``config_file`` command line flag.
298
293
299 To generate the default configuration files, do::
294 To generate the default configuration files, do::
300
295
301 $ ipython profile create
296 $ ipython profile create
302
297
303 and you will have a default :file:`ipython_config.py` in your IPython directory
298 and you will have a default :file:`ipython_config.py` in your IPython directory
304 under :file:`profile_default`. If you want the default config files for the
299 under :file:`profile_default`. If you want the default config files for the
305 :mod:`IPython.parallel` applications, add ``--parallel`` to the end of the
300 :mod:`IPython.parallel` applications, add ``--parallel`` to the end of the
306 command-line args.
301 command-line args.
307
302
308
303
309 Locating these files
304 Locating these files
310 --------------------
305 --------------------
311
306
312 From the command-line, you can quickly locate the IPYTHONDIR or a specific
307 From the command-line, you can quickly locate the IPYTHONDIR or a specific
313 profile with:
308 profile with:
314
309
315 .. sourcecode:: bash
310 .. sourcecode:: bash
316
311
317 $ ipython locate
312 $ ipython locate
318 /home/you/.ipython
313 /home/you/.ipython
319
314
320 $ ipython locate profile foo
315 $ ipython locate profile foo
321 /home/you/.ipython/profile_foo
316 /home/you/.ipython/profile_foo
322
317
323 These map to the utility functions: :func:`IPython.utils.path.get_ipython_dir`
318 These map to the utility functions: :func:`IPython.utils.path.get_ipython_dir`
324 and :func:`IPython.utils.path.locate_profile` respectively.
319 and :func:`IPython.utils.path.locate_profile` respectively.
325
320
326
321
327 .. _Profiles:
322 .. _profiles_dev:
328
323
329 Profiles
324 Profiles
330 ========
325 ========
331
326
332 A profile is a directory containing configuration and runtime files, such as
327 A profile is a directory containing configuration and runtime files, such as
333 logs, connection info for the parallel apps, and your IPython command history.
328 logs, connection info for the parallel apps, and your IPython command history.
334
329
335 The idea is that users often want to maintain a set of configuration files for
330 The idea is that users often want to maintain a set of configuration files for
336 different purposes: one for doing numerical computing with NumPy and SciPy and
331 different purposes: one for doing numerical computing with NumPy and SciPy and
337 another for doing symbolic computing with SymPy. Profiles make it easy to keep a
332 another for doing symbolic computing with SymPy. Profiles make it easy to keep a
338 separate configuration files, logs, and histories for each of these purposes.
333 separate configuration files, logs, and histories for each of these purposes.
339
334
340 Let's start by showing how a profile is used:
335 Let's start by showing how a profile is used:
341
336
342 .. code-block:: bash
337 .. code-block:: bash
343
338
344 $ ipython --profile=sympy
339 $ ipython --profile=sympy
345
340
346 This tells the :command:`ipython` command line program to get its configuration
341 This tells the :command:`ipython` command line program to get its configuration
347 from the "sympy" profile. The file names for various profiles do not change. The
342 from the "sympy" profile. The file names for various profiles do not change. The
348 only difference is that profiles are named in a special way. In the case above,
343 only difference is that profiles are named in a special way. In the case above,
349 the "sympy" profile means looking for :file:`ipython_config.py` in :file:`<IPYTHONDIR>/profile_sympy`.
344 the "sympy" profile means looking for :file:`ipython_config.py` in :file:`<IPYTHONDIR>/profile_sympy`.
350
345
351 The general pattern is this: simply create a new profile with:
346 The general pattern is this: simply create a new profile with:
352
347
353 .. code-block:: bash
348 .. code-block:: bash
354
349
355 $ ipython profile create <name>
350 $ ipython profile create <name>
356
351
357 which adds a directory called ``profile_<name>`` to your IPython directory. Then
352 which adds a directory called ``profile_<name>`` to your IPython directory. Then
358 you can load this profile by adding ``--profile=<name>`` to your command line
353 you can load this profile by adding ``--profile=<name>`` to your command line
359 options. Profiles are supported by all IPython applications.
354 options. Profiles are supported by all IPython applications.
360
355
361 IPython ships with some sample profiles in :file:`IPython/config/profile`. If
356 IPython ships with some sample profiles in :file:`IPython/config/profile`. If
362 you create profiles with the name of one of our shipped profiles, these config
357 you create profiles with the name of one of our shipped profiles, these config
363 files will be copied over instead of starting with the automatically generated
358 files will be copied over instead of starting with the automatically generated
364 config files.
359 config files.
365
360
366 Security Files
361 Security Files
367 --------------
362 --------------
368
363
369 If you are using the notebook, qtconsole, or parallel code, IPython stores
364 If you are using the notebook, qtconsole, or parallel code, IPython stores
370 connection information in small JSON files in the active profile's security
365 connection information in small JSON files in the active profile's security
371 directory. This directory is made private, so only you can see the files inside. If
366 directory. This directory is made private, so only you can see the files inside. If
372 you need to move connection files around to other computers, this is where they will
367 you need to move connection files around to other computers, this is where they will
373 be. If you want your code to be able to open security files by name, we have a
368 be. If you want your code to be able to open security files by name, we have a
374 convenience function :func:`IPython.utils.path.get_security_file`, which will return
369 convenience function :func:`IPython.utils.path.get_security_file`, which will return
375 the absolute path to a security file from its filename and [optionally] profile
370 the absolute path to a security file from its filename and [optionally] profile
376 name.
371 name.
377
372
378 .. _startup_files:
373 .. _startup_files:
379
374
380 Startup Files
375 Startup Files
381 -------------
376 -------------
382
377
383 If you want some code to be run at the beginning of every IPython session with
378 If you want some code to be run at the beginning of every IPython session with
384 a particular profile, the easiest way is to add Python (``.py``) or
379 a particular profile, the easiest way is to add Python (``.py``) or
385 IPython (``.ipy``) scripts to your :file:`<profile>/startup` directory. Files
380 IPython (``.ipy``) scripts to your :file:`<profile>/startup` directory. Files
386 in this directory will always be executed as soon as the IPython shell is
381 in this directory will always be executed as soon as the IPython shell is
387 constructed, and before any other code or scripts you have specified. If you
382 constructed, and before any other code or scripts you have specified. If you
388 have multiple files in the startup directory, they will be run in
383 have multiple files in the startup directory, they will be run in
389 lexicographical order, so you can control the ordering by adding a '00-'
384 lexicographical order, so you can control the ordering by adding a '00-'
390 prefix.
385 prefix.
391
386
392
387
393 .. _commandline:
388 .. _commandline:
394
389
395 Command-line arguments
390 Command-line arguments
396 ======================
391 ======================
397
392
398 IPython exposes *all* configurable options on the command-line. The command-line
393 IPython exposes *all* configurable options on the command-line. The command-line
399 arguments are generated from the Configurable traits of the classes associated
394 arguments are generated from the Configurable traits of the classes associated
400 with a given Application. Configuring IPython from the command-line may look
395 with a given Application. Configuring IPython from the command-line may look
401 very similar to an IPython config file
396 very similar to an IPython config file
402
397
403 IPython applications use a parser called
398 IPython applications use a parser called
404 :class:`~IPython.config.loader.KeyValueLoader` to load values into a Config
399 :class:`~IPython.config.loader.KeyValueLoader` to load values into a Config
405 object. Values are assigned in much the same way as in a config file:
400 object. Values are assigned in much the same way as in a config file:
406
401
407 .. code-block:: bash
402 .. code-block:: bash
408
403
409 $ ipython --InteractiveShell.use_readline=False --BaseIPythonApplication.profile='myprofile'
404 $ ipython --InteractiveShell.use_readline=False --BaseIPythonApplication.profile='myprofile'
410
405
411 Is the same as adding:
406 Is the same as adding:
412
407
413 .. sourcecode:: python
408 .. sourcecode:: python
414
409
415 c.InteractiveShell.use_readline=False
410 c.InteractiveShell.use_readline=False
416 c.BaseIPythonApplication.profile='myprofile'
411 c.BaseIPythonApplication.profile='myprofile'
417
412
418 to your config file. Key/Value arguments *always* take a value, separated by '='
413 to your config file. Key/Value arguments *always* take a value, separated by '='
419 and no spaces.
414 and no spaces.
420
415
421 Common Arguments
416 Common Arguments
422 ----------------
417 ----------------
423
418
424 Since the strictness and verbosity of the KVLoader above are not ideal for everyday
419 Since the strictness and verbosity of the KVLoader above are not ideal for everyday
425 use, common arguments can be specified as flags_ or aliases_.
420 use, common arguments can be specified as flags_ or aliases_.
426
421
427 Flags and Aliases are handled by :mod:`argparse` instead, allowing for more flexible
422 Flags and Aliases are handled by :mod:`argparse` instead, allowing for more flexible
428 parsing. In general, flags and aliases are prefixed by ``--``, except for those
423 parsing. In general, flags and aliases are prefixed by ``--``, except for those
429 that are single characters, in which case they can be specified with a single ``-``, e.g.:
424 that are single characters, in which case they can be specified with a single ``-``, e.g.:
430
425
431 .. code-block:: bash
426 .. code-block:: bash
432
427
433 $ ipython -i -c "import numpy; x=numpy.linspace(0,1)" --profile testing --colors=lightbg
428 $ ipython -i -c "import numpy; x=numpy.linspace(0,1)" --profile testing --colors=lightbg
434
429
435 Aliases
430 Aliases
436 *******
431 *******
437
432
438 For convenience, applications have a mapping of commonly used traits, so you don't have
433 For convenience, applications have a mapping of commonly used traits, so you don't have
439 to specify the whole class name:
434 to specify the whole class name:
440
435
441 .. code-block:: bash
436 .. code-block:: bash
442
437
443 $ ipython --profile myprofile
438 $ ipython --profile myprofile
444 # and
439 # and
445 $ ipython --profile='myprofile'
440 $ ipython --profile='myprofile'
446 # are equivalent to
441 # are equivalent to
447 $ ipython --BaseIPythonApplication.profile='myprofile'
442 $ ipython --BaseIPythonApplication.profile='myprofile'
448
443
449 Flags
444 Flags
450 *****
445 *****
451
446
452 Applications can also be passed **flags**. Flags are options that take no
447 Applications can also be passed **flags**. Flags are options that take no
453 arguments. They are simply wrappers for
448 arguments. They are simply wrappers for
454 setting one or more configurables with predefined values, often True/False.
449 setting one or more configurables with predefined values, often True/False.
455
450
456 For instance:
451 For instance:
457
452
458 .. code-block:: bash
453 .. code-block:: bash
459
454
460 $ ipcontroller --debug
455 $ ipcontroller --debug
461 # is equivalent to
456 # is equivalent to
462 $ ipcontroller --Application.log_level=DEBUG
457 $ ipcontroller --Application.log_level=DEBUG
463 # and
458 # and
464 $ ipython --matploitlib
459 $ ipython --matploitlib
465 # is equivalent to
460 # is equivalent to
466 $ ipython --matplotlib auto
461 $ ipython --matplotlib auto
467 # or
462 # or
468 $ ipython --no-banner
463 $ ipython --no-banner
469 # is equivalent to
464 # is equivalent to
470 $ ipython --TerminalIPythonApp.display_banner=False
465 $ ipython --TerminalIPythonApp.display_banner=False
471
466
472 Subcommands
467 Subcommands
473 -----------
468 -----------
474
469
475
470
476 Some IPython applications have **subcommands**. Subcommands are modeled after
471 Some IPython applications have **subcommands**. Subcommands are modeled after
477 :command:`git`, and are called with the form :command:`command subcommand
472 :command:`git`, and are called with the form :command:`command subcommand
478 [...args]`. Currently, the QtConsole is a subcommand of terminal IPython:
473 [...args]`. Currently, the QtConsole is a subcommand of terminal IPython:
479
474
480 .. code-block:: bash
475 .. code-block:: bash
481
476
482 $ ipython qtconsole --profile myprofile
477 $ ipython qtconsole --profile myprofile
483
478
484 and :command:`ipcluster` is simply a wrapper for its various subcommands (start,
479 and :command:`ipcluster` is simply a wrapper for its various subcommands (start,
485 stop, engines).
480 stop, engines).
486
481
487 .. code-block:: bash
482 .. code-block:: bash
488
483
489 $ ipcluster start --profile=myprofile -n 4
484 $ ipcluster start --profile=myprofile -n 4
490
485
491
486
492 To see a list of the available aliases, flags, and subcommands for an IPython application, simply pass ``-h`` or ``--help``. And to see the full list of configurable options (*very* long), pass ``--help-all``.
487 To see a list of the available aliases, flags, and subcommands for an IPython application, simply pass ``-h`` or ``--help``. And to see the full list of configurable options (*very* long), pass ``--help-all``.
493
488
494
489
495 Design requirements
490 Design requirements
496 ===================
491 ===================
497
492
498 Here are the main requirements we wanted our configuration system to have:
493 Here are the main requirements we wanted our configuration system to have:
499
494
500 * Support for hierarchical configuration information.
495 * Support for hierarchical configuration information.
501
496
502 * Full integration with command line option parsers. Often, you want to read
497 * Full integration with command line option parsers. Often, you want to read
503 a configuration file, but then override some of the values with command line
498 a configuration file, but then override some of the values with command line
504 options. Our configuration system automates this process and allows each
499 options. Our configuration system automates this process and allows each
505 command line option to be linked to a particular attribute in the
500 command line option to be linked to a particular attribute in the
506 configuration hierarchy that it will override.
501 configuration hierarchy that it will override.
507
502
508 * Configuration files that are themselves valid Python code. This accomplishes
503 * Configuration files that are themselves valid Python code. This accomplishes
509 many things. First, it becomes possible to put logic in your configuration
504 many things. First, it becomes possible to put logic in your configuration
510 files that sets attributes based on your operating system, network setup,
505 files that sets attributes based on your operating system, network setup,
511 Python version, etc. Second, Python has a super simple syntax for accessing
506 Python version, etc. Second, Python has a super simple syntax for accessing
512 hierarchical data structures, namely regular attribute access
507 hierarchical data structures, namely regular attribute access
513 (``Foo.Bar.Bam.name``). Third, using Python makes it easy for users to
508 (``Foo.Bar.Bam.name``). Third, using Python makes it easy for users to
514 import configuration attributes from one configuration file to another.
509 import configuration attributes from one configuration file to another.
515 Fourth, even though Python is dynamically typed, it does have types that can
510 Fourth, even though Python is dynamically typed, it does have types that can
516 be checked at runtime. Thus, a ``1`` in a config file is the integer '1',
511 be checked at runtime. Thus, a ``1`` in a config file is the integer '1',
517 while a ``'1'`` is a string.
512 while a ``'1'`` is a string.
518
513
519 * A fully automated method for getting the configuration information to the
514 * A fully automated method for getting the configuration information to the
520 classes that need it at runtime. Writing code that walks a configuration
515 classes that need it at runtime. Writing code that walks a configuration
521 hierarchy to extract a particular attribute is painful. When you have
516 hierarchy to extract a particular attribute is painful. When you have
522 complex configuration information with hundreds of attributes, this makes
517 complex configuration information with hundreds of attributes, this makes
523 you want to cry.
518 you want to cry.
524
519
525 * Type checking and validation that doesn't require the entire configuration
520 * Type checking and validation that doesn't require the entire configuration
526 hierarchy to be specified statically before runtime. Python is a very
521 hierarchy to be specified statically before runtime. Python is a very
527 dynamic language and you don't always know everything that needs to be
522 dynamic language and you don't always know everything that needs to be
528 configured when a program starts.
523 configured when a program starts.
529
524
@@ -1,27 +1,28
1 .. _developer_guide:
1 .. _developer_guide:
2
2
3 =========================
3 =========================
4 IPython developer's guide
4 IPython developer's guide
5 =========================
5 =========================
6
6
7 This are two categories of developer focused documentation:
7 This are two categories of developer focused documentation:
8
8
9 1. Documentation for developers of *IPython itself*.
9 1. Documentation for developers of *IPython itself*.
10 2. Documentation for developers of third party tools and libraries
10 2. Documentation for developers of third party tools and libraries
11 that use IPython.
11 that use IPython.
12
12
13 This part of our documentation only contains information in the second category.
13 This part of our documentation only contains information in the second category.
14
14
15 Developers interested in working on IPython itself should consult
15 Developers interested in working on IPython itself should consult
16 our `developer information <https://github.com/ipython/ipython/wiki/Dev:-Index>`_
16 our `developer information <https://github.com/ipython/ipython/wiki/Dev:-Index>`_
17 on the IPython GitHub wiki.
17 on the IPython GitHub wiki.
18
18
19 .. toctree::
19 .. toctree::
20 :maxdepth: 1
20 :maxdepth: 1
21
21
22
22
23 gitwash/index
23 gitwash/index
24 messaging
24 messaging
25 parallel_messages
25 parallel_messages
26 parallel_connections
26 parallel_connections
27 pycompat
27 pycompat
28 config
@@ -1,765 +1,765
1 =============
1 =============
2 0.11 Series
2 0.11 Series
3 =============
3 =============
4
4
5 Release 0.11
5 Release 0.11
6 ============
6 ============
7
7
8 IPython 0.11 is a *major* overhaul of IPython, two years in the making. Most
8 IPython 0.11 is a *major* overhaul of IPython, two years in the making. Most
9 of the code base has been rewritten or at least reorganized, breaking backward
9 of the code base has been rewritten or at least reorganized, breaking backward
10 compatibility with several APIs in previous versions. It is the first major
10 compatibility with several APIs in previous versions. It is the first major
11 release in two years, and probably the most significant change to IPython since
11 release in two years, and probably the most significant change to IPython since
12 its inception. We plan to have a relatively quick succession of releases, as
12 its inception. We plan to have a relatively quick succession of releases, as
13 people discover new bugs and regressions. Once we iron out any significant
13 people discover new bugs and regressions. Once we iron out any significant
14 bugs in this process and settle down the new APIs, this series will become
14 bugs in this process and settle down the new APIs, this series will become
15 IPython 1.0. We encourage feedback now on the core APIs, which we hope to
15 IPython 1.0. We encourage feedback now on the core APIs, which we hope to
16 maintain stable during the 1.0 series.
16 maintain stable during the 1.0 series.
17
17
18 Since the internal APIs have changed so much, projects using IPython as a
18 Since the internal APIs have changed so much, projects using IPython as a
19 library (as opposed to end-users of the application) are the most likely to
19 library (as opposed to end-users of the application) are the most likely to
20 encounter regressions or changes that break their existing use patterns. We
20 encounter regressions or changes that break their existing use patterns. We
21 will make every effort to provide updated versions of the APIs to facilitate
21 will make every effort to provide updated versions of the APIs to facilitate
22 the transition, and we encourage you to contact us on the `development mailing
22 the transition, and we encourage you to contact us on the `development mailing
23 list`__ with questions and feedback.
23 list`__ with questions and feedback.
24
24
25 .. __: http://mail.scipy.org/mailman/listinfo/ipython-dev
25 .. __: http://mail.scipy.org/mailman/listinfo/ipython-dev
26
26
27 Chris Fonnesbeck recently wrote an `excellent post`__ that highlights some of
27 Chris Fonnesbeck recently wrote an `excellent post`__ that highlights some of
28 our major new features, with examples and screenshots. We encourage you to
28 our major new features, with examples and screenshots. We encourage you to
29 read it as it provides an illustrated, high-level overview complementing the
29 read it as it provides an illustrated, high-level overview complementing the
30 detailed feature breakdown in this document.
30 detailed feature breakdown in this document.
31
31
32 .. __: http://fonnesbeck.calepin.co/innovations-in-ipython.html
32 .. __: http://fonnesbeck.calepin.co/innovations-in-ipython.html
33
33
34 A quick summary of the major changes (see below for details):
34 A quick summary of the major changes (see below for details):
35
35
36 * **Standalone Qt console**: a new rich console has been added to IPython,
36 * **Standalone Qt console**: a new rich console has been added to IPython,
37 started with `ipython qtconsole`. In this application we have tried to
37 started with `ipython qtconsole`. In this application we have tried to
38 retain the feel of a terminal for fast and efficient workflows, while adding
38 retain the feel of a terminal for fast and efficient workflows, while adding
39 many features that a line-oriented terminal simply can not support, such as
39 many features that a line-oriented terminal simply can not support, such as
40 inline figures, full multiline editing with syntax highlighting, graphical
40 inline figures, full multiline editing with syntax highlighting, graphical
41 tooltips for function calls and much more. This development was sponsored by
41 tooltips for function calls and much more. This development was sponsored by
42 `Enthought Inc.`__. See :ref:`below <qtconsole_011>` for details.
42 `Enthought Inc.`__. See :ref:`below <qtconsole_011>` for details.
43
43
44 .. __: http://enthought.com
44 .. __: http://enthought.com
45
45
46 * **High-level parallel computing with ZeroMQ**. Using the same architecture
46 * **High-level parallel computing with ZeroMQ**. Using the same architecture
47 that our Qt console is based on, we have completely rewritten our high-level
47 that our Qt console is based on, we have completely rewritten our high-level
48 parallel computing machinery that in prior versions used the Twisted
48 parallel computing machinery that in prior versions used the Twisted
49 networking framework. While this change will require users to update their
49 networking framework. While this change will require users to update their
50 codes, the improvements in performance, memory control and internal
50 codes, the improvements in performance, memory control and internal
51 consistency across our codebase convinced us it was a price worth paying. We
51 consistency across our codebase convinced us it was a price worth paying. We
52 have tried to explain how to best proceed with this update, and will be happy
52 have tried to explain how to best proceed with this update, and will be happy
53 to answer questions that may arise. A full tutorial describing these
53 to answer questions that may arise. A full tutorial describing these
54 features `was presented at SciPy'11`__, more details :ref:`below
54 features `was presented at SciPy'11`__, more details :ref:`below
55 <parallel_011>`.
55 <parallel_011>`.
56
56
57 .. __: http://minrk.github.com/scipy-tutorial-2011
57 .. __: http://minrk.github.com/scipy-tutorial-2011
58
58
59 * **New model for GUI/plotting support in the terminal**. Now instead of the
59 * **New model for GUI/plotting support in the terminal**. Now instead of the
60 various `-Xthread` flags we had before, GUI support is provided without the
60 various `-Xthread` flags we had before, GUI support is provided without the
61 use of any threads, by directly integrating GUI event loops with Python's
61 use of any threads, by directly integrating GUI event loops with Python's
62 `PyOS_InputHook` API. A new command-line flag `--gui` controls GUI support,
62 `PyOS_InputHook` API. A new command-line flag `--gui` controls GUI support,
63 and it can also be enabled after IPython startup via the new `%gui` magic.
63 and it can also be enabled after IPython startup via the new `%gui` magic.
64 This requires some changes if you want to execute GUI-using scripts inside
64 This requires some changes if you want to execute GUI-using scripts inside
65 IPython, see :ref:`the GUI support section <gui_support>` for more details.
65 IPython, see :ref:`the GUI support section <gui_support>` for more details.
66
66
67 * **A two-process architecture.** The Qt console is the first use of a new
67 * **A two-process architecture.** The Qt console is the first use of a new
68 model that splits IPython between a kernel process where code is executed and
68 model that splits IPython between a kernel process where code is executed and
69 a client that handles user interaction. We plan on also providing terminal
69 a client that handles user interaction. We plan on also providing terminal
70 and web-browser based clients using this infrastructure in future releases.
70 and web-browser based clients using this infrastructure in future releases.
71 This model allows multiple clients to interact with an IPython process
71 This model allows multiple clients to interact with an IPython process
72 through a :ref:`well-documented messaging protocol <messaging>` using the
72 through a :ref:`well-documented messaging protocol <messaging>` using the
73 ZeroMQ networking library.
73 ZeroMQ networking library.
74
74
75 * **Refactoring.** the entire codebase has been refactored, in order to make it
75 * **Refactoring.** the entire codebase has been refactored, in order to make it
76 more modular and easier to contribute to. IPython has traditionally been a
76 more modular and easier to contribute to. IPython has traditionally been a
77 hard project to participate because the old codebase was very monolithic. We
77 hard project to participate because the old codebase was very monolithic. We
78 hope this (ongoing) restructuring will make it easier for new developers to
78 hope this (ongoing) restructuring will make it easier for new developers to
79 join us.
79 join us.
80
80
81 * **Vim integration**. Vim can be configured to seamlessly control an IPython
81 * **Vim integration**. Vim can be configured to seamlessly control an IPython
82 kernel, see the files in :file:`docs/examples/vim` for the full details.
82 kernel, see the files in :file:`docs/examples/vim` for the full details.
83 This work was done by Paul Ivanov, who prepared a nice `video
83 This work was done by Paul Ivanov, who prepared a nice `video
84 demonstration`__ of the features it provides.
84 demonstration`__ of the features it provides.
85
85
86 .. __: http://pirsquared.org/blog/2011/07/28/vim-ipython/
86 .. __: http://pirsquared.org/blog/2011/07/28/vim-ipython/
87
87
88 * **Integration into Microsoft Visual Studio**. Thanks to the work of the
88 * **Integration into Microsoft Visual Studio**. Thanks to the work of the
89 Microsoft `Python Tools for Visual Studio`__ team, this version of IPython
89 Microsoft `Python Tools for Visual Studio`__ team, this version of IPython
90 has been integrated into Microsoft Visual Studio's Python tools open source
90 has been integrated into Microsoft Visual Studio's Python tools open source
91 plug-in. `Details below`_
91 plug-in. `Details below`_
92
92
93 .. __: http://pytools.codeplex.com
93 .. __: http://pytools.codeplex.com
94 .. _details below: ms_visual_studio_011_
94 .. _details below: ms_visual_studio_011_
95
95
96 * **Improved unicode support**. We closed many bugs related to unicode input.
96 * **Improved unicode support**. We closed many bugs related to unicode input.
97
97
98 * **Python 3**. IPython now runs on Python 3.x. See :ref:`python3_011` for
98 * **Python 3**. IPython now runs on Python 3.x. See :ref:`python3_011` for
99 details.
99 details.
100
100
101 * **New profile model**. Profiles are now directories that contain all relevant
101 * **New profile model**. Profiles are now directories that contain all relevant
102 information for that session, and thus better isolate IPython use-cases.
102 information for that session, and thus better isolate IPython use-cases.
103
103
104 * **SQLite storage for history**. All history is now stored in a SQLite
104 * **SQLite storage for history**. All history is now stored in a SQLite
105 database, providing support for multiple simultaneous sessions that won't
105 database, providing support for multiple simultaneous sessions that won't
106 clobber each other as well as the ability to perform queries on all stored
106 clobber each other as well as the ability to perform queries on all stored
107 data.
107 data.
108
108
109 * **New configuration system**. All parts of IPython are now configured via a
109 * **New configuration system**. All parts of IPython are now configured via a
110 mechanism inspired by the Enthought Traits library. Any configurable element
110 mechanism inspired by the Enthought Traits library. Any configurable element
111 can have its attributes set either via files that now use real Python syntax
111 can have its attributes set either via files that now use real Python syntax
112 or from the command-line.
112 or from the command-line.
113
113
114 * **Pasting of code with prompts**. IPython now intelligently strips out input
114 * **Pasting of code with prompts**. IPython now intelligently strips out input
115 prompts , be they plain Python ones (``>>>`` and ``...``) or IPython ones
115 prompts , be they plain Python ones (``>>>`` and ``...``) or IPython ones
116 (``In [N]:`` and ``...:``). More details :ref:`here <pasting_with_prompts>`.
116 (``In [N]:`` and ``...:``). More details :ref:`here <pasting_with_prompts>`.
117
117
118
118
119 Authors and support
119 Authors and support
120 -------------------
120 -------------------
121
121
122 Over 60 separate authors have contributed to this release, see :ref:`below
122 Over 60 separate authors have contributed to this release, see :ref:`below
123 <credits_011>` for a full list. In particular, we want to highlight the
123 <credits_011>` for a full list. In particular, we want to highlight the
124 extremely active participation of two new core team members: Evan Patterson
124 extremely active participation of two new core team members: Evan Patterson
125 implemented the Qt console, and Thomas Kluyver started with our Python 3 port
125 implemented the Qt console, and Thomas Kluyver started with our Python 3 port
126 and by now has made major contributions to just about every area of IPython.
126 and by now has made major contributions to just about every area of IPython.
127
127
128 We are also grateful for the support we have received during this development
128 We are also grateful for the support we have received during this development
129 cycle from several institutions:
129 cycle from several institutions:
130
130
131 - `Enthought Inc`__ funded the development of our new Qt console, an effort that
131 - `Enthought Inc`__ funded the development of our new Qt console, an effort that
132 required developing major pieces of underlying infrastructure, which now
132 required developing major pieces of underlying infrastructure, which now
133 power not only the Qt console but also our new parallel machinery. We'd like
133 power not only the Qt console but also our new parallel machinery. We'd like
134 to thank Eric Jones and Travis Oliphant for their support, as well as Ilan
134 to thank Eric Jones and Travis Oliphant for their support, as well as Ilan
135 Schnell for his tireless work integrating and testing IPython in the
135 Schnell for his tireless work integrating and testing IPython in the
136 `Enthought Python Distribution`_.
136 `Enthought Python Distribution`_.
137
137
138 .. __: http://enthought.com
138 .. __: http://enthought.com
139 .. _Enthought Python Distribution: http://www.enthought.com/products/epd.php
139 .. _Enthought Python Distribution: http://www.enthought.com/products/epd.php
140
140
141 - Nipy/NIH: funding via the `NiPy project`__ (NIH grant 5R01MH081909-02) helped
141 - Nipy/NIH: funding via the `NiPy project`__ (NIH grant 5R01MH081909-02) helped
142 us jumpstart the development of this series by restructuring the entire
142 us jumpstart the development of this series by restructuring the entire
143 codebase two years ago in a way that would make modular development and
143 codebase two years ago in a way that would make modular development and
144 testing more approachable. Without this initial groundwork, all the new
144 testing more approachable. Without this initial groundwork, all the new
145 features we have added would have been impossible to develop.
145 features we have added would have been impossible to develop.
146
146
147 .. __: http://nipy.org
147 .. __: http://nipy.org
148
148
149 - Sage/NSF: funding via the grant `Sage: Unifying Mathematical Software for
149 - Sage/NSF: funding via the grant `Sage: Unifying Mathematical Software for
150 Scientists, Engineers, and Mathematicians`__ (NSF grant DMS-1015114)
150 Scientists, Engineers, and Mathematicians`__ (NSF grant DMS-1015114)
151 supported a meeting in spring 2011 of several of the core IPython developers
151 supported a meeting in spring 2011 of several of the core IPython developers
152 where major progress was made integrating the last key pieces leading to this
152 where major progress was made integrating the last key pieces leading to this
153 release.
153 release.
154
154
155 .. __: http://modular.math.washington.edu/grants/compmath09
155 .. __: http://modular.math.washington.edu/grants/compmath09
156
156
157 - Microsoft's team working on `Python Tools for Visual Studio`__ developed the
157 - Microsoft's team working on `Python Tools for Visual Studio`__ developed the
158 integraton of IPython into the Python plugin for Visual Studio 2010.
158 integraton of IPython into the Python plugin for Visual Studio 2010.
159
159
160 .. __: http://pytools.codeplex.com
160 .. __: http://pytools.codeplex.com
161
161
162 - Google Summer of Code: in 2010, we had two students developing prototypes of
162 - Google Summer of Code: in 2010, we had two students developing prototypes of
163 the new machinery that is now maturing in this release: `Omar Zapata`_ and
163 the new machinery that is now maturing in this release: `Omar Zapata`_ and
164 `Gerardo Gutiérrez`_.
164 `Gerardo Gutiérrez`_.
165
165
166 .. _Omar Zapata: http://ipythonzmq.blogspot.com/2010/08/ipython-zmq-status.html
166 .. _Omar Zapata: http://ipythonzmq.blogspot.com/2010/08/ipython-zmq-status.html
167 .. _Gerardo Gutiérrez: http://ipythonqt.blogspot.com/2010/04/ipython-qt-interface-gsoc-2010-proposal.html>
167 .. _Gerardo Gutiérrez: http://ipythonqt.blogspot.com/2010/04/ipython-qt-interface-gsoc-2010-proposal.html>
168
168
169
169
170 Development summary: moving to Git and Github
170 Development summary: moving to Git and Github
171 ---------------------------------------------
171 ---------------------------------------------
172
172
173 In April 2010, after `one breakage too many with bzr`__, we decided to move our
173 In April 2010, after `one breakage too many with bzr`__, we decided to move our
174 entire development process to Git and Github.com. This has proven to be one of
174 entire development process to Git and Github.com. This has proven to be one of
175 the best decisions in the project's history, as the combination of git and
175 the best decisions in the project's history, as the combination of git and
176 github have made us far, far more productive than we could be with our previous
176 github have made us far, far more productive than we could be with our previous
177 tools. We first converted our bzr repo to a git one without losing history,
177 tools. We first converted our bzr repo to a git one without losing history,
178 and a few weeks later ported all open Launchpad bugs to github issues with
178 and a few weeks later ported all open Launchpad bugs to github issues with
179 their comments mostly intact (modulo some formatting changes). This ensured a
179 their comments mostly intact (modulo some formatting changes). This ensured a
180 smooth transition where no development history or submitted bugs were lost.
180 smooth transition where no development history or submitted bugs were lost.
181 Feel free to use our little Launchpad to Github issues `porting script`_ if you
181 Feel free to use our little Launchpad to Github issues `porting script`_ if you
182 need to make a similar transition.
182 need to make a similar transition.
183
183
184 .. __: http://mail.scipy.org/pipermail/ipython-dev/2010-April/005944.html
184 .. __: http://mail.scipy.org/pipermail/ipython-dev/2010-April/005944.html
185 .. _porting script: https://gist.github.com/835577
185 .. _porting script: https://gist.github.com/835577
186
186
187 These simple statistics show how much work has been done on the new release, by
187 These simple statistics show how much work has been done on the new release, by
188 comparing the current code to the last point it had in common with the 0.10
188 comparing the current code to the last point it had in common with the 0.10
189 series. A huge diff and ~2200 commits make up this cycle::
189 series. A huge diff and ~2200 commits make up this cycle::
190
190
191 git diff $(git merge-base 0.10.2 HEAD) | wc -l
191 git diff $(git merge-base 0.10.2 HEAD) | wc -l
192 288019
192 288019
193
193
194 git log $(git merge-base 0.10.2 HEAD)..HEAD --oneline | wc -l
194 git log $(git merge-base 0.10.2 HEAD)..HEAD --oneline | wc -l
195 2200
195 2200
196
196
197 Since our move to github, 511 issues were closed, 226 of which were pull
197 Since our move to github, 511 issues were closed, 226 of which were pull
198 requests and 285 regular issues (:ref:`a full list with links
198 requests and 285 regular issues (:ref:`a full list with links
199 <issues_list_011>` is available for those interested in the details). Github's
199 <issues_list_011>` is available for those interested in the details). Github's
200 pull requests are a fantastic mechanism for reviewing code and building a
200 pull requests are a fantastic mechanism for reviewing code and building a
201 shared ownership of the project, and we are making enthusiastic use of it.
201 shared ownership of the project, and we are making enthusiastic use of it.
202
202
203 .. Note::
203 .. Note::
204
204
205 This undercounts the number of issues closed in this development cycle,
205 This undercounts the number of issues closed in this development cycle,
206 since we only moved to github for issue tracking in May 2010, but we have no
206 since we only moved to github for issue tracking in May 2010, but we have no
207 way of collecting statistics on the number of issues closed in the old
207 way of collecting statistics on the number of issues closed in the old
208 Launchpad bug tracker prior to that.
208 Launchpad bug tracker prior to that.
209
209
210
210
211 .. _qtconsole_011:
211 .. _qtconsole_011:
212
212
213 Qt Console
213 Qt Console
214 ----------
214 ----------
215
215
216 IPython now ships with a Qt application that feels very much like a terminal,
216 IPython now ships with a Qt application that feels very much like a terminal,
217 but is in fact a rich GUI that runs an IPython client but supports inline
217 but is in fact a rich GUI that runs an IPython client but supports inline
218 figures, saving sessions to PDF and HTML, multiline editing with syntax
218 figures, saving sessions to PDF and HTML, multiline editing with syntax
219 highlighting, graphical calltips and much more:
219 highlighting, graphical calltips and much more:
220
220
221 .. figure:: ../_images/qtconsole.png
221 .. figure:: ../_images/qtconsole.png
222 :width: 400px
222 :width: 400px
223 :alt: IPython Qt console with embedded plots
223 :alt: IPython Qt console with embedded plots
224 :align: center
224 :align: center
225 :target: ../_images/qtconsole.png
225 :target: ../_images/qtconsole.png
226
226
227 The Qt console for IPython, using inline matplotlib plots.
227 The Qt console for IPython, using inline matplotlib plots.
228
228
229 We hope that many projects will embed this widget, which we've kept
229 We hope that many projects will embed this widget, which we've kept
230 deliberately very lightweight, into their own environments. In the future we
230 deliberately very lightweight, into their own environments. In the future we
231 may also offer a slightly more featureful application (with menus and other GUI
231 may also offer a slightly more featureful application (with menus and other GUI
232 elements), but we remain committed to always shipping this easy to embed
232 elements), but we remain committed to always shipping this easy to embed
233 widget.
233 widget.
234
234
235 See the :ref:`Qt console section <qtconsole>` of the docs for a detailed
235 See the :ref:`Qt console section <qtconsole>` of the docs for a detailed
236 description of the console's features and use.
236 description of the console's features and use.
237
237
238
238
239 .. _parallel_011:
239 .. _parallel_011:
240
240
241 High-level parallel computing with ZeroMQ
241 High-level parallel computing with ZeroMQ
242 -----------------------------------------
242 -----------------------------------------
243
243
244 We have completely rewritten the Twisted-based code for high-level parallel
244 We have completely rewritten the Twisted-based code for high-level parallel
245 computing to work atop our new ZeroMQ architecture. While we realize this will
245 computing to work atop our new ZeroMQ architecture. While we realize this will
246 break compatibility for a number of users, we hope to make the transition as
246 break compatibility for a number of users, we hope to make the transition as
247 easy as possible with our docs, and we are convinced the change is worth it.
247 easy as possible with our docs, and we are convinced the change is worth it.
248 ZeroMQ provides us with much tighter control over memory, higher performance,
248 ZeroMQ provides us with much tighter control over memory, higher performance,
249 and its communications are impervious to the Python Global Interpreter Lock
249 and its communications are impervious to the Python Global Interpreter Lock
250 because they take place in a system-level C++ thread. The impact of the GIL in
250 because they take place in a system-level C++ thread. The impact of the GIL in
251 our previous code was something we could simply not work around, given that
251 our previous code was something we could simply not work around, given that
252 Twisted is itself a Python library. So while Twisted is a very capable
252 Twisted is itself a Python library. So while Twisted is a very capable
253 framework, we think ZeroMQ fits our needs much better and we hope you will find
253 framework, we think ZeroMQ fits our needs much better and we hope you will find
254 the change to be a significant improvement in the long run.
254 the change to be a significant improvement in the long run.
255
255
256 Our manual contains :ref:`a full description of how to use IPython for parallel
256 Our manual contains :ref:`a full description of how to use IPython for parallel
257 computing <parallel_overview>`, and the `tutorial`__ presented by Min
257 computing <parallel_overview>`, and the `tutorial`__ presented by Min
258 Ragan-Kelley at the SciPy 2011 conference provides a hands-on complement to the
258 Ragan-Kelley at the SciPy 2011 conference provides a hands-on complement to the
259 reference docs.
259 reference docs.
260
260
261 .. __: http://minrk.github.com/scipy-tutorial-2011
261 .. __: http://minrk.github.com/scipy-tutorial-2011
262
262
263
263
264 Refactoring
264 Refactoring
265 -----------
265 -----------
266
266
267 As of this release, a signifiant portion of IPython has been refactored. This
267 As of this release, a signifiant portion of IPython has been refactored. This
268 refactoring is founded on a number of new abstractions. The main new classes
268 refactoring is founded on a number of new abstractions. The main new classes
269 that implement these abstractions are:
269 that implement these abstractions are:
270
270
271 * :class:`IPython.utils.traitlets.HasTraits`.
271 * :class:`IPython.utils.traitlets.HasTraits`.
272 * :class:`IPython.config.configurable.Configurable`.
272 * :class:`IPython.config.configurable.Configurable`.
273 * :class:`IPython.config.application.Application`.
273 * :class:`IPython.config.application.Application`.
274 * :class:`IPython.config.loader.ConfigLoader`.
274 * :class:`IPython.config.loader.ConfigLoader`.
275 * :class:`IPython.config.loader.Config`
275 * :class:`IPython.config.loader.Config`
276
276
277 We are still in the process of writing developer focused documentation about
277 We are still in the process of writing developer focused documentation about
278 these classes, but for now our :ref:`configuration documentation
278 these classes, but for now our :ref:`configuration documentation
279 <config_overview>` contains a high level overview of the concepts that these
279 <config_overview>` contains a high level overview of the concepts that these
280 classes express.
280 classes express.
281
281
282 The biggest user-visible change is likely the move to using the config system
282 The biggest user-visible change is likely the move to using the config system
283 to determine the command-line arguments for IPython applications. The benefit
283 to determine the command-line arguments for IPython applications. The benefit
284 of this is that *all* configurable values in IPython are exposed on the
284 of this is that *all* configurable values in IPython are exposed on the
285 command-line, but the syntax for specifying values has changed. The gist is
285 command-line, but the syntax for specifying values has changed. The gist is
286 that assigning values is pure Python assignment. Simple flags exist for
286 that assigning values is pure Python assignment. Simple flags exist for
287 commonly used options, these are always prefixed with '--'.
287 commonly used options, these are always prefixed with '--'.
288
288
289 The IPython command-line help has the details of all the options (via
289 The IPython command-line help has the details of all the options (via
290 ``ipythyon --help``), but a simple example should clarify things; the ``pylab``
290 ``ipythyon --help``), but a simple example should clarify things; the ``pylab``
291 flag can be used to start in pylab mode with the qt4 backend::
291 flag can be used to start in pylab mode with the qt4 backend::
292
292
293 ipython --pylab=qt
293 ipython --pylab=qt
294
294
295 which is equivalent to using the fully qualified form::
295 which is equivalent to using the fully qualified form::
296
296
297 ipython --TerminalIPythonApp.pylab=qt
297 ipython --TerminalIPythonApp.pylab=qt
298
298
299 The long-form options can be listed via ``ipython --help-all``.
299 The long-form options can be listed via ``ipython --help-all``.
300
300
301
301
302 ZeroMQ architecture
302 ZeroMQ architecture
303 -------------------
303 -------------------
304
304
305 There is a new GUI framework for IPython, based on a client-server model in
305 There is a new GUI framework for IPython, based on a client-server model in
306 which multiple clients can communicate with one IPython kernel, using the
306 which multiple clients can communicate with one IPython kernel, using the
307 ZeroMQ messaging framework. There is already a Qt console client, which can
307 ZeroMQ messaging framework. There is already a Qt console client, which can
308 be started by calling ``ipython qtconsole``. The protocol is :ref:`documented
308 be started by calling ``ipython qtconsole``. The protocol is :ref:`documented
309 <messaging>`.
309 <messaging>`.
310
310
311 The parallel computing framework has also been rewritten using ZMQ. The
311 The parallel computing framework has also been rewritten using ZMQ. The
312 protocol is described :ref:`here <parallel_messages>`, and the code is in the
312 protocol is described :ref:`here <parallel_messages>`, and the code is in the
313 new :mod:`IPython.parallel` module.
313 new :mod:`IPython.parallel` module.
314
314
315 .. _python3_011:
315 .. _python3_011:
316
316
317 Python 3 support
317 Python 3 support
318 ----------------
318 ----------------
319
319
320 A Python 3 version of IPython has been prepared. For the time being, this is
320 A Python 3 version of IPython has been prepared. For the time being, this is
321 maintained separately and updated from the main codebase. Its code can be found
321 maintained separately and updated from the main codebase. Its code can be found
322 `here <https://github.com/ipython/ipython-py3k>`_. The parallel computing
322 `here <https://github.com/ipython/ipython-py3k>`_. The parallel computing
323 components are not perfect on Python3, but most functionality appears to be
323 components are not perfect on Python3, but most functionality appears to be
324 working. As this work is evolving quickly, the best place to find updated
324 working. As this work is evolving quickly, the best place to find updated
325 information about it is our `Python 3 wiki page`__.
325 information about it is our `Python 3 wiki page`__.
326
326
327 .. __: http://wiki.ipython.org/index.php?title=Python_3
327 .. __: http://wiki.ipython.org/index.php?title=Python_3
328
328
329
329
330 Unicode
330 Unicode
331 -------
331 -------
332
332
333 Entering non-ascii characters in unicode literals (``u"€ø"``) now works
333 Entering non-ascii characters in unicode literals (``u"€ø"``) now works
334 properly on all platforms. However, entering these in byte/string literals
334 properly on all platforms. However, entering these in byte/string literals
335 (``"€ø"``) will not work as expected on Windows (or any platform where the
335 (``"€ø"``) will not work as expected on Windows (or any platform where the
336 terminal encoding is not UTF-8, as it typically is for Linux & Mac OS X). You
336 terminal encoding is not UTF-8, as it typically is for Linux & Mac OS X). You
337 can use escape sequences (``"\xe9\x82"``) to get bytes above 128, or use
337 can use escape sequences (``"\xe9\x82"``) to get bytes above 128, or use
338 unicode literals and encode them. This is a limitation of Python 2 which we
338 unicode literals and encode them. This is a limitation of Python 2 which we
339 cannot easily work around.
339 cannot easily work around.
340
340
341 .. _ms_visual_studio_011:
341 .. _ms_visual_studio_011:
342
342
343 Integration with Microsoft Visual Studio
343 Integration with Microsoft Visual Studio
344 ----------------------------------------
344 ----------------------------------------
345
345
346 IPython can be used as the interactive shell in the `Python plugin for
346 IPython can be used as the interactive shell in the `Python plugin for
347 Microsoft Visual Studio`__, as seen here:
347 Microsoft Visual Studio`__, as seen here:
348
348
349 .. figure:: ../_images/ms_visual_studio.png
349 .. figure:: ../_images/ms_visual_studio.png
350 :width: 500px
350 :width: 500px
351 :alt: IPython console embedded in Microsoft Visual Studio.
351 :alt: IPython console embedded in Microsoft Visual Studio.
352 :align: center
352 :align: center
353 :target: ../_images/ms_visual_studio.png
353 :target: ../_images/ms_visual_studio.png
354
354
355 IPython console embedded in Microsoft Visual Studio.
355 IPython console embedded in Microsoft Visual Studio.
356
356
357 The Microsoft team developing this currently has a release candidate out using
357 The Microsoft team developing this currently has a release candidate out using
358 IPython 0.11. We will continue to collaborate with them to ensure that as they
358 IPython 0.11. We will continue to collaborate with them to ensure that as they
359 approach their final release date, the integration with IPython remains smooth.
359 approach their final release date, the integration with IPython remains smooth.
360 We'd like to thank Dino Viehland and Shahrokh Mortazavi for the work they have
360 We'd like to thank Dino Viehland and Shahrokh Mortazavi for the work they have
361 done towards this feature, as well as Wenming Ye for his support of our WinHPC
361 done towards this feature, as well as Wenming Ye for his support of our WinHPC
362 capabilities.
362 capabilities.
363
363
364 .. __: http://pytools.codeplex.com
364 .. __: http://pytools.codeplex.com
365
365
366
366
367 Additional new features
367 Additional new features
368 -----------------------
368 -----------------------
369
369
370 * Added ``Bytes`` traitlet, removing ``Str``. All 'string' traitlets should
370 * Added ``Bytes`` traitlet, removing ``Str``. All 'string' traitlets should
371 either be ``Unicode`` if a real string, or ``Bytes`` if a C-string. This
371 either be ``Unicode`` if a real string, or ``Bytes`` if a C-string. This
372 removes ambiguity and helps the Python 3 transition.
372 removes ambiguity and helps the Python 3 transition.
373
373
374 * New magic ``%loadpy`` loads a python file from disk or web URL into
374 * New magic ``%loadpy`` loads a python file from disk or web URL into
375 the current input buffer.
375 the current input buffer.
376
376
377 * New magic ``%pastebin`` for sharing code via the 'Lodge it' pastebin.
377 * New magic ``%pastebin`` for sharing code via the 'Lodge it' pastebin.
378
378
379 * New magic ``%precision`` for controlling float and numpy pretty printing.
379 * New magic ``%precision`` for controlling float and numpy pretty printing.
380
380
381 * IPython applications initiate logging, so any object can gain access to
381 * IPython applications initiate logging, so any object can gain access to
382 a the logger of the currently running Application with:
382 a the logger of the currently running Application with:
383
383
384 .. sourcecode:: python
384 .. sourcecode:: python
385
385
386 from IPython.config.application import Application
386 from IPython.config.application import Application
387 logger = Application.instance().log
387 logger = Application.instance().log
388
388
389 * You can now get help on an object halfway through typing a command. For
389 * You can now get help on an object halfway through typing a command. For
390 instance, typing ``a = zip?`` shows the details of :func:`zip`. It also
390 instance, typing ``a = zip?`` shows the details of :func:`zip`. It also
391 leaves the command at the next prompt so you can carry on with it.
391 leaves the command at the next prompt so you can carry on with it.
392
392
393 * The input history is now written to an SQLite database. The API for
393 * The input history is now written to an SQLite database. The API for
394 retrieving items from the history has also been redesigned.
394 retrieving items from the history has also been redesigned.
395
395
396 * The :mod:`IPython.extensions.pretty` extension has been moved out of
396 * The :mod:`IPython.extensions.pretty` extension has been moved out of
397 quarantine and fully updated to the new extension API.
397 quarantine and fully updated to the new extension API.
398
398
399 * New magics for loading/unloading/reloading extensions have been added:
399 * New magics for loading/unloading/reloading extensions have been added:
400 ``%load_ext``, ``%unload_ext`` and ``%reload_ext``.
400 ``%load_ext``, ``%unload_ext`` and ``%reload_ext``.
401
401
402 * The configuration system and configuration files are brand new. See the
402 * The configuration system and configuration files are brand new. See the
403 configuration system :ref:`documentation <config_index>` for more details.
403 configuration system :ref:`documentation <config_index>` for more details.
404
404
405 * The :class:`~IPython.core.interactiveshell.InteractiveShell` class is now a
405 * The :class:`~IPython.core.interactiveshell.InteractiveShell` class is now a
406 :class:`~IPython.config.configurable.Configurable` subclass and has traitlets
406 :class:`~IPython.config.configurable.Configurable` subclass and has traitlets
407 that determine the defaults and runtime environment. The ``__init__`` method
407 that determine the defaults and runtime environment. The ``__init__`` method
408 has also been refactored so this class can be instantiated and run without
408 has also been refactored so this class can be instantiated and run without
409 the old :mod:`ipmaker` module.
409 the old :mod:`ipmaker` module.
410
410
411 * The methods of :class:`~IPython.core.interactiveshell.InteractiveShell` have
411 * The methods of :class:`~IPython.core.interactiveshell.InteractiveShell` have
412 been organized into sections to make it easier to turn more sections
412 been organized into sections to make it easier to turn more sections
413 of functionality into components.
413 of functionality into components.
414
414
415 * The embedded shell has been refactored into a truly standalone subclass of
415 * The embedded shell has been refactored into a truly standalone subclass of
416 :class:`InteractiveShell` called :class:`InteractiveShellEmbed`. All
416 :class:`InteractiveShell` called :class:`InteractiveShellEmbed`. All
417 embedding logic has been taken out of the base class and put into the
417 embedding logic has been taken out of the base class and put into the
418 embedded subclass.
418 embedded subclass.
419
419
420 * Added methods of :class:`~IPython.core.interactiveshell.InteractiveShell` to
420 * Added methods of :class:`~IPython.core.interactiveshell.InteractiveShell` to
421 help it cleanup after itself. The :meth:`cleanup` method controls this. We
421 help it cleanup after itself. The :meth:`cleanup` method controls this. We
422 couldn't do this in :meth:`__del__` because we have cycles in our object
422 couldn't do this in :meth:`__del__` because we have cycles in our object
423 graph that prevent it from being called.
423 graph that prevent it from being called.
424
424
425 * Created a new module :mod:`IPython.utils.importstring` for resolving
425 * Created a new module :mod:`IPython.utils.importstring` for resolving
426 strings like ``foo.bar.Bar`` to the actual class.
426 strings like ``foo.bar.Bar`` to the actual class.
427
427
428 * Completely refactored the :mod:`IPython.core.prefilter` module into
428 * Completely refactored the :mod:`IPython.core.prefilter` module into
429 :class:`~IPython.config.configurable.Configurable` subclasses. Added a new
429 :class:`~IPython.config.configurable.Configurable` subclasses. Added a new
430 layer into the prefilter system, called "transformations" that all new
430 layer into the prefilter system, called "transformations" that all new
431 prefilter logic should use (rather than the older "checker/handler"
431 prefilter logic should use (rather than the older "checker/handler"
432 approach).
432 approach).
433
433
434 * Aliases are now components (:mod:`IPython.core.alias`).
434 * Aliases are now components (:mod:`IPython.core.alias`).
435
435
436 * New top level :func:`~IPython.frontend.terminal.embed.embed` function that can
436 * New top level :func:`~IPython.frontend.terminal.embed.embed` function that can
437 be called to embed IPython at any place in user's code. On the first call it
437 be called to embed IPython at any place in user's code. On the first call it
438 will create an :class:`~IPython.frontend.terminal.embed.InteractiveShellEmbed`
438 will create an :class:`~IPython.frontend.terminal.embed.InteractiveShellEmbed`
439 instance and call it. In later calls, it just calls the previously created
439 instance and call it. In later calls, it just calls the previously created
440 :class:`~IPython.frontend.terminal.embed.InteractiveShellEmbed`.
440 :class:`~IPython.frontend.terminal.embed.InteractiveShellEmbed`.
441
441
442 * Created a configuration system (:mod:`IPython.config.configurable`) that is
442 * Created a configuration system (:mod:`IPython.config.configurable`) that is
443 based on :mod:`IPython.utils.traitlets`. Configurables are arranged into a
443 based on :mod:`IPython.utils.traitlets`. Configurables are arranged into a
444 runtime containment tree (not inheritance) that i) automatically propagates
444 runtime containment tree (not inheritance) that i) automatically propagates
445 configuration information and ii) allows singletons to discover each other in
445 configuration information and ii) allows singletons to discover each other in
446 a loosely coupled manner. In the future all parts of IPython will be
446 a loosely coupled manner. In the future all parts of IPython will be
447 subclasses of :class:`~IPython.config.configurable.Configurable`. All IPython
447 subclasses of :class:`~IPython.config.configurable.Configurable`. All IPython
448 developers should become familiar with the config system.
448 developers should become familiar with the config system.
449
449
450 * Created a new :class:`~IPython.config.loader.Config` for holding
450 * Created a new :class:`~IPython.config.loader.Config` for holding
451 configuration information. This is a dict like class with a few extras: i)
451 configuration information. This is a dict like class with a few extras: i)
452 it supports attribute style access, ii) it has a merge function that merges
452 it supports attribute style access, ii) it has a merge function that merges
453 two :class:`~IPython.config.loader.Config` instances recursively and iii) it
453 two :class:`~IPython.config.loader.Config` instances recursively and iii) it
454 will automatically create sub-:class:`~IPython.config.loader.Config`
454 will automatically create sub-:class:`~IPython.config.loader.Config`
455 instances for attributes that start with an uppercase character.
455 instances for attributes that start with an uppercase character.
456
456
457 * Created new configuration loaders in :mod:`IPython.config.loader`. These
457 * Created new configuration loaders in :mod:`IPython.config.loader`. These
458 loaders provide a unified loading interface for all configuration
458 loaders provide a unified loading interface for all configuration
459 information including command line arguments and configuration files. We
459 information including command line arguments and configuration files. We
460 have two default implementations based on :mod:`argparse` and plain python
460 have two default implementations based on :mod:`argparse` and plain python
461 files. These are used to implement the new configuration system.
461 files. These are used to implement the new configuration system.
462
462
463 * Created a top-level :class:`Application` class in
463 * Created a top-level :class:`Application` class in
464 :mod:`IPython.core.application` that is designed to encapsulate the starting
464 :mod:`IPython.core.application` that is designed to encapsulate the starting
465 of any basic Python program. An application loads and merges all the
465 of any basic Python program. An application loads and merges all the
466 configuration objects, constructs the main application, configures and
466 configuration objects, constructs the main application, configures and
467 initiates logging, and creates and configures any :class:`Configurable`
467 initiates logging, and creates and configures any :class:`Configurable`
468 instances and then starts the application running. An extended
468 instances and then starts the application running. An extended
469 :class:`BaseIPythonApplication` class adds logic for handling the
469 :class:`BaseIPythonApplication` class adds logic for handling the
470 IPython directory as well as profiles, and all IPython entry points
470 IPython directory as well as profiles, and all IPython entry points
471 extend it.
471 extend it.
472
472
473 * The :class:`Type` and :class:`Instance` traitlets now handle classes given
473 * The :class:`Type` and :class:`Instance` traitlets now handle classes given
474 as strings, like ``foo.bar.Bar``. This is needed for forward declarations.
474 as strings, like ``foo.bar.Bar``. This is needed for forward declarations.
475 But, this was implemented in a careful way so that string to class
475 But, this was implemented in a careful way so that string to class
476 resolution is done at a single point, when the parent
476 resolution is done at a single point, when the parent
477 :class:`~IPython.utils.traitlets.HasTraitlets` is instantiated.
477 :class:`~IPython.utils.traitlets.HasTraitlets` is instantiated.
478
478
479 * :mod:`IPython.utils.ipstruct` has been refactored to be a subclass of
479 * :mod:`IPython.utils.ipstruct` has been refactored to be a subclass of
480 dict. It also now has full docstrings and doctests.
480 dict. It also now has full docstrings and doctests.
481
481
482 * Created a Traits like implementation in :mod:`IPython.utils.traitlets`. This
482 * Created a Traits like implementation in :mod:`IPython.utils.traitlets`. This
483 is a pure Python, lightweight version of a library that is similar to
483 is a pure Python, lightweight version of a library that is similar to
484 Enthought's Traits project, but has no dependencies on Enthought's code. We
484 Enthought's Traits project, but has no dependencies on Enthought's code. We
485 are using this for validation, defaults and notification in our new component
485 are using this for validation, defaults and notification in our new component
486 system. Although it is not 100% API compatible with Enthought's Traits, we
486 system. Although it is not 100% API compatible with Enthought's Traits, we
487 plan on moving in this direction so that eventually our implementation could
487 plan on moving in this direction so that eventually our implementation could
488 be replaced by a (yet to exist) pure Python version of Enthought Traits.
488 be replaced by a (yet to exist) pure Python version of Enthought Traits.
489
489
490 * Added a new module :mod:`IPython.lib.inputhook` to manage the integration
490 * Added a new module :mod:`IPython.lib.inputhook` to manage the integration
491 with GUI event loops using `PyOS_InputHook`. See the docstrings in this
491 with GUI event loops using `PyOS_InputHook`. See the docstrings in this
492 module or the main IPython docs for details.
492 module or the main IPython docs for details.
493
493
494 * For users, GUI event loop integration is now handled through the new
494 * For users, GUI event loop integration is now handled through the new
495 :command:`%gui` magic command. Type ``%gui?`` at an IPython prompt for
495 :command:`%gui` magic command. Type ``%gui?`` at an IPython prompt for
496 documentation.
496 documentation.
497
497
498 * For developers :mod:`IPython.lib.inputhook` provides a simple interface
498 * For developers :mod:`IPython.lib.inputhook` provides a simple interface
499 for managing the event loops in their interactive GUI applications.
499 for managing the event loops in their interactive GUI applications.
500 Examples can be found in our :file:`examples/lib` directory.
500 Examples can be found in our :file:`examples/lib` directory.
501
501
502 Backwards incompatible changes
502 Backwards incompatible changes
503 ------------------------------
503 ------------------------------
504
504
505 * The Twisted-based :mod:`IPython.kernel` has been removed, and completely
505 * The Twisted-based :mod:`IPython.kernel` has been removed, and completely
506 rewritten as :mod:`IPython.parallel`, using ZeroMQ.
506 rewritten as :mod:`IPython.parallel`, using ZeroMQ.
507
507
508 * Profiles are now directories. Instead of a profile being a single config file,
508 * Profiles are now directories. Instead of a profile being a single config file,
509 profiles are now self-contained directories. By default, profiles get their
509 profiles are now self-contained directories. By default, profiles get their
510 own IPython history, log files, and everything. To create a new profile, do
510 own IPython history, log files, and everything. To create a new profile, do
511 ``ipython profile create <name>``.
511 ``ipython profile create <name>``.
512
512
513 * All IPython applications have been rewritten to use
513 * All IPython applications have been rewritten to use
514 :class:`~IPython.config.loader.KeyValueConfigLoader`. This means that
514 :class:`~IPython.config.loader.KeyValueConfigLoader`. This means that
515 command-line options have changed. Now, all configurable values are accessible
515 command-line options have changed. Now, all configurable values are accessible
516 from the command-line with the same syntax as in a configuration file.
516 from the command-line with the same syntax as in a configuration file.
517
517
518 * The command line options ``-wthread``, ``-qthread`` and
518 * The command line options ``-wthread``, ``-qthread`` and
519 ``-gthread`` have been removed. Use ``--gui=wx``, ``--gui=qt``, ``--gui=gtk``
519 ``-gthread`` have been removed. Use ``--gui=wx``, ``--gui=qt``, ``--gui=gtk``
520 instead.
520 instead.
521
521
522 * The extension loading functions have been renamed to
522 * The extension loading functions have been renamed to
523 :func:`load_ipython_extension` and :func:`unload_ipython_extension`.
523 :func:`load_ipython_extension` and :func:`unload_ipython_extension`.
524
524
525 * :class:`~IPython.core.interactiveshell.InteractiveShell` no longer takes an
525 * :class:`~IPython.core.interactiveshell.InteractiveShell` no longer takes an
526 ``embedded`` argument. Instead just use the
526 ``embedded`` argument. Instead just use the
527 :class:`~IPython.core.interactiveshell.InteractiveShellEmbed` class.
527 :class:`~IPython.core.interactiveshell.InteractiveShellEmbed` class.
528
528
529 * ``__IPYTHON__`` is no longer injected into ``__builtin__``.
529 * ``__IPYTHON__`` is no longer injected into ``__builtin__``.
530
530
531 * :meth:`Struct.__init__` no longer takes `None` as its first argument. It
531 * :meth:`Struct.__init__` no longer takes `None` as its first argument. It
532 must be a :class:`dict` or :class:`Struct`.
532 must be a :class:`dict` or :class:`Struct`.
533
533
534 * :meth:`~IPython.core.interactiveshell.InteractiveShell.ipmagic` has been
534 * :meth:`~IPython.core.interactiveshell.InteractiveShell.ipmagic` has been
535 renamed :meth:`~IPython.core.interactiveshell.InteractiveShell.magic.`
535 renamed :meth:`~IPython.core.interactiveshell.InteractiveShell.magic.`
536
536
537 * The functions :func:`ipmagic` and :func:`ipalias` have been removed from
537 * The functions :func:`ipmagic` and :func:`ipalias` have been removed from
538 :mod:`__builtins__`.
538 :mod:`__builtins__`.
539
539
540 * The references to the global
540 * The references to the global
541 :class:`~IPython.core.interactivehell.InteractiveShell` instance (``_ip``, and
541 :class:`~IPython.core.interactivehell.InteractiveShell` instance (``_ip``, and
542 ``__IP``) have been removed from the user's namespace. They are replaced by a
542 ``__IP``) have been removed from the user's namespace. They are replaced by a
543 new function called :func:`get_ipython` that returns the current
543 new function called :func:`get_ipython` that returns the current
544 :class:`~IPython.core.interactiveshell.InteractiveShell` instance. This
544 :class:`~IPython.core.interactiveshell.InteractiveShell` instance. This
545 function is injected into the user's namespace and is now the main way of
545 function is injected into the user's namespace and is now the main way of
546 accessing the running IPython.
546 accessing the running IPython.
547
547
548 * Old style configuration files :file:`ipythonrc` and :file:`ipy_user_conf.py`
548 * Old style configuration files :file:`ipythonrc` and :file:`ipy_user_conf.py`
549 are no longer supported. Users should migrate there configuration files to
549 are no longer supported. Users should migrate there configuration files to
550 the new format described :ref:`here <config_overview>` and :ref:`here
550 the new format described :doc:`here <config/intro>` and
551 <configuring_ipython>`.
551 :ref:`here <config_overview>`.
552
552
553 * The old IPython extension API that relied on :func:`ipapi` has been
553 * The old IPython extension API that relied on :func:`ipapi` has been
554 completely removed. The new extension API is described :ref:`here
554 completely removed. The new extension API is described :ref:`here
555 <configuring_ipython>`.
555 <extensions_overview>`.
556
556
557 * Support for ``qt3`` has been dropped. Users who need this should use
557 * Support for ``qt3`` has been dropped. Users who need this should use
558 previous versions of IPython.
558 previous versions of IPython.
559
559
560 * Removed :mod:`shellglobals` as it was obsolete.
560 * Removed :mod:`shellglobals` as it was obsolete.
561
561
562 * Removed all the threaded shells in :mod:`IPython.core.shell`. These are no
562 * Removed all the threaded shells in :mod:`IPython.core.shell`. These are no
563 longer needed because of the new capabilities in
563 longer needed because of the new capabilities in
564 :mod:`IPython.lib.inputhook`.
564 :mod:`IPython.lib.inputhook`.
565
565
566 * New top-level sub-packages have been created: :mod:`IPython.core`,
566 * New top-level sub-packages have been created: :mod:`IPython.core`,
567 :mod:`IPython.lib`, :mod:`IPython.utils`, :mod:`IPython.deathrow`,
567 :mod:`IPython.lib`, :mod:`IPython.utils`, :mod:`IPython.deathrow`,
568 :mod:`IPython.quarantine`. All existing top-level modules have been
568 :mod:`IPython.quarantine`. All existing top-level modules have been
569 moved to appropriate sub-packages. All internal import statements
569 moved to appropriate sub-packages. All internal import statements
570 have been updated and tests have been added. The build system (setup.py
570 have been updated and tests have been added. The build system (setup.py
571 and friends) have been updated. See :doc:`/api/index` for details of these
571 and friends) have been updated. See :doc:`/api/index` for details of these
572 new sub-packages.
572 new sub-packages.
573
573
574 * :mod:`IPython.ipapi` has been moved to :mod:`IPython.core.ipapi`.
574 * :mod:`IPython.ipapi` has been moved to :mod:`IPython.core.ipapi`.
575 :mod:`IPython.Shell` and :mod:`IPython.iplib` have been split and removed as
575 :mod:`IPython.Shell` and :mod:`IPython.iplib` have been split and removed as
576 part of the refactor.
576 part of the refactor.
577
577
578 * :mod:`Extensions` has been moved to :mod:`extensions` and all existing
578 * :mod:`Extensions` has been moved to :mod:`extensions` and all existing
579 extensions have been moved to either :mod:`IPython.quarantine` or
579 extensions have been moved to either :mod:`IPython.quarantine` or
580 :mod:`IPython.deathrow`. :mod:`IPython.quarantine` contains modules that we
580 :mod:`IPython.deathrow`. :mod:`IPython.quarantine` contains modules that we
581 plan on keeping but that need to be updated. :mod:`IPython.deathrow` contains
581 plan on keeping but that need to be updated. :mod:`IPython.deathrow` contains
582 modules that are either dead or that should be maintained as third party
582 modules that are either dead or that should be maintained as third party
583 libraries.
583 libraries.
584
584
585 * Previous IPython GUIs in :mod:`IPython.frontend` and :mod:`IPython.gui` are
585 * Previous IPython GUIs in :mod:`IPython.frontend` and :mod:`IPython.gui` are
586 likely broken, and have been removed to :mod:`IPython.deathrow` because of the
586 likely broken, and have been removed to :mod:`IPython.deathrow` because of the
587 refactoring in the core. With proper updates, these should still work.
587 refactoring in the core. With proper updates, these should still work.
588
588
589
589
590 Known Regressions
590 Known Regressions
591 -----------------
591 -----------------
592
592
593 We do our best to improve IPython, but there are some known regressions in 0.11
593 We do our best to improve IPython, but there are some known regressions in 0.11
594 relative to 0.10.2. First of all, there are features that have yet to be
594 relative to 0.10.2. First of all, there are features that have yet to be
595 ported to the new APIs, and in order to ensure that all of the installed code
595 ported to the new APIs, and in order to ensure that all of the installed code
596 runs for our users, we have moved them to two separate directories in the
596 runs for our users, we have moved them to two separate directories in the
597 source distribution, `quarantine` and `deathrow`. Finally, we have some other
597 source distribution, `quarantine` and `deathrow`. Finally, we have some other
598 miscellaneous regressions that we hope to fix as soon as possible. We now
598 miscellaneous regressions that we hope to fix as soon as possible. We now
599 describe all of these in more detail.
599 describe all of these in more detail.
600
600
601 Quarantine
601 Quarantine
602 ~~~~~~~~~~
602 ~~~~~~~~~~
603
603
604 These are tools and extensions that we consider relatively easy to update to
604 These are tools and extensions that we consider relatively easy to update to
605 the new classes and APIs, but that we simply haven't had time for. Any user
605 the new classes and APIs, but that we simply haven't had time for. Any user
606 who is interested in one of these is encouraged to help us by porting it and
606 who is interested in one of these is encouraged to help us by porting it and
607 submitting a pull request on our `development site`_.
607 submitting a pull request on our `development site`_.
608
608
609 .. _development site: http://github.com/ipython/ipython
609 .. _development site: http://github.com/ipython/ipython
610
610
611 Currently, the quarantine directory contains::
611 Currently, the quarantine directory contains::
612
612
613 clearcmd.py ipy_fsops.py ipy_signals.py
613 clearcmd.py ipy_fsops.py ipy_signals.py
614 envpersist.py ipy_gnuglobal.py ipy_synchronize_with.py
614 envpersist.py ipy_gnuglobal.py ipy_synchronize_with.py
615 ext_rescapture.py ipy_greedycompleter.py ipy_system_conf.py
615 ext_rescapture.py ipy_greedycompleter.py ipy_system_conf.py
616 InterpreterExec.py ipy_jot.py ipy_which.py
616 InterpreterExec.py ipy_jot.py ipy_which.py
617 ipy_app_completers.py ipy_lookfor.py ipy_winpdb.py
617 ipy_app_completers.py ipy_lookfor.py ipy_winpdb.py
618 ipy_autoreload.py ipy_profile_doctest.py ipy_workdir.py
618 ipy_autoreload.py ipy_profile_doctest.py ipy_workdir.py
619 ipy_completers.py ipy_pydb.py jobctrl.py
619 ipy_completers.py ipy_pydb.py jobctrl.py
620 ipy_editors.py ipy_rehashdir.py ledit.py
620 ipy_editors.py ipy_rehashdir.py ledit.py
621 ipy_exportdb.py ipy_render.py pspersistence.py
621 ipy_exportdb.py ipy_render.py pspersistence.py
622 ipy_extutil.py ipy_server.py win32clip.py
622 ipy_extutil.py ipy_server.py win32clip.py
623
623
624 Deathrow
624 Deathrow
625 ~~~~~~~~
625 ~~~~~~~~
626
626
627 These packages may be harder to update or make most sense as third-party
627 These packages may be harder to update or make most sense as third-party
628 libraries. Some of them are completely obsolete and have been already replaced
628 libraries. Some of them are completely obsolete and have been already replaced
629 by better functionality (we simply haven't had the time to carefully weed them
629 by better functionality (we simply haven't had the time to carefully weed them
630 out so they are kept here for now). Others simply require fixes to code that
630 out so they are kept here for now). Others simply require fixes to code that
631 the current core team may not be familiar with. If a tool you were used to is
631 the current core team may not be familiar with. If a tool you were used to is
632 included here, we encourage you to contact the dev list and we can discuss
632 included here, we encourage you to contact the dev list and we can discuss
633 whether it makes sense to keep it in IPython (if it can be maintained).
633 whether it makes sense to keep it in IPython (if it can be maintained).
634
634
635 Currently, the deathrow directory contains::
635 Currently, the deathrow directory contains::
636
636
637 astyle.py ipy_defaults.py ipy_vimserver.py
637 astyle.py ipy_defaults.py ipy_vimserver.py
638 dtutils.py ipy_kitcfg.py numeric_formats.py
638 dtutils.py ipy_kitcfg.py numeric_formats.py
639 Gnuplot2.py ipy_legacy.py numutils.py
639 Gnuplot2.py ipy_legacy.py numutils.py
640 GnuplotInteractive.py ipy_p4.py outputtrap.py
640 GnuplotInteractive.py ipy_p4.py outputtrap.py
641 GnuplotRuntime.py ipy_profile_none.py PhysicalQInput.py
641 GnuplotRuntime.py ipy_profile_none.py PhysicalQInput.py
642 ibrowse.py ipy_profile_numpy.py PhysicalQInteractive.py
642 ibrowse.py ipy_profile_numpy.py PhysicalQInteractive.py
643 igrid.py ipy_profile_scipy.py quitter.py*
643 igrid.py ipy_profile_scipy.py quitter.py*
644 ipipe.py ipy_profile_sh.py scitedirector.py
644 ipipe.py ipy_profile_sh.py scitedirector.py
645 iplib.py ipy_profile_zope.py Shell.py
645 iplib.py ipy_profile_zope.py Shell.py
646 ipy_constants.py ipy_traits_completer.py twshell.py
646 ipy_constants.py ipy_traits_completer.py twshell.py
647
647
648
648
649 Other regressions
649 Other regressions
650 ~~~~~~~~~~~~~~~~~
650 ~~~~~~~~~~~~~~~~~
651
651
652 * The machinery that adds functionality to the 'sh' profile for using IPython
652 * The machinery that adds functionality to the 'sh' profile for using IPython
653 as your system shell has not been updated to use the new APIs. As a result,
653 as your system shell has not been updated to use the new APIs. As a result,
654 only the aesthetic (prompt) changes are still implemented. We intend to fix
654 only the aesthetic (prompt) changes are still implemented. We intend to fix
655 this by 0.12. Tracked as issue 547_.
655 this by 0.12. Tracked as issue 547_.
656
656
657 .. _547: https://github.com/ipython/ipython/issues/547
657 .. _547: https://github.com/ipython/ipython/issues/547
658
658
659 * The installation of scripts on Windows was broken without setuptools, so we
659 * The installation of scripts on Windows was broken without setuptools, so we
660 now depend on setuptools on Windows. We hope to fix setuptools-less
660 now depend on setuptools on Windows. We hope to fix setuptools-less
661 installation, and then remove the setuptools dependency. Issue 539_.
661 installation, and then remove the setuptools dependency. Issue 539_.
662
662
663 .. _539: https://github.com/ipython/ipython/issues/539
663 .. _539: https://github.com/ipython/ipython/issues/539
664
664
665 * The directory history `_dh` is not saved between sessions. Issue 634_.
665 * The directory history `_dh` is not saved between sessions. Issue 634_.
666
666
667 .. _634: https://github.com/ipython/ipython/issues/634
667 .. _634: https://github.com/ipython/ipython/issues/634
668
668
669
669
670 Removed Features
670 Removed Features
671 ----------------
671 ----------------
672
672
673 As part of the updating of IPython, we have removed a few features for the
673 As part of the updating of IPython, we have removed a few features for the
674 purposes of cleaning up the codebase and interfaces. These removals are
674 purposes of cleaning up the codebase and interfaces. These removals are
675 permanent, but for any item listed below, equivalent functionality is
675 permanent, but for any item listed below, equivalent functionality is
676 available.
676 available.
677
677
678 * The magics Exit and Quit have been dropped as ways to exit IPython. Instead,
678 * The magics Exit and Quit have been dropped as ways to exit IPython. Instead,
679 the lowercase forms of both work either as a bare name (``exit``) or a
679 the lowercase forms of both work either as a bare name (``exit``) or a
680 function call (``exit()``). You can assign these to other names using
680 function call (``exit()``). You can assign these to other names using
681 exec_lines in the config file.
681 exec_lines in the config file.
682
682
683
683
684 .. _credits_011:
684 .. _credits_011:
685
685
686 Credits
686 Credits
687 -------
687 -------
688
688
689 Many users and developers contributed code, features, bug reports and ideas to
689 Many users and developers contributed code, features, bug reports and ideas to
690 this release. Please do not hesitate in contacting us if we've failed to
690 this release. Please do not hesitate in contacting us if we've failed to
691 acknowledge your contribution here. In particular, for this release we have
691 acknowledge your contribution here. In particular, for this release we have
692 contribution from the following people, a mix of new and regular names (in
692 contribution from the following people, a mix of new and regular names (in
693 alphabetical order by first name):
693 alphabetical order by first name):
694
694
695 * Aenugu Sai Kiran Reddy <saikrn08-at-gmail.com>
695 * Aenugu Sai Kiran Reddy <saikrn08-at-gmail.com>
696 * andy wilson <wilson.andrew.j+github-at-gmail.com>
696 * andy wilson <wilson.andrew.j+github-at-gmail.com>
697 * Antonio Cuni <antocuni>
697 * Antonio Cuni <antocuni>
698 * Barry Wark <barrywark-at-gmail.com>
698 * Barry Wark <barrywark-at-gmail.com>
699 * Beetoju Anuradha <anu.beethoju-at-gmail.com>
699 * Beetoju Anuradha <anu.beethoju-at-gmail.com>
700 * Benjamin Ragan-Kelley <minrk-at-Mercury.local>
700 * Benjamin Ragan-Kelley <minrk-at-Mercury.local>
701 * Brad Reisfeld
701 * Brad Reisfeld
702 * Brian E. Granger <ellisonbg-at-gmail.com>
702 * Brian E. Granger <ellisonbg-at-gmail.com>
703 * Christoph Gohlke <cgohlke-at-uci.edu>
703 * Christoph Gohlke <cgohlke-at-uci.edu>
704 * Cody Precord
704 * Cody Precord
705 * dan.milstein
705 * dan.milstein
706 * Darren Dale <dsdale24-at-gmail.com>
706 * Darren Dale <dsdale24-at-gmail.com>
707 * Dav Clark <davclark-at-berkeley.edu>
707 * Dav Clark <davclark-at-berkeley.edu>
708 * David Warde-Farley <wardefar-at-iro.umontreal.ca>
708 * David Warde-Farley <wardefar-at-iro.umontreal.ca>
709 * epatters <ejpatters-at-gmail.com>
709 * epatters <ejpatters-at-gmail.com>
710 * epatters <epatters-at-caltech.edu>
710 * epatters <epatters-at-caltech.edu>
711 * epatters <epatters-at-enthought.com>
711 * epatters <epatters-at-enthought.com>
712 * Eric Firing <efiring-at-hawaii.edu>
712 * Eric Firing <efiring-at-hawaii.edu>
713 * Erik Tollerud <erik.tollerud-at-gmail.com>
713 * Erik Tollerud <erik.tollerud-at-gmail.com>
714 * Evan Patterson <epatters-at-enthought.com>
714 * Evan Patterson <epatters-at-enthought.com>
715 * Fernando Perez <Fernando.Perez-at-berkeley.edu>
715 * Fernando Perez <Fernando.Perez-at-berkeley.edu>
716 * Gael Varoquaux <gael.varoquaux-at-normalesup.org>
716 * Gael Varoquaux <gael.varoquaux-at-normalesup.org>
717 * Gerardo <muzgash-at-Muzpelheim>
717 * Gerardo <muzgash-at-Muzpelheim>
718 * Jason Grout <jason.grout-at-drake.edu>
718 * Jason Grout <jason.grout-at-drake.edu>
719 * John Hunter <jdh2358-at-gmail.com>
719 * John Hunter <jdh2358-at-gmail.com>
720 * Jens Hedegaard Nielsen <jenshnielsen-at-gmail.com>
720 * Jens Hedegaard Nielsen <jenshnielsen-at-gmail.com>
721 * Johann Cohen-Tanugi <johann.cohentanugi-at-gmail.com>
721 * Johann Cohen-Tanugi <johann.cohentanugi-at-gmail.com>
722 * Jörgen Stenarson <jorgen.stenarson-at-bostream.nu>
722 * Jörgen Stenarson <jorgen.stenarson-at-bostream.nu>
723 * Justin Riley <justin.t.riley-at-gmail.com>
723 * Justin Riley <justin.t.riley-at-gmail.com>
724 * Kiorky
724 * Kiorky
725 * Laurent Dufrechou <laurent.dufrechou-at-gmail.com>
725 * Laurent Dufrechou <laurent.dufrechou-at-gmail.com>
726 * Luis Pedro Coelho <lpc-at-cmu.edu>
726 * Luis Pedro Coelho <lpc-at-cmu.edu>
727 * Mani chandra <mchandra-at-iitk.ac.in>
727 * Mani chandra <mchandra-at-iitk.ac.in>
728 * Mark E. Smith
728 * Mark E. Smith
729 * Mark Voorhies <mark.voorhies-at-ucsf.edu>
729 * Mark Voorhies <mark.voorhies-at-ucsf.edu>
730 * Martin Spacek <git-at-mspacek.mm.st>
730 * Martin Spacek <git-at-mspacek.mm.st>
731 * Michael Droettboom <mdroe-at-stsci.edu>
731 * Michael Droettboom <mdroe-at-stsci.edu>
732 * MinRK <benjaminrk-at-gmail.com>
732 * MinRK <benjaminrk-at-gmail.com>
733 * muzuiget <muzuiget-at-gmail.com>
733 * muzuiget <muzuiget-at-gmail.com>
734 * Nick Tarleton <nick-at-quixey.com>
734 * Nick Tarleton <nick-at-quixey.com>
735 * Nicolas Rougier <Nicolas.rougier-at-inria.fr>
735 * Nicolas Rougier <Nicolas.rougier-at-inria.fr>
736 * Omar Andres Zapata Mesa <andresete.chaos-at-gmail.com>
736 * Omar Andres Zapata Mesa <andresete.chaos-at-gmail.com>
737 * Paul Ivanov <pivanov314-at-gmail.com>
737 * Paul Ivanov <pivanov314-at-gmail.com>
738 * Pauli Virtanen <pauli.virtanen-at-iki.fi>
738 * Pauli Virtanen <pauli.virtanen-at-iki.fi>
739 * Prabhu Ramachandran
739 * Prabhu Ramachandran
740 * Ramana <sramana9-at-gmail.com>
740 * Ramana <sramana9-at-gmail.com>
741 * Robert Kern <robert.kern-at-gmail.com>
741 * Robert Kern <robert.kern-at-gmail.com>
742 * Sathesh Chandra <satheshchandra88-at-gmail.com>
742 * Sathesh Chandra <satheshchandra88-at-gmail.com>
743 * Satrajit Ghosh <satra-at-mit.edu>
743 * Satrajit Ghosh <satra-at-mit.edu>
744 * Sebastian Busch
744 * Sebastian Busch
745 * Skipper Seabold <jsseabold-at-gmail.com>
745 * Skipper Seabold <jsseabold-at-gmail.com>
746 * Stefan van der Walt <bzr-at-mentat.za.net>
746 * Stefan van der Walt <bzr-at-mentat.za.net>
747 * Stephan Peijnik <debian-at-sp.or.at>
747 * Stephan Peijnik <debian-at-sp.or.at>
748 * Steven Bethard
748 * Steven Bethard
749 * Thomas Kluyver <takowl-at-gmail.com>
749 * Thomas Kluyver <takowl-at-gmail.com>
750 * Thomas Spura <tomspur-at-fedoraproject.org>
750 * Thomas Spura <tomspur-at-fedoraproject.org>
751 * Tom Fetherston <tfetherston-at-aol.com>
751 * Tom Fetherston <tfetherston-at-aol.com>
752 * Tom MacWright
752 * Tom MacWright
753 * tzanko
753 * tzanko
754 * vankayala sowjanya <hai.sowjanya-at-gmail.com>
754 * vankayala sowjanya <hai.sowjanya-at-gmail.com>
755 * Vivian De Smedt <vds2212-at-VIVIAN>
755 * Vivian De Smedt <vds2212-at-VIVIAN>
756 * Ville M. Vainio <vivainio-at-gmail.com>
756 * Ville M. Vainio <vivainio-at-gmail.com>
757 * Vishal Vatsa <vishal.vatsa-at-gmail.com>
757 * Vishal Vatsa <vishal.vatsa-at-gmail.com>
758 * Vishnu S G <sgvishnu777-at-gmail.com>
758 * Vishnu S G <sgvishnu777-at-gmail.com>
759 * Walter Doerwald <walter-at-livinglogic.de>
759 * Walter Doerwald <walter-at-livinglogic.de>
760
760
761 .. note::
761 .. note::
762
762
763 This list was generated with the output of
763 This list was generated with the output of
764 ``git log dev-0.11 HEAD --format='* %aN <%aE>' | sed 's/@/\-at\-/' | sed 's/<>//' | sort -u``
764 ``git log dev-0.11 HEAD --format='* %aN <%aE>' | sed 's/@/\-at\-/' | sed 's/<>//' | sort -u``
765 after some cleanup. If you should be on this list, please add yourself.
765 after some cleanup. If you should be on this list, please add yourself.
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now