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