Show More
The requested changes are too big and content was truncated. Show full diff
@@ -0,0 +1,244 b'' | |||
|
1 | """ | |
|
2 | IPython extension: autoreload modules before executing the next line | |
|
3 | ||
|
4 | Try:: | |
|
5 | ||
|
6 | %autoreload? | |
|
7 | ||
|
8 | for documentation. | |
|
9 | """ | |
|
10 | ||
|
11 | # Pauli Virtanen <pav@iki.fi>, 2008. | |
|
12 | # Thomas Heller, 2000. | |
|
13 | # | |
|
14 | # This IPython module is written by Pauli Virtanen, based on the autoreload | |
|
15 | # code by Thomas Heller. | |
|
16 | ||
|
17 | #------------------------------------------------------------------------------ | |
|
18 | # Autoreload functionality | |
|
19 | #------------------------------------------------------------------------------ | |
|
20 | ||
|
21 | import time, os, threading, sys, types, imp, inspect, traceback, atexit | |
|
22 | ||
|
23 | def _get_compiled_ext(): | |
|
24 | """Official way to get the extension of compiled files (.pyc or .pyo)""" | |
|
25 | for ext, mode, typ in imp.get_suffixes(): | |
|
26 | if typ == imp.PY_COMPILED: | |
|
27 | return ext | |
|
28 | ||
|
29 | PY_COMPILED_EXT = _get_compiled_ext() | |
|
30 | ||
|
31 | class ModuleReloader(object): | |
|
32 | skipped = {} | |
|
33 | """Modules that failed to reload: {module: mtime-on-failed-reload, ...}""" | |
|
34 | ||
|
35 | modules = {} | |
|
36 | """Modules specially marked as autoreloadable.""" | |
|
37 | ||
|
38 | skip_modules = {} | |
|
39 | """Modules specially marked as not autoreloadable.""" | |
|
40 | ||
|
41 | check_all = True | |
|
42 | """Autoreload all modules, not just those listed in 'modules'""" | |
|
43 | ||
|
44 | def check(self, check_all=False): | |
|
45 | """Check whether some modules need to be reloaded.""" | |
|
46 | ||
|
47 | if check_all or self.check_all: | |
|
48 | modules = sys.modules.keys() | |
|
49 | else: | |
|
50 | modules = self.modules.keys() | |
|
51 | ||
|
52 | for modname in modules: | |
|
53 | m = sys.modules.get(modname, None) | |
|
54 | ||
|
55 | if modname in self.skip_modules: | |
|
56 | continue | |
|
57 | ||
|
58 | if not hasattr(m, '__file__'): | |
|
59 | continue | |
|
60 | ||
|
61 | if m.__name__ == '__main__': | |
|
62 | # we cannot reload(__main__) | |
|
63 | continue | |
|
64 | ||
|
65 | filename = m.__file__ | |
|
66 | dirname = os.path.dirname(filename) | |
|
67 | path, ext = os.path.splitext(filename) | |
|
68 | ||
|
69 | if ext.lower() == '.py': | |
|
70 | ext = PY_COMPILED_EXT | |
|
71 | filename = os.path.join(dirname, path + PY_COMPILED_EXT) | |
|
72 | ||
|
73 | if ext != PY_COMPILED_EXT: | |
|
74 | continue | |
|
75 | ||
|
76 | try: | |
|
77 | pymtime = os.stat(filename[:-1]).st_mtime | |
|
78 | if pymtime <= os.stat(filename).st_mtime: | |
|
79 | continue | |
|
80 | if self.skipped.get(filename[:-1], None) == pymtime: | |
|
81 | continue | |
|
82 | except OSError: | |
|
83 | continue | |
|
84 | ||
|
85 | try: | |
|
86 | superreload(m) | |
|
87 | if filename[:-1] in self.skipped: | |
|
88 | del self.skipped[filename[:-1]] | |
|
89 | except: | |
|
90 | self.skipped[filename[:-1]] = pymtime | |
|
91 | ||
|
92 | def update_function(old, new, attrnames): | |
|
93 | for name in attrnames: | |
|
94 | setattr(old, name, getattr(new, name)) | |
|
95 | ||
|
96 | def superreload(module, reload=reload): | |
|
97 | """Enhanced version of the builtin reload function. | |
|
98 | ||
|
99 | superreload replaces the class dictionary of every top-level | |
|
100 | class in the module with the new one automatically, | |
|
101 | as well as every function's code object. | |
|
102 | ||
|
103 | """ | |
|
104 | ||
|
105 | module = reload(module) | |
|
106 | ||
|
107 | # iterate over all objects and update them | |
|
108 | count = 0 | |
|
109 | for name, new_obj in module.__dict__.items(): | |
|
110 | key = (module.__name__, name) | |
|
111 | if _old_objects.has_key(key): | |
|
112 | for old_obj in _old_objects[key]: | |
|
113 | if type(new_obj) == types.ClassType: | |
|
114 | old_obj.__dict__.update(new_obj.__dict__) | |
|
115 | count += 1 | |
|
116 | elif type(new_obj) == types.FunctionType: | |
|
117 | update_function(old_obj, | |
|
118 | new_obj, | |
|
119 | "func_code func_defaults func_doc".split()) | |
|
120 | count += 1 | |
|
121 | elif type(new_obj) == types.MethodType: | |
|
122 | update_function(old_obj.im_func, | |
|
123 | new_obj.im_func, | |
|
124 | "func_code func_defaults func_doc".split()) | |
|
125 | count += 1 | |
|
126 | ||
|
127 | return module | |
|
128 | ||
|
129 | reloader = ModuleReloader() | |
|
130 | ||
|
131 | #------------------------------------------------------------------------------ | |
|
132 | # IPython monkey-patching | |
|
133 | #------------------------------------------------------------------------------ | |
|
134 | ||
|
135 | import IPython.iplib | |
|
136 | ||
|
137 | autoreload_enabled = False | |
|
138 | ||
|
139 | def runcode_hook(self): | |
|
140 | if not autoreload_enabled: | |
|
141 | raise IPython.ipapi.TryNext | |
|
142 | try: | |
|
143 | reloader.check() | |
|
144 | except: | |
|
145 | pass | |
|
146 | ||
|
147 | ||
|
148 | def enable_autoreload(): | |
|
149 | global autoreload_enabled | |
|
150 | autoreload_enabled = True | |
|
151 | ||
|
152 | ||
|
153 | def disable_autoreload(): | |
|
154 | global autoreload_enabled | |
|
155 | autoreload_enabled = False | |
|
156 | ||
|
157 | #------------------------------------------------------------------------------ | |
|
158 | # IPython connectivity | |
|
159 | #------------------------------------------------------------------------------ | |
|
160 | ||
|
161 | import IPython.ipapi | |
|
162 | ip = IPython.ipapi.get() | |
|
163 | ||
|
164 | def autoreload_f(self, parameter_s=''): | |
|
165 | r""" %autoreload => Reload modules automatically | |
|
166 | ||
|
167 | %autoreload | |
|
168 | Reload all modules (except thoses excluded by %aimport) automatically now. | |
|
169 | ||
|
170 | %autoreload 1 | |
|
171 | Reload all modules imported with %aimport every time before executing | |
|
172 | the Python code typed. | |
|
173 | ||
|
174 | %autoreload 2 | |
|
175 | Reload all modules (except thoses excluded by %aimport) every time | |
|
176 | before executing the Python code typed. | |
|
177 | ||
|
178 | Reloading Python modules in a reliable way is in general | |
|
179 | difficult, and unexpected things may occur. Some of the common | |
|
180 | caveats relevant for 'autoreload' are: | |
|
181 | ||
|
182 | - Modules are not reloaded in any specific order, and no dependency | |
|
183 | analysis is done. For example, modules with 'from xxx import foo' | |
|
184 | retain old versions of 'foo' when 'xxx' is autoreloaded. | |
|
185 | - Functions or objects imported from the autoreloaded module to | |
|
186 | the interactive namespace are not updated. | |
|
187 | - C extension modules cannot be reloaded, and so cannot be | |
|
188 | autoreloaded. | |
|
189 | """ | |
|
190 | if parameter_s == '': | |
|
191 | reloader.check(True) | |
|
192 | elif parameter_s == '0': | |
|
193 | disable_autoreload() | |
|
194 | elif parameter_s == '1': | |
|
195 | reloader.check_all = False | |
|
196 | enable_autoreload() | |
|
197 | elif parameter_s == '2': | |
|
198 | reloader.check_all = True | |
|
199 | enable_autoreload() | |
|
200 | ||
|
201 | def aimport_f(self, parameter_s=''): | |
|
202 | """%aimport => Import modules for automatic reloading. | |
|
203 | ||
|
204 | %aimport | |
|
205 | List modules to automatically import and not to import. | |
|
206 | ||
|
207 | %aimport foo | |
|
208 | Import module 'foo' and mark it to be autoreloaded for %autoreload 1 | |
|
209 | ||
|
210 | %aimport -foo | |
|
211 | Mark module 'foo' to not be autoreloaded for %autoreload 1 | |
|
212 | ||
|
213 | """ | |
|
214 | ||
|
215 | modname = parameter_s | |
|
216 | if not modname: | |
|
217 | to_reload = reloader.modules.keys() | |
|
218 | to_reload.sort() | |
|
219 | to_skip = reloader.skip_modules.keys() | |
|
220 | to_skip.sort() | |
|
221 | if reloader.check_all: | |
|
222 | print "Modules to reload:\nall-expect-skipped" | |
|
223 | else: | |
|
224 | print "Modules to reload:\n%s" % ' '.join(to_reload) | |
|
225 | print "\nModules to skip:\n%s" % ' '.join(to_skip) | |
|
226 | elif modname.startswith('-'): | |
|
227 | modname = modname[1:] | |
|
228 | try: del reloader.modules[modname] | |
|
229 | except KeyError: pass | |
|
230 | reloader.skip_modules[modname] = True | |
|
231 | else: | |
|
232 | try: del reloader.skip_modules[modname] | |
|
233 | except KeyError: pass | |
|
234 | reloader.modules[modname] = True | |
|
235 | ||
|
236 | mod = __import__(modname) | |
|
237 | ip.to_user_ns({modname: mod}) | |
|
238 | ||
|
239 | def init(): | |
|
240 | ip.expose_magic('autoreload', autoreload_f) | |
|
241 | ip.expose_magic('aimport', aimport_f) | |
|
242 | ip.set_hook('pre_runcode_hook', runcode_hook) | |
|
243 | ||
|
244 | init() No newline at end of file |
@@ -0,0 +1,343 b'' | |||
|
1 | """ Extension for bzr command tab completer. Supports comlpeting commands and options | |
|
2 | ||
|
3 | Unlike the core IPython, you should note that this extension is under GPL, not BSD. | |
|
4 | ||
|
5 | Based on "shell" bzr plugin by Aaron Bentley, license is below. The IPython additions | |
|
6 | are at the bottom of the file, the rest is left untouched. | |
|
7 | ||
|
8 | Must be loaded with ip.load('ipy_bzr') | |
|
9 | ||
|
10 | """ | |
|
11 | ||
|
12 | # Copyright (C) 2004, 2005 Aaron Bentley | |
|
13 | # <aaron@aaronbentley.com> | |
|
14 | # | |
|
15 | # This program is free software; you can redistribute it and/or modify | |
|
16 | # it under the terms of the GNU General Public License as published by | |
|
17 | # the Free Software Foundation; either version 2 of the License, or | |
|
18 | # (at your option) any later version. | |
|
19 | # | |
|
20 | # This program is distributed in the hope that it will be useful, | |
|
21 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
|
22 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
|
23 | # GNU General Public License for more details. | |
|
24 | # | |
|
25 | # You should have received a copy of the GNU General Public License | |
|
26 | # along with this program; if not, write to the Free Software | |
|
27 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
|
28 | ||
|
29 | import cmd | |
|
30 | from itertools import chain | |
|
31 | import os | |
|
32 | import shlex | |
|
33 | import stat | |
|
34 | import string | |
|
35 | import sys | |
|
36 | ||
|
37 | from bzrlib import osutils | |
|
38 | from bzrlib.branch import Branch | |
|
39 | from bzrlib.config import config_dir, ensure_config_dir_exists | |
|
40 | from bzrlib.commands import get_cmd_object, get_all_cmds, get_alias | |
|
41 | from bzrlib.errors import BzrError | |
|
42 | from bzrlib.workingtree import WorkingTree | |
|
43 | import bzrlib.plugin | |
|
44 | ||
|
45 | ||
|
46 | SHELL_BLACKLIST = set(['rm', 'ls']) | |
|
47 | COMPLETION_BLACKLIST = set(['shell']) | |
|
48 | ||
|
49 | ||
|
50 | class BlackListedCommand(BzrError): | |
|
51 | def __init__(self, command): | |
|
52 | BzrError.__init__(self, "The command %s is blacklisted for shell use" % | |
|
53 | command) | |
|
54 | ||
|
55 | ||
|
56 | class CompletionContext(object): | |
|
57 | def __init__(self, text, command=None, prev_opt=None, arg_pos=None): | |
|
58 | self.text = text | |
|
59 | self.command = command | |
|
60 | self.prev_opt = prev_opt | |
|
61 | self.arg_pos = None | |
|
62 | ||
|
63 | def get_completions(self): | |
|
64 | try: | |
|
65 | return self.get_completions_or_raise() | |
|
66 | except Exception, e: | |
|
67 | print e, type(e) | |
|
68 | return [] | |
|
69 | ||
|
70 | def get_option_completions(self): | |
|
71 | try: | |
|
72 | command_obj = get_cmd_object(self.command) | |
|
73 | except BzrError: | |
|
74 | return [] | |
|
75 | opts = [o+" " for o in iter_opt_completions(command_obj)] | |
|
76 | return list(filter_completions(opts, self.text)) | |
|
77 | ||
|
78 | def get_completions_or_raise(self): | |
|
79 | if self.command is None: | |
|
80 | if '/' in self.text: | |
|
81 | iter = iter_executables(self.text) | |
|
82 | else: | |
|
83 | iter = (c+" " for c in iter_command_names() if | |
|
84 | c not in COMPLETION_BLACKLIST) | |
|
85 | return list(filter_completions(iter, self.text)) | |
|
86 | if self.prev_opt is None: | |
|
87 | completions = self.get_option_completions() | |
|
88 | if self.command == "cd": | |
|
89 | iter = iter_dir_completions(self.text) | |
|
90 | completions.extend(list(filter_completions(iter, self.text))) | |
|
91 | else: | |
|
92 | iter = iter_file_completions(self.text) | |
|
93 | completions.extend(filter_completions(iter, self.text)) | |
|
94 | return completions | |
|
95 | ||
|
96 | ||
|
97 | class PromptCmd(cmd.Cmd): | |
|
98 | ||
|
99 | def __init__(self): | |
|
100 | cmd.Cmd.__init__(self) | |
|
101 | self.prompt = "bzr> " | |
|
102 | try: | |
|
103 | self.tree = WorkingTree.open_containing('.')[0] | |
|
104 | except: | |
|
105 | self.tree = None | |
|
106 | self.set_title() | |
|
107 | self.set_prompt() | |
|
108 | self.identchars += '-' | |
|
109 | ensure_config_dir_exists() | |
|
110 | self.history_file = osutils.pathjoin(config_dir(), 'shell-history') | |
|
111 | readline.set_completer_delims(string.whitespace) | |
|
112 | if os.access(self.history_file, os.R_OK) and \ | |
|
113 | os.path.isfile(self.history_file): | |
|
114 | readline.read_history_file(self.history_file) | |
|
115 | self.cwd = os.getcwd() | |
|
116 | ||
|
117 | def write_history(self): | |
|
118 | readline.write_history_file(self.history_file) | |
|
119 | ||
|
120 | def do_quit(self, args): | |
|
121 | self.write_history() | |
|
122 | raise StopIteration | |
|
123 | ||
|
124 | def do_exit(self, args): | |
|
125 | self.do_quit(args) | |
|
126 | ||
|
127 | def do_EOF(self, args): | |
|
128 | ||
|
129 | self.do_quit(args) | |
|
130 | ||
|
131 | def postcmd(self, line, bar): | |
|
132 | self.set_title() | |
|
133 | self.set_prompt() | |
|
134 | ||
|
135 | def set_prompt(self): | |
|
136 | if self.tree is not None: | |
|
137 | try: | |
|
138 | prompt_data = (self.tree.branch.nick, self.tree.branch.revno(), | |
|
139 | self.tree.relpath('.')) | |
|
140 | prompt = " %s:%d/%s" % prompt_data | |
|
141 | except: | |
|
142 | prompt = "" | |
|
143 | else: | |
|
144 | prompt = "" | |
|
145 | self.prompt = "bzr%s> " % prompt | |
|
146 | ||
|
147 | def set_title(self, command=None): | |
|
148 | try: | |
|
149 | b = Branch.open_containing('.')[0] | |
|
150 | version = "%s:%d" % (b.nick, b.revno()) | |
|
151 | except: | |
|
152 | version = "[no version]" | |
|
153 | if command is None: | |
|
154 | command = "" | |
|
155 | sys.stdout.write(terminal.term_title("bzr %s %s" % (command, version))) | |
|
156 | ||
|
157 | def do_cd(self, line): | |
|
158 | if line == "": | |
|
159 | line = "~" | |
|
160 | line = os.path.expanduser(line) | |
|
161 | if os.path.isabs(line): | |
|
162 | newcwd = line | |
|
163 | else: | |
|
164 | newcwd = self.cwd+'/'+line | |
|
165 | newcwd = os.path.normpath(newcwd) | |
|
166 | try: | |
|
167 | os.chdir(newcwd) | |
|
168 | self.cwd = newcwd | |
|
169 | except Exception, e: | |
|
170 | print e | |
|
171 | try: | |
|
172 | self.tree = WorkingTree.open_containing(".")[0] | |
|
173 | except: | |
|
174 | self.tree = None | |
|
175 | ||
|
176 | def do_help(self, line): | |
|
177 | self.default("help "+line) | |
|
178 | ||
|
179 | def default(self, line): | |
|
180 | args = shlex.split(line) | |
|
181 | alias_args = get_alias(args[0]) | |
|
182 | if alias_args is not None: | |
|
183 | args[0] = alias_args.pop(0) | |
|
184 | ||
|
185 | commandname = args.pop(0) | |
|
186 | for char in ('|', '<', '>'): | |
|
187 | commandname = commandname.split(char)[0] | |
|
188 | if commandname[-1] in ('|', '<', '>'): | |
|
189 | commandname = commandname[:-1] | |
|
190 | try: | |
|
191 | if commandname in SHELL_BLACKLIST: | |
|
192 | raise BlackListedCommand(commandname) | |
|
193 | cmd_obj = get_cmd_object(commandname) | |
|
194 | except (BlackListedCommand, BzrError): | |
|
195 | return os.system(line) | |
|
196 | ||
|
197 | try: | |
|
198 | if too_complicated(line): | |
|
199 | return os.system("bzr "+line) | |
|
200 | else: | |
|
201 | return (cmd_obj.run_argv_aliases(args, alias_args) or 0) | |
|
202 | except BzrError, e: | |
|
203 | print e | |
|
204 | except KeyboardInterrupt, e: | |
|
205 | print "Interrupted" | |
|
206 | except Exception, e: | |
|
207 | # print "Unhandled error:\n%s" % errors.exception_str(e) | |
|
208 | print "Unhandled error:\n%s" % (e) | |
|
209 | ||
|
210 | ||
|
211 | def completenames(self, text, line, begidx, endidx): | |
|
212 | return CompletionContext(text).get_completions() | |
|
213 | ||
|
214 | def completedefault(self, text, line, begidx, endidx): | |
|
215 | """Perform completion for native commands. | |
|
216 | ||
|
217 | :param text: The text to complete | |
|
218 | :type text: str | |
|
219 | :param line: The entire line to complete | |
|
220 | :type line: str | |
|
221 | :param begidx: The start of the text in the line | |
|
222 | :type begidx: int | |
|
223 | :param endidx: The end of the text in the line | |
|
224 | :type endidx: int | |
|
225 | """ | |
|
226 | (cmd, args, foo) = self.parseline(line) | |
|
227 | if cmd == "bzr": | |
|
228 | cmd = None | |
|
229 | return CompletionContext(text, command=cmd).get_completions() | |
|
230 | ||
|
231 | ||
|
232 | def run_shell(): | |
|
233 | try: | |
|
234 | prompt = PromptCmd() | |
|
235 | try: | |
|
236 | prompt.cmdloop() | |
|
237 | finally: | |
|
238 | prompt.write_history() | |
|
239 | except StopIteration: | |
|
240 | pass | |
|
241 | ||
|
242 | ||
|
243 | def iter_opt_completions(command_obj): | |
|
244 | for option_name, option in command_obj.options().items(): | |
|
245 | yield "--" + option_name | |
|
246 | short_name = option.short_name() | |
|
247 | if short_name: | |
|
248 | yield "-" + short_name | |
|
249 | ||
|
250 | ||
|
251 | def iter_file_completions(arg, only_dirs = False): | |
|
252 | """Generate an iterator that iterates through filename completions. | |
|
253 | ||
|
254 | :param arg: The filename fragment to match | |
|
255 | :type arg: str | |
|
256 | :param only_dirs: If true, match only directories | |
|
257 | :type only_dirs: bool | |
|
258 | """ | |
|
259 | cwd = os.getcwd() | |
|
260 | if cwd != "/": | |
|
261 | extras = [".", ".."] | |
|
262 | else: | |
|
263 | extras = [] | |
|
264 | (dir, file) = os.path.split(arg) | |
|
265 | if dir != "": | |
|
266 | listingdir = os.path.expanduser(dir) | |
|
267 | else: | |
|
268 | listingdir = cwd | |
|
269 | for file in chain(os.listdir(listingdir), extras): | |
|
270 | if dir != "": | |
|
271 | userfile = dir+'/'+file | |
|
272 | else: | |
|
273 | userfile = file | |
|
274 | if userfile.startswith(arg): | |
|
275 | if os.path.isdir(listingdir+'/'+file): | |
|
276 | userfile+='/' | |
|
277 | yield userfile | |
|
278 | elif not only_dirs: | |
|
279 | yield userfile + ' ' | |
|
280 | ||
|
281 | ||
|
282 | def iter_dir_completions(arg): | |
|
283 | """Generate an iterator that iterates through directory name completions. | |
|
284 | ||
|
285 | :param arg: The directory name fragment to match | |
|
286 | :type arg: str | |
|
287 | """ | |
|
288 | return iter_file_completions(arg, True) | |
|
289 | ||
|
290 | ||
|
291 | def iter_command_names(hidden=False): | |
|
292 | for real_cmd_name, cmd_class in get_all_cmds(): | |
|
293 | if not hidden and cmd_class.hidden: | |
|
294 | continue | |
|
295 | for name in [real_cmd_name] + cmd_class.aliases: | |
|
296 | # Don't complete on aliases that are prefixes of the canonical name | |
|
297 | if name == real_cmd_name or not real_cmd_name.startswith(name): | |
|
298 | yield name | |
|
299 | ||
|
300 | ||
|
301 | def iter_executables(path): | |
|
302 | dirname, partial = os.path.split(path) | |
|
303 | for filename in os.listdir(dirname): | |
|
304 | if not filename.startswith(partial): | |
|
305 | continue | |
|
306 | fullpath = os.path.join(dirname, filename) | |
|
307 | mode=os.lstat(fullpath)[stat.ST_MODE] | |
|
308 | if stat.S_ISREG(mode) and 0111 & mode: | |
|
309 | yield fullpath + ' ' | |
|
310 | ||
|
311 | ||
|
312 | def filter_completions(iter, arg): | |
|
313 | return (c for c in iter if c.startswith(arg)) | |
|
314 | ||
|
315 | ||
|
316 | def iter_munged_completions(iter, arg, text): | |
|
317 | for completion in iter: | |
|
318 | completion = str(completion) | |
|
319 | if completion.startswith(arg): | |
|
320 | yield completion[len(arg)-len(text):]+" " | |
|
321 | ||
|
322 | ||
|
323 | def too_complicated(line): | |
|
324 | for char in '|<>*?': | |
|
325 | if char in line: | |
|
326 | return True | |
|
327 | return False | |
|
328 | ||
|
329 | ||
|
330 | ### IPython mods start | |
|
331 | ||
|
332 | def init_ipython(ip): | |
|
333 | def bzr_completer(self,ev): | |
|
334 | #print "bzr complete" | |
|
335 | tup = ev.line.split(None,2) | |
|
336 | if len(tup) > 2: | |
|
337 | cmd = tup[1] | |
|
338 | else: | |
|
339 | cmd = None | |
|
340 | ||
|
341 | return CompletionContext(ev.symbol, command = cmd).get_completions() | |
|
342 | bzrlib.plugin.load_plugins() | |
|
343 | ip.set_hook('complete_command', bzr_completer, str_key = 'bzr') |
@@ -0,0 +1,75 b'' | |||
|
1 | """ Greedy completer extension for IPython | |
|
2 | ||
|
3 | Normal tab completer refuses to evaluate nonsafe stuff. This will evaluate | |
|
4 | everything, so you need to consider the consequences of pressing tab | |
|
5 | yourself! | |
|
6 | ||
|
7 | Note that this extension simplifies readline interaction by setting | |
|
8 | only whitespace as completer delimiter. If this works well, we will | |
|
9 | do the same in default completer. | |
|
10 | ||
|
11 | """ | |
|
12 | from IPython import generics,ipapi | |
|
13 | from IPython.genutils import dir2 | |
|
14 | ||
|
15 | def attr_matches(self, text): | |
|
16 | """Compute matches when text contains a dot. | |
|
17 | ||
|
18 | MONKEYPATCHED VERSION (ipy_greedycompleter.py) | |
|
19 | ||
|
20 | Assuming the text is of the form NAME.NAME....[NAME], and is | |
|
21 | evaluatable in self.namespace or self.global_namespace, it will be | |
|
22 | evaluated and its attributes (as revealed by dir()) are used as | |
|
23 | possible completions. (For class instances, class members are are | |
|
24 | also considered.) | |
|
25 | ||
|
26 | WARNING: this can still invoke arbitrary C code, if an object | |
|
27 | with a __getattr__ hook is evaluated. | |
|
28 | ||
|
29 | """ | |
|
30 | import re | |
|
31 | ||
|
32 | force_complete = 1 | |
|
33 | # Another option, seems to work great. Catches things like ''.<tab> | |
|
34 | m = re.match(r"(\S+(\.\w+)*)\.(\w*)$", text) | |
|
35 | ||
|
36 | if m: | |
|
37 | expr, attr = m.group(1, 3) | |
|
38 | else: | |
|
39 | # force match - eval anything that ends with colon | |
|
40 | if not force_complete: | |
|
41 | return [] | |
|
42 | ||
|
43 | m2 = re.match(r"(.+)\.(\w*)$", self.lbuf) | |
|
44 | if not m2: | |
|
45 | return [] | |
|
46 | expr, attr = m2.group(1,2) | |
|
47 | ||
|
48 | ||
|
49 | try: | |
|
50 | obj = eval(expr, self.namespace) | |
|
51 | except: | |
|
52 | try: | |
|
53 | obj = eval(expr, self.global_namespace) | |
|
54 | except: | |
|
55 | return [] | |
|
56 | ||
|
57 | words = dir2(obj) | |
|
58 | ||
|
59 | try: | |
|
60 | words = generics.complete_object(obj, words) | |
|
61 | except ipapi.TryNext: | |
|
62 | pass | |
|
63 | # Build match list to return | |
|
64 | n = len(attr) | |
|
65 | res = ["%s.%s" % (expr, w) for w in words if w[:n] == attr ] | |
|
66 | return res | |
|
67 | ||
|
68 | def main(): | |
|
69 | import readline | |
|
70 | readline.set_completer_delims(" \n\t") | |
|
71 | # monkeypatch - the code will be folded to normal completer later on | |
|
72 | import IPython.completer | |
|
73 | IPython.completer.Completer.attr_matches = attr_matches | |
|
74 | ||
|
75 | main() No newline at end of file |
@@ -0,0 +1,311 b'' | |||
|
1 | # -*- coding: utf-8 -*- | |
|
2 | """ | |
|
3 | %jot magic for lightweight persistence. | |
|
4 | ||
|
5 | Stores variables in Struct with some notes in PicleShare database | |
|
6 | ||
|
7 | ||
|
8 | """ | |
|
9 | ||
|
10 | from datetime import datetime | |
|
11 | import IPython.ipapi | |
|
12 | ip = IPython.ipapi.get() | |
|
13 | ||
|
14 | import pickleshare | |
|
15 | ||
|
16 | import inspect,pickle,os,sys,textwrap | |
|
17 | from IPython.FakeModule import FakeModule | |
|
18 | from IPython.ipstruct import Struct | |
|
19 | ||
|
20 | ||
|
21 | def refresh_variables(ip, key=None): | |
|
22 | db = ip.db | |
|
23 | if key is None: | |
|
24 | keys = db.keys('jot/*') | |
|
25 | else: | |
|
26 | keys = db.keys('jot/'+key) | |
|
27 | for key in keys: | |
|
28 | # strip autorestore | |
|
29 | justkey = os.path.basename(key) | |
|
30 | print "Restoring from", justkey, "..." | |
|
31 | try: | |
|
32 | obj = db[key] | |
|
33 | except KeyError: | |
|
34 | print "Unable to restore variable '%s', ignoring (use %%jot -d to forget!)" % justkey | |
|
35 | print "The error was:",sys.exc_info()[0] | |
|
36 | else: | |
|
37 | #print "restored",justkey,"=",obj #dbg | |
|
38 | try: | |
|
39 | origname = obj.name | |
|
40 | except: | |
|
41 | ip.user_ns[justkey] = obj | |
|
42 | print "Restored", justkey | |
|
43 | else: | |
|
44 | ip.user_ns[origname] = obj['val'] | |
|
45 | print "Restored", origname | |
|
46 | ||
|
47 | def read_variables(ip, key=None): | |
|
48 | db = ip.db | |
|
49 | if key is None: | |
|
50 | return None | |
|
51 | else: | |
|
52 | keys = db.keys('jot/'+key) | |
|
53 | for key in keys: | |
|
54 | # strip autorestore | |
|
55 | justkey = os.path.basename(key) | |
|
56 | print "restoring from ", justkey | |
|
57 | try: | |
|
58 | obj = db[key] | |
|
59 | except KeyError: | |
|
60 | print "Unable to read variable '%s', ignoring (use %%jot -d to forget!)" % justkey | |
|
61 | print "The error was:",sys.exc_info()[0] | |
|
62 | else: | |
|
63 | return obj | |
|
64 | ||
|
65 | ||
|
66 | def detail_variables(ip, key=None): | |
|
67 | db, get = ip.db, ip.db.get | |
|
68 | ||
|
69 | if key is None: | |
|
70 | keys = db.keys('jot/*') | |
|
71 | else: | |
|
72 | keys = db.keys('jot/'+key) | |
|
73 | if keys: | |
|
74 | size = max(map(len,keys)) | |
|
75 | else: | |
|
76 | size = 0 | |
|
77 | ||
|
78 | fmthead = '%-'+str(size)+'s [%s]' | |
|
79 | fmtbody = 'Comment:\n %s' | |
|
80 | fmtdata = 'Data:\n %s, %s' | |
|
81 | for key in keys: | |
|
82 | v = get(key,'<unavailable>') | |
|
83 | justkey = os.path.basename(key) | |
|
84 | try: | |
|
85 | print fmthead % (justkey, datetime.ctime(v.get('time','<unavailable>'))) | |
|
86 | print fmtbody % (v.get('comment','<unavailable>')) | |
|
87 | d = v.get('val','unavailable') | |
|
88 | print fmtdata % (repr(type(d)), '') | |
|
89 | print repr(d)[0:200] | |
|
90 | ||
|
91 | ||
|
92 | except AttributeError: | |
|
93 | print fmt % (justkey, '<unavailable>', '<unavailable>', repr(v)[:50]) | |
|
94 | ||
|
95 | ||
|
96 | def intm(n): | |
|
97 | try: | |
|
98 | return int(n) | |
|
99 | except: | |
|
100 | return 0 | |
|
101 | ||
|
102 | def jot_obj(self, obj, name, comment=''): | |
|
103 | """ | |
|
104 | write obj data to the note database, with whatever that should be noted. | |
|
105 | """ | |
|
106 | had = self.db.keys('jot/'+name+'*') | |
|
107 | # if it the same name but a later version, we stupidly add a number to the | |
|
108 | # so the name doesn't collide. Any better idea? | |
|
109 | suffix = '' | |
|
110 | if len(had)>0: | |
|
111 | pre = os.path.commonprefix(had) | |
|
112 | suf = [n.split(pre)[1] for n in had] | |
|
113 | versions = map(intm, suf) | |
|
114 | suffix = str(max(versions)+1) | |
|
115 | ||
|
116 | uname = 'jot/'+name+suffix | |
|
117 | ||
|
118 | # which one works better? | |
|
119 | #all = ip.IP.shadowhist.all() | |
|
120 | all = ip.IP.shell.input_hist | |
|
121 | ||
|
122 | # We may actually want to make snapshot of files that are run-ned. | |
|
123 | ||
|
124 | # get the comment | |
|
125 | try: | |
|
126 | comment = ip.IP.magic_edit('-x').strip() | |
|
127 | except: | |
|
128 | print "No comment is recorded." | |
|
129 | comment = '' | |
|
130 | ||
|
131 | self.db[uname] = Struct({'val':obj, | |
|
132 | 'time' : datetime.now(), | |
|
133 | 'hist' : all, | |
|
134 | 'name' : name, | |
|
135 | 'comment' : comment,}) | |
|
136 | ||
|
137 | print "Jotted down notes for '%s' (%s)" % (uname, obj.__class__.__name__) | |
|
138 | ||
|
139 | ||
|
140 | ||
|
141 | def magic_jot(self, parameter_s=''): | |
|
142 | """Lightweight persistence for python variables. | |
|
143 | ||
|
144 | Example: | |
|
145 | ||
|
146 | ville@badger[~]|1> A = ['hello',10,'world']\\ | |
|
147 | ville@badger[~]|2> %jot A\\ | |
|
148 | ville@badger[~]|3> Exit | |
|
149 | ||
|
150 | (IPython session is closed and started again...) | |
|
151 | ||
|
152 | ville@badger:~$ ipython -p pysh\\ | |
|
153 | ville@badger[~]|1> print A | |
|
154 | ||
|
155 | ['hello', 10, 'world'] | |
|
156 | ||
|
157 | Usage: | |
|
158 | ||
|
159 | %jot - Show list of all variables and their current values\\ | |
|
160 | %jot -l - Show list of all variables and their current values in detail\\ | |
|
161 | %jot -l <var> - Show one variable and its current values in detail\\ | |
|
162 | %jot <var> - Store the *current* value of the variable to disk\\ | |
|
163 | %jot -d <var> - Remove the variable and its value from storage\\ | |
|
164 | %jot -z - Remove all variables from storage (disabled)\\ | |
|
165 | %jot -r <var> - Refresh/Load variable from jot (delete current vals)\\ | |
|
166 | %jot foo >a.txt - Store value of foo to new file a.txt\\ | |
|
167 | %jot foo >>a.txt - Append value of foo to file a.txt\\ | |
|
168 | ||
|
169 | It should be noted that if you change the value of a variable, you | |
|
170 | need to %note it again if you want to persist the new value. | |
|
171 | ||
|
172 | Note also that the variables will need to be pickleable; most basic | |
|
173 | python types can be safely %stored. | |
|
174 | ||
|
175 | """ | |
|
176 | ||
|
177 | opts,argsl = self.parse_options(parameter_s,'drzl',mode='string') | |
|
178 | args = argsl.split(None,1) | |
|
179 | ip = self.getapi() | |
|
180 | db = ip.db | |
|
181 | # delete | |
|
182 | if opts.has_key('d'): | |
|
183 | try: | |
|
184 | todel = args[0] | |
|
185 | except IndexError: | |
|
186 | error('You must provide the variable to forget') | |
|
187 | else: | |
|
188 | try: | |
|
189 | del db['jot/' + todel] | |
|
190 | except: | |
|
191 | error("Can't delete variable '%s'" % todel) | |
|
192 | # reset the whole database | |
|
193 | elif opts.has_key('z'): | |
|
194 | print "reseting the whole database has been disabled." | |
|
195 | #for k in db.keys('autorestore/*'): | |
|
196 | # del db[k] | |
|
197 | ||
|
198 | elif opts.has_key('r'): | |
|
199 | try: | |
|
200 | toret = args[0] | |
|
201 | except: | |
|
202 | print "restoring all the variables jotted down..." | |
|
203 | refresh_variables(ip) | |
|
204 | else: | |
|
205 | refresh_variables(ip, toret) | |
|
206 | ||
|
207 | elif opts.has_key('l'): | |
|
208 | try: | |
|
209 | tolist = args[0] | |
|
210 | except: | |
|
211 | print "List details for all the items." | |
|
212 | detail_variables(ip) | |
|
213 | else: | |
|
214 | print "Details for", tolist, ":" | |
|
215 | detail_variables(ip, tolist) | |
|
216 | ||
|
217 | # run without arguments -> list noted variables & notes | |
|
218 | elif not args: | |
|
219 | vars = self.db.keys('jot/*') | |
|
220 | vars.sort() | |
|
221 | if vars: | |
|
222 | size = max(map(len,vars)) - 4 | |
|
223 | else: | |
|
224 | size = 0 | |
|
225 | ||
|
226 | print 'Variables and their in-db values:' | |
|
227 | fmt = '%-'+str(size)+'s [%s] -> %s' | |
|
228 | get = db.get | |
|
229 | for var in vars: | |
|
230 | justkey = os.path.basename(var) | |
|
231 | v = get(var,'<unavailable>') | |
|
232 | try: | |
|
233 | print fmt % (justkey,\ | |
|
234 | datetime.ctime(v.get('time','<unavailable>')),\ | |
|
235 | v.get('comment','<unavailable>')[:70].replace('\n',' '),) | |
|
236 | except AttributeError: | |
|
237 | print fmt % (justkey, '<unavailable>', '<unavailable>', repr(v)[:50]) | |
|
238 | ||
|
239 | ||
|
240 | # default action - store the variable | |
|
241 | else: | |
|
242 | # %store foo >file.txt or >>file.txt | |
|
243 | if len(args) > 1 and args[1].startswith('>'): | |
|
244 | fnam = os.path.expanduser(args[1].lstrip('>').lstrip()) | |
|
245 | if args[1].startswith('>>'): | |
|
246 | fil = open(fnam,'a') | |
|
247 | else: | |
|
248 | fil = open(fnam,'w') | |
|
249 | obj = ip.ev(args[0]) | |
|
250 | print "Writing '%s' (%s) to file '%s'." % (args[0], | |
|
251 | obj.__class__.__name__, fnam) | |
|
252 | ||
|
253 | ||
|
254 | if not isinstance (obj,basestring): | |
|
255 | from pprint import pprint | |
|
256 | pprint(obj,fil) | |
|
257 | else: | |
|
258 | fil.write(obj) | |
|
259 | if not obj.endswith('\n'): | |
|
260 | fil.write('\n') | |
|
261 | ||
|
262 | fil.close() | |
|
263 | return | |
|
264 | ||
|
265 | # %note foo | |
|
266 | try: | |
|
267 | obj = ip.user_ns[args[0]] | |
|
268 | except KeyError: | |
|
269 | # this should not be alias, for aliases, use %store | |
|
270 | ||
|
271 | print "Error: %s doesn't exist." % args[0] | |
|
272 | ||
|
273 | print "Use %note -r <var> to retrieve variables. This should not be used " +\ | |
|
274 | "to store alias, for saving aliases, use %store" | |
|
275 | return | |
|
276 | else: | |
|
277 | if isinstance(inspect.getmodule(obj), FakeModule): | |
|
278 | print textwrap.dedent("""\ | |
|
279 | Warning:%s is %s | |
|
280 | Proper storage of interactively declared classes (or instances | |
|
281 | of those classes) is not possible! Only instances | |
|
282 | of classes in real modules on file system can be %%store'd. | |
|
283 | """ % (args[0], obj) ) | |
|
284 | return | |
|
285 | #pickled = pickle.dumps(obj) | |
|
286 | #self.db[ 'jot/' + args[0] ] = obj | |
|
287 | jot_obj(self, obj, args[0]) | |
|
288 | ||
|
289 | ||
|
290 | def magic_read(self, parameter_s=''): | |
|
291 | """ | |
|
292 | %read <var> - Load variable from data that is jotted down.\\ | |
|
293 | ||
|
294 | """ | |
|
295 | ||
|
296 | opts,argsl = self.parse_options(parameter_s,'drzl',mode='string') | |
|
297 | args = argsl.split(None,1) | |
|
298 | ip = self.getapi() | |
|
299 | db = ip.db | |
|
300 | #if opts.has_key('r'): | |
|
301 | try: | |
|
302 | toret = args[0] | |
|
303 | except: | |
|
304 | print "which record do you want to read out?" | |
|
305 | return | |
|
306 | else: | |
|
307 | return read_variables(ip, toret) | |
|
308 | ||
|
309 | ||
|
310 | ip.expose_magic('jot',magic_jot) | |
|
311 | ip.expose_magic('read',magic_read) |
@@ -0,0 +1,234 b'' | |||
|
1 | """ | |
|
2 | IPython extension: %lookfor command for searching docstrings | |
|
3 | ||
|
4 | """ | |
|
5 | # Pauli Virtanen <pav@iki.fi>, 2008. | |
|
6 | ||
|
7 | import re, inspect, pkgutil, pydoc | |
|
8 | ||
|
9 | #------------------------------------------------------------------------------ | |
|
10 | # Lookfor functionality | |
|
11 | #------------------------------------------------------------------------------ | |
|
12 | ||
|
13 | # Cache for lookfor: {id(module): {name: (docstring, kind, index), ...}...} | |
|
14 | # where kind: "func", "class", "module", "object" | |
|
15 | # and index: index in breadth-first namespace traversal | |
|
16 | _lookfor_caches = {} | |
|
17 | ||
|
18 | # regexp whose match indicates that the string may contain a function signature | |
|
19 | _function_signature_re = re.compile(r"[a-z_]+\(.*[,=].*\)", re.I) | |
|
20 | ||
|
21 | def lookfor(what, modules=None, import_modules=True, regenerate=False): | |
|
22 | """ | |
|
23 | Search for objects whose documentation contains all given words. | |
|
24 | Shows a summary of matching objects, sorted roughly by relevance. | |
|
25 | ||
|
26 | Parameters | |
|
27 | ---------- | |
|
28 | what : str | |
|
29 | String containing words to look for. | |
|
30 | ||
|
31 | module : str, module | |
|
32 | Module whose docstrings to go through. | |
|
33 | import_modules : bool | |
|
34 | Whether to import sub-modules in packages. | |
|
35 | Will import only modules in __all__ | |
|
36 | regenerate: bool | |
|
37 | Re-generate the docstring cache | |
|
38 | ||
|
39 | """ | |
|
40 | # Cache | |
|
41 | cache = {} | |
|
42 | for module in modules: | |
|
43 | try: | |
|
44 | c = _lookfor_generate_cache(module, import_modules, regenerate) | |
|
45 | cache.update(c) | |
|
46 | except ImportError: | |
|
47 | pass | |
|
48 | ||
|
49 | # Search | |
|
50 | # XXX: maybe using a real stemming search engine would be better? | |
|
51 | found = [] | |
|
52 | whats = str(what).lower().split() | |
|
53 | if not whats: return | |
|
54 | ||
|
55 | for name, (docstring, kind, index) in cache.iteritems(): | |
|
56 | if kind in ('module', 'object'): | |
|
57 | # don't show modules or objects | |
|
58 | continue | |
|
59 | ok = True | |
|
60 | doc = docstring.lower() | |
|
61 | for w in whats: | |
|
62 | if w not in doc: | |
|
63 | ok = False | |
|
64 | break | |
|
65 | if ok: | |
|
66 | found.append(name) | |
|
67 | ||
|
68 | # Relevance sort | |
|
69 | # XXX: this is full Harrison-Stetson heuristics now, | |
|
70 | # XXX: it probably could be improved | |
|
71 | ||
|
72 | kind_relevance = {'func': 1000, 'class': 1000, | |
|
73 | 'module': -1000, 'object': -1000} | |
|
74 | ||
|
75 | def relevance(name, docstr, kind, index): | |
|
76 | r = 0 | |
|
77 | # do the keywords occur within the start of the docstring? | |
|
78 | first_doc = "\n".join(docstr.lower().strip().split("\n")[:3]) | |
|
79 | r += sum([200 for w in whats if w in first_doc]) | |
|
80 | # do the keywords occur in the function name? | |
|
81 | r += sum([30 for w in whats if w in name]) | |
|
82 | # is the full name long? | |
|
83 | r += -len(name) * 5 | |
|
84 | # is the object of bad type? | |
|
85 | r += kind_relevance.get(kind, -1000) | |
|
86 | # is the object deep in namespace hierarchy? | |
|
87 | r += -name.count('.') * 10 | |
|
88 | r += max(-index / 100, -100) | |
|
89 | return r | |
|
90 | ||
|
91 | def relevance_sort(a, b): | |
|
92 | dr = relevance(b, *cache[b]) - relevance(a, *cache[a]) | |
|
93 | if dr != 0: return dr | |
|
94 | else: return cmp(a, b) | |
|
95 | found.sort(relevance_sort) | |
|
96 | ||
|
97 | # Pretty-print | |
|
98 | s = "Search results for '%s'" % (' '.join(whats)) | |
|
99 | help_text = [s, "-"*len(s)] | |
|
100 | for name in found: | |
|
101 | doc, kind, ix = cache[name] | |
|
102 | ||
|
103 | doclines = [line.strip() for line in doc.strip().split("\n") | |
|
104 | if line.strip()] | |
|
105 | ||
|
106 | # find a suitable short description | |
|
107 | try: | |
|
108 | first_doc = doclines[0].strip() | |
|
109 | if _function_signature_re.search(first_doc): | |
|
110 | first_doc = doclines[1].strip() | |
|
111 | except IndexError: | |
|
112 | first_doc = "" | |
|
113 | help_text.append("%s\n %s" % (name, first_doc)) | |
|
114 | ||
|
115 | # Output | |
|
116 | if len(help_text) > 10: | |
|
117 | pager = pydoc.getpager() | |
|
118 | pager("\n".join(help_text)) | |
|
119 | else: | |
|
120 | print "\n".join(help_text) | |
|
121 | ||
|
122 | def _lookfor_generate_cache(module, import_modules, regenerate): | |
|
123 | """ | |
|
124 | Generate docstring cache for given module. | |
|
125 | ||
|
126 | Parameters | |
|
127 | ---------- | |
|
128 | module : str, None, module | |
|
129 | Module for which to generate docstring cache | |
|
130 | import_modules : bool | |
|
131 | Whether to import sub-modules in packages. | |
|
132 | Will import only modules in __all__ | |
|
133 | regenerate: bool | |
|
134 | Re-generate the docstring cache | |
|
135 | ||
|
136 | Returns | |
|
137 | ------- | |
|
138 | cache : dict {obj_full_name: (docstring, kind, index), ...} | |
|
139 | Docstring cache for the module, either cached one (regenerate=False) | |
|
140 | or newly generated. | |
|
141 | ||
|
142 | """ | |
|
143 | global _lookfor_caches | |
|
144 | ||
|
145 | if module is None: | |
|
146 | module = "numpy" | |
|
147 | ||
|
148 | if isinstance(module, str): | |
|
149 | module = __import__(module) | |
|
150 | ||
|
151 | if id(module) in _lookfor_caches and not regenerate: | |
|
152 | return _lookfor_caches[id(module)] | |
|
153 | ||
|
154 | # walk items and collect docstrings | |
|
155 | cache = {} | |
|
156 | _lookfor_caches[id(module)] = cache | |
|
157 | seen = {} | |
|
158 | index = 0 | |
|
159 | stack = [(module.__name__, module)] | |
|
160 | while stack: | |
|
161 | name, item = stack.pop(0) | |
|
162 | if id(item) in seen: continue | |
|
163 | seen[id(item)] = True | |
|
164 | ||
|
165 | index += 1 | |
|
166 | kind = "object" | |
|
167 | ||
|
168 | if inspect.ismodule(item): | |
|
169 | kind = "module" | |
|
170 | try: | |
|
171 | _all = item.__all__ | |
|
172 | except AttributeError: | |
|
173 | _all = None | |
|
174 | # import sub-packages | |
|
175 | if import_modules and hasattr(item, '__path__'): | |
|
176 | for m in pkgutil.iter_modules(item.__path__): | |
|
177 | if _all is not None and m[1] not in _all: | |
|
178 | continue | |
|
179 | try: | |
|
180 | __import__("%s.%s" % (name, m[1])) | |
|
181 | except ImportError: | |
|
182 | continue | |
|
183 | for n, v in inspect.getmembers(item): | |
|
184 | if _all is not None and n not in _all: | |
|
185 | continue | |
|
186 | stack.append(("%s.%s" % (name, n), v)) | |
|
187 | elif inspect.isclass(item): | |
|
188 | kind = "class" | |
|
189 | for n, v in inspect.getmembers(item): | |
|
190 | stack.append(("%s.%s" % (name, n), v)) | |
|
191 | elif callable(item): | |
|
192 | kind = "func" | |
|
193 | ||
|
194 | doc = inspect.getdoc(item) | |
|
195 | if doc is not None: | |
|
196 | cache[name] = (doc, kind, index) | |
|
197 | ||
|
198 | return cache | |
|
199 | ||
|
200 | #------------------------------------------------------------------------------ | |
|
201 | # IPython connectivity | |
|
202 | #------------------------------------------------------------------------------ | |
|
203 | ||
|
204 | import IPython.ipapi | |
|
205 | ip = IPython.ipapi.get() | |
|
206 | ||
|
207 | _lookfor_modules = ['numpy', 'scipy'] | |
|
208 | ||
|
209 | def lookfor_f(self, arg=''): | |
|
210 | r""" | |
|
211 | Search for objects whose documentation contains all given words. | |
|
212 | Shows a summary of matching objects, sorted roughly by relevance. | |
|
213 | ||
|
214 | Usage | |
|
215 | ----- | |
|
216 | %lookfor +numpy some words | |
|
217 | Search module 'numpy' | |
|
218 | ||
|
219 | %lookfor_modules numpy scipy | |
|
220 | Set default modules whose docstrings to search | |
|
221 | ||
|
222 | """ | |
|
223 | lookfor(arg, modules=_lookfor_modules) | |
|
224 | ||
|
225 | def lookfor_modules_f(self, arg=''): | |
|
226 | global _lookfor_modules | |
|
227 | if not arg: | |
|
228 | print "Modules included in %lookfor search:", _lookfor_modules | |
|
229 | else: | |
|
230 | _lookfor_modules = arg.split() | |
|
231 | ||
|
232 | ip.expose_magic('lookfor', lookfor_f) | |
|
233 | ip.expose_magic('lookfor_modules', lookfor_modules_f) | |
|
234 |
@@ -0,0 +1,239 b'' | |||
|
1 | """ Integration with gvim, by Erich Heine | |
|
2 | ||
|
3 | Provides a %vim magic command, and reuses the same vim session. Uses | |
|
4 | unix domain sockets for communication between vim and IPython. ipy.vim is | |
|
5 | available in doc/examples of the IPython distribution. | |
|
6 | ||
|
7 | Slightly touched up email announcement (and description how to use it) by | |
|
8 | Erich Heine is here: | |
|
9 | ||
|
10 | Ive recently been playing with ipython, and like it quite a bit. I did | |
|
11 | however discover a bit of frustration, namely with editor interaction. | |
|
12 | I am a gvim user, and using the command edit on a new file causes | |
|
13 | ipython to try and run that file as soon as the text editor opens | |
|
14 | up. The -x command of course fixes this, but its still a bit annoying, | |
|
15 | switching windows to do a run file, then back to the text | |
|
16 | editor. Being a heavy tab user in gvim, another annoyance is not being | |
|
17 | able to specify weather a new tab is how I choose to open the file. | |
|
18 | ||
|
19 | Not being one to shirk my open source duties (and seeing this as a | |
|
20 | good excuse to poke around ipython internals), Ive created a script | |
|
21 | for having gvim and ipython work very nicely together. Ive attached | |
|
22 | both to this email (hoping of course that the mailing list allows such | |
|
23 | things). | |
|
24 | ||
|
25 | There are 2 files: | |
|
26 | ||
|
27 | ipy_vimserver.py -- this file contains the ipython stuff | |
|
28 | ipy.vim -- this file contains the gvim stuff | |
|
29 | ||
|
30 | In combination they allow for a few functionalities: | |
|
31 | ||
|
32 | #1. the vim magic command. This is a fancy wrapper around the edit | |
|
33 | magic, that allows for a new option, -t, which opens the text in a new | |
|
34 | gvim tab. Otherwise it works the same as edit -x. (it internally | |
|
35 | calls edit -x). This magic command also juggles vim server management, | |
|
36 | so when it is called when there is not a gvim running, it creates a | |
|
37 | new gvim instance, named after the ipython session name. Once such a | |
|
38 | gvim instance is running, it will be used for subsequent uses of the | |
|
39 | vim command. | |
|
40 | ||
|
41 | #2. ipython - gvim interaction. Once a file has been opened with the | |
|
42 | vim magic (and a session set up, see below), pressing the F5 key in | |
|
43 | vim will cause the calling ipython instance to execute run | |
|
44 | filename.py. (if you typo like I do, this is very useful) | |
|
45 | ||
|
46 | #3. ipython server - this is a thread wich listens on a unix domain | |
|
47 | socket, and runs commands sent to that socket. | |
|
48 | ||
|
49 | Note, this only works on POSIX systems, that allow for AF_UNIX type | |
|
50 | sockets. It has only been tested on linux (a fairly recent debian | |
|
51 | testing distro). | |
|
52 | ||
|
53 | To install it put, the ipserver.py in your favorite locaion for | |
|
54 | sourcing ipython scripts. I put the ipy.vim in | |
|
55 | ~/.vim/after/ftplugin/python/. | |
|
56 | ||
|
57 | To use (this can be scripted im sure, but i usually have 2 or 3 | |
|
58 | ipythons and corresponding gvims open): | |
|
59 | ||
|
60 | import ipy_vimserver | |
|
61 | ipy_vimserver.setup('sessionname') | |
|
62 | ||
|
63 | (Editors note - you can probably add these to your ipy_user_conf.py) | |
|
64 | ||
|
65 | Then use ipython as you normally would, until you need to edit | |
|
66 | something. Instead of edit, use the vim magic. Thats it! | |
|
67 | ||
|
68 | """ | |
|
69 | ||
|
70 | import IPython.ipapi | |
|
71 | #import ipythonhooks | |
|
72 | import socket, select | |
|
73 | import os, threading, subprocess | |
|
74 | import re | |
|
75 | ||
|
76 | ERRCONDS = select.POLLHUP|select.POLLERR | |
|
77 | SERVER = None | |
|
78 | ip = IPython.ipapi.get() | |
|
79 | ||
|
80 | # this listens to a unix domain socket in a separate thread, so that comms | |
|
81 | # between a vim instance and ipython can happen in a fun and productive way | |
|
82 | class IpyServer(threading.Thread): | |
|
83 | def __init__(self, sname): | |
|
84 | super(IpyServer, self).__init__() | |
|
85 | self.keep_running = True | |
|
86 | self.__sname = sname | |
|
87 | self.socket = socket.socket(socket.AF_UNIX) | |
|
88 | self.poller = select.poll() | |
|
89 | self.current_conns = dict() | |
|
90 | self.setDaemon(True) | |
|
91 | ||
|
92 | def listen(self): | |
|
93 | self.socket.bind(self.__sname) | |
|
94 | self.socket.listen(1) | |
|
95 | ||
|
96 | def __handle_error(self, socket): | |
|
97 | if socket == self.socket.fileno(): | |
|
98 | self.keep_running = False | |
|
99 | for a in self.current_conns.values(): | |
|
100 | a.close() | |
|
101 | return False | |
|
102 | else: | |
|
103 | y = self.current_conns[socket] | |
|
104 | del self.current_conns[socket] | |
|
105 | y.close() | |
|
106 | self.poller.unregister(socket) | |
|
107 | ||
|
108 | def serve_me(self): | |
|
109 | self.listen() | |
|
110 | self.poller.register(self.socket,select.POLLIN|ERRCONDS) | |
|
111 | ||
|
112 | while self.keep_running: | |
|
113 | try: | |
|
114 | avail = self.poller.poll(1) | |
|
115 | except: | |
|
116 | continue | |
|
117 | ||
|
118 | if not avail: continue | |
|
119 | ||
|
120 | for sock, conds in avail: | |
|
121 | if conds & (ERRCONDS): | |
|
122 | if self.__handle_error(sock): continue | |
|
123 | else: break | |
|
124 | ||
|
125 | if sock == self.socket.fileno(): | |
|
126 | y = self.socket.accept()[0] | |
|
127 | self.poller.register(y, select.POLLIN|ERRCONDS) | |
|
128 | self.current_conns[y.fileno()] = y | |
|
129 | else: y = self.current_conns.get(sock) | |
|
130 | ||
|
131 | self.handle_request(y) | |
|
132 | ||
|
133 | os.remove(self.__sname) | |
|
134 | ||
|
135 | run = serve_me | |
|
136 | ||
|
137 | def stop(self): | |
|
138 | self.keep_running = False | |
|
139 | ||
|
140 | def handle_request(self,sock): | |
|
141 | sock.settimeout(1) | |
|
142 | while self.keep_running: | |
|
143 | try: | |
|
144 | x = sock.recv(4096) | |
|
145 | except socket.timeout: | |
|
146 | pass | |
|
147 | else: | |
|
148 | break | |
|
149 | self.do_it(x) | |
|
150 | ||
|
151 | def do_it(self, data): | |
|
152 | data = data.split('\n') | |
|
153 | cmds = list() | |
|
154 | for line in data: | |
|
155 | cmds.append(line) | |
|
156 | ip.runlines(cmds) | |
|
157 | ||
|
158 | ||
|
159 | # try to help ensure that the unix domain socket is cleaned up proper | |
|
160 | def shutdown_server(self): | |
|
161 | if SERVER: | |
|
162 | SERVER.stop() | |
|
163 | SERVER.join(3) | |
|
164 | raise IPython.ipapi.TryNext | |
|
165 | ||
|
166 | ip.set_hook('shutdown_hook', shutdown_server, 10) | |
|
167 | ||
|
168 | # this fun function exists to make setup easier for all, and makes the | |
|
169 | # vimhook function ready for instance specific communication | |
|
170 | def setup(sessionname='',socketdir=os.path.expanduser('~/.ipython/')): | |
|
171 | global SERVER | |
|
172 | ||
|
173 | if sessionname: | |
|
174 | session = sessionname | |
|
175 | elif os.environ.get('IPY_SESSION'): | |
|
176 | session = os.environ.get('IPY_SESSION') | |
|
177 | else: | |
|
178 | session = 'IPYS' | |
|
179 | vimhook.vimserver=session | |
|
180 | vimhook.ipyserver = os.path.join(socketdir, session) | |
|
181 | if not SERVER: | |
|
182 | SERVER = IpyServer(vimhook.ipyserver) | |
|
183 | SERVER.start() | |
|
184 | ||
|
185 | ||
|
186 | ||
|
187 | # calls gvim, with all ops happening on the correct gvim instance for this | |
|
188 | # ipython instance. it then calls edit -x (since gvim will return right away) | |
|
189 | # things of note: it sets up a special environment, so that the ipy.vim script | |
|
190 | # can connect back to the ipython instance and do fun things, like run the file | |
|
191 | def vimhook(self, fname, line): | |
|
192 | env = os.environ.copy() | |
|
193 | vserver = vimhook.vimserver.upper() | |
|
194 | check = subprocess.Popen('gvim --serverlist', stdout = subprocess.PIPE, | |
|
195 | shell=True) | |
|
196 | check.wait() | |
|
197 | cval = [l for l in check.stdout.readlines() if vserver in l] | |
|
198 | ||
|
199 | if cval: | |
|
200 | vimargs = '--remote%s' % (vimhook.extras,) | |
|
201 | else: | |
|
202 | vimargs = '' | |
|
203 | vimhook.extras = '' | |
|
204 | ||
|
205 | env['IPY_SESSION'] = vimhook.vimserver | |
|
206 | env['IPY_SERVER'] = vimhook.ipyserver | |
|
207 | ||
|
208 | if line is None: line = '' | |
|
209 | else: line = '+' + line | |
|
210 | vim_cmd = 'gvim --servername %s %s %s %s' % (vimhook.vimserver, vimargs, | |
|
211 | line, fname) | |
|
212 | subprocess.call(vim_cmd, env=env, shell=True) | |
|
213 | ||
|
214 | ||
|
215 | #default values to keep it sane... | |
|
216 | vimhook.vimserver = '' | |
|
217 | vimhook.ipyserver = '' | |
|
218 | ||
|
219 | ip.set_hook('editor',vimhook) | |
|
220 | ||
|
221 | # this is set up so more vim specific commands can be added, instead of just | |
|
222 | # the current -t. all thats required is a compiled regex, a call to do_arg(pat) | |
|
223 | # and the logic to deal with the new feature | |
|
224 | newtab = re.compile(r'-t(?:\s|$)') | |
|
225 | def vim(self, argstr): | |
|
226 | def do_arg(pat, rarg): | |
|
227 | x = len(pat.findall(argstr)) | |
|
228 | if x: | |
|
229 | a = pat.sub('',argstr) | |
|
230 | return rarg, a | |
|
231 | else: return '', argstr | |
|
232 | ||
|
233 | t, argstr = do_arg(newtab, '-tab') | |
|
234 | vimhook.extras = t | |
|
235 | argstr = 'edit -x ' + argstr | |
|
236 | ip.magic(argstr) | |
|
237 | ||
|
238 | ip.expose_magic('vim', vim) | |
|
239 |
@@ -0,0 +1,73 b'' | |||
|
1 | """ Debug a script (like %run -d) in IPython process, Using WinPdb | |
|
2 | ||
|
3 | Usage: | |
|
4 | ||
|
5 | %wdb test.py | |
|
6 | run test.py, with a winpdb breakpoint at start of the file | |
|
7 | ||
|
8 | %wdb pass | |
|
9 | Change the password (e.g. if you have forgotten the old one) | |
|
10 | """ | |
|
11 | ||
|
12 | import os | |
|
13 | ||
|
14 | import IPython.ipapi | |
|
15 | import rpdb2 | |
|
16 | ||
|
17 | ip = IPython.ipapi.get() | |
|
18 | ||
|
19 | rpdb_started = False | |
|
20 | ||
|
21 | def wdb_f(self, arg): | |
|
22 | """ Debug a script (like %run -d) in IPython process, Using WinPdb | |
|
23 | ||
|
24 | Usage: | |
|
25 | ||
|
26 | %wdb test.py | |
|
27 | run test.py, with a winpdb breakpoint at start of the file | |
|
28 | ||
|
29 | %wdb pass | |
|
30 | Change the password (e.g. if you have forgotten the old one) | |
|
31 | ||
|
32 | Note that after the script has been run, you need to do "Go" (f5) | |
|
33 | in WinPdb to resume normal IPython operation. | |
|
34 | """ | |
|
35 | ||
|
36 | global rpdb_started | |
|
37 | if not arg.strip(): | |
|
38 | print __doc__ | |
|
39 | return | |
|
40 | ||
|
41 | if arg.strip() == 'pass': | |
|
42 | passwd = raw_input('Enter new winpdb session password: ') | |
|
43 | ip.db['winpdb_pass'] = passwd | |
|
44 | print "Winpdb password changed" | |
|
45 | if rpdb_started: | |
|
46 | print "You need to restart IPython to use the new password" | |
|
47 | return | |
|
48 | ||
|
49 | path = os.path.abspath(arg) | |
|
50 | if not os.path.isfile(path): | |
|
51 | raise IPython.ipapi.UsageError("%%wdb: file %s does not exist" % path) | |
|
52 | if not rpdb_started: | |
|
53 | passwd = ip.db.get('winpdb_pass', None) | |
|
54 | if passwd is None: | |
|
55 | import textwrap | |
|
56 | print textwrap.dedent("""\ | |
|
57 | Winpdb sessions need a password that you use for attaching the external | |
|
58 | winpdb session. IPython will remember this. You can change the password later | |
|
59 | by '%wpdb pass' | |
|
60 | """) | |
|
61 | passwd = raw_input('Enter new winpdb session password: ') | |
|
62 | ip.db['winpdb_pass'] = passwd | |
|
63 | ||
|
64 | print "Starting rpdb2 in IPython process" | |
|
65 | rpdb2.start_embedded_debugger(passwd, timeout = 0) | |
|
66 | rpdb_started = True | |
|
67 | ||
|
68 | rpdb2.set_temp_breakpoint(path) | |
|
69 | print 'It is time to attach with WinPdb (launch WinPdb if needed, File -> Attach)' | |
|
70 | ip.magic('%run ' + arg) | |
|
71 | ||
|
72 | ||
|
73 | ip.expose_magic('wdb', wdb_f) |
@@ -0,0 +1,460 b'' | |||
|
1 | #!/usr/bin/python | |
|
2 | # -*- coding: iso-8859-15 -*- | |
|
3 | ''' | |
|
4 | Provides IPython remote instance. | |
|
5 | ||
|
6 | @author: Laurent Dufrechou | |
|
7 | laurent.dufrechou _at_ gmail.com | |
|
8 | @license: BSD | |
|
9 | ||
|
10 | All rights reserved. This program and the accompanying materials are made | |
|
11 | available under the terms of the BSD which accompanies this distribution, and | |
|
12 | is available at U{http://www.opensource.org/licenses/bsd-license.php} | |
|
13 | ''' | |
|
14 | ||
|
15 | __version__ = 0.9 | |
|
16 | __author__ = "Laurent Dufrechou" | |
|
17 | __email__ = "laurent.dufrechou _at_ gmail.com" | |
|
18 | __license__ = "BSD" | |
|
19 | ||
|
20 | import re | |
|
21 | import sys | |
|
22 | import os | |
|
23 | import locale | |
|
24 | import time | |
|
25 | import pydoc,__builtin__,site | |
|
26 | from thread_ex import ThreadEx | |
|
27 | from StringIO import StringIO | |
|
28 | ||
|
29 | try: | |
|
30 | import IPython | |
|
31 | except Exception,e: | |
|
32 | raise "Error importing IPython (%s)" % str(e) | |
|
33 | ||
|
34 | ############################################################################## | |
|
35 | class _Helper(object): | |
|
36 | """Redefine the built-in 'help'. | |
|
37 | This is a wrapper around pydoc.help (with a twist). | |
|
38 | """ | |
|
39 | ||
|
40 | def __init__(self,pager): | |
|
41 | self._pager = pager | |
|
42 | ||
|
43 | def __repr__(self): | |
|
44 | return "Type help() for interactive help, " \ | |
|
45 | "or help(object) for help about object." | |
|
46 | ||
|
47 | def __call__(self, *args, **kwds): | |
|
48 | class DummyWriter(object): | |
|
49 | def __init__(self,pager): | |
|
50 | self._pager = pager | |
|
51 | ||
|
52 | def write(self,data): | |
|
53 | self._pager(data) | |
|
54 | ||
|
55 | import pydoc | |
|
56 | pydoc.help.output = DummyWriter(self._pager) | |
|
57 | pydoc.help.interact = lambda :1 | |
|
58 | ||
|
59 | return pydoc.help(*args, **kwds) | |
|
60 | ||
|
61 | ||
|
62 | ############################################################################## | |
|
63 | class _CodeExecutor(ThreadEx): | |
|
64 | ||
|
65 | def __init__(self, instance, after): | |
|
66 | ThreadEx.__init__(self) | |
|
67 | self.instance = instance | |
|
68 | self._afterExecute=after | |
|
69 | ||
|
70 | def run(self): | |
|
71 | try: | |
|
72 | self.instance._doc_text = None | |
|
73 | self.instance._help_text = None | |
|
74 | self.instance._execute() | |
|
75 | # used for uper class to generate event after execution | |
|
76 | self._afterExecute() | |
|
77 | ||
|
78 | except KeyboardInterrupt: | |
|
79 | pass | |
|
80 | ||
|
81 | ||
|
82 | ############################################################################## | |
|
83 | class NonBlockingIPShell(object): | |
|
84 | ''' | |
|
85 | Create an IPython instance, running the commands in a separate, | |
|
86 | non-blocking thread. | |
|
87 | This allows embedding in any GUI without blockage. | |
|
88 | ||
|
89 | Note: The ThreadEx class supports asynchroneous function call | |
|
90 | via raise_exc() | |
|
91 | ''' | |
|
92 | ||
|
93 | def __init__(self,argv=[],user_ns={},user_global_ns=None, | |
|
94 | cin=None, cout=None, cerr=None, | |
|
95 | ask_exit_handler=None): | |
|
96 | ''' | |
|
97 | @param argv: Command line options for IPython | |
|
98 | @type argv: list | |
|
99 | @param user_ns: User namespace. | |
|
100 | @type user_ns: dictionary | |
|
101 | @param user_global_ns: User global namespace. | |
|
102 | @type user_global_ns: dictionary. | |
|
103 | @param cin: Console standard input. | |
|
104 | @type cin: IO stream | |
|
105 | @param cout: Console standard output. | |
|
106 | @type cout: IO stream | |
|
107 | @param cerr: Console standard error. | |
|
108 | @type cerr: IO stream | |
|
109 | @param exit_handler: Replacement for builtin exit() function | |
|
110 | @type exit_handler: function | |
|
111 | @param time_loop: Define the sleep time between two thread's loop | |
|
112 | @type int | |
|
113 | ''' | |
|
114 | #ipython0 initialisation | |
|
115 | self.initIpython0(argv, user_ns, user_global_ns, | |
|
116 | cin, cout, cerr, | |
|
117 | ask_exit_handler) | |
|
118 | ||
|
119 | #vars used by _execute | |
|
120 | self._iter_more = 0 | |
|
121 | self._history_level = 0 | |
|
122 | self._complete_sep = re.compile('[\s\{\}\[\]\(\)\=]') | |
|
123 | self._prompt = str(self._IP.outputcache.prompt1).strip() | |
|
124 | ||
|
125 | #thread working vars | |
|
126 | self._line_to_execute = '' | |
|
127 | ||
|
128 | #vars that will be checked by GUI loop to handle thread states... | |
|
129 | #will be replaced later by PostEvent GUI funtions... | |
|
130 | self._doc_text = None | |
|
131 | self._help_text = None | |
|
132 | self._add_button = None | |
|
133 | ||
|
134 | def initIpython0(self, argv=[], user_ns={}, user_global_ns=None, | |
|
135 | cin=None, cout=None, cerr=None, | |
|
136 | ask_exit_handler=None): | |
|
137 | #first we redefine in/out/error functions of IPython | |
|
138 | if cin: | |
|
139 | IPython.Shell.Term.cin = cin | |
|
140 | if cout: | |
|
141 | IPython.Shell.Term.cout = cout | |
|
142 | if cerr: | |
|
143 | IPython.Shell.Term.cerr = cerr | |
|
144 | ||
|
145 | # This is to get rid of the blockage that accurs during | |
|
146 | # IPython.Shell.InteractiveShell.user_setup() | |
|
147 | IPython.iplib.raw_input = lambda x: None | |
|
148 | ||
|
149 | self._term = IPython.genutils.IOTerm(cin=cin, cout=cout, cerr=cerr) | |
|
150 | ||
|
151 | excepthook = sys.excepthook | |
|
152 | ||
|
153 | self._IP = IPython.Shell.make_IPython( | |
|
154 | argv,user_ns=user_ns, | |
|
155 | user_global_ns=user_global_ns, | |
|
156 | embedded=True, | |
|
157 | shell_class=IPython.Shell.InteractiveShell) | |
|
158 | ||
|
159 | #we replace IPython default encoding by wx locale encoding | |
|
160 | loc = locale.getpreferredencoding() | |
|
161 | if loc: | |
|
162 | self._IP.stdin_encoding = loc | |
|
163 | #we replace the ipython default pager by our pager | |
|
164 | self._IP.set_hook('show_in_pager',self._pager) | |
|
165 | ||
|
166 | #we replace the ipython default shell command caller by our shell handler | |
|
167 | self._IP.set_hook('shell_hook',self._shell) | |
|
168 | ||
|
169 | #we replace the ipython default input command caller by our method | |
|
170 | IPython.iplib.raw_input_original = self._raw_input | |
|
171 | #we replace the ipython default exit command by our method | |
|
172 | self._IP.exit = ask_exit_handler | |
|
173 | #we replace the help command | |
|
174 | self._IP.user_ns['help'] = _Helper(self._pager_help) | |
|
175 | ||
|
176 | #we disable cpase magic... until we found a way to use it properly. | |
|
177 | #import IPython.ipapi | |
|
178 | ip = IPython.ipapi.get() | |
|
179 | def bypassMagic(self, arg): | |
|
180 | print '%this magic is currently disabled.' | |
|
181 | ip.expose_magic('cpaste', bypassMagic) | |
|
182 | ||
|
183 | sys.excepthook = excepthook | |
|
184 | ||
|
185 | #----------------------- Thread management section ---------------------- | |
|
186 | def doExecute(self,line): | |
|
187 | """ | |
|
188 | Tell the thread to process the 'line' command | |
|
189 | """ | |
|
190 | ||
|
191 | self._line_to_execute = line | |
|
192 | #we launch the ipython line execution in a thread to make it interruptible | |
|
193 | self.ce = _CodeExecutor(self,self._afterExecute) | |
|
194 | self.ce.start() | |
|
195 | ||
|
196 | #----------------------- IPython management section ---------------------- | |
|
197 | def getDocText(self): | |
|
198 | """ | |
|
199 | Returns the output of the processing that need to be paged (if any) | |
|
200 | ||
|
201 | @return: The std output string. | |
|
202 | @rtype: string | |
|
203 | """ | |
|
204 | return self._doc_text | |
|
205 | ||
|
206 | def getHelpText(self): | |
|
207 | """ | |
|
208 | Returns the output of the processing that need to be paged via help pager(if any) | |
|
209 | ||
|
210 | @return: The std output string. | |
|
211 | @rtype: string | |
|
212 | """ | |
|
213 | return self._help_text | |
|
214 | ||
|
215 | def getBanner(self): | |
|
216 | """ | |
|
217 | Returns the IPython banner for useful info on IPython instance | |
|
218 | ||
|
219 | @return: The banner string. | |
|
220 | @rtype: string | |
|
221 | """ | |
|
222 | return self._IP.BANNER | |
|
223 | ||
|
224 | def getPromptCount(self): | |
|
225 | """ | |
|
226 | Returns the prompt number. | |
|
227 | Each time a user execute a line in the IPython shell the prompt count is increased | |
|
228 | ||
|
229 | @return: The prompt number | |
|
230 | @rtype: int | |
|
231 | """ | |
|
232 | return self._IP.outputcache.prompt_count | |
|
233 | ||
|
234 | def getPrompt(self): | |
|
235 | """ | |
|
236 | Returns current prompt inside IPython instance | |
|
237 | (Can be In [...]: ot ...:) | |
|
238 | ||
|
239 | @return: The current prompt. | |
|
240 | @rtype: string | |
|
241 | """ | |
|
242 | return self._prompt | |
|
243 | ||
|
244 | def getIndentation(self): | |
|
245 | """ | |
|
246 | Returns the current indentation level | |
|
247 | Usefull to put the caret at the good start position if we want to do autoindentation. | |
|
248 | ||
|
249 | @return: The indentation level. | |
|
250 | @rtype: int | |
|
251 | """ | |
|
252 | return self._IP.indent_current_nsp | |
|
253 | ||
|
254 | def updateNamespace(self, ns_dict): | |
|
255 | ''' | |
|
256 | Add the current dictionary to the shell namespace. | |
|
257 | ||
|
258 | @param ns_dict: A dictionary of symbol-values. | |
|
259 | @type ns_dict: dictionary | |
|
260 | ''' | |
|
261 | self._IP.user_ns.update(ns_dict) | |
|
262 | ||
|
263 | def complete(self, line): | |
|
264 | ''' | |
|
265 | Returns an auto completed line and/or posibilities for completion. | |
|
266 | ||
|
267 | @param line: Given line so far. | |
|
268 | @type line: string | |
|
269 | ||
|
270 | @return: Line completed as for as possible, | |
|
271 | and possible further completions. | |
|
272 | @rtype: tuple | |
|
273 | ''' | |
|
274 | split_line = self._complete_sep.split(line) | |
|
275 | possibilities = self._IP.complete(split_line[-1]) | |
|
276 | if possibilities: | |
|
277 | ||
|
278 | def _commonPrefix(str1, str2): | |
|
279 | ''' | |
|
280 | Reduction function. returns common prefix of two given strings. | |
|
281 | ||
|
282 | @param str1: First string. | |
|
283 | @type str1: string | |
|
284 | @param str2: Second string | |
|
285 | @type str2: string | |
|
286 | ||
|
287 | @return: Common prefix to both strings. | |
|
288 | @rtype: string | |
|
289 | ''' | |
|
290 | for i in range(len(str1)): | |
|
291 | if not str2.startswith(str1[:i+1]): | |
|
292 | return str1[:i] | |
|
293 | return str1 | |
|
294 | common_prefix = reduce(_commonPrefix, possibilities) | |
|
295 | completed = line[:-len(split_line[-1])]+common_prefix | |
|
296 | else: | |
|
297 | completed = line | |
|
298 | return completed, possibilities | |
|
299 | ||
|
300 | def historyBack(self): | |
|
301 | ''' | |
|
302 | Provides one history command back. | |
|
303 | ||
|
304 | @return: The command string. | |
|
305 | @rtype: string | |
|
306 | ''' | |
|
307 | history = '' | |
|
308 | #the below while loop is used to suppress empty history lines | |
|
309 | while((history == '' or history == '\n') and self._history_level >0): | |
|
310 | if self._history_level>=1: | |
|
311 | self._history_level -= 1 | |
|
312 | history = self._getHistory() | |
|
313 | return history | |
|
314 | ||
|
315 | def historyForward(self): | |
|
316 | ''' | |
|
317 | Provides one history command forward. | |
|
318 | ||
|
319 | @return: The command string. | |
|
320 | @rtype: string | |
|
321 | ''' | |
|
322 | history = '' | |
|
323 | #the below while loop is used to suppress empty history lines | |
|
324 | while((history == '' or history == '\n') and self._history_level <= self._getHistoryMaxIndex()): | |
|
325 | if self._history_level < self._getHistoryMaxIndex(): | |
|
326 | self._history_level += 1 | |
|
327 | history = self._getHistory() | |
|
328 | else: | |
|
329 | if self._history_level == self._getHistoryMaxIndex(): | |
|
330 | history = self._getHistory() | |
|
331 | self._history_level += 1 | |
|
332 | else: | |
|
333 | history = '' | |
|
334 | return history | |
|
335 | ||
|
336 | def initHistoryIndex(self): | |
|
337 | ''' | |
|
338 | set history to last command entered | |
|
339 | ''' | |
|
340 | self._history_level = self._getHistoryMaxIndex()+1 | |
|
341 | ||
|
342 | #----------------------- IPython PRIVATE management section -------------- | |
|
343 | def _afterExecute(self): | |
|
344 | ''' | |
|
345 | Can be redefined to generate post event after excution is done | |
|
346 | ''' | |
|
347 | pass | |
|
348 | ||
|
349 | #def _askExit(self): | |
|
350 | # ''' | |
|
351 | # Can be redefined to generate post event to exit the Ipython shell | |
|
352 | # ''' | |
|
353 | # pass | |
|
354 | ||
|
355 | def _getHistoryMaxIndex(self): | |
|
356 | ''' | |
|
357 | returns the max length of the history buffer | |
|
358 | ||
|
359 | @return: history length | |
|
360 | @rtype: int | |
|
361 | ''' | |
|
362 | return len(self._IP.input_hist_raw)-1 | |
|
363 | ||
|
364 | def _getHistory(self): | |
|
365 | ''' | |
|
366 | Get's the command string of the current history level. | |
|
367 | ||
|
368 | @return: Historic command stri | |
|
369 | @rtype: string | |
|
370 | ''' | |
|
371 | rv = self._IP.input_hist_raw[self._history_level].strip('\n') | |
|
372 | return rv | |
|
373 | ||
|
374 | def _pager_help(self,text): | |
|
375 | ''' | |
|
376 | This function is used as a callback replacment to IPython help pager function | |
|
377 | ||
|
378 | It puts the 'text' value inside the self._help_text string that can be retrived via getHelpText | |
|
379 | function. | |
|
380 | ''' | |
|
381 | if self._help_text == None: | |
|
382 | self._help_text = text | |
|
383 | else: | |
|
384 | self._help_text += text | |
|
385 | ||
|
386 | def _pager(self,IP,text): | |
|
387 | ''' | |
|
388 | This function is used as a callback replacment to IPython pager function | |
|
389 | ||
|
390 | It puts the 'text' value inside the self._doc_text string that can be retrived via getDocText | |
|
391 | function. | |
|
392 | ''' | |
|
393 | self._doc_text = text | |
|
394 | ||
|
395 | def _raw_input(self, prompt=''): | |
|
396 | ''' | |
|
397 | Custom raw_input() replacement. Get's current line from console buffer. | |
|
398 | ||
|
399 | @param prompt: Prompt to print. Here for compatability as replacement. | |
|
400 | @type prompt: string | |
|
401 | ||
|
402 | @return: The current command line text. | |
|
403 | @rtype: string | |
|
404 | ''' | |
|
405 | return self._line_to_execute | |
|
406 | ||
|
407 | def _execute(self): | |
|
408 | ''' | |
|
409 | Executes the current line provided by the shell object. | |
|
410 | ''' | |
|
411 | orig_stdout = sys.stdout | |
|
412 | sys.stdout = IPython.Shell.Term.cout | |
|
413 | ||
|
414 | try: | |
|
415 | line = self._IP.raw_input(None, self._iter_more) | |
|
416 | if self._IP.autoindent: | |
|
417 | self._IP.readline_startup_hook(None) | |
|
418 | ||
|
419 | except KeyboardInterrupt: | |
|
420 | self._IP.write('\nKeyboardInterrupt\n') | |
|
421 | self._IP.resetbuffer() | |
|
422 | # keep cache in sync with the prompt counter: | |
|
423 | self._IP.outputcache.prompt_count -= 1 | |
|
424 | ||
|
425 | if self._IP.autoindent: | |
|
426 | self._IP.indent_current_nsp = 0 | |
|
427 | self._iter_more = 0 | |
|
428 | except: | |
|
429 | self._IP.showtraceback() | |
|
430 | else: | |
|
431 | self._iter_more = self._IP.push(line) | |
|
432 | if (self._IP.SyntaxTB.last_syntax_error and | |
|
433 | self._IP.rc.autoedit_syntax): | |
|
434 | self._IP.edit_syntax_error() | |
|
435 | if self._iter_more: | |
|
436 | self._prompt = str(self._IP.outputcache.prompt2).strip() | |
|
437 | if self._IP.autoindent: | |
|
438 | self._IP.readline_startup_hook(self._IP.pre_readline) | |
|
439 | else: | |
|
440 | self._prompt = str(self._IP.outputcache.prompt1).strip() | |
|
441 | self._IP.indent_current_nsp = 0 #we set indentation to 0 | |
|
442 | sys.stdout = orig_stdout | |
|
443 | ||
|
444 | def _shell(self, ip, cmd): | |
|
445 | ''' | |
|
446 | Replacement method to allow shell commands without them blocking. | |
|
447 | ||
|
448 | @param ip: Ipython instance, same as self._IP | |
|
449 | @type cmd: Ipython instance | |
|
450 | @param cmd: Shell command to execute. | |
|
451 | @type cmd: string | |
|
452 | ''' | |
|
453 | stdin, stdout = os.popen4(cmd) | |
|
454 | result = stdout.read().decode('cp437').encode(locale.getpreferredencoding()) | |
|
455 | #we use print command because the shell command is called inside IPython instance and thus is | |
|
456 | #redirected to thread cout | |
|
457 | #"\x01\x1b[1;36m\x02" <-- add colour to the text... | |
|
458 | print "\x01\x1b[1;36m\x02"+result | |
|
459 | stdout.close() | |
|
460 | stdin.close() |
@@ -0,0 +1,6 b'' | |||
|
1 | completion=IPYTHON | |
|
2 | background_color=BLACK | |
|
3 | filter_empty=True | |
|
4 | filter_magic=True | |
|
5 | filter_doc=True | |
|
6 | filter_cmd=True |
@@ -0,0 +1,96 b'' | |||
|
1 | from IPython.genutils import Term,warn,error,flag_calls, ask_yes_no | |
|
2 | ||
|
3 | import thread,inspect | |
|
4 | ||
|
5 | try: | |
|
6 | import ctypes | |
|
7 | HAS_CTYPES = True | |
|
8 | except ImportError: | |
|
9 | HAS_CTYPES = False | |
|
10 | ||
|
11 | ||
|
12 | # Globals | |
|
13 | # global flag to pass around information about Ctrl-C without exceptions | |
|
14 | KBINT = False | |
|
15 | ||
|
16 | # global flag to turn on/off Tk support. | |
|
17 | USE_TK = False | |
|
18 | ||
|
19 | # ID for the main thread, used for cross-thread exceptions | |
|
20 | MAIN_THREAD_ID = thread.get_ident() | |
|
21 | ||
|
22 | # Tag when runcode() is active, for exception handling | |
|
23 | CODE_RUN = None | |
|
24 | ||
|
25 | ||
|
26 | #----------------------------------------------------------------------------- | |
|
27 | # This class is trivial now, but I want to have it in to publish a clean | |
|
28 | # interface. Later when the internals are reorganized, code that uses this | |
|
29 | # shouldn't have to change. | |
|
30 | ||
|
31 | ||
|
32 | if HAS_CTYPES: | |
|
33 | # Add async exception support. Trick taken from: | |
|
34 | # http://sebulba.wikispaces.com/recipe+thread2 | |
|
35 | def _async_raise(tid, exctype): | |
|
36 | """raises the exception, performs cleanup if needed""" | |
|
37 | if not inspect.isclass(exctype): | |
|
38 | raise TypeError("Only types can be raised (not instances)") | |
|
39 | res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, | |
|
40 | ctypes.py_object(exctype)) | |
|
41 | if res == 0: | |
|
42 | raise ValueError("invalid thread id") | |
|
43 | elif res != 1: | |
|
44 | # """if it returns a number greater than one, you're in trouble, | |
|
45 | # and you should call it again with exc=NULL to revert the effect""" | |
|
46 | ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, 0) | |
|
47 | raise SystemError("PyThreadState_SetAsyncExc failed") | |
|
48 | ||
|
49 | def sigint_handler (signum,stack_frame): | |
|
50 | """Sigint handler for threaded apps. | |
|
51 | ||
|
52 | This is a horrible hack to pass information about SIGINT _without_ | |
|
53 | using exceptions, since I haven't been able to properly manage | |
|
54 | cross-thread exceptions in GTK/WX. In fact, I don't think it can be | |
|
55 | done (or at least that's my understanding from a c.l.py thread where | |
|
56 | this was discussed).""" | |
|
57 | ||
|
58 | global KBINT | |
|
59 | ||
|
60 | if CODE_RUN: | |
|
61 | _async_raise(MAIN_THREAD_ID,KeyboardInterrupt) | |
|
62 | else: | |
|
63 | KBINT = True | |
|
64 | print '\nKeyboardInterrupt - Press <Enter> to continue.', | |
|
65 | Term.cout.flush() | |
|
66 | ||
|
67 | else: | |
|
68 | def sigint_handler (signum,stack_frame): | |
|
69 | """Sigint handler for threaded apps. | |
|
70 | ||
|
71 | This is a horrible hack to pass information about SIGINT _without_ | |
|
72 | using exceptions, since I haven't been able to properly manage | |
|
73 | cross-thread exceptions in GTK/WX. In fact, I don't think it can be | |
|
74 | done (or at least that's my understanding from a c.l.py thread where | |
|
75 | this was discussed).""" | |
|
76 | ||
|
77 | global KBINT | |
|
78 | ||
|
79 | print '\nKeyboardInterrupt - Press <Enter> to continue.', | |
|
80 | Term.cout.flush() | |
|
81 | # Set global flag so that runsource can know that Ctrl-C was hit | |
|
82 | KBINT = True | |
|
83 | ||
|
84 | def run_in_frontend(src): | |
|
85 | """ Check if source snippet can be run in the REPL thread, as opposed to GUI mainloop | |
|
86 | ||
|
87 | (to prevent unnecessary hanging of mainloop). | |
|
88 | ||
|
89 | """ | |
|
90 | ||
|
91 | if src.startswith('_ip.system(') and not '\n' in src: | |
|
92 | return True | |
|
93 | return False | |
|
94 | ||
|
95 | ||
|
96 |
@@ -0,0 +1,278 b'' | |||
|
1 | import sys | |
|
2 | ||
|
3 | from twisted.internet import reactor, threads | |
|
4 | ||
|
5 | from IPython.ipmaker import make_IPython | |
|
6 | from IPython.iplib import InteractiveShell | |
|
7 | from IPython.ipstruct import Struct | |
|
8 | import Queue,thread,threading,signal | |
|
9 | from signal import signal, SIGINT | |
|
10 | from IPython.genutils import Term,warn,error,flag_calls, ask_yes_no | |
|
11 | import shellglobals | |
|
12 | ||
|
13 | def install_gtk2(): | |
|
14 | """ Install gtk2 reactor, needs to be called bef """ | |
|
15 | from twisted.internet import gtk2reactor | |
|
16 | gtk2reactor.install() | |
|
17 | ||
|
18 | ||
|
19 | def hijack_reactor(): | |
|
20 | """Modifies Twisted's reactor with a dummy so user code does | |
|
21 | not block IPython. This function returns the original | |
|
22 | 'twisted.internet.reactor' that has been hijacked. | |
|
23 | ||
|
24 | NOTE: Make sure you call this *AFTER* you've installed | |
|
25 | the reactor of your choice. | |
|
26 | """ | |
|
27 | from twisted import internet | |
|
28 | orig_reactor = internet.reactor | |
|
29 | ||
|
30 | class DummyReactor(object): | |
|
31 | def run(self): | |
|
32 | pass | |
|
33 | def __getattr__(self, name): | |
|
34 | return getattr(orig_reactor, name) | |
|
35 | def __setattr__(self, name, value): | |
|
36 | return setattr(orig_reactor, name, value) | |
|
37 | ||
|
38 | internet.reactor = DummyReactor() | |
|
39 | return orig_reactor | |
|
40 | ||
|
41 | class TwistedInteractiveShell(InteractiveShell): | |
|
42 | """Simple multi-threaded shell.""" | |
|
43 | ||
|
44 | # Threading strategy taken from: | |
|
45 | # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/65109, by Brian | |
|
46 | # McErlean and John Finlay. Modified with corrections by Antoon Pardon, | |
|
47 | # from the pygtk mailing list, to avoid lockups with system calls. | |
|
48 | ||
|
49 | # class attribute to indicate whether the class supports threads or not. | |
|
50 | # Subclasses with thread support should override this as needed. | |
|
51 | isthreaded = True | |
|
52 | ||
|
53 | def __init__(self,name,usage=None,rc=Struct(opts=None,args=None), | |
|
54 | user_ns=None,user_global_ns=None,banner2='',**kw): | |
|
55 | """Similar to the normal InteractiveShell, but with threading control""" | |
|
56 | ||
|
57 | InteractiveShell.__init__(self,name,usage,rc,user_ns, | |
|
58 | user_global_ns,banner2) | |
|
59 | ||
|
60 | ||
|
61 | # A queue to hold the code to be executed. | |
|
62 | self.code_queue = Queue.Queue() | |
|
63 | ||
|
64 | # Stuff to do at closing time | |
|
65 | self._kill = None | |
|
66 | on_kill = kw.get('on_kill', []) | |
|
67 | # Check that all things to kill are callable: | |
|
68 | for t in on_kill: | |
|
69 | if not callable(t): | |
|
70 | raise TypeError,'on_kill must be a list of callables' | |
|
71 | self.on_kill = on_kill | |
|
72 | # thread identity of the "worker thread" (that may execute code directly) | |
|
73 | self.worker_ident = None | |
|
74 | self.reactor_started = False | |
|
75 | self.first_run = True | |
|
76 | ||
|
77 | def runsource(self, source, filename="<input>", symbol="single"): | |
|
78 | """Compile and run some source in the interpreter. | |
|
79 | ||
|
80 | Modified version of code.py's runsource(), to handle threading issues. | |
|
81 | See the original for full docstring details.""" | |
|
82 | ||
|
83 | # If Ctrl-C was typed, we reset the flag and return right away | |
|
84 | if shellglobals.KBINT: | |
|
85 | shellglobals.KBINT = False | |
|
86 | return False | |
|
87 | ||
|
88 | if self._kill: | |
|
89 | # can't queue new code if we are being killed | |
|
90 | return True | |
|
91 | ||
|
92 | try: | |
|
93 | code = self.compile(source, filename, symbol) | |
|
94 | except (OverflowError, SyntaxError, ValueError): | |
|
95 | # Case 1 | |
|
96 | self.showsyntaxerror(filename) | |
|
97 | return False | |
|
98 | ||
|
99 | if code is None: | |
|
100 | # Case 2 | |
|
101 | return True | |
|
102 | ||
|
103 | # shortcut - if we are in worker thread, or the worker thread is not running, | |
|
104 | # execute directly (to allow recursion and prevent deadlock if code is run early | |
|
105 | # in IPython construction) | |
|
106 | ||
|
107 | if (not self.reactor_started or (self.worker_ident is None and not self.first_run) | |
|
108 | or self.worker_ident == thread.get_ident() or shellglobals.run_in_frontend(source)): | |
|
109 | InteractiveShell.runcode(self,code) | |
|
110 | return | |
|
111 | ||
|
112 | # Case 3 | |
|
113 | # Store code in queue, so the execution thread can handle it. | |
|
114 | ||
|
115 | self.first_run = False | |
|
116 | completed_ev, received_ev = threading.Event(), threading.Event() | |
|
117 | ||
|
118 | self.code_queue.put((code,completed_ev, received_ev)) | |
|
119 | ||
|
120 | reactor.callLater(0.0,self.runcode) | |
|
121 | received_ev.wait(5) | |
|
122 | if not received_ev.isSet(): | |
|
123 | # the mainloop is dead, start executing code directly | |
|
124 | print "Warning: Timeout for mainloop thread exceeded" | |
|
125 | print "switching to nonthreaded mode (until mainloop wakes up again)" | |
|
126 | self.worker_ident = None | |
|
127 | else: | |
|
128 | completed_ev.wait() | |
|
129 | ||
|
130 | return False | |
|
131 | ||
|
132 | def runcode(self): | |
|
133 | """Execute a code object. | |
|
134 | ||
|
135 | Multithreaded wrapper around IPython's runcode().""" | |
|
136 | ||
|
137 | ||
|
138 | # we are in worker thread, stash out the id for runsource() | |
|
139 | self.worker_ident = thread.get_ident() | |
|
140 | ||
|
141 | if self._kill: | |
|
142 | print >>Term.cout, 'Closing threads...', | |
|
143 | Term.cout.flush() | |
|
144 | for tokill in self.on_kill: | |
|
145 | tokill() | |
|
146 | print >>Term.cout, 'Done.' | |
|
147 | # allow kill() to return | |
|
148 | self._kill.set() | |
|
149 | return True | |
|
150 | ||
|
151 | # Install SIGINT handler. We do it every time to ensure that if user | |
|
152 | # code modifies it, we restore our own handling. | |
|
153 | try: | |
|
154 | pass | |
|
155 | signal(SIGINT,shellglobals.sigint_handler) | |
|
156 | except SystemError: | |
|
157 | # This happens under Windows, which seems to have all sorts | |
|
158 | # of problems with signal handling. Oh well... | |
|
159 | pass | |
|
160 | ||
|
161 | # Flush queue of pending code by calling the run methood of the parent | |
|
162 | # class with all items which may be in the queue. | |
|
163 | code_to_run = None | |
|
164 | while 1: | |
|
165 | try: | |
|
166 | code_to_run, completed_ev, received_ev = self.code_queue.get_nowait() | |
|
167 | except Queue.Empty: | |
|
168 | break | |
|
169 | received_ev.set() | |
|
170 | ||
|
171 | ||
|
172 | # Exceptions need to be raised differently depending on which | |
|
173 | # thread is active. This convoluted try/except is only there to | |
|
174 | # protect against asynchronous exceptions, to ensure that a shellglobals.KBINT | |
|
175 | # at the wrong time doesn't deadlock everything. The global | |
|
176 | # CODE_TO_RUN is set to true/false as close as possible to the | |
|
177 | # runcode() call, so that the KBINT handler is correctly informed. | |
|
178 | try: | |
|
179 | try: | |
|
180 | shellglobals.CODE_RUN = True | |
|
181 | InteractiveShell.runcode(self,code_to_run) | |
|
182 | except KeyboardInterrupt: | |
|
183 | print "Keyboard interrupted in mainloop" | |
|
184 | while not self.code_queue.empty(): | |
|
185 | code = self.code_queue.get_nowait() | |
|
186 | break | |
|
187 | finally: | |
|
188 | shellglobals.CODE_RUN = False | |
|
189 | # allow runsource() return from wait | |
|
190 | completed_ev.set() | |
|
191 | ||
|
192 | # This MUST return true for gtk threading to work | |
|
193 | return True | |
|
194 | ||
|
195 | def kill(self): | |
|
196 | """Kill the thread, returning when it has been shut down.""" | |
|
197 | self._kill = threading.Event() | |
|
198 | reactor.callLater(0.0,self.runcode) | |
|
199 | self._kill.wait() | |
|
200 | ||
|
201 | ||
|
202 | ||
|
203 | class IPShellTwisted: | |
|
204 | """Run a Twisted reactor while in an IPython session. | |
|
205 | ||
|
206 | Python commands can be passed to the thread where they will be | |
|
207 | executed. This is implemented by periodically checking for | |
|
208 | passed code using a Twisted reactor callback. | |
|
209 | """ | |
|
210 | ||
|
211 | TIMEOUT = 0.01 # Millisecond interval between reactor runs. | |
|
212 | ||
|
213 | def __init__(self, argv=None, user_ns=None, debug=1, | |
|
214 | shell_class=TwistedInteractiveShell): | |
|
215 | ||
|
216 | from twisted.internet import reactor | |
|
217 | self.reactor = hijack_reactor() | |
|
218 | ||
|
219 | mainquit = self.reactor.stop | |
|
220 | ||
|
221 | # Make sure IPython keeps going after reactor stop. | |
|
222 | def reactorstop(): | |
|
223 | pass | |
|
224 | self.reactor.stop = reactorstop | |
|
225 | reactorrun_orig = self.reactor.run | |
|
226 | self.quitting = False | |
|
227 | def reactorrun(): | |
|
228 | while True and not self.quitting: | |
|
229 | reactorrun_orig() | |
|
230 | self.reactor.run = reactorrun | |
|
231 | ||
|
232 | self.IP = make_IPython(argv, user_ns=user_ns, debug=debug, | |
|
233 | shell_class=shell_class, | |
|
234 | on_kill=[mainquit]) | |
|
235 | ||
|
236 | # threading.Thread.__init__(self) | |
|
237 | ||
|
238 | def run(self): | |
|
239 | self.IP.mainloop() | |
|
240 | self.quitting = True | |
|
241 | self.IP.kill() | |
|
242 | ||
|
243 | def mainloop(self): | |
|
244 | def mainLoopThreadDeath(r): | |
|
245 | print "mainLoopThreadDeath: ", str(r) | |
|
246 | def spawnMainloopThread(): | |
|
247 | d=threads.deferToThread(self.run) | |
|
248 | d.addBoth(mainLoopThreadDeath) | |
|
249 | reactor.callWhenRunning(spawnMainloopThread) | |
|
250 | self.IP.reactor_started = True | |
|
251 | self.reactor.run() | |
|
252 | print "mainloop ending...." | |
|
253 | ||
|
254 | exists = True | |
|
255 | ||
|
256 | ||
|
257 | if __name__ == '__main__': | |
|
258 | # Sample usage. | |
|
259 | ||
|
260 | # Create the shell object. This steals twisted.internet.reactor | |
|
261 | # for its own purposes, to make sure you've already installed a | |
|
262 | # reactor of your choice. | |
|
263 | shell = IPShellTwisted( | |
|
264 | argv=[], | |
|
265 | user_ns={'__name__': '__example__', | |
|
266 | 'hello': 'world', | |
|
267 | }, | |
|
268 | ) | |
|
269 | ||
|
270 | # Run the mainloop. This runs the actual reactor.run() method. | |
|
271 | # The twisted.internet.reactor object at this point is a dummy | |
|
272 | # object that passes through to the actual reactor, but prevents | |
|
273 | # run() from being called on it again. | |
|
274 | shell.mainloop() | |
|
275 | ||
|
276 | # You must exit IPython to terminate your program. | |
|
277 | print 'Goodbye!' | |
|
278 |
@@ -0,0 +1,1 b'' | |||
|
1 | doc/ipython.el usr/share/emacs/site-lisp |
@@ -0,0 +1,52 b'' | |||
|
1 | .TH IRUNNER 1 "April 24, 2007" "" "" | |
|
2 | .SH NAME | |
|
3 | \fBirunner \- interactive runner interface | |
|
4 | .SH SYNOPSIS | |
|
5 | .nf | |
|
6 | .fam C | |
|
7 | \fBirunner\fP [\fIoptions\fP] \fIfile_to_run\fP | |
|
8 | .fam T | |
|
9 | .fi | |
|
10 | .SH DESCRIPTION | |
|
11 | irunner is an interface to the various interactive runners | |
|
12 | available in IPython's \fBirunner\fP module. | |
|
13 | .PP | |
|
14 | The already implemented runners are listed below; adding | |
|
15 | one for a new program is a trivial task, see the source | |
|
16 | for examples. | |
|
17 | .SH OPTIONS | |
|
18 | .TP | |
|
19 | .B | |
|
20 | \-h, \-\-help | |
|
21 | show this help message and exit | |
|
22 | .TP | |
|
23 | .B | |
|
24 | \-\-ipython | |
|
25 | IPython interactive runner (default). | |
|
26 | .TP | |
|
27 | .B | |
|
28 | \-\-python | |
|
29 | Python interactive runner. | |
|
30 | .TP | |
|
31 | .B | |
|
32 | \-\-sage | |
|
33 | SAGE interactive runner. | |
|
34 | .SH EXAMPLE | |
|
35 | irunner.py \-\-python \-\- \-\-help | |
|
36 | will pass \-\-help to the python runner. | |
|
37 | Similarly, | |
|
38 | irunner.py \-\-ipython \-\- \-\-interact script.ipy | |
|
39 | .SH SEE ALSO | |
|
40 | .BR ipython(1) | |
|
41 | .br | |
|
42 | .SH BUGS | |
|
43 | The SAGE runner only works if you manually configure your SAGE | |
|
44 | copy to use 'colors NoColor' in the ipythonrc config file, since | |
|
45 | currently the prompt matching regexp does not identify color sequences. | |
|
46 | .SH AUTHOR | |
|
47 | \fBirunner\fP is an extension of Ken Schutte <kschutte-AT-csail.mit.edu>'s | |
|
48 | script contributed on the ipython-user list: | |
|
49 | http://scipy.net/pipermail/ipython-user/2006-May/001705.html | |
|
50 | .PP | |
|
51 | This manual page was written by Bernd Zeimetz <bernd@bzed.de>, | |
|
52 | for the Debian project (but may be used by others). |
@@ -0,0 +1,1 b'' | |||
|
1 | debian/irunner.1 |
@@ -0,0 +1,1 b'' | |||
|
1 | 2 |
@@ -0,0 +1,74 b'' | |||
|
1 | #!/usr/bin/env python | |
|
2 | """Script to build documentation using Sphinx. | |
|
3 | """ | |
|
4 | ||
|
5 | import fileinput,os,sys | |
|
6 | ||
|
7 | def oscmd(c): | |
|
8 | os.system(c) | |
|
9 | ||
|
10 | # html manual. | |
|
11 | oscmd('sphinx-build -d build/doctrees source build/html') | |
|
12 | ||
|
13 | if sys.platform != 'win32': | |
|
14 | # LaTeX format. | |
|
15 | oscmd('sphinx-build -b latex -d build/doctrees source build/latex') | |
|
16 | ||
|
17 | # Produce pdf. | |
|
18 | topdir = os.getcwd() | |
|
19 | os.chdir('build/latex') | |
|
20 | ||
|
21 | # Change chapter style to section style: allows chapters to start on | |
|
22 | # the current page. Works much better for the short chapters we have. | |
|
23 | # This must go in the class file rather than the preamble, so we modify | |
|
24 | # manual.cls at runtime. | |
|
25 | chapter_cmds=r''' | |
|
26 | % Local changes. | |
|
27 | \renewcommand\chapter{ | |
|
28 | \thispagestyle{plain} | |
|
29 | \global\@topnum\z@ | |
|
30 | \@afterindentfalse | |
|
31 | \secdef\@chapter\@schapter | |
|
32 | } | |
|
33 | \def\@makechapterhead#1{ | |
|
34 | \vspace*{10\p@} | |
|
35 | {\raggedright \reset@font \Huge \bfseries \thechapter \quad #1} | |
|
36 | \par\nobreak | |
|
37 | \hrulefill | |
|
38 | \par\nobreak | |
|
39 | \vspace*{10\p@} | |
|
40 | } | |
|
41 | \def\@makeschapterhead#1{ | |
|
42 | \vspace*{10\p@} | |
|
43 | {\raggedright \reset@font \Huge \bfseries #1} | |
|
44 | \par\nobreak | |
|
45 | \hrulefill | |
|
46 | \par\nobreak | |
|
47 | \vspace*{10\p@} | |
|
48 | } | |
|
49 | ''' | |
|
50 | ||
|
51 | unmodified=True | |
|
52 | for line in fileinput.FileInput('manual.cls',inplace=1): | |
|
53 | if 'Support for module synopsis' in line and unmodified: | |
|
54 | line=chapter_cmds+line | |
|
55 | elif 'makechapterhead' in line: | |
|
56 | # Already have altered manual.cls: don't need to again. | |
|
57 | unmodified=False | |
|
58 | print line, | |
|
59 | ||
|
60 | # Copying the makefile produced by sphinx... | |
|
61 | oscmd('pdflatex ipython.tex') | |
|
62 | oscmd('pdflatex ipython.tex') | |
|
63 | oscmd('pdflatex ipython.tex') | |
|
64 | oscmd('makeindex -s python.ist ipython.idx') | |
|
65 | oscmd('makeindex -s python.ist modipython.idx') | |
|
66 | oscmd('pdflatex ipython.tex') | |
|
67 | oscmd('pdflatex ipython.tex') | |
|
68 | ||
|
69 | # Create a manual/ directory with final html/pdf output | |
|
70 | os.chdir(topdir) | |
|
71 | oscmd('rm -rf manual') | |
|
72 | oscmd('mkdir manual') | |
|
73 | oscmd('cp -r build/html/*.html build/html/_static manual/') | |
|
74 | oscmd('cp build/latex/ipython.pdf manual/') |
@@ -0,0 +1,67 b'' | |||
|
1 | if !exists("$IPY_SESSION") | |
|
2 | finish | |
|
3 | endif | |
|
4 | ||
|
5 | " set up the python interpreter within vim, to have all the right modules | |
|
6 | " imported, as well as certain useful globals set | |
|
7 | python import socket | |
|
8 | python import os | |
|
9 | python import vim | |
|
10 | python IPYSERVER = None | |
|
11 | ||
|
12 | python << EOF | |
|
13 | # do we have a connection to the ipython instance? | |
|
14 | def check_server(): | |
|
15 | global IPYSERVER | |
|
16 | if IPYSERVER: | |
|
17 | return True | |
|
18 | else: | |
|
19 | return False | |
|
20 | ||
|
21 | # connect to the ipython server, if we need to | |
|
22 | def connect(): | |
|
23 | global IPYSERVER | |
|
24 | if check_server(): | |
|
25 | return | |
|
26 | try: | |
|
27 | IPYSERVER = socket.socket(socket.AF_UNIX) | |
|
28 | IPYSERVER.connect(os.environ.get('IPY_SERVER')) | |
|
29 | except: | |
|
30 | IPYSERVER = None | |
|
31 | ||
|
32 | def disconnect(): | |
|
33 | if IPYSERVER: | |
|
34 | IPYSERVER.close() | |
|
35 | ||
|
36 | def send(cmd): | |
|
37 | x = 0 | |
|
38 | while True: | |
|
39 | x += IPYSERVER.send(cmd) | |
|
40 | if x < len(cmd): | |
|
41 | cmd = cmd[x:] | |
|
42 | else: | |
|
43 | break | |
|
44 | ||
|
45 | def run_this_file(): | |
|
46 | if check_server(): | |
|
47 | send('run %s' % (vim.current.buffer.name,)) | |
|
48 | else: | |
|
49 | raise Exception, "Not connected to an IPython server" | |
|
50 | EOF | |
|
51 | ||
|
52 | fun! <SID>toggle_send_on_save() | |
|
53 | if exists("s:ssos") && s:ssos == 1 | |
|
54 | let s:ssos = 0 | |
|
55 | au! BufWritePost *.py :py run_this_file() | |
|
56 | echo "Autosend Off" | |
|
57 | else | |
|
58 | let s:ssos = 1 | |
|
59 | au BufWritePost *.py :py run_this_file() | |
|
60 | echo "Autowsend On" | |
|
61 | endif | |
|
62 | endfun | |
|
63 | ||
|
64 | map <silent> <F5> :python run_this_file()<CR> | |
|
65 | imap <silent> <C-F5> <ESC><F5>a | |
|
66 | map <F7> :call <SID>toggle_send_on_save()<CR> | |
|
67 | py connect() |
This diff has been collapsed as it changes many lines, (552 lines changed) Show them Hide them | |||
@@ -0,0 +1,552 b'' | |||
|
1 | <?xml version="1.0" encoding="utf-8"?> | |
|
2 | <?xml-stylesheet ekr_test?> | |
|
3 | <leo_file> | |
|
4 | <leo_header file_format="2" tnodes="0" max_tnode_index="0" clone_windows="0"/> | |
|
5 | <globals body_outline_ratio="0.307814992026"> | |
|
6 | <global_window_position top="257" left="131" height="627" width="1280"/> | |
|
7 | <global_log_window_position top="0" left="0" height="0" width="0"/> | |
|
8 | </globals> | |
|
9 | <preferences/> | |
|
10 | <find_panel_settings/> | |
|
11 | <vnodes> | |
|
12 | <v t="vivainio.20080222193236" a="E"><vh>Documentation</vh> | |
|
13 | <v t="vivainio.20080223121915" tnodeList="vivainio.20080223121915,vivainio.20080222193236.1,vivainio.20080223133858,vivainio.20080223133922,vivainio.20080223133947,vivainio.20080223134018,vivainio.20080223134100,vivainio.20080223134118,vivainio.20080223134433,vivainio.20080223142207,vivainio.20080223134136"><vh>@nosent ILeo_doc.txt</vh> | |
|
14 | <v t="vivainio.20080222193236.1"><vh>Documentation</vh> | |
|
15 | <v t="vivainio.20080223133858"><vh>Introduction</vh></v> | |
|
16 | <v t="vivainio.20080223133922"><vh>Installation</vh></v> | |
|
17 | <v t="vivainio.20080223133947"><vh>Accessing IPython from Leo</vh></v> | |
|
18 | <v t="vivainio.20080223134018"><vh>Accessing Leo nodes from IPython</vh></v> | |
|
19 | <v t="vivainio.20080223134100"><vh>Cl definitions</vh></v> | |
|
20 | <v t="vivainio.20080223134118"><vh>Special node types</vh></v> | |
|
21 | <v t="vivainio.20080223134433"><vh>Custom push</vh></v> | |
|
22 | <v t="vivainio.20080223142207" a="E"><vh>Code snippets</vh></v> | |
|
23 | <v t="vivainio.20080223134136"><vh>Acknowledgements and history</vh></v> | |
|
24 | </v> | |
|
25 | </v> | |
|
26 | </v> | |
|
27 | <v t="vivainio.20080218184525"><vh>@chapters</vh></v> | |
|
28 | <v t="vivainio.20080223133721" a="E"><vh>@settings</vh> | |
|
29 | <v t="vivainio.20080316092617"><vh>@@string ipython_argv = ipython -pylab</vh></v> | |
|
30 | <v t="vivainio.20080223133721.1"><vh>@enabled-plugins</vh></v> | |
|
31 | </v> | |
|
32 | <v t="vivainio.20080218184540"><vh>@ipy-startup</vh> | |
|
33 | <v t="vivainio.20080218184613.1"><vh>b</vh></v> | |
|
34 | <v t="vivainio.20080218200031"><vh>Some classes P</vh> | |
|
35 | <v t="vivainio.20080218190816"><vh>File-like access</vh></v> | |
|
36 | <v t="vivainio.20080218200106"><vh>csv data</vh></v> | |
|
37 | <v t="vivainio.20080219225120"><vh>String list</vh></v> | |
|
38 | <v t="vivainio.20080219230342"><vh>slist to leo</vh></v> | |
|
39 | </v> | |
|
40 | </v> | |
|
41 | <v t="vivainio.20080218195413" a="E"><vh>Class tests</vh> | |
|
42 | <v t="vivainio.20080218200509"><vh>csvr</vh></v> | |
|
43 | <v t="vivainio.20080218191007"><vh>tempfile</vh></v> | |
|
44 | <v t="vivainio.20080218195413.1"><vh>rfile</vh></v> | |
|
45 | <v t="vivainio.20080219225804"><vh>strlist</vh></v> | |
|
46 | </v> | |
|
47 | <v t="vivainio.20080222201226" a="V"><vh>IPython script push tests</vh></v> | |
|
48 | <v t="vivainio.20080218201219" a="E"><vh>Direct variables</vh> | |
|
49 | <v t="vivainio.20080218201219.2"><vh>bar</vh></v> | |
|
50 | </v> | |
|
51 | <v t="vivainio.20080316144536" a="E"><vh>pylab tests</vh> | |
|
52 | <v t="vivainio.20080316145539.2"><vh>Generate testarr</vh></v> | |
|
53 | <v t="vivainio.20080316085925"><vh>testarr</vh></v> | |
|
54 | <v t="vivainio.20080316085950"><vh>Call plotter on testarr</vh></v> | |
|
55 | </v> | |
|
56 | <v t="vivainio.20080222202211"><vh>test stuff</vh></v> | |
|
57 | <v t="vivainio.20080223142403"><vh>@ipy-results</vh> | |
|
58 | <v t="vivainio.20080223142403.1"><vh>foo</vh></v> | |
|
59 | </v> | |
|
60 | <v t="vivainio.20080222202211.1" a="E"><vh>spam</vh></v> | |
|
61 | </vnodes> | |
|
62 | <tnodes> | |
|
63 | <t tx="vivainio.20080218184525">?</t> | |
|
64 | <t tx="vivainio.20080218184540">?Direct children of this node will be pushed at ipython bridge startup | |
|
65 | ||
|
66 | This node itself will *not* be pushed</t> | |
|
67 | <t tx="vivainio.20080218184613.1">print "world"</t> | |
|
68 | <t tx="vivainio.20080218190816">def rfile(body,n): | |
|
69 | """ @cl rfile | |
|
70 | ||
|
71 | produces a StringIO (file like obj of the rest of the body) """ | |
|
72 | ||
|
73 | import StringIO | |
|
74 | return StringIO.StringIO(body) | |
|
75 | ||
|
76 | def tmpfile(body,n): | |
|
77 | """ @cl tmpfile | |
|
78 | ||
|
79 | Produces a temporary file, with node body as contents | |
|
80 | ||
|
81 | """ | |
|
82 | import tempfile | |
|
83 | h, fname = tempfile.mkstemp() | |
|
84 | f = open(fname,'w') | |
|
85 | f.write(body) | |
|
86 | f.close() | |
|
87 | return fname | |
|
88 | </t> | |
|
89 | <t tx="vivainio.20080218191007">@cl tmpfile | |
|
90 | ||
|
91 | Hello</t> | |
|
92 | <t tx="vivainio.20080218195413">?</t> | |
|
93 | <t tx="vivainio.20080218195413.1">@cl rfile | |
|
94 | These | |
|
95 | lines | |
|
96 | should | |
|
97 | be | |
|
98 | readable </t> | |
|
99 | <t tx="vivainio.20080218200031">@others</t> | |
|
100 | <t tx="vivainio.20080218200106">def csvdata(body,n): | |
|
101 | import csv | |
|
102 | d = csv.Sniffer().sniff(body) | |
|
103 | reader = csv.reader(body.splitlines(), dialect = d) | |
|
104 | return reader</t> | |
|
105 | <t tx="vivainio.20080218200509">@cl csvdata | |
|
106 | ||
|
107 | a,b,b | |
|
108 | 1,2,2</t> | |
|
109 | <t tx="vivainio.20080218201219"></t> | |
|
110 | <t tx="vivainio.20080218201219.2">@cl | |
|
111 | "hello world"</t> | |
|
112 | <t tx="vivainio.20080219225120">import IPython.genutils | |
|
113 | def slist(body,n): | |
|
114 | return IPython.genutils.SList(body.splitlines()) | |
|
115 | </t> | |
|
116 | <t tx="vivainio.20080219225804">@cl slist | |
|
117 | hello | |
|
118 | world | |
|
119 | on | |
|
120 | many | |
|
121 | lines | |
|
122 | </t> | |
|
123 | <t tx="vivainio.20080219230342">import ipy_leo | |
|
124 | @ipy_leo.format_for_leo.when_type(IPython.genutils.SList) | |
|
125 | def format_slist(obj): | |
|
126 | return "@cl slist\n" + obj.n | |
|
127 | </t> | |
|
128 | <t tx="vivainio.20080222193236">?</t> | |
|
129 | <t tx="vivainio.20080222193236.1">@wrap | |
|
130 | @nocolor</t> | |
|
131 | <t tx="vivainio.20080222201226"># test ipython script 'cleanup' with complex blocks | |
|
132 | 1+2 | |
|
133 | print "hello" | |
|
134 | 3+4 | |
|
135 | ||
|
136 | def f(x): | |
|
137 | return x.upper() | |
|
138 | ||
|
139 | ||
|
140 | if 0: | |
|
141 | print "foo" | |
|
142 | else: | |
|
143 | print "bar" | |
|
144 | ||
|
145 | def g(): | |
|
146 | pass | |
|
147 | ||
|
148 | g() | |
|
149 | ||
|
150 | if 1: | |
|
151 | if 1: | |
|
152 | print "hello" | |
|
153 | ||
|
154 | print "world" | |
|
155 | ||
|
156 | if 1: | |
|
157 | print "hello" | |
|
158 | ||
|
159 | print "word" | |
|
160 | else: | |
|
161 | print "foo" | |
|
162 | ||
|
163 | print "bar" | |
|
164 | print "baz" | |
|
165 | ||
|
166 | try: | |
|
167 | raise Exception | |
|
168 | except: | |
|
169 | print "exc ok"</t> | |
|
170 | <t tx="vivainio.20080222202211"></t> | |
|
171 | <t tx="vivainio.20080222202211.1" ipython="7d71005506636f6f7264737101284b0c4bde747102732e">@cl rfile | |
|
172 | hello | |
|
173 | world | |
|
174 | and whatever</t> | |
|
175 | <t tx="vivainio.20080223121915">@others | |
|
176 | </t> | |
|
177 | <t tx="vivainio.20080223133721"></t> | |
|
178 | <t tx="vivainio.20080223133721.1">ipython.py</t> | |
|
179 | <t tx="vivainio.20080223133858"> | |
|
180 | Introduction | |
|
181 | ============ | |
|
182 | ||
|
183 | The purpose of ILeo, or leo-ipython bridge, is being a two-way communication | |
|
184 | channel between Leo and IPython. The level of integration is much deeper than | |
|
185 | conventional integration in IDEs; most notably, you are able to store *data* in | |
|
186 | Leo nodes, in addition to mere program code. The possibilities of this are | |
|
187 | endless, and this degree of integration has not been seen previously in the python | |
|
188 | world. | |
|
189 | ||
|
190 | IPython users are accustomed to using things like %edit to produce non-trivial | |
|
191 | functions/classes (i.e. something that they don't want to enter directly on the | |
|
192 | interactive prompt, but creating a proper script/module involves too much | |
|
193 | overhead). In ILeo, this task consists just going to the Leo window, creating a node | |
|
194 | and writing the code there, and pressing alt+I (push-to-ipython). | |
|
195 | ||
|
196 | Obviously, you can save the Leo document as usual - this is a great advantage | |
|
197 | of ILeo over using %edit, you can save your experimental scripts all at one | |
|
198 | time, without having to organize them into script/module files (before you | |
|
199 | really want to, of course!) | |
|
200 | </t> | |
|
201 | <t tx="vivainio.20080223133922"> | |
|
202 | Installation | |
|
203 | ============ | |
|
204 | ||
|
205 | You need at least Leo 4.4.7, and the development version of IPython (ILeo | |
|
206 | will be incorporated to IPython 0.8.3). | |
|
207 | ||
|
208 | You can get IPython from Launchpad by installing bzr and doing | |
|
209 | ||
|
210 | bzr branch lp:ipython | |
|
211 | ||
|
212 | and running "setup.py install". | |
|
213 | ||
|
214 | You need to enable the 'ipython.py' plugin in Leo: | |
|
215 | ||
|
216 | - Help -> Open LeoSettings.leo | |
|
217 | ||
|
218 | - Edit @settings-->Plugins-->@enabled-plugins, add/uncomment 'ipython.py' | |
|
219 | ||
|
220 | - Alternatively, you can add @settings-->@enabled-plugins with body ipython.py to your leo document. | |
|
221 | ||
|
222 | - Restart Leo. Be sure that you have the console window open (start leo.py from console, or double-click leo.py on windows) | |
|
223 | ||
|
224 | - Press alt+5 OR alt-x start-ipython to launch IPython in the console that | |
|
225 | started leo. You can start entering IPython commands normally, and Leo will keep | |
|
226 | running at the same time. | |
|
227 | </t> | |
|
228 | <t tx="vivainio.20080223133947"> | |
|
229 | Accessing IPython from Leo | |
|
230 | ========================== | |
|
231 | ||
|
232 | IPython code | |
|
233 | ------------ | |
|
234 | ||
|
235 | Just enter IPython commands on a Leo node and press alt-I to execute | |
|
236 | push-to-ipython in order to execute the script in IPython. 'commands' is | |
|
237 | interpreted loosely here - you can enter function and class definitions, in | |
|
238 | addition to the things you would usually enter at IPython prompt - calculations, | |
|
239 | system commands etc. | |
|
240 | ||
|
241 | Everything that would be legal to enter on IPython prompt is legal to execute | |
|
242 | from ILeo. | |
|
243 | ||
|
244 | Results will be shows in Leo log window for convenience, in addition to the console. | |
|
245 | ||
|
246 | Suppose that a node had the following contents: | |
|
247 | {{{ | |
|
248 | 1+2 | |
|
249 | print "hello" | |
|
250 | 3+4 | |
|
251 | ||
|
252 | def f(x): | |
|
253 | return x.upper() | |
|
254 | ||
|
255 | f('hello world') | |
|
256 | }}} | |
|
257 | ||
|
258 | If you press alt+I on that node, you will see the following in Leo log window (IPython tab): | |
|
259 | ||
|
260 | {{{ | |
|
261 | In: 1+2 | |
|
262 | <2> 3 | |
|
263 | In: 3+4 | |
|
264 | <4> 7 | |
|
265 | In: f('hello world') | |
|
266 | <6> 'HELLO WORLD' | |
|
267 | }}} | |
|
268 | ||
|
269 | (numbers like <6> mean IPython output history indices; the actual object can be | |
|
270 | referenced with _6 as usual in IPython). | |
|
271 | ||
|
272 | ||
|
273 | Plain Python code | |
|
274 | ----------------- | |
|
275 | ||
|
276 | If the headline of the node ends with capital P, alt-I will not run the code | |
|
277 | through IPython translation mechanism but use the direct python 'exec' statement | |
|
278 | (in IPython user namespace) to execute the code. It wont be shown in IPython | |
|
279 | history, and sometimes it is safer (and more efficient) to execute things as | |
|
280 | plain Python statements. Large class definitions are good candidates for P | |
|
281 | nodes. | |
|
282 | </t> | |
|
283 | <t tx="vivainio.20080223134018"> | |
|
284 | Accessing Leo nodes from IPython | |
|
285 | ================================ | |
|
286 | ||
|
287 | The real fun starts when you start entering text to leo nodes, and are using | |
|
288 | that as data (input/output) for your IPython work. | |
|
289 | ||
|
290 | Accessing Leo nodes happens through the variable 'wb' (short for "WorkBook") | |
|
291 | that exist in the IPython user namespace. Nodes that are directly accessible are | |
|
292 | the ones that have simple names which could also be Python variable names; | |
|
293 | 'foo_1' will be accessible directly from IPython, whereas 'my scripts' will not. | |
|
294 | If you want to access a node with arbitrary headline, add a child node '@a foo' | |
|
295 | (@a stands for 'anchor'). Then, the parent of '@a foo' is accessible through | |
|
296 | 'wb.foo'. | |
|
297 | ||
|
298 | You can see what nodes are accessible be entering (in IPython) wb.<TAB>. Example: | |
|
299 | ||
|
300 | [C:leo/src]|12> wb. | |
|
301 | wb.b wb.tempfile wb.rfile wb.NewHeadline | |
|
302 | wb.bar wb.Docs wb.strlist wb.csvr | |
|
303 | [C:leo/src]|12> wb.tempfile | |
|
304 | <12> <ipy_leo.LeoNode object at 0x044B6D90> | |
|
305 | ||
|
306 | So here, we meet the 'LeoNode' class that is your key to manipulating Leo | |
|
307 | content from IPython! | |
|
308 | ||
|
309 | LeoNode | |
|
310 | ------- | |
|
311 | ||
|
312 | Suppose that we had a node with headline 'spam' and body: | |
|
313 | ||
|
314 | ['12',2222+32] | |
|
315 | ||
|
316 | we can access it from IPython (or from scripts entered into other Leo nodes!) by doing: | |
|
317 | ||
|
318 | C:leo/src]|19> wb.spam.v | |
|
319 | <19> ['12', 2254] | |
|
320 | ||
|
321 | 'v' attribute stands for 'value', which means the node contents will be run | |
|
322 | through 'eval' and everything you would be able to enter into IPython prompt | |
|
323 | will be converted to objects. This mechanism can be extended far beyond direct | |
|
324 | evaluation (see '@cl definitions'). | |
|
325 | ||
|
326 | 'v' attribute also has a setter, i.e. you can do: | |
|
327 | ||
|
328 | wb.spam.v = "mystring" | |
|
329 | ||
|
330 | Which will result in the node 'spam' having the following text: | |
|
331 | ||
|
332 | 'mystring' | |
|
333 | ||
|
334 | What assignment to 'v' does can be configured through generic functions | |
|
335 | ('simplegeneric' module, will be explained later). | |
|
336 | ||
|
337 | Besides v, you can set the body text directly through | |
|
338 | ||
|
339 | wb.spam.b = "some\nstring", | |
|
340 | ||
|
341 | headline by | |
|
342 | ||
|
343 | wb.spam.h = 'new_headline' | |
|
344 | ||
|
345 | (obviously you must access the node through wb.new_headline from that point | |
|
346 | onwards), and access the contents as string list (IPython SList) through | |
|
347 | 'wb.spam.l'. | |
|
348 | ||
|
349 | If you do 'wb.foo.v = 12' when node named 'foo' does not exist, the node titled | |
|
350 | 'foo' will be automatically created and assigned body 12. | |
|
351 | ||
|
352 | LeoNode also supports go() that focuses the node in the Leo window, and ipush() | |
|
353 | that simulates pressing alt+I on the node. | |
|
354 | ||
|
355 | You can access unknownAttributes by .uA property dictionary. Unknown attributes | |
|
356 | allow you to store arbitrary (pickleable) python objects in the Leo nodes; the | |
|
357 | attributes are stored when you save the .leo document, and recreated when you | |
|
358 | open the document again. The attributes are not visible anywhere, but can be | |
|
359 | used for domain-specific metatada. Example: | |
|
360 | ||
|
361 | [C:leo/src]|12> wb.spam.uA['coords'] = (12,222) | |
|
362 | [C:leo/src]|13> wb.spam.uA | |
|
363 | <13> {'coords': (12, 222)} | |
|
364 | ||
|
365 | Accessing children with iteration and dict notation | |
|
366 | --------------------------------------------------- | |
|
367 | ||
|
368 | Sometimes, you may want to treat a node as a 'database', where the nodes | |
|
369 | children represent elements in the database. You can create a new child node for | |
|
370 | node 'spam', with headline 'foo bar' like this: | |
|
371 | ||
|
372 | wb.spam['foo bar'] = "Hello" | |
|
373 | ||
|
374 | And assign a new value for it by doing | |
|
375 | ||
|
376 | wb.spam['foo bar'].v = "Hello again" | |
|
377 | ||
|
378 | Note how you can't use .v when you first create the node - i.e. the node needs | |
|
379 | to be initialized by simple assignment, that will be interpreted as assignment | |
|
380 | to '.v'. This is a conscious design choice. | |
|
381 | ||
|
382 | If you try to do wb.spam['bar'] = 'Hello', ILeo will assign '@k bar' as the | |
|
383 | headline for the child instead, because 'bar' is a legal python name (and as | |
|
384 | such would be incorporated in the workbook namespace). This is done to avoid | |
|
385 | crowding the workbook namespace with extraneous items. The item will still be | |
|
386 | accessible as wb.spam['bar'] | |
|
387 | ||
|
388 | LeoNodes are iterable, so to see the headlines of all the children of 'spam' do: | |
|
389 | ||
|
390 | for n in wb.spam: | |
|
391 | print n.h | |
|
392 | </t> | |
|
393 | <t tx="vivainio.20080223134100"> | |
|
394 | @cl definitions | |
|
395 | =============== | |
|
396 | ||
|
397 | If the first line in the body text is of the form '@cl sometext', IPython will | |
|
398 | evaluate 'sometext' and call the result with the rest of the body when you do | |
|
399 | 'wb.foo.v'. An example is in place here. Suppose that we have defined a class (I | |
|
400 | use the term class in a non-python sense here) | |
|
401 | ||
|
402 | {{{ | |
|
403 | def rfile(body,node): | |
|
404 | """ @cl rfile | |
|
405 | ||
|
406 | produces a StringIO (file like obj) of the rest of the body """ | |
|
407 | ||
|
408 | import StringIO | |
|
409 | return StringIO.StringIO(body) | |
|
410 | }}} | |
|
411 | ||
|
412 | (note that node is ignored here - but it could be used to access headline, | |
|
413 | children etc.), | |
|
414 | ||
|
415 | Now, let's say you have node 'spam' with text | |
|
416 | ||
|
417 | {{{ | |
|
418 | @cl rfile | |
|
419 | hello | |
|
420 | world | |
|
421 | and whatever | |
|
422 | }}} | |
|
423 | ||
|
424 | Now, in IPython, we can do this: | |
|
425 | ||
|
426 | {{{ | |
|
427 | [C:leo/src]|22> f = wb.spam.v | |
|
428 | [C:leo/src]|23> f | |
|
429 | <23> <StringIO.StringIO instance at 0x04E7E490> | |
|
430 | [C:leo/src]|24> f.readline() | |
|
431 | <24> u'hello\n' | |
|
432 | [C:leo/src]|25> f.readline() | |
|
433 | <25> u'world\n' | |
|
434 | [C:leo/src]|26> f.readline() | |
|
435 | <26> u'and whatever' | |
|
436 | [C:leo/src]|27> f.readline() | |
|
437 | <27> u'' | |
|
438 | }}} | |
|
439 | ||
|
440 | You should declare new @cl types to make ILeo as convenient your problem domain | |
|
441 | as possible. For example, a "@cl etree" could return the elementtree object for | |
|
442 | xml content. | |
|
443 | </t> | |
|
444 | <t tx="vivainio.20080223134118"> | |
|
445 | Special node types | |
|
446 | ================== | |
|
447 | ||
|
448 | @ipy-startup | |
|
449 | ------------ | |
|
450 | ||
|
451 | If this node exist, the *direct children* of this will be pushed to IPython when | |
|
452 | ILeo is started (you press alt+5). Use it to push your own @cl definitions etc. | |
|
453 | The contents of of the node itself will be ignored. | |
|
454 | ||
|
455 | @ipy-results | |
|
456 | ------------ | |
|
457 | ||
|
458 | When you create a new node (wb.foo.v = 'stuff'), the node foo will be created as | |
|
459 | a child of this node. If @ipy-results does not exist, the new node will be created after the currently selected node. | |
|
460 | ||
|
461 | @a nodes | |
|
462 | -------- | |
|
463 | ||
|
464 | You can attach these as children of existing nodes to provide a way to access | |
|
465 | nodes with arbitrary headlines, or to provide aliases to other nodes. If | |
|
466 | multiple @a nodes are attached as children of a node, all the names can be used | |
|
467 | to access the same object. | |
|
468 | </t> | |
|
469 | <t tx="vivainio.20080223134136"> | |
|
470 | Acknowledgements & History | |
|
471 | ========================== | |
|
472 | ||
|
473 | This idea got started when I (Ville) saw this post by Edward Ream (the author of | |
|
474 | Leo) on IPython developer mailing list: | |
|
475 | ||
|
476 | http://lists.ipython.scipy.org/pipermail/ipython-dev/2008-January/003551.html | |
|
477 | ||
|
478 | I was using FreeMind as mind mapping software, and so I had an immediate use | |
|
479 | case for Leo (which, incidentally, is superior to FreeMind as mind mapper). The | |
|
480 | wheels started rolling, I got obsessed with the power of this concept | |
|
481 | (everything clicked together), and Edwards excitement paralleled mine. | |
|
482 | Everything was mind-bogglingly easy/trivial, something that is typical of all | |
|
483 | revolutionary technologies (think Python here). | |
|
484 | ||
|
485 | The discussion that "built" ILeo is here: | |
|
486 | http://sourceforge.net/forum/forum.php?thread_id=1911662&forum_id=10226 | |
|
487 | ||
|
488 | ?</t> | |
|
489 | <t tx="vivainio.20080223134433"> | |
|
490 | Declaring custom push-to-ipython handlers | |
|
491 | ========================================= | |
|
492 | ||
|
493 | Sometimes, you might want to configure what alt+I on a node does. You can do | |
|
494 | that by creating your own push function and expose it using | |
|
495 | ipy_leo.expose_ileo_push(f, priority). The function should check whether the | |
|
496 | node should by handled by the function and raise IPython.ipapi.TryNext if it | |
|
497 | will not do the handling, giving the next function in the chain a chance to see | |
|
498 | whether it should handle the push. | |
|
499 | ||
|
500 | This example would print an uppercase version of node body if the node headline ends | |
|
501 | with U (yes, this is completely useless!): | |
|
502 | ||
|
503 | {{{ | |
|
504 | def push_upcase(node): | |
|
505 | if not node.h.endswith('U'): | |
|
506 | raise TryNext | |
|
507 | print node.b.upper() | |
|
508 | ||
|
509 | ipy_leo.expose_ileo_push(push_upcase, 12) | |
|
510 | }}} | |
|
511 | ||
|
512 | (the priority should be between 0-100 - typically, you don't need to care about | |
|
513 | it and can usually omit the argument altogether) | |
|
514 | </t> | |
|
515 | <t tx="vivainio.20080223142207"> | |
|
516 | Example code snippets | |
|
517 | ===================== | |
|
518 | ||
|
519 | Get list of all headlines of all the nodes in leo: | |
|
520 | ||
|
521 | [node.h for node in wb] | |
|
522 | ||
|
523 | Create node with headline 'baz', empty body: | |
|
524 | wb.baz | |
|
525 | ||
|
526 | Create 10 child nodes for baz, where i is headline and 'Hello ' + i is body: | |
|
527 | ||
|
528 | for i in range(10): | |
|
529 | wb.baz[i] = 'Hello %d' % i | |
|
530 | ||
|
531 | ||
|
532 | </t> | |
|
533 | <t tx="vivainio.20080223142403"></t> | |
|
534 | <t tx="vivainio.20080223142403.1">12</t> | |
|
535 | <t tx="vivainio.20080316085925">array([[ 0, 1, 2], | |
|
536 | [ 3, 4, 5], | |
|
537 | [ 6, 7, 8], | |
|
538 | [ 9, 10, 11]])</t> | |
|
539 | <t tx="vivainio.20080316085950"># press alt+i here to plot testarr | |
|
540 | ||
|
541 | plot(wb.testarr.v)</t> | |
|
542 | <t tx="vivainio.20080316092617"></t> | |
|
543 | <t tx="vivainio.20080316144536">Quickstart: | |
|
544 | easy_install numpy | |
|
545 | easy_install matplotlib | |
|
546 | ||
|
547 | Make sure you have '@string ipython-argv = ipython -pylab' in @settings. We currently recommend using TkAgg as the backend (it's also the default)</t> | |
|
548 | <t tx="vivainio.20080316145539.2">#press alt+i here to generate an array for plotter | |
|
549 | ||
|
550 | wb.testarr.v = arange(12).reshape(4,3)</t> | |
|
551 | </tnodes> | |
|
552 | </leo_file> |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
@@ -1,86 +1,86 b'' | |||
|
1 | <?xml version='1.0' encoding='iso-8859-1'?> | |
|
2 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | |
|
3 | <html> | |
|
4 | <head> | |
|
5 | <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> | |
|
6 | <link rel="stylesheet" href="igrid_help.css" type="text/css" /> | |
|
7 | <title>igrid help</title> | |
|
8 | </head> | |
|
9 | <body> | |
|
10 | <h1>igrid help</h1> | |
|
11 | ||
|
12 | ||
|
13 | <h2>Commands</h2> | |
|
14 | ||
|
15 | ||
|
16 | <h3>pick (P)</h3> | |
|
17 | <p>Pick the whole row (object is available as "_")</p> | |
|
18 | ||
|
19 | <h3>pickattr (Shift-P)</h3> | |
|
20 | <p>Pick the attribute under the cursor</p> | |
|
21 | ||
|
22 | <h3>pickallattrs (Shift-C)</h3> | |
|
23 | <p>Pick' the complete column under the cursor (i.e. the attribute under the | |
|
24 | cursor) from all currently fetched objects. These attributes will be returned | |
|
25 | as a list.</p> | |
|
26 | ||
|
27 | <h3>enter (E)</h3> | |
|
28 | <p>Enter the object under the cursor. (what this mean depends on the object | |
|
29 | itself, i.e. how it implements iteration). This opens a new browser 'level'.</p> | |
|
30 | ||
|
31 | <h3>enterattr (Shift-E)</h3> | |
|
32 | <p>Enter the attribute under the cursor.</p> | |
|
33 | ||
|
34 | <h3>detail (D)</h3> | |
|
35 | <p>Show a detail view of the object under the cursor. This shows the name, | |
|
36 | type, doc string and value of the object attributes (and it might show more | |
|
37 | attributes than in the list view, depending on the object).</p> | |
|
38 | ||
|
39 | <h3>detailattr (Shift-D)</h3> | |
|
40 | <p>Show a detail view of the attribute under the cursor.</p> | |
|
41 | ||
|
42 | <h3>pickrows (M)</h3> | |
|
43 | <p>Pick multiple selected rows (M)</p> | |
|
44 | ||
|
45 | <h3>pickrowsattr (CTRL-M)</h3> | |
|
46 | <p>From multiple selected rows pick the cells matching the attribute the cursor is in (CTRL-M)</p> | |
|
47 | ||
|
48 | <h3>find (CTRL-F)</h3> | |
|
49 | <p>Find text</p> | |
|
50 | ||
|
51 | <h3>find_next (F3)</h3> | |
|
52 | <p>Find next occurrence of the searchtext</p> | |
|
53 | ||
|
54 | <h3>find_previous (Shift-F3)</h3> | |
|
55 | <p>Find previous occurrence of the searchtext </p> | |
|
56 | ||
|
57 | <h3>sortattrasc (V)</h3> | |
|
58 | <p>Sort the objects (in ascending order) using the attribute under the cursor as the sort key.</p> | |
|
59 | ||
|
60 | <h3>sortattrdesc (Shift-V)</h3> | |
|
61 | <p>Sort the objects (in descending order) using the attribute under the cursor as the sort key.</p> | |
|
62 | ||
|
63 | <h3>leave (Backspace, DEL, X)</h3> | |
|
64 | <p>Close current tab (and all the tabs to the right of the current one).</h3> | |
|
65 | ||
|
66 | <h3>quit (ESC,Q)</h3> | |
|
67 | <p>Quit igrid and return to the IPython prompt.</p> | |
|
68 | ||
|
69 | ||
|
70 | <h2>Navigation</h2> | |
|
71 | ||
|
72 | ||
|
73 | <h3>Jump to the last column of the current row (END, CTRL-E, CTRL-Right)</h3> | |
|
74 | ||
|
75 | <h3>Jump to the first column of the current row (HOME, CTRL-A, CTRL-Left)</h3> | |
|
76 | ||
|
77 | <h3>Move the cursor one column to the left (<)</h3> | |
|
78 | ||
|
79 | <h3>Move the cursor one column to the right (>)</h3> | |
|
80 | ||
|
81 | <h3>Jump to the first row in the current column (CTRL-Up)</h3> | |
|
82 | ||
|
83 | <h3>Jump to the last row in the current column (CTRL-Down)</h3> | |
|
84 | ||
|
85 | </body> | |
|
86 | </html> | |
|
1 | <?xml version='1.0' encoding='iso-8859-1'?> | |
|
2 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | |
|
3 | <html> | |
|
4 | <head> | |
|
5 | <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> | |
|
6 | <link rel="stylesheet" href="igrid_help.css" type="text/css" /> | |
|
7 | <title>igrid help</title> | |
|
8 | </head> | |
|
9 | <body> | |
|
10 | <h1>igrid help</h1> | |
|
11 | ||
|
12 | ||
|
13 | <h2>Commands</h2> | |
|
14 | ||
|
15 | ||
|
16 | <h3>pick (P)</h3> | |
|
17 | <p>Pick the whole row (object is available as "_")</p> | |
|
18 | ||
|
19 | <h3>pickattr (Shift-P)</h3> | |
|
20 | <p>Pick the attribute under the cursor</p> | |
|
21 | ||
|
22 | <h3>pickallattrs (Shift-C)</h3> | |
|
23 | <p>Pick' the complete column under the cursor (i.e. the attribute under the | |
|
24 | cursor) from all currently fetched objects. These attributes will be returned | |
|
25 | as a list.</p> | |
|
26 | ||
|
27 | <h3>enter (E)</h3> | |
|
28 | <p>Enter the object under the cursor. (what this mean depends on the object | |
|
29 | itself, i.e. how it implements iteration). This opens a new browser 'level'.</p> | |
|
30 | ||
|
31 | <h3>enterattr (Shift-E)</h3> | |
|
32 | <p>Enter the attribute under the cursor.</p> | |
|
33 | ||
|
34 | <h3>detail (D)</h3> | |
|
35 | <p>Show a detail view of the object under the cursor. This shows the name, | |
|
36 | type, doc string and value of the object attributes (and it might show more | |
|
37 | attributes than in the list view, depending on the object).</p> | |
|
38 | ||
|
39 | <h3>detailattr (Shift-D)</h3> | |
|
40 | <p>Show a detail view of the attribute under the cursor.</p> | |
|
41 | ||
|
42 | <h3>pickrows (M)</h3> | |
|
43 | <p>Pick multiple selected rows (M)</p> | |
|
44 | ||
|
45 | <h3>pickrowsattr (CTRL-M)</h3> | |
|
46 | <p>From multiple selected rows pick the cells matching the attribute the cursor is in (CTRL-M)</p> | |
|
47 | ||
|
48 | <h3>find (CTRL-F)</h3> | |
|
49 | <p>Find text</p> | |
|
50 | ||
|
51 | <h3>find_next (F3)</h3> | |
|
52 | <p>Find next occurrence of the searchtext</p> | |
|
53 | ||
|
54 | <h3>find_previous (Shift-F3)</h3> | |
|
55 | <p>Find previous occurrence of the searchtext </p> | |
|
56 | ||
|
57 | <h3>sortattrasc (V)</h3> | |
|
58 | <p>Sort the objects (in ascending order) using the attribute under the cursor as the sort key.</p> | |
|
59 | ||
|
60 | <h3>sortattrdesc (Shift-V)</h3> | |
|
61 | <p>Sort the objects (in descending order) using the attribute under the cursor as the sort key.</p> | |
|
62 | ||
|
63 | <h3>leave (Backspace, DEL, X)</h3> | |
|
64 | <p>Close current tab (and all the tabs to the right of the current one).</h3> | |
|
65 | ||
|
66 | <h3>quit (ESC,Q)</h3> | |
|
67 | <p>Quit igrid and return to the IPython prompt.</p> | |
|
68 | ||
|
69 | ||
|
70 | <h2>Navigation</h2> | |
|
71 | ||
|
72 | ||
|
73 | <h3>Jump to the last column of the current row (END, CTRL-E, CTRL-Right)</h3> | |
|
74 | ||
|
75 | <h3>Jump to the first column of the current row (HOME, CTRL-A, CTRL-Left)</h3> | |
|
76 | ||
|
77 | <h3>Move the cursor one column to the left (<)</h3> | |
|
78 | ||
|
79 | <h3>Move the cursor one column to the right (>)</h3> | |
|
80 | ||
|
81 | <h3>Jump to the first row in the current column (CTRL-Up)</h3> | |
|
82 | ||
|
83 | <h3>Jump to the last row in the current column (CTRL-Down)</h3> | |
|
84 | ||
|
85 | </body> | |
|
86 | </html> |
@@ -14,4 +14,6 b' from ipy_completers import *' | |||
|
14 | 14 | ip.set_hook('complete_command', apt_completer, re_key = '.*apt-get') |
|
15 | 15 | ip.set_hook('complete_command', svn_completer, str_key = 'svn') |
|
16 | 16 | ip.set_hook('complete_command', hg_completer, str_key = 'hg') |
|
17 | ip.set_hook('complete_command', bzr_completer, str_key = 'bzr') | |
|
17 | ||
|
18 | # the old bzr completer is deprecated, we recommend ipy_bzr | |
|
19 | #ip.set_hook('complete_command', bzr_completer, str_key = 'bzr') |
@@ -1,4 +1,3 b'' | |||
|
1 | #!/usr/bin/env python | |
|
2 | 1 |
|
|
3 | 2 | """ Implementations for various useful completers |
|
4 | 3 | |
@@ -12,6 +11,7 b' import IPython.ipapi' | |||
|
12 | 11 | import glob,os,shlex,sys |
|
13 | 12 | import inspect |
|
14 | 13 | from time import time |
|
14 | from zipimport import zipimporter | |
|
15 | 15 | ip = IPython.ipapi.get() |
|
16 | 16 | |
|
17 | 17 | try: |
@@ -86,13 +86,18 b' def moduleList(path):' | |||
|
86 | 86 | |
|
87 | 87 | if os.path.isdir(path): |
|
88 | 88 | folder_list = os.listdir(path) |
|
89 | elif path.endswith('.egg'): | |
|
90 | try: | |
|
91 | folder_list = [f for f in zipimporter(path)._files] | |
|
92 | except: | |
|
93 | folder_list = [] | |
|
89 | 94 | else: |
|
90 | 95 | folder_list = [] |
|
91 | 96 | #folder_list = glob.glob(os.path.join(path,'*')) |
|
92 | 97 | folder_list = [p for p in folder_list \ |
|
93 | 98 | if os.path.exists(os.path.join(path, p,'__init__.py'))\ |
|
94 | 99 | or p[-3:] in ('.py','.so')\ |
|
95 | or p[-4:] in ('.pyc','.pyo')] | |
|
100 | or p[-4:] in ('.pyc','.pyo','.pyd')] | |
|
96 | 101 | |
|
97 | 102 | folder_list = [os.path.basename(p).split('.')[0] for p in folder_list] |
|
98 | 103 | return folder_list |
@@ -211,15 +216,15 b' def hg_completer(self,event):' | |||
|
211 | 216 | |
|
212 | 217 | |
|
213 | 218 | |
|
214 |
bzr_commands = |
|
|
215 | add annotate bind branch break-lock bundle-revisions cat check | |
|
216 | checkout commit conflicts deleted diff export gannotate gbranch | |
|
217 | gcommit gdiff help ignore ignored info init init-repository inventory | |
|
218 | log merge missing mkdir mv nick pull push reconcile register-branch | |
|
219 | remerge remove renames resolve revert revno root serve sign-my-commits | |
|
220 | status testament unbind uncommit unknowns update upgrade version | |
|
221 | version-info visualise whoami | |
|
222 | """ | |
|
219 | __bzr_commands = None | |
|
220 | ||
|
221 | def bzr_commands(): | |
|
222 | global __bzr_commands | |
|
223 | if __bzr_commands is not None: | |
|
224 | return __bzr_commands | |
|
225 | out = os.popen('bzr help commands') | |
|
226 | __bzr_commands = [l.split()[0] for l in out] | |
|
227 | return __bzr_commands | |
|
223 | 228 | |
|
224 | 229 | def bzr_completer(self,event): |
|
225 | 230 | """ Completer for bazaar commands """ |
@@ -232,7 +237,7 b' def bzr_completer(self,event):' | |||
|
232 | 237 | param = cmd_param[-1] |
|
233 | 238 | output_file = (param == '--output=') |
|
234 | 239 | if cmd == 'help': |
|
235 |
return bzr_commands |
|
|
240 | return bzr_commands() | |
|
236 | 241 | elif cmd in ['bundle-revisions','conflicts', |
|
237 | 242 | 'deleted','nick','register-branch', |
|
238 | 243 | 'serve','unbind','upgrade','version', |
@@ -242,7 +247,7 b' def bzr_completer(self,event):' | |||
|
242 | 247 | # the rest are probably file names |
|
243 | 248 | return ip.IP.Completer.file_matches(event.symbol) |
|
244 | 249 | |
|
245 |
return bzr_commands |
|
|
250 | return bzr_commands() | |
|
246 | 251 | |
|
247 | 252 | |
|
248 | 253 | def shlex_split(x): |
@@ -326,7 +331,29 b' def cd_completer(self, event):' | |||
|
326 | 331 | if os.path.isdir(relpath): |
|
327 | 332 | return [relpath] |
|
328 | 333 | raise IPython.ipapi.TryNext |
|
329 | return found | |
|
334 | ||
|
335 | ||
|
336 | def single_dir_expand(matches): | |
|
337 | "Recursively expand match lists containing a single dir." | |
|
338 | ||
|
339 | if len(matches) == 1 and os.path.isdir(matches[0]): | |
|
340 | # Takes care of links to directories also. Use '/' | |
|
341 | # explicitly, even under Windows, so that name completions | |
|
342 | # don't end up escaped. | |
|
343 | d = matches[0] | |
|
344 | if d[-1] in ['/','\\']: | |
|
345 | d = d[:-1] | |
|
346 | ||
|
347 | subdirs = [p for p in os.listdir(d) if os.path.isdir( d + '/' + p)] | |
|
348 | if subdirs: | |
|
349 | matches = [ (d + '/' + p) for p in subdirs ] | |
|
350 | return single_dir_expand(matches) | |
|
351 | else: | |
|
352 | return matches | |
|
353 | else: | |
|
354 | return matches | |
|
355 | ||
|
356 | return single_dir_expand(found) | |
|
330 | 357 | |
|
331 | 358 | def apt_get_packages(prefix): |
|
332 | 359 | out = os.popen('apt-cache pkgnames') |
This diff has been collapsed as it changes many lines, (840 lines changed) Show them Hide them | |||
@@ -1,248 +1,592 b'' | |||
|
1 | """ Leo plugin for IPython | |
|
2 | ||
|
3 | Example use: | |
|
4 | ||
|
5 | nodes.foo = "hello world" | |
|
6 | ||
|
7 | -> create '@ipy foo' node with text "hello world" | |
|
8 | ||
|
9 | Access works also, and so does tab completion. | |
|
10 | ||
|
11 | """ | |
|
12 |
import IPython. |
|
|
13 |
import IPython. |
|
|
14 | import IPython.generics | |
|
15 | import re | |
|
16 | ||
|
17 | ||
|
18 | ||
|
19 | ip = IPython.ipapi.get() | |
|
20 | leo = ip.user_ns['leox'] | |
|
21 | c,g = leo.c, leo.g | |
|
22 | ||
|
23 | # will probably be overwritten by user, but handy for experimentation early on | |
|
24 | ip.user_ns['c'] = c | |
|
25 | ip.user_ns['g'] = g | |
|
26 | ||
|
27 | ||
|
28 | from IPython.external.simplegeneric import generic | |
|
29 | import pprint | |
|
30 | ||
|
31 | @generic | |
|
32 | def format_for_leo(obj): | |
|
33 | """ Convert obj to string representiation (for editing in Leo)""" | |
|
34 | return pprint.pformat(obj) | |
|
35 | ||
|
36 | @format_for_leo.when_type(list) | |
|
37 | def format_list(obj): | |
|
38 | return "\n".join(str(s) for s in obj) | |
|
39 | ||
|
40 | nodename_re = r'(@ipy?[\w-]+)?\s?(\w+)' | |
|
41 | ||
|
42 | def all_cells(): | |
|
43 | d = {} | |
|
44 | for p in c.allNodes_iter(): | |
|
45 | h = p.headString() | |
|
46 | if h.startswith('@') and len(h.split()) == 1: | |
|
47 | continue | |
|
48 | mo = re.match(nodename_re, h) | |
|
49 | if not mo: | |
|
50 | continue | |
|
51 | d[mo.group(2)] = p.copy() | |
|
52 | return d | |
|
53 | ||
|
54 | ||
|
55 | class TrivialLeoWorkbook: | |
|
56 | """ class to find cells with simple syntax | |
|
57 | ||
|
58 | """ | |
|
59 | def __getattr__(self, key): | |
|
60 | cells = all_cells() | |
|
61 | p = cells[key] | |
|
62 | body = p.bodyString() | |
|
63 | return eval_body(body) | |
|
64 | def __setattr__(self,key,val): | |
|
65 | cells = all_cells() | |
|
66 | p = cells.get(key,None) | |
|
67 | if p is None: | |
|
68 | add_var(key,val) | |
|
69 | else: | |
|
70 | c.setBodyString(p,format_for_leo(val)) | |
|
71 | def __str__(self): | |
|
72 | return "<TrivialLeoWorkbook>" | |
|
73 | __repr__ = __str__ | |
|
74 | ||
|
75 | ip.user_ns['nodes'] = TrivialLeoWorkbook() | |
|
76 | ||
|
77 | ||
|
78 | class LeoNode(object): | |
|
79 | def __init__(self,p): | |
|
80 | self.p = p.copy() | |
|
81 | ||
|
82 | def get_h(self): return self.p.headString() | |
|
83 | def set_h(self,val): | |
|
84 | print "set head",val | |
|
85 | c.beginUpdate() | |
|
86 | try: | |
|
87 | c.setHeadString(self.p,val) | |
|
88 | finally: | |
|
89 | c.endUpdate() | |
|
90 | ||
|
91 | h = property( get_h, set_h) | |
|
92 | ||
|
93 | def get_b(self): return self.p.bodyString() | |
|
94 | def set_b(self,val): | |
|
95 | print "set body",val | |
|
96 | c.beginUpdate() | |
|
97 | try: | |
|
98 | c.setBodyString(self.p, val) | |
|
99 | finally: | |
|
100 | c.endUpdate() | |
|
101 | ||
|
102 | b = property(get_b, set_b) | |
|
103 | ||
|
104 | def set_val(self, val): | |
|
105 | self.b = pprint.pformat(val) | |
|
106 | ||
|
107 | v = property(lambda self: ip.ev(self.b.strip()), set_val) | |
|
108 | ||
|
109 | def set_l(self,val): | |
|
110 | self.b = '\n'.join(val ) | |
|
111 | l = property(lambda self : IPython.genutils.SList(self.b.splitlines()), | |
|
112 | set_l) | |
|
113 | ||
|
114 | def __iter__(self): | |
|
115 | return (LeoNode(p) for p in self.p.children_iter()) | |
|
116 | ||
|
117 | ||
|
118 | class LeoWorkbook: | |
|
119 | """ class for 'advanced' node access """ | |
|
120 | def __getattr__(self, key): | |
|
121 | if key.startswith('_') or key == 'trait_names': | |
|
122 | raise AttributeError | |
|
123 | cells = all_cells() | |
|
124 | p = cells.get(key, None) | |
|
125 | if p is None: | |
|
126 | p = add_var(key,None) | |
|
127 | ||
|
128 | return LeoNode(p) | |
|
129 | ||
|
130 | def __str__(self): | |
|
131 | return "<LeoWorkbook>" | |
|
132 | __repr__ = __str__ | |
|
133 | ip.user_ns['wb'] = LeoWorkbook() | |
|
134 | ||
|
135 | ||
|
136 | _dummyval = object() | |
|
137 | @IPython.generics.complete_object.when_type(LeoWorkbook) | |
|
138 | def workbook_complete(obj, prev): | |
|
139 | return all_cells().keys() | |
|
140 | ||
|
141 | ||
|
142 | def add_var(varname, value = _dummyval): | |
|
143 | c.beginUpdate() | |
|
144 | try: | |
|
145 |
|
|
|
146 | nodename = '@ipy-var ' + varname | |
|
147 | p2 = g.findNodeAnywhere(c,nodename) | |
|
148 | if not c.positionExists(p2): | |
|
149 | p2 = c.currentPosition().insertAfter() | |
|
150 | c.setHeadString(p2,'@ipy ' + varname) | |
|
151 | ||
|
152 | c.setCurrentPosition(p2) | |
|
153 | if value is _dummyval: | |
|
154 | val = ip.user_ns[varname] | |
|
155 | else: | |
|
156 | val = value | |
|
157 | if val is not None: | |
|
158 | formatted = format_for_leo(val) | |
|
159 | c.setBodyString(p2,formatted) | |
|
160 | return p2 | |
|
161 | finally: | |
|
162 | c.endUpdate() | |
|
163 | ||
|
164 | def add_file(self,fname): | |
|
165 | p2 = c.currentPosition().insertAfter() | |
|
166 | ||
|
167 | def push_script(p): | |
|
168 | c.beginUpdate() | |
|
169 | try: | |
|
170 | ohist = ip.IP.output_hist | |
|
171 | hstart = len(ip.IP.input_hist) | |
|
172 | script = g.getScript(c,p,useSelectedText=False,forcePythonSentinels=False,useSentinels=False) | |
|
173 | ||
|
174 | script = g.splitLines(script + '\n') | |
|
175 | script = ''.join(z for z in script if z.strip()) | |
|
176 | ||
|
177 | ip.runlines(script) | |
|
178 | ||
|
179 | has_output = False | |
|
180 | for idx in range(hstart,len(ip.IP.input_hist)): | |
|
181 | val = ohist.get(idx,None) | |
|
182 | if val is None: | |
|
183 | continue | |
|
184 | has_output = True | |
|
185 | inp = ip.IP.input_hist[idx] | |
|
186 | if inp.strip(): | |
|
187 | g.es('In: %s' % (inp[:40], ), tabName = 'IPython') | |
|
188 | ||
|
189 | g.es('<%d> %s' % (idx, pprint.pformat(ohist[idx],width = 40)), tabName = 'IPython') | |
|
190 | ||
|
191 | if not has_output: | |
|
192 | g.es('ipy run: %s' %( p.headString(),), tabName = 'IPython') | |
|
193 | finally: | |
|
194 | c.endUpdate() | |
|
195 | ||
|
196 | ||
|
197 | def eval_body(body): | |
|
198 | try: | |
|
199 | val = ip.ev(body) | |
|
200 | except: | |
|
201 | # just use stringlist if it's not completely legal python expression | |
|
202 | val = IPython.genutils.SList(body.splitlines()) | |
|
203 | return val | |
|
204 | ||
|
205 | def push_variable(p,varname): | |
|
206 | body = p.bodyString() | |
|
207 | val = eval_body(body.strip()) | |
|
208 | ip.user_ns[varname] = val | |
|
209 | g.es('ipy var: %s' % (varname,), tabName = "IPython") | |
|
210 | ||
|
211 | def push_from_leo(p): | |
|
212 | tup = p.headString().split(None,1) | |
|
213 | # @ipy foo is variable foo | |
|
214 | if len(tup) == 2 and tup[0] == '@ipy': | |
|
215 | varname = tup[1] | |
|
216 | push_variable(p,varname) | |
|
217 | return | |
|
218 | ||
|
219 | push_script(p) | |
|
220 | return | |
|
221 | ||
|
222 | ||
|
223 | ip.user_ns['leox'].push = push_from_leo | |
|
224 | ||
|
225 |
def |
|
|
226 | """ open file(s) in Leo | |
|
227 | ||
|
228 | Takes an mglob pattern, e.g. '%leo *.cpp' or %leo 'rec:*.cpp' | |
|
229 | """ | |
|
230 | import os | |
|
231 | from IPython.external import mglob | |
|
232 | ||
|
233 | files = mglob.expand(s) | |
|
234 | c.beginUpdate() | |
|
235 | try: | |
|
236 | for fname in files: | |
|
237 | p = g.findNodeAnywhere(c,'@auto ' + fname) | |
|
238 | if not p: | |
|
239 | p = c.currentPosition().insertAfter() | |
|
240 | ||
|
241 | p.setHeadString('@auto ' + fname) | |
|
242 | if os.path.isfile(fname): | |
|
243 | c.setBodyString(p,open(fname).read()) | |
|
244 | c.selectPosition(p) | |
|
245 | finally: | |
|
246 | c.endUpdate() | |
|
247 | ||
|
248 | ip.expose_magic('leo',leo_f) | |
|
1 | """ ILeo - Leo plugin for IPython | |
|
2 | ||
|
3 | ||
|
4 | """ | |
|
5 | import IPython.ipapi | |
|
6 | import IPython.genutils | |
|
7 | import IPython.generics | |
|
8 | from IPython.hooks import CommandChainDispatcher | |
|
9 | import re | |
|
10 | import UserDict | |
|
11 | from IPython.ipapi import TryNext | |
|
12 | import IPython.macro | |
|
13 | import IPython.Shell | |
|
14 | ||
|
15 | def init_ipython(ipy): | |
|
16 | """ This will be run by _ip.load('ipy_leo') | |
|
17 | ||
|
18 | Leo still needs to run update_commander() after this. | |
|
19 | ||
|
20 | """ | |
|
21 | global ip | |
|
22 | ip = ipy | |
|
23 | IPython.Shell.hijack_tk() | |
|
24 | ip.set_hook('complete_command', mb_completer, str_key = '%mb') | |
|
25 | ip.expose_magic('mb',mb_f) | |
|
26 | ip.expose_magic('lee',lee_f) | |
|
27 | ip.expose_magic('leoref',leoref_f) | |
|
28 | expose_ileo_push(push_cl_node,100) | |
|
29 | # this should be the LAST one that will be executed, and it will never raise TryNext | |
|
30 | expose_ileo_push(push_ipython_script, 1000) | |
|
31 | expose_ileo_push(push_plain_python, 100) | |
|
32 | expose_ileo_push(push_ev_node, 100) | |
|
33 | global wb | |
|
34 | wb = LeoWorkbook() | |
|
35 | ip.user_ns['wb'] = wb | |
|
36 | ||
|
37 | show_welcome() | |
|
38 | ||
|
39 | ||
|
40 | def update_commander(new_leox): | |
|
41 | """ Set the Leo commander to use | |
|
42 | ||
|
43 | This will be run every time Leo does ipython-launch; basically, | |
|
44 | when the user switches the document he is focusing on, he should do | |
|
45 | ipython-launch to tell ILeo what document the commands apply to. | |
|
46 | ||
|
47 | """ | |
|
48 | ||
|
49 | global c,g | |
|
50 | c,g = new_leox.c, new_leox.g | |
|
51 | print "Set Leo Commander:",c.frame.getTitle() | |
|
52 | ||
|
53 | # will probably be overwritten by user, but handy for experimentation early on | |
|
54 | ip.user_ns['c'] = c | |
|
55 | ip.user_ns['g'] = g | |
|
56 | ip.user_ns['_leo'] = new_leox | |
|
57 | ||
|
58 | new_leox.push = push_position_from_leo | |
|
59 | run_leo_startup_node() | |
|
60 | ||
|
61 | from IPython.external.simplegeneric import generic | |
|
62 | import pprint | |
|
63 | ||
|
64 | def es(s): | |
|
65 | g.es(s, tabName = 'IPython') | |
|
66 | pass | |
|
67 | ||
|
68 | @generic | |
|
69 | def format_for_leo(obj): | |
|
70 | """ Convert obj to string representiation (for editing in Leo)""" | |
|
71 | return pprint.pformat(obj) | |
|
72 | ||
|
73 | @format_for_leo.when_type(list) | |
|
74 | def format_list(obj): | |
|
75 | return "\n".join(str(s) for s in obj) | |
|
76 | ||
|
77 | ||
|
78 | attribute_re = re.compile('^[a-zA-Z_][a-zA-Z0-9_]*$') | |
|
79 | def valid_attribute(s): | |
|
80 | return attribute_re.match(s) | |
|
81 | ||
|
82 | _rootnode = None | |
|
83 | def rootnode(): | |
|
84 | """ Get ileo root node (@ipy-root) | |
|
85 | ||
|
86 | if node has become invalid or has not been set, return None | |
|
87 | ||
|
88 | Note that the root is the *first* @ipy-root item found | |
|
89 | """ | |
|
90 | global _rootnode | |
|
91 | if _rootnode is None: | |
|
92 | return None | |
|
93 | if c.positionExists(_rootnode.p): | |
|
94 | return _rootnode | |
|
95 | _rootnode = None | |
|
96 | return None | |
|
97 | ||
|
98 | def all_cells(): | |
|
99 | global _rootnode | |
|
100 | d = {} | |
|
101 | r = rootnode() | |
|
102 | if r is not None: | |
|
103 | nodes = r.p.children_iter() | |
|
104 | else: | |
|
105 | nodes = c.allNodes_iter() | |
|
106 | ||
|
107 | for p in nodes: | |
|
108 | h = p.headString() | |
|
109 | if h.strip() == '@ipy-root': | |
|
110 | # update root node (found it for the first time) | |
|
111 | _rootnode = LeoNode(p) | |
|
112 | # the next recursive call will use the children of new root | |
|
113 | return all_cells() | |
|
114 | ||
|
115 | if h.startswith('@a '): | |
|
116 | d[h.lstrip('@a ').strip()] = p.parent().copy() | |
|
117 | elif not valid_attribute(h): | |
|
118 | continue | |
|
119 | d[h] = p.copy() | |
|
120 | return d | |
|
121 | ||
|
122 | def eval_node(n): | |
|
123 | body = n.b | |
|
124 | if not body.startswith('@cl'): | |
|
125 | # plain python repr node, just eval it | |
|
126 | return ip.ev(n.b) | |
|
127 | # @cl nodes deserve special treatment - first eval the first line (minus cl), then use it to call the rest of body | |
|
128 | first, rest = body.split('\n',1) | |
|
129 | tup = first.split(None, 1) | |
|
130 | # @cl alone SPECIAL USE-> dump var to user_ns | |
|
131 | if len(tup) == 1: | |
|
132 | val = ip.ev(rest) | |
|
133 | ip.user_ns[n.h] = val | |
|
134 | es("%s = %s" % (n.h, repr(val)[:20] )) | |
|
135 | return val | |
|
136 | ||
|
137 | cl, hd = tup | |
|
138 | ||
|
139 | xformer = ip.ev(hd.strip()) | |
|
140 | es('Transform w/ %s' % repr(xformer)) | |
|
141 | return xformer(rest, n) | |
|
142 | ||
|
143 | class LeoNode(object, UserDict.DictMixin): | |
|
144 | """ Node in Leo outline | |
|
145 | ||
|
146 | Most important attributes (getters/setters available: | |
|
147 | .v - evaluate node, can also be alligned | |
|
148 | .b, .h - body string, headline string | |
|
149 | .l - value as string list | |
|
150 | ||
|
151 | Also supports iteration, | |
|
152 | ||
|
153 | setitem / getitem (indexing): | |
|
154 | wb.foo['key'] = 12 | |
|
155 | assert wb.foo['key'].v == 12 | |
|
156 | ||
|
157 | Note the asymmetry on setitem and getitem! Also other | |
|
158 | dict methods are available. | |
|
159 | ||
|
160 | .ipush() - run push-to-ipython | |
|
161 | ||
|
162 | Minibuffer command access (tab completion works): | |
|
163 | ||
|
164 | mb save-to-file | |
|
165 | ||
|
166 | """ | |
|
167 | def __init__(self,p): | |
|
168 | self.p = p.copy() | |
|
169 | ||
|
170 | def __str__(self): | |
|
171 | return "<LeoNode %s>" % str(self.p) | |
|
172 | ||
|
173 | __repr__ = __str__ | |
|
174 | ||
|
175 | def __get_h(self): return self.p.headString() | |
|
176 | def __set_h(self,val): | |
|
177 | print "set head",val | |
|
178 | c.beginUpdate() | |
|
179 | try: | |
|
180 | c.setHeadString(self.p,val) | |
|
181 | finally: | |
|
182 | c.endUpdate() | |
|
183 | ||
|
184 | h = property( __get_h, __set_h, doc = "Node headline string") | |
|
185 | ||
|
186 | def __get_b(self): return self.p.bodyString() | |
|
187 | def __set_b(self,val): | |
|
188 | print "set body",val | |
|
189 | c.beginUpdate() | |
|
190 | try: | |
|
191 | c.setBodyString(self.p, val) | |
|
192 | finally: | |
|
193 | c.endUpdate() | |
|
194 | ||
|
195 | b = property(__get_b, __set_b, doc = "Nody body string") | |
|
196 | ||
|
197 | def __set_val(self, val): | |
|
198 | self.b = format_for_leo(val) | |
|
199 | ||
|
200 | v = property(lambda self: eval_node(self), __set_val, doc = "Node evaluated value") | |
|
201 | ||
|
202 | def __set_l(self,val): | |
|
203 | self.b = '\n'.join(val ) | |
|
204 | l = property(lambda self : IPython.genutils.SList(self.b.splitlines()), | |
|
205 | __set_l, doc = "Node value as string list") | |
|
206 | ||
|
207 | def __iter__(self): | |
|
208 | """ Iterate through nodes direct children """ | |
|
209 | ||
|
210 | return (LeoNode(p) for p in self.p.children_iter()) | |
|
211 | ||
|
212 | def __children(self): | |
|
213 | d = {} | |
|
214 | for child in self: | |
|
215 | head = child.h | |
|
216 | tup = head.split(None,1) | |
|
217 | if len(tup) > 1 and tup[0] == '@k': | |
|
218 | d[tup[1]] = child | |
|
219 | continue | |
|
220 | ||
|
221 | if not valid_attribute(head): | |
|
222 | d[head] = child | |
|
223 | continue | |
|
224 | return d | |
|
225 | def keys(self): | |
|
226 | d = self.__children() | |
|
227 | return d.keys() | |
|
228 | def __getitem__(self, key): | |
|
229 | """ wb.foo['Some stuff'] Return a child node with headline 'Some stuff' | |
|
230 | ||
|
231 | If key is a valid python name (e.g. 'foo'), look for headline '@k foo' as well | |
|
232 | """ | |
|
233 | key = str(key) | |
|
234 | d = self.__children() | |
|
235 | return d[key] | |
|
236 | def __setitem__(self, key, val): | |
|
237 | """ You can do wb.foo['My Stuff'] = 12 to create children | |
|
238 | ||
|
239 | This will create 'My Stuff' as a child of foo (if it does not exist), and | |
|
240 | do .v = 12 assignment. | |
|
241 | ||
|
242 | Exception: | |
|
243 | ||
|
244 | wb.foo['bar'] = 12 | |
|
245 | ||
|
246 | will create a child with headline '@k bar', because bar is a valid python name | |
|
247 | and we don't want to crowd the WorkBook namespace with (possibly numerous) entries | |
|
248 | """ | |
|
249 | key = str(key) | |
|
250 | d = self.__children() | |
|
251 | if key in d: | |
|
252 | d[key].v = val | |
|
253 | return | |
|
254 | ||
|
255 | if not valid_attribute(key): | |
|
256 | head = key | |
|
257 | else: | |
|
258 | head = '@k ' + key | |
|
259 | p = c.createLastChildNode(self.p, head, '') | |
|
260 | LeoNode(p).v = val | |
|
261 | ||
|
262 | def ipush(self): | |
|
263 | """ Does push-to-ipython on the node """ | |
|
264 | push_from_leo(self) | |
|
265 | ||
|
266 | def go(self): | |
|
267 | """ Set node as current node (to quickly see it in Outline) """ | |
|
268 | c.beginUpdate() | |
|
269 | try: | |
|
270 | c.setCurrentPosition(self.p) | |
|
271 | finally: | |
|
272 | c.endUpdate() | |
|
273 | ||
|
274 | def script(self): | |
|
275 | """ Method to get the 'tangled' contents of the node | |
|
276 | ||
|
277 | (parse @others, << section >> references etc.) | |
|
278 | """ | |
|
279 | return g.getScript(c,self.p,useSelectedText=False,useSentinels=False) | |
|
280 | ||
|
281 | def __get_uA(self): | |
|
282 | p = self.p | |
|
283 | # Create the uA if necessary. | |
|
284 | if not hasattr(p.v.t,'unknownAttributes'): | |
|
285 | p.v.t.unknownAttributes = {} | |
|
286 | ||
|
287 | d = p.v.t.unknownAttributes.setdefault('ipython', {}) | |
|
288 | return d | |
|
289 | ||
|
290 | uA = property(__get_uA, doc = "Access persistent unknownAttributes of node") | |
|
291 | ||
|
292 | ||
|
293 | class LeoWorkbook: | |
|
294 | """ class for 'advanced' node access | |
|
295 | ||
|
296 | Has attributes for all "discoverable" nodes. Node is discoverable if it | |
|
297 | either | |
|
298 | ||
|
299 | - has a valid python name (Foo, bar_12) | |
|
300 | - is a parent of an anchor node (if it has a child '@a foo', it is visible as foo) | |
|
301 | ||
|
302 | """ | |
|
303 | def __getattr__(self, key): | |
|
304 | if key.startswith('_') or key == 'trait_names' or not valid_attribute(key): | |
|
305 | raise AttributeError | |
|
306 | cells = all_cells() | |
|
307 | p = cells.get(key, None) | |
|
308 | if p is None: | |
|
309 | return add_var(key) | |
|
310 | ||
|
311 | return LeoNode(p) | |
|
312 | ||
|
313 | def __str__(self): | |
|
314 | return "<LeoWorkbook>" | |
|
315 | def __setattr__(self,key, val): | |
|
316 | raise AttributeError("Direct assignment to workbook denied, try wb.%s.v = %s" % (key,val)) | |
|
317 | ||
|
318 | __repr__ = __str__ | |
|
319 | ||
|
320 | def __iter__(self): | |
|
321 | """ Iterate all (even non-exposed) nodes """ | |
|
322 | cells = all_cells() | |
|
323 | return (LeoNode(p) for p in c.allNodes_iter()) | |
|
324 | ||
|
325 | current = property(lambda self: LeoNode(c.currentPosition()), doc = "Currently selected node") | |
|
326 | ||
|
327 | def match_h(self, regex): | |
|
328 | cmp = re.compile(regex) | |
|
329 | for node in self: | |
|
330 | if re.match(cmp, node.h, re.IGNORECASE): | |
|
331 | yield node | |
|
332 | return | |
|
333 | ||
|
334 | @IPython.generics.complete_object.when_type(LeoWorkbook) | |
|
335 | def workbook_complete(obj, prev): | |
|
336 | return all_cells().keys() + [s for s in prev if not s.startswith('_')] | |
|
337 | ||
|
338 | ||
|
339 | def add_var(varname): | |
|
340 | c.beginUpdate() | |
|
341 | r = rootnode() | |
|
342 | try: | |
|
343 | if r is None: | |
|
344 | p2 = g.findNodeAnywhere(c,varname) | |
|
345 | else: | |
|
346 | p2 = g.findNodeInChildren(c, r.p, varname) | |
|
347 | if p2: | |
|
348 | return LeoNode(p2) | |
|
349 | ||
|
350 | if r is not None: | |
|
351 | p2 = r.p.insertAsLastChild() | |
|
352 | ||
|
353 | else: | |
|
354 | p2 = c.currentPosition().insertAfter() | |
|
355 | ||
|
356 | c.setHeadString(p2,varname) | |
|
357 | return LeoNode(p2) | |
|
358 | finally: | |
|
359 | c.endUpdate() | |
|
360 | ||
|
361 | def add_file(self,fname): | |
|
362 | p2 = c.currentPosition().insertAfter() | |
|
363 | ||
|
364 | push_from_leo = CommandChainDispatcher() | |
|
365 | ||
|
366 | def expose_ileo_push(f, prio = 0): | |
|
367 | push_from_leo.add(f, prio) | |
|
368 | ||
|
369 | def push_ipython_script(node): | |
|
370 | """ Execute the node body in IPython, as if it was entered in interactive prompt """ | |
|
371 | c.beginUpdate() | |
|
372 | try: | |
|
373 | ohist = ip.IP.output_hist | |
|
374 | hstart = len(ip.IP.input_hist) | |
|
375 | script = node.script() | |
|
376 | ||
|
377 | ip.user_ns['_p'] = node | |
|
378 | ip.runlines(script) | |
|
379 | ip.user_ns.pop('_p',None) | |
|
380 | ||
|
381 | has_output = False | |
|
382 | for idx in range(hstart,len(ip.IP.input_hist)): | |
|
383 | val = ohist.get(idx,None) | |
|
384 | if val is None: | |
|
385 | continue | |
|
386 | has_output = True | |
|
387 | inp = ip.IP.input_hist[idx] | |
|
388 | if inp.strip(): | |
|
389 | es('In: %s' % (inp[:40], )) | |
|
390 | ||
|
391 | es('<%d> %s' % (idx, pprint.pformat(ohist[idx],width = 40))) | |
|
392 | ||
|
393 | if not has_output: | |
|
394 | es('ipy run: %s (%d LL)' %( node.h,len(script))) | |
|
395 | finally: | |
|
396 | c.endUpdate() | |
|
397 | ||
|
398 | ||
|
399 | def eval_body(body): | |
|
400 | try: | |
|
401 | val = ip.ev(body) | |
|
402 | except: | |
|
403 | # just use stringlist if it's not completely legal python expression | |
|
404 | val = IPython.genutils.SList(body.splitlines()) | |
|
405 | return val | |
|
406 | ||
|
407 | def push_plain_python(node): | |
|
408 | if not node.h.endswith('P'): | |
|
409 | raise TryNext | |
|
410 | script = node.script() | |
|
411 | lines = script.count('\n') | |
|
412 | try: | |
|
413 | exec script in ip.user_ns | |
|
414 | except: | |
|
415 | print " -- Exception in script:\n"+script + "\n --" | |
|
416 | raise | |
|
417 | es('ipy plain: %s (%d LL)' % (node.h,lines)) | |
|
418 | ||
|
419 | ||
|
420 | def push_cl_node(node): | |
|
421 | """ If node starts with @cl, eval it | |
|
422 | ||
|
423 | The result is put as last child of @ipy-results node, if it exists | |
|
424 | """ | |
|
425 | if not node.b.startswith('@cl'): | |
|
426 | raise TryNext | |
|
427 | ||
|
428 | p2 = g.findNodeAnywhere(c,'@ipy-results') | |
|
429 | val = node.v | |
|
430 | if p2: | |
|
431 | es("=> @ipy-results") | |
|
432 | LeoNode(p2).v = val | |
|
433 | es(val) | |
|
434 | ||
|
435 | def push_ev_node(node): | |
|
436 | """ If headline starts with @ev, eval it and put result in body """ | |
|
437 | if not node.h.startswith('@ev '): | |
|
438 | raise TryNext | |
|
439 | expr = node.h.lstrip('@ev ') | |
|
440 | es('ipy eval ' + expr) | |
|
441 | res = ip.ev(expr) | |
|
442 | node.v = res | |
|
443 | ||
|
444 | ||
|
445 | def push_position_from_leo(p): | |
|
446 | try: | |
|
447 | push_from_leo(LeoNode(p)) | |
|
448 | except AttributeError,e: | |
|
449 | if e.args == ("Commands instance has no attribute 'frame'",): | |
|
450 | es("Error: ILeo not associated with .leo document") | |
|
451 | es("Press alt+shift+I to fix!") | |
|
452 | else: | |
|
453 | raise | |
|
454 | ||
|
455 | @generic | |
|
456 | def edit_object_in_leo(obj, varname): | |
|
457 | """ Make it @cl node so it can be pushed back directly by alt+I """ | |
|
458 | node = add_var(varname) | |
|
459 | formatted = format_for_leo(obj) | |
|
460 | if not formatted.startswith('@cl'): | |
|
461 | formatted = '@cl\n' + formatted | |
|
462 | node.b = formatted | |
|
463 | node.go() | |
|
464 | ||
|
465 | @edit_object_in_leo.when_type(IPython.macro.Macro) | |
|
466 | def edit_macro(obj,varname): | |
|
467 | bod = '_ip.defmacro("""\\\n' + obj.value + '""")' | |
|
468 | node = add_var('Macro_' + varname) | |
|
469 | node.b = bod | |
|
470 | node.go() | |
|
471 | ||
|
472 | def get_history(hstart = 0): | |
|
473 | res = [] | |
|
474 | ohist = ip.IP.output_hist | |
|
475 | ||
|
476 | for idx in range(hstart, len(ip.IP.input_hist)): | |
|
477 | val = ohist.get(idx,None) | |
|
478 | has_output = True | |
|
479 | inp = ip.IP.input_hist_raw[idx] | |
|
480 | if inp.strip(): | |
|
481 | res.append('In [%d]: %s' % (idx, inp)) | |
|
482 | if val: | |
|
483 | res.append(pprint.pformat(val)) | |
|
484 | res.append('\n') | |
|
485 | return ''.join(res) | |
|
486 | ||
|
487 | ||
|
488 | def lee_f(self,s): | |
|
489 | """ Open file(s)/objects in Leo | |
|
490 | ||
|
491 | - %lee hist -> open full session history in leo | |
|
492 | - Takes an object. l = [1,2,"hello"]; %lee l. Alt+I in leo pushes the object back | |
|
493 | - Takes an mglob pattern, e.g. '%lee *.cpp' or %lee 'rec:*.cpp' | |
|
494 | - Takes input history indices: %lee 4 6-8 10 12-47 | |
|
495 | """ | |
|
496 | import os | |
|
497 | ||
|
498 | c.beginUpdate() | |
|
499 | try: | |
|
500 | if s == 'hist': | |
|
501 | wb.ipython_history.b = get_history() | |
|
502 | wb.ipython_history.go() | |
|
503 | return | |
|
504 | ||
|
505 | ||
|
506 | if s and s[0].isdigit(): | |
|
507 | # numbers; push input slices to leo | |
|
508 | lines = self.extract_input_slices(s.strip().split(), True) | |
|
509 | v = add_var('stored_ipython_input') | |
|
510 | v.b = '\n'.join(lines) | |
|
511 | return | |
|
512 | ||
|
513 | ||
|
514 | # try editing the object directly | |
|
515 | obj = ip.user_ns.get(s, None) | |
|
516 | if obj is not None: | |
|
517 | edit_object_in_leo(obj,s) | |
|
518 | return | |
|
519 | ||
|
520 | ||
|
521 | # if it's not object, it's a file name / mglob pattern | |
|
522 | from IPython.external import mglob | |
|
523 | ||
|
524 | files = (os.path.abspath(f) for f in mglob.expand(s)) | |
|
525 | for fname in files: | |
|
526 | p = g.findNodeAnywhere(c,'@auto ' + fname) | |
|
527 | if not p: | |
|
528 | p = c.currentPosition().insertAfter() | |
|
529 | ||
|
530 | p.setHeadString('@auto ' + fname) | |
|
531 | if os.path.isfile(fname): | |
|
532 | c.setBodyString(p,open(fname).read()) | |
|
533 | c.selectPosition(p) | |
|
534 | print "Editing file(s), press ctrl+shift+w in Leo to write @auto nodes" | |
|
535 | finally: | |
|
536 | c.endUpdate() | |
|
537 | ||
|
538 | ||
|
539 | ||
|
540 | def leoref_f(self,s): | |
|
541 | """ Quick reference for ILeo """ | |
|
542 | import textwrap | |
|
543 | print textwrap.dedent("""\ | |
|
544 | %leoe file/object - open file / object in leo | |
|
545 | wb.foo.v - eval node foo (i.e. headstring is 'foo' or '@ipy foo') | |
|
546 | wb.foo.v = 12 - assign to body of node foo | |
|
547 | wb.foo.b - read or write the body of node foo | |
|
548 | wb.foo.l - body of node foo as string list | |
|
549 | ||
|
550 | for el in wb.foo: | |
|
551 | print el.v | |
|
552 | ||
|
553 | """ | |
|
554 | ) | |
|
555 | ||
|
556 | ||
|
557 | ||
|
558 | def mb_f(self, arg): | |
|
559 | """ Execute leo minibuffer commands | |
|
560 | ||
|
561 | Example: | |
|
562 | mb save-to-file | |
|
563 | """ | |
|
564 | c.executeMinibufferCommand(arg) | |
|
565 | ||
|
566 | def mb_completer(self,event): | |
|
567 | """ Custom completer for minibuffer """ | |
|
568 | cmd_param = event.line.split() | |
|
569 | if event.line.endswith(' '): | |
|
570 | cmd_param.append('') | |
|
571 | if len(cmd_param) > 2: | |
|
572 | return ip.IP.Completer.file_matches(event.symbol) | |
|
573 | cmds = c.commandsDict.keys() | |
|
574 | cmds.sort() | |
|
575 | return cmds | |
|
576 | ||
|
577 | def show_welcome(): | |
|
578 | print "------------------" | |
|
579 | print "Welcome to Leo-enabled IPython session!" | |
|
580 | print "Try %leoref for quick reference." | |
|
581 | import IPython.platutils | |
|
582 | IPython.platutils.set_term_title('ILeo') | |
|
583 | IPython.platutils.freeze_term_title() | |
|
584 | ||
|
585 | def run_leo_startup_node(): | |
|
586 | p = g.findNodeAnywhere(c,'@ipy-startup') | |
|
587 | if p: | |
|
588 | print "Running @ipy-startup nodes" | |
|
589 | for n in LeoNode(p): | |
|
590 | push_from_leo(n) | |
|
591 | ||
|
592 |
@@ -43,11 +43,13 b' def main():' | |||
|
43 | 43 | # %store foo |
|
44 | 44 | # %store bar |
|
45 | 45 | import ipy_rehashdir |
|
46 | import ipy_signals | |
|
46 | ||
|
47 | # does not work without subprocess module! | |
|
48 | #import ipy_signals | |
|
47 | 49 | |
|
48 | 50 | ip.ex('import os') |
|
49 | 51 | ip.ex("def up(): os.chdir('..')") |
|
50 | ||
|
52 | ip.user_ns['LA'] = LastArgFinder() | |
|
51 | 53 | # Nice prompt |
|
52 | 54 | |
|
53 | 55 | o.prompt_in1= r'\C_LightBlue[\C_LightCyan\Y2\C_LightBlue]\C_Green|\#> ' |
@@ -117,6 +119,29 b' def main():' | |||
|
117 | 119 | |
|
118 | 120 | extend_shell_behavior(ip) |
|
119 | 121 | |
|
122 | class LastArgFinder: | |
|
123 | """ Allow $LA to work as "last argument of previous command", like $! in bash | |
|
124 | ||
|
125 | To call this in normal IPython code, do LA() | |
|
126 | """ | |
|
127 | def __call__(self, hist_idx = None): | |
|
128 | ip = ipapi.get() | |
|
129 | if hist_idx is None: | |
|
130 | return str(self) | |
|
131 | return ip.IP.input_hist_raw[hist_idx].strip().split()[-1] | |
|
132 | def __str__(self): | |
|
133 | ip = ipapi.get() | |
|
134 | for cmd in reversed(ip.IP.input_hist_raw): | |
|
135 | parts = cmd.strip().split() | |
|
136 | if len(parts) < 2 or parts[-1] in ['$LA', 'LA()']: | |
|
137 | continue | |
|
138 | return parts[-1] | |
|
139 | return "" | |
|
140 | ||
|
141 | ||
|
142 | ||
|
143 | ||
|
144 | ||
|
120 | 145 | # XXX You do not need to understand the next function! |
|
121 | 146 | # This should probably be moved out of profile |
|
122 | 147 |
@@ -41,8 +41,9 b' Now launch a new IPython prompt and kill the process:' | |||
|
41 | 41 | (you don't need to specify PID for %kill if only one task is running) |
|
42 | 42 | """ |
|
43 | 43 | |
|
44 |
from subprocess import |
|
|
44 | from subprocess import * | |
|
45 | 45 | import os,shlex,sys,time |
|
46 | import threading,Queue | |
|
46 | 47 | |
|
47 | 48 | from IPython import genutils |
|
48 | 49 | |
@@ -71,15 +72,70 b' def startjob(job):' | |||
|
71 | 72 | p.line = job |
|
72 | 73 | return p |
|
73 | 74 | |
|
75 | class AsyncJobQ(threading.Thread): | |
|
76 | def __init__(self): | |
|
77 | threading.Thread.__init__(self) | |
|
78 | self.q = Queue.Queue() | |
|
79 | self.output = [] | |
|
80 | self.stop = False | |
|
81 | def run(self): | |
|
82 | while 1: | |
|
83 | cmd,cwd = self.q.get() | |
|
84 | if self.stop: | |
|
85 | self.output.append("** Discarding: '%s' - %s" % (cmd,cwd)) | |
|
86 | continue | |
|
87 | self.output.append("** Task started: '%s' - %s" % (cmd,cwd)) | |
|
88 | ||
|
89 | p = Popen(cmd, shell=True, stdout=PIPE, stderr=STDOUT, cwd = cwd) | |
|
90 | out = p.stdout.read() | |
|
91 | self.output.append("** Task complete: '%s'\n" % cmd) | |
|
92 | self.output.append(out) | |
|
93 | ||
|
94 | def add(self,cmd): | |
|
95 | self.q.put_nowait((cmd, os.getcwd())) | |
|
96 | ||
|
97 | def dumpoutput(self): | |
|
98 | while self.output: | |
|
99 | item = self.output.pop(0) | |
|
100 | print item | |
|
101 | ||
|
102 | _jobq = None | |
|
103 | ||
|
104 | def jobqueue_f(self, line): | |
|
105 | ||
|
106 | global _jobq | |
|
107 | if not _jobq: | |
|
108 | print "Starting jobqueue - do '&some_long_lasting_system_command' to enqueue" | |
|
109 | _jobq = AsyncJobQ() | |
|
110 | _jobq.setDaemon(True) | |
|
111 | _jobq.start() | |
|
112 | ip.jobq = _jobq.add | |
|
113 | return | |
|
114 | if line.strip() == 'stop': | |
|
115 | print "Stopping and clearing jobqueue, %jobqueue start to start again" | |
|
116 | _jobq.stop = True | |
|
117 | return | |
|
118 | if line.strip() == 'start': | |
|
119 | _jobq.stop = False | |
|
120 | return | |
|
121 | ||
|
74 | 122 | def jobctrl_prefilter_f(self,line): |
|
75 | 123 | if line.startswith('&'): |
|
76 | 124 | pre,fn,rest = self.split_user_input(line[1:]) |
|
77 | 125 | |
|
78 | 126 | line = ip.IP.expand_aliases(fn,rest) |
|
79 | return '_ip.startjob(%s)' % genutils.make_quoted_expr(line) | |
|
127 | if not _jobq: | |
|
128 | return '_ip.startjob(%s)' % genutils.make_quoted_expr(line) | |
|
129 | return '_ip.jobq(%s)' % genutils.make_quoted_expr(line) | |
|
80 | 130 | |
|
81 | 131 | raise IPython.ipapi.TryNext |
|
82 | 132 | |
|
133 | def jobq_output_hook(self): | |
|
134 | if not _jobq: | |
|
135 | return | |
|
136 | _jobq.dumpoutput() | |
|
137 | ||
|
138 | ||
|
83 | 139 | |
|
84 | 140 | def job_list(ip): |
|
85 | 141 | keys = ip.db.keys('tasks/*') |
@@ -91,8 +147,16 b' def magic_tasks(self,line):' | |||
|
91 | 147 | |
|
92 | 148 | A 'task' is a process that has been started in IPython when 'jobctrl' extension is enabled. |
|
93 | 149 | Tasks can be killed with %kill. |
|
150 | ||
|
151 | '%tasks clear' clears the task list (from stale tasks) | |
|
94 | 152 | """ |
|
95 | 153 | ip = self.getapi() |
|
154 | if line.strip() == 'clear': | |
|
155 | for k in ip.db.keys('tasks/*'): | |
|
156 | print "Clearing",ip.db[k] | |
|
157 | del ip.db[k] | |
|
158 | return | |
|
159 | ||
|
96 | 160 | ents = job_list(ip) |
|
97 | 161 | if not ents: |
|
98 | 162 | print "No tasks running" |
@@ -125,7 +189,7 b' def magic_kill(self,line):' | |||
|
125 | 189 | magic_tasks(self,line) |
|
126 | 190 | |
|
127 | 191 | if sys.platform == 'win32': |
|
128 |
shell_internal_commands = 'break chcp cls copy ctty date del erase dir md mkdir path prompt rd rmdir time |
|
|
192 | shell_internal_commands = 'break chcp cls copy ctty date del erase dir md mkdir path prompt rd rmdir start time type ver vol'.split() | |
|
129 | 193 | else: |
|
130 | 194 | # todo linux commands |
|
131 | 195 | shell_internal_commands = [] |
@@ -133,20 +197,33 b' else:' | |||
|
133 | 197 | |
|
134 | 198 | def jobctrl_shellcmd(ip,cmd): |
|
135 | 199 | """ os.system replacement that stores process info to db['tasks/t1234'] """ |
|
200 | cmd = cmd.strip() | |
|
136 | 201 | cmdname = cmd.split(None,1)[0] |
|
137 | if cmdname in shell_internal_commands: | |
|
202 | if cmdname in shell_internal_commands or '|' in cmd or '>' in cmd or '<' in cmd: | |
|
138 | 203 | use_shell = True |
|
139 | 204 | else: |
|
140 | 205 | use_shell = False |
|
141 | 206 | |
|
142 | p = Popen(cmd,shell = use_shell) | |
|
143 | jobentry = 'tasks/t' + str(p.pid) | |
|
144 | ||
|
207 | jobentry = None | |
|
145 | 208 | try: |
|
209 | try: | |
|
210 | p = Popen(cmd,shell = use_shell) | |
|
211 | except WindowsError: | |
|
212 | if use_shell: | |
|
213 | # try with os.system | |
|
214 | os.system(cmd) | |
|
215 | return | |
|
216 | else: | |
|
217 | # have to go via shell, sucks | |
|
218 | p = Popen(cmd,shell = True) | |
|
219 | ||
|
220 | jobentry = 'tasks/t' + str(p.pid) | |
|
146 | 221 | ip.db[jobentry] = (p.pid,cmd,os.getcwd(),time.time()) |
|
147 | p.communicate() | |
|
222 | p.communicate() | |
|
223 | ||
|
148 | 224 | finally: |
|
149 |
|
|
|
225 | if jobentry: | |
|
226 | del ip.db[jobentry] | |
|
150 | 227 | |
|
151 | 228 | |
|
152 | 229 | def install(): |
@@ -158,5 +235,6 b' def install():' | |||
|
158 | 235 | ip.set_hook('shell_hook', jobctrl_shellcmd) |
|
159 | 236 | ip.expose_magic('kill',magic_kill) |
|
160 | 237 | ip.expose_magic('tasks',magic_tasks) |
|
161 | ||
|
238 | ip.expose_magic('jobqueue',jobqueue_f) | |
|
239 | ip.set_hook('pre_prompt_hook', jobq_output_hook) | |
|
162 | 240 | install() |
@@ -94,8 +94,16 b' def matchorfail(text, pos):' | |||
|
94 | 94 | match = tokenprog.match(text, pos) |
|
95 | 95 | if match is None: |
|
96 | 96 | raise ItplError(text, pos) |
|
97 | ||
|
97 | 98 | return match, match.end() |
|
98 | 99 | |
|
100 | try: | |
|
101 | itpl_encoding = sys.stdin.encoding or 'ascii' | |
|
102 | except AttributeError: | |
|
103 | itpl_encoding = 'ascii' | |
|
104 | ||
|
105 | ||
|
106 | ||
|
99 | 107 | class Itpl: |
|
100 | 108 | """Class representing a string with interpolation abilities. |
|
101 | 109 | |
@@ -104,7 +112,7 b' class Itpl:' | |||
|
104 | 112 | evaluation and substitution happens in the namespace of the |
|
105 | 113 | caller when str(instance) is called.""" |
|
106 | 114 | |
|
107 |
def __init__(self, format,codec= |
|
|
115 | def __init__(self, format,codec=itpl_encoding,encoding_errors='backslashreplace'): | |
|
108 | 116 | """The single mandatory argument to this constructor is a format |
|
109 | 117 | string. |
|
110 | 118 |
@@ -115,9 +115,9 b' class Magic:' | |||
|
115 | 115 | |
|
116 | 116 | def profile_missing_notice(self, *args, **kwargs): |
|
117 | 117 | error("""\ |
|
118 |
The profile module could not be found. |
|
|
119 | it has been removed from the standard Debian package because of its non-free | |
|
120 | license. To use profiling, please install"python2.3-profiler" from non-free.""") | |
|
118 | The profile module could not be found. It has been removed from the standard | |
|
119 | python packages because of its non-free license. To use profiling, install the | |
|
120 | python-profiler package from non-free.""") | |
|
121 | 121 | |
|
122 | 122 | def default_option(self,fn,optstr): |
|
123 | 123 | """Make an entry in the options_table for fn, with value optstr""" |
@@ -147,7 +147,7 b' license. To use profiling, please install"python2.3-profiler" from non-free.""")' | |||
|
147 | 147 | filter(inst_magic,self.__dict__.keys()) + \ |
|
148 | 148 | filter(inst_bound_magic,self.__class__.__dict__.keys()) |
|
149 | 149 | out = [] |
|
150 | for fn in magics: | |
|
150 | for fn in Set(magics): | |
|
151 | 151 | out.append(fn.replace('magic_','',1)) |
|
152 | 152 | out.sort() |
|
153 | 153 | return out |
@@ -386,7 +386,10 b' license. To use profiling, please install"python2.3-profiler" from non-free.""")' | |||
|
386 | 386 | return None |
|
387 | 387 | |
|
388 | 388 | def magic_magic(self, parameter_s = ''): |
|
389 |
"""Print information about the magic function system. |
|
|
389 | """Print information about the magic function system. | |
|
390 | ||
|
391 | Supported formats: -latex, -brief, -rest | |
|
392 | """ | |
|
390 | 393 | |
|
391 | 394 | mode = '' |
|
392 | 395 | try: |
@@ -394,6 +397,9 b' license. To use profiling, please install"python2.3-profiler" from non-free.""")' | |||
|
394 | 397 | mode = 'latex' |
|
395 | 398 | if parameter_s.split()[0] == '-brief': |
|
396 | 399 | mode = 'brief' |
|
400 | if parameter_s.split()[0] == '-rest': | |
|
401 | mode = 'rest' | |
|
402 | rest_docs = [] | |
|
397 | 403 | except: |
|
398 | 404 | pass |
|
399 | 405 | |
@@ -409,14 +415,26 b' license. To use profiling, please install"python2.3-profiler" from non-free.""")' | |||
|
409 | 415 | break |
|
410 | 416 | if mode == 'brief': |
|
411 | 417 | # only first line |
|
412 | fndoc = fn.__doc__.split('\n',1)[0] | |
|
418 | if fn.__doc__: | |
|
419 | fndoc = fn.__doc__.split('\n',1)[0] | |
|
420 | else: | |
|
421 | fndoc = 'No documentation' | |
|
422 | else: | |
|
423 | fndoc = fn.__doc__.rstrip() | |
|
424 | ||
|
425 | if mode == 'rest': | |
|
426 | rest_docs.append('**%s%s**::\n\n\t%s\n\n' %(self.shell.ESC_MAGIC, | |
|
427 | fname,fndoc)) | |
|
428 | ||
|
413 | 429 | else: |
|
414 | fndoc = fn.__doc__ | |
|
430 | magic_docs.append('%s%s:\n\t%s\n' %(self.shell.ESC_MAGIC, | |
|
431 | fname,fndoc)) | |
|
415 | 432 | |
|
416 | magic_docs.append('%s%s:\n\t%s\n' %(self.shell.ESC_MAGIC, | |
|
417 | fname,fndoc)) | |
|
418 | 433 | magic_docs = ''.join(magic_docs) |
|
419 | 434 | |
|
435 | if mode == 'rest': | |
|
436 | return "".join(rest_docs) | |
|
437 | ||
|
420 | 438 | if mode == 'latex': |
|
421 | 439 | print self.format_latex(magic_docs) |
|
422 | 440 | return |
@@ -2612,7 +2630,7 b' Defaulting color scheme to \'NoColor\'"""' | |||
|
2612 | 2630 | os.chdir(pdir) |
|
2613 | 2631 | for ff in os.listdir(pdir): |
|
2614 | 2632 | base, ext = os.path.splitext(ff) |
|
2615 | if isexec(ff) and base not in self.shell.no_alias: | |
|
2633 | if isexec(ff) and base.lower() not in self.shell.no_alias: | |
|
2616 | 2634 | if ext.lower() == '.exe': |
|
2617 | 2635 | ff = base |
|
2618 | 2636 | alias_table[base.lower()] = (0,ff) |
@@ -2667,6 +2685,7 b' Defaulting color scheme to \'NoColor\'"""' | |||
|
2667 | 2685 | parameter_s = parameter_s.strip() |
|
2668 | 2686 | #bkms = self.shell.persist.get("bookmarks",{}) |
|
2669 | 2687 | |
|
2688 | oldcwd = os.getcwd() | |
|
2670 | 2689 | numcd = re.match(r'(-)(\d+)$',parameter_s) |
|
2671 | 2690 | # jump in directory history by number |
|
2672 | 2691 | if numcd: |
@@ -2705,7 +2724,7 b' Defaulting color scheme to \'NoColor\'"""' | |||
|
2705 | 2724 | |
|
2706 | 2725 | # at this point ps should point to the target dir |
|
2707 | 2726 | if ps: |
|
2708 | try: | |
|
2727 | try: | |
|
2709 | 2728 | os.chdir(os.path.expanduser(ps)) |
|
2710 | 2729 | if self.shell.rc.term_title: |
|
2711 | 2730 | #print 'set term title:',self.shell.rc.term_title # dbg |
@@ -2716,8 +2735,9 b' Defaulting color scheme to \'NoColor\'"""' | |||
|
2716 | 2735 | else: |
|
2717 | 2736 | cwd = os.getcwd() |
|
2718 | 2737 | dhist = self.shell.user_ns['_dh'] |
|
2719 |
|
|
|
2720 | self.db['dhist'] = compress_dhist(dhist)[-100:] | |
|
2738 | if oldcwd != cwd: | |
|
2739 | dhist.append(cwd) | |
|
2740 | self.db['dhist'] = compress_dhist(dhist)[-100:] | |
|
2721 | 2741 | |
|
2722 | 2742 | else: |
|
2723 | 2743 | os.chdir(self.shell.home_dir) |
@@ -2725,8 +2745,10 b' Defaulting color scheme to \'NoColor\'"""' | |||
|
2725 | 2745 | platutils.set_term_title("IPy ~") |
|
2726 | 2746 | cwd = os.getcwd() |
|
2727 | 2747 | dhist = self.shell.user_ns['_dh'] |
|
2728 | dhist.append(cwd) | |
|
2729 | self.db['dhist'] = compress_dhist(dhist)[-100:] | |
|
2748 | ||
|
2749 | if oldcwd != cwd: | |
|
2750 | dhist.append(cwd) | |
|
2751 | self.db['dhist'] = compress_dhist(dhist)[-100:] | |
|
2730 | 2752 | if not 'q' in opts and self.shell.user_ns['_dh']: |
|
2731 | 2753 | print self.shell.user_ns['_dh'][-1] |
|
2732 | 2754 | |
@@ -3118,7 +3140,7 b' Defaulting color scheme to \'NoColor\'"""' | |||
|
3118 | 3140 | screen_lines=self.shell.rc.screen_length) |
|
3119 | 3141 | |
|
3120 | 3142 | def magic_cpaste(self, parameter_s=''): |
|
3121 | """Allows you to paste & execute a pre-formatted code block from clipboard | |
|
3143 | """Allows you to paste & execute a pre-formatted code block from clipboard. | |
|
3122 | 3144 | |
|
3123 | 3145 | You must terminate the block with '--' (two minus-signs) alone on the |
|
3124 | 3146 | line. You can also provide your own sentinel with '%paste -s %%' ('%%' |
@@ -3126,13 +3148,14 b' Defaulting color scheme to \'NoColor\'"""' | |||
|
3126 | 3148 | |
|
3127 | 3149 | The block is dedented prior to execution to enable execution of method |
|
3128 | 3150 | definitions. '>' and '+' characters at the beginning of a line are |
|
3129 |
ignored, to allow pasting directly from e-mails |
|
|
3151 | ignored, to allow pasting directly from e-mails, diff files and | |
|
3152 | doctests (the '...' continuation prompt is also stripped). The | |
|
3130 | 3153 | executed block is also assigned to variable named 'pasted_block' for |
|
3131 | 3154 | later editing with '%edit pasted_block'. |
|
3132 | 3155 | |
|
3133 | 3156 | You can also pass a variable name as an argument, e.g. '%cpaste foo'. |
|
3134 | 3157 | This assigns the pasted block to variable 'foo' as string, without |
|
3135 |
dedenting or executing it |
|
|
3158 | dedenting or executing it (preceding >>> and + is still stripped) | |
|
3136 | 3159 | |
|
3137 | 3160 | Do not be alarmed by garbled output on Windows (it's a readline bug). |
|
3138 | 3161 | Just press enter and type -- (and press enter again) and the block |
@@ -3143,6 +3166,15 b' Defaulting color scheme to \'NoColor\'"""' | |||
|
3143 | 3166 | opts,args = self.parse_options(parameter_s,'s:',mode='string') |
|
3144 | 3167 | par = args.strip() |
|
3145 | 3168 | sentinel = opts.get('s','--') |
|
3169 | ||
|
3170 | # Regular expressions that declare text we strip from the input: | |
|
3171 | strip_re = [r'^\s*In \[\d+\]:', # IPython input prompt | |
|
3172 | r'^\s*(\s?>)+', # Python input prompt | |
|
3173 | r'^\s*\.{3,}', # Continuation prompts | |
|
3174 | r'^\++', | |
|
3175 | ] | |
|
3176 | ||
|
3177 | strip_from_start = map(re.compile,strip_re) | |
|
3146 | 3178 | |
|
3147 | 3179 | from IPython import iplib |
|
3148 | 3180 | lines = [] |
@@ -3151,7 +3183,11 b' Defaulting color scheme to \'NoColor\'"""' | |||
|
3151 | 3183 | l = iplib.raw_input_original(':') |
|
3152 | 3184 | if l ==sentinel: |
|
3153 | 3185 | break |
|
3154 | lines.append(l.lstrip('>').lstrip('+')) | |
|
3186 | ||
|
3187 | for pat in strip_from_start: | |
|
3188 | l = pat.sub('',l) | |
|
3189 | lines.append(l) | |
|
3190 | ||
|
3155 | 3191 | block = "\n".join(lines) + '\n' |
|
3156 | 3192 | #print "block:\n",block |
|
3157 | 3193 | if not par: |
@@ -404,7 +404,13 b' class Inspector:' | |||
|
404 | 404 | # Filename where object was defined |
|
405 | 405 | binary_file = False |
|
406 | 406 | try: |
|
407 | fname = inspect.getabsfile(obj) | |
|
407 | try: | |
|
408 | fname = inspect.getabsfile(obj) | |
|
409 | except TypeError: | |
|
410 | # For an instance, the file that matters is where its class was | |
|
411 | # declared. | |
|
412 | if hasattr(obj,'__class__'): | |
|
413 | fname = inspect.getabsfile(obj.__class__) | |
|
408 | 414 | if fname.endswith('<string>'): |
|
409 | 415 | fname = 'Dynamically generated function. No source code available.' |
|
410 | 416 | if (fname.endswith('.so') or fname.endswith('.dll')): |
@@ -432,8 +438,13 b' class Inspector:' | |||
|
432 | 438 | linecache.checkcache() |
|
433 | 439 | source_success = False |
|
434 | 440 | try: |
|
435 | source = self.format(getsource(obj,binary_file)) | |
|
436 | if source: | |
|
441 | try: | |
|
442 | src = getsource(obj,binary_file) | |
|
443 | except TypeError: | |
|
444 | if hasattr(obj,'__class__'): | |
|
445 | src = getsource(obj.__class__,binary_file) | |
|
446 | if src is not None: | |
|
447 | source = self.format(src) | |
|
437 | 448 | out.write(header('Source:\n')+source.rstrip()) |
|
438 | 449 | source_success = True |
|
439 | 450 | except Exception, msg: |
@@ -22,9 +22,15 b" name = 'ipython'" | |||
|
22 | 22 | # because bdist_rpm does not accept dashes (an RPM) convention, and |
|
23 | 23 | # bdist_deb does not accept underscores (a Debian convention). |
|
24 | 24 | |
|
25 |
revision = ' |
|
|
25 | revision = '128' | |
|
26 | branch = 'ipython' | |
|
26 | 27 | |
|
27 | version = '0.8.3.svn.r' + revision.rstrip('M') | |
|
28 | if branch == 'ipython': | |
|
29 | version = '0.8.4.bzr.r' + revision | |
|
30 | else: | |
|
31 | version = '0.8.4.bzr.r%s.%s' % (revision,branch) | |
|
32 | ||
|
33 | version = '0.8.4' | |
|
28 | 34 | |
|
29 | 35 | description = "An enhanced interactive Python shell." |
|
30 | 36 |
@@ -46,6 +46,13 b' from IPython.ipmaker import make_IPython' | |||
|
46 | 46 | from IPython.Magic import Magic |
|
47 | 47 | from IPython.ipstruct import Struct |
|
48 | 48 | |
|
49 | try: # Python 2.3 compatibility | |
|
50 | set | |
|
51 | except NameError: | |
|
52 | import sets | |
|
53 | set = sets.Set | |
|
54 | ||
|
55 | ||
|
49 | 56 | # Globals |
|
50 | 57 | # global flag to pass around information about Ctrl-C without exceptions |
|
51 | 58 | KBINT = False |
@@ -358,36 +365,37 b' class MTInteractiveShell(InteractiveShell):' | |||
|
358 | 365 | InteractiveShell.__init__(self,name,usage,rc,user_ns, |
|
359 | 366 | user_global_ns,banner2) |
|
360 | 367 | |
|
361 | # Locking control variable. | |
|
362 | self.thread_ready = threading.Condition(threading.RLock()) | |
|
363 | 368 | |
|
364 |
# A queue to hold the code to be executed. |
|
|
365 | # enough, because uses like macros cause reentrancy. | |
|
369 | # A queue to hold the code to be executed. | |
|
366 | 370 | self.code_queue = Queue.Queue() |
|
367 | 371 | |
|
368 | 372 | # Stuff to do at closing time |
|
369 |
self._kill = |
|
|
370 | on_kill = kw.get('on_kill') | |
|
371 | if on_kill is None: | |
|
372 | on_kill = [] | |
|
373 | self._kill = None | |
|
374 | on_kill = kw.get('on_kill', []) | |
|
373 | 375 | # Check that all things to kill are callable: |
|
374 | 376 | for t in on_kill: |
|
375 | 377 | if not callable(t): |
|
376 | 378 | raise TypeError,'on_kill must be a list of callables' |
|
377 | 379 | self.on_kill = on_kill |
|
378 | ||
|
380 | # thread identity of the "worker thread" (that may execute code directly) | |
|
381 | self.worker_ident = None | |
|
382 | ||
|
379 | 383 | def runsource(self, source, filename="<input>", symbol="single"): |
|
380 | 384 | """Compile and run some source in the interpreter. |
|
381 | 385 | |
|
382 | 386 | Modified version of code.py's runsource(), to handle threading issues. |
|
383 | 387 | See the original for full docstring details.""" |
|
384 | ||
|
388 | ||
|
385 | 389 | global KBINT |
|
386 | 390 | |
|
387 | 391 | # If Ctrl-C was typed, we reset the flag and return right away |
|
388 | 392 | if KBINT: |
|
389 | 393 | KBINT = False |
|
390 | 394 | return False |
|
395 | ||
|
396 | if self._kill: | |
|
397 | # can't queue new code if we are being killed | |
|
398 | return True | |
|
391 | 399 | |
|
392 | 400 | try: |
|
393 | 401 | code = self.compile(source, filename, symbol) |
@@ -400,29 +408,40 b' class MTInteractiveShell(InteractiveShell):' | |||
|
400 | 408 | # Case 2 |
|
401 | 409 | return True |
|
402 | 410 | |
|
411 | # shortcut - if we are in worker thread, or the worker thread is not running, | |
|
412 | # execute directly (to allow recursion and prevent deadlock if code is run early | |
|
413 | # in IPython construction) | |
|
414 | ||
|
415 | if (self.worker_ident is None or self.worker_ident == thread.get_ident()): | |
|
416 | InteractiveShell.runcode(self,code) | |
|
417 | return | |
|
418 | ||
|
403 | 419 | # Case 3 |
|
404 | 420 | # Store code in queue, so the execution thread can handle it. |
|
405 | 421 | |
|
406 | # Note that with macros and other applications, we MAY re-enter this | |
|
407 | # section, so we have to acquire the lock with non-blocking semantics, | |
|
408 | # else we deadlock. | |
|
409 | got_lock = self.thread_ready.acquire() | |
|
410 | self.code_queue.put(code) | |
|
411 | if got_lock: | |
|
412 | self.thread_ready.wait() # Wait until processed in timeout interval | |
|
413 | self.thread_ready.release() | |
|
414 | ||
|
422 | completed_ev, received_ev = threading.Event(), threading.Event() | |
|
423 | ||
|
424 | self.code_queue.put((code,completed_ev, received_ev)) | |
|
425 | # first make sure the message was received, with timeout | |
|
426 | received_ev.wait(5) | |
|
427 | if not received_ev.isSet(): | |
|
428 | # the mainloop is dead, start executing code directly | |
|
429 | print "Warning: Timeout for mainloop thread exceeded" | |
|
430 | print "switching to nonthreaded mode (until mainloop wakes up again)" | |
|
431 | self.worker_ident = None | |
|
432 | else: | |
|
433 | completed_ev.wait() | |
|
415 | 434 | return False |
|
416 | 435 | |
|
417 | 436 | def runcode(self): |
|
418 | 437 | """Execute a code object. |
|
419 | 438 | |
|
420 | 439 | Multithreaded wrapper around IPython's runcode().""" |
|
421 | ||
|
440 | ||
|
422 | 441 | global CODE_RUN |
|
423 | ||
|
424 | # lock thread-protected stuff | |
|
425 | got_lock = self.thread_ready.acquire() | |
|
442 | ||
|
443 | # we are in worker thread, stash out the id for runsource() | |
|
444 | self.worker_ident = thread.get_ident() | |
|
426 | 445 | |
|
427 | 446 | if self._kill: |
|
428 | 447 | print >>Term.cout, 'Closing threads...', |
@@ -430,6 +449,9 b' class MTInteractiveShell(InteractiveShell):' | |||
|
430 | 449 | for tokill in self.on_kill: |
|
431 | 450 | tokill() |
|
432 | 451 | print >>Term.cout, 'Done.' |
|
452 | # allow kill() to return | |
|
453 | self._kill.set() | |
|
454 | return True | |
|
433 | 455 | |
|
434 | 456 | # Install sigint handler. We do it every time to ensure that if user |
|
435 | 457 | # code modifies it, we restore our own handling. |
@@ -445,10 +467,11 b' class MTInteractiveShell(InteractiveShell):' | |||
|
445 | 467 | code_to_run = None |
|
446 | 468 | while 1: |
|
447 | 469 | try: |
|
448 | code_to_run = self.code_queue.get_nowait() | |
|
470 | code_to_run, completed_ev, received_ev = self.code_queue.get_nowait() | |
|
449 | 471 | except Queue.Empty: |
|
450 | 472 | break |
|
451 | ||
|
473 | received_ev.set() | |
|
474 | ||
|
452 | 475 | # Exceptions need to be raised differently depending on which |
|
453 | 476 | # thread is active. This convoluted try/except is only there to |
|
454 | 477 | # protect against asynchronous exceptions, to ensure that a KBINT |
@@ -462,28 +485,23 b' class MTInteractiveShell(InteractiveShell):' | |||
|
462 | 485 | except KeyboardInterrupt: |
|
463 | 486 | print "Keyboard interrupted in mainloop" |
|
464 | 487 | while not self.code_queue.empty(): |
|
465 | self.code_queue.get_nowait() | |
|
488 | code, ev1,ev2 = self.code_queue.get_nowait() | |
|
489 | ev1.set() | |
|
490 | ev2.set() | |
|
466 | 491 | break |
|
467 | 492 | finally: |
|
468 |
|
|
|
469 | CODE_RUN = False | |
|
470 | ||
|
471 | # We're done with thread-protected variables | |
|
472 | if code_to_run is not None: | |
|
473 | self.thread_ready.notify() | |
|
474 | self.thread_ready.release() | |
|
475 | ||
|
476 | # We're done... | |
|
477 | CODE_RUN = False | |
|
493 | CODE_RUN = False | |
|
494 | # allow runsource() return from wait | |
|
495 | completed_ev.set() | |
|
496 | ||
|
497 | ||
|
478 | 498 | # This MUST return true for gtk threading to work |
|
479 | 499 | return True |
|
480 | 500 | |
|
481 | 501 | def kill(self): |
|
482 | 502 | """Kill the thread, returning when it has been shut down.""" |
|
483 | got_lock = self.thread_ready.acquire(False) | |
|
484 |
self._kill |
|
|
485 | if got_lock: | |
|
486 | self.thread_ready.release() | |
|
503 | self._kill = threading.Event() | |
|
504 | self._kill.wait() | |
|
487 | 505 | |
|
488 | 506 | class MatplotlibShellBase: |
|
489 | 507 | """Mixin class to provide the necessary modifications to regular IPython |
@@ -1051,7 +1069,9 b' def _load_pylab(user_ns):' | |||
|
1051 | 1069 | |
|
1052 | 1070 | ip = IPython.ipapi.get() |
|
1053 | 1071 | if ip.options.pylab_import_all: |
|
1054 |
|
|
|
1072 | ip.ex("from matplotlib.pylab import *") | |
|
1073 | ip.IP.user_config_ns.update(ip.user_ns) | |
|
1074 | ||
|
1055 | 1075 | |
|
1056 | 1076 | class IPShellMatplotlib(IPShell): |
|
1057 | 1077 | """Subclass IPShell with MatplotlibShell as the internal shell. |
@@ -1144,7 +1164,7 b' def _select_shell(argv):' | |||
|
1144 | 1164 | all_opts = set(['tk','pylab','gthread','qthread','q4thread','wthread', |
|
1145 | 1165 | 'tkthread']) |
|
1146 | 1166 | user_opts = set([s.replace('-','') for s in argv[:3]]) |
|
1147 | special_opts = user_opts & all_opts | |
|
1167 | special_opts = user_opts & all_opts | |
|
1148 | 1168 | |
|
1149 | 1169 | if 'tk' in special_opts: |
|
1150 | 1170 | USE_TK = True |
@@ -72,6 +72,27 b' def main():' | |||
|
72 | 72 | #o.autoexec.append('%colors NoColor') |
|
73 | 73 | #o.autoexec.append('%colors Linux') |
|
74 | 74 | |
|
75 | # for sane integer division that converts to float (1/2 == 0.5) | |
|
76 | #o.autoexec.append('from __future__ import division') | |
|
77 | ||
|
78 | # For %tasks and %kill | |
|
79 | #import jobctrl | |
|
80 | ||
|
81 | # For autoreloading of modules (%autoreload, %aimport) | |
|
82 | #import ipy_autoreload | |
|
83 | ||
|
84 | # For winpdb support (%wdb) | |
|
85 | #import ipy_winpdb | |
|
86 | ||
|
87 | # For bzr completer, requires bzrlib (the python installation of bzr) | |
|
88 | #ip.load('ipy_bzr') | |
|
89 | ||
|
90 | # Tab completer that is not quite so picky (i.e. | |
|
91 | # "foo".<TAB> and str(2).<TAB> will work). Complete | |
|
92 | # at your own risk! | |
|
93 | #import ipy_greedycompleter | |
|
94 | ||
|
95 | ||
|
75 | 96 | |
|
76 | 97 | # some config helper functions you can use |
|
77 | 98 | def import_all(modules): |
@@ -321,7 +321,7 b' class IPCompleter(Completer):' | |||
|
321 | 321 | # don't want to treat as delimiters in filename matching |
|
322 | 322 | # when escaped with backslash |
|
323 | 323 | |
|
324 |
protectables = ' |
|
|
324 | protectables = ' ' | |
|
325 | 325 | |
|
326 | 326 | if text.startswith('!'): |
|
327 | 327 | text = text[1:] |
@@ -32,7 +32,9 b' import imp' | |||
|
32 | 32 | import sys |
|
33 | 33 | |
|
34 | 34 | # Replacement for __import__() |
|
35 | def deep_import_hook(name, globals=None, locals=None, fromlist=None): | |
|
35 | def deep_import_hook(name, globals=None, locals=None, fromlist=None, level=-1): | |
|
36 | # For now level is ignored, it's just there to prevent crash | |
|
37 | # with from __future__ import absolute_import | |
|
36 | 38 | parent = determine_parent(globals) |
|
37 | 39 | q, tail = find_head_package(parent, name) |
|
38 | 40 | m = load_tail(q, tail) |
@@ -34,35 +34,35 b' License: MIT Open Source license' | |||
|
34 | 34 | #Assigned in variable for "usage" printing convenience" |
|
35 | 35 | |
|
36 | 36 | globsyntax = """\ |
|
37 | This program allows specifying filenames with "mglob" mechanism. | |
|
38 | Supported syntax in globs (wilcard matching patterns):: | |
|
39 | ||
|
40 | *.cpp ?ellowo* | |
|
41 | - obvious. Differs from normal glob in that dirs are not included. | |
|
42 | Unix users might want to write this as: "*.cpp" "?ellowo*" | |
|
43 | rec:/usr/share=*.txt,*.doc | |
|
44 | - get all *.txt and *.doc under /usr/share, | |
|
45 | recursively | |
|
46 | rec:/usr/share | |
|
47 | - All files under /usr/share, recursively | |
|
48 | rec:*.py | |
|
49 | - All .py files under current working dir, recursively | |
|
50 | foo | |
|
51 | - File or dir foo | |
|
52 | !*.bak readme* | |
|
53 | - readme*, exclude files ending with .bak | |
|
54 | !.svn/ !.hg/ !*_Data/ rec:. | |
|
55 | - Skip .svn, .hg, foo_Data dirs (and their subdirs) in recurse. | |
|
56 | Trailing / is the key, \ does not work! | |
|
57 | dir:foo | |
|
58 | - the directory foo if it exists (not files in foo) | |
|
59 | dir:* | |
|
60 | - all directories in current folder | |
|
61 | foo.py bar.* !h* rec:*.py | |
|
62 | - Obvious. !h* exclusion only applies for rec:*.py. | |
|
63 | foo.py is *not* included twice. | |
|
64 | @filelist.txt | |
|
65 | - All files listed in 'filelist.txt' file, on separate lines. | |
|
37 | This program allows specifying filenames with "mglob" mechanism. | |
|
38 | Supported syntax in globs (wilcard matching patterns):: | |
|
39 | ||
|
40 | *.cpp ?ellowo* | |
|
41 | - obvious. Differs from normal glob in that dirs are not included. | |
|
42 | Unix users might want to write this as: "*.cpp" "?ellowo*" | |
|
43 | rec:/usr/share=*.txt,*.doc | |
|
44 | - get all *.txt and *.doc under /usr/share, | |
|
45 | recursively | |
|
46 | rec:/usr/share | |
|
47 | - All files under /usr/share, recursively | |
|
48 | rec:*.py | |
|
49 | - All .py files under current working dir, recursively | |
|
50 | foo | |
|
51 | - File or dir foo | |
|
52 | !*.bak readme* | |
|
53 | - readme*, exclude files ending with .bak | |
|
54 | !.svn/ !.hg/ !*_Data/ rec:. | |
|
55 | - Skip .svn, .hg, foo_Data dirs (and their subdirs) in recurse. | |
|
56 | Trailing / is the key, \ does not work! | |
|
57 | dir:foo | |
|
58 | - the directory foo if it exists (not files in foo) | |
|
59 | dir:* | |
|
60 | - all directories in current folder | |
|
61 | foo.py bar.* !h* rec:*.py | |
|
62 | - Obvious. !h* exclusion only applies for rec:*.py. | |
|
63 | foo.py is *not* included twice. | |
|
64 | @filelist.txt | |
|
65 | - All files listed in 'filelist.txt' file, on separate lines. | |
|
66 | 66 | """ |
|
67 | 67 | |
|
68 | 68 |
@@ -29,7 +29,10 b' Date: 9 Mar 2007' | |||
|
29 | 29 | |
|
30 | 30 | from __future__ import generators |
|
31 | 31 | |
|
32 |
import sys, warnings, os, fnmatch, glob, shutil, codecs |
|
|
32 | import sys, warnings, os, fnmatch, glob, shutil, codecs | |
|
33 | # deprecated in python 2.6 | |
|
34 | warnings.filterwarnings('ignore', r'.*md5.*') | |
|
35 | import md5 | |
|
33 | 36 | |
|
34 | 37 | __version__ = '2.2' |
|
35 | 38 | __all__ = ['path'] |
@@ -22,7 +22,10 b' __license__ = Release.license' | |||
|
22 | 22 | # required modules from the Python standard library |
|
23 | 23 | import __main__ |
|
24 | 24 | import commands |
|
25 | import doctest | |
|
25 | try: | |
|
26 | import doctest | |
|
27 | except ImportError: | |
|
28 | pass | |
|
26 | 29 | import os |
|
27 | 30 | import re |
|
28 | 31 | import shlex |
@@ -33,6 +36,18 b' import time' | |||
|
33 | 36 | import types |
|
34 | 37 | import warnings |
|
35 | 38 | |
|
39 | # Curses and termios are Unix-only modules | |
|
40 | try: | |
|
41 | import curses | |
|
42 | # We need termios as well, so if its import happens to raise, we bail on | |
|
43 | # using curses altogether. | |
|
44 | import termios | |
|
45 | except ImportError: | |
|
46 | USE_CURSES = False | |
|
47 | else: | |
|
48 | # Curses on Solaris may not be complete, so we can't use it there | |
|
49 | USE_CURSES = hasattr(curses,'initscr') | |
|
50 | ||
|
36 | 51 | # Other IPython utilities |
|
37 | 52 | import IPython |
|
38 | 53 | from IPython.Itpl import Itpl,itpl,printpl |
@@ -1052,25 +1067,40 b' class SList(list):' | |||
|
1052 | 1067 | |
|
1053 | 1068 | p = paths = property(get_paths) |
|
1054 | 1069 | |
|
1055 | def grep(self, pattern, prune = False): | |
|
1070 | def grep(self, pattern, prune = False, field = None): | |
|
1056 | 1071 | """ Return all strings matching 'pattern' (a regex or callable) |
|
1057 | 1072 | |
|
1058 | 1073 | This is case-insensitive. If prune is true, return all items |
|
1059 | 1074 | NOT matching the pattern. |
|
1060 | 1075 | |
|
1076 | If field is specified, the match must occur in the specified | |
|
1077 | whitespace-separated field. | |
|
1078 | ||
|
1061 | 1079 | Examples:: |
|
1062 | 1080 | |
|
1063 | 1081 | a.grep( lambda x: x.startswith('C') ) |
|
1064 | 1082 | a.grep('Cha.*log', prune=1) |
|
1083 | a.grep('chm', field=-1) | |
|
1065 | 1084 | """ |
|
1085 | ||
|
1086 | def match_target(s): | |
|
1087 | if field is None: | |
|
1088 | return s | |
|
1089 | parts = s.split() | |
|
1090 | try: | |
|
1091 | tgt = parts[field] | |
|
1092 | return tgt | |
|
1093 | except IndexError: | |
|
1094 | return "" | |
|
1095 | ||
|
1066 | 1096 | if isinstance(pattern, basestring): |
|
1067 | 1097 | pred = lambda x : re.search(pattern, x, re.IGNORECASE) |
|
1068 | 1098 | else: |
|
1069 | 1099 | pred = pattern |
|
1070 | 1100 | if not prune: |
|
1071 | return SList([el for el in self if pred(el)]) | |
|
1101 | return SList([el for el in self if pred(match_target(el))]) | |
|
1072 | 1102 | else: |
|
1073 | return SList([el for el in self if not pred(el)]) | |
|
1103 | return SList([el for el in self if not pred(match_target(el))]) | |
|
1074 | 1104 | def fields(self, *fields): |
|
1075 | 1105 | """ Collect whitespace-separated fields from string list |
|
1076 | 1106 | |
@@ -1083,6 +1113,7 b' class SList(list):' | |||
|
1083 | 1113 | a.fields(0) is ['-rwxrwxrwx', 'drwxrwxrwx+'] |
|
1084 | 1114 | a.fields(1,0) is ['1 -rwxrwxrwx', '6 drwxrwxrwx+'] |
|
1085 | 1115 | (note the joining by space). |
|
1116 | a.fields(-1) is ['ChangeLog', 'IPython'] | |
|
1086 | 1117 | |
|
1087 | 1118 | IndexErrors are ignored. |
|
1088 | 1119 | |
@@ -1532,26 +1563,30 b' def page(strng,start=0,screen_lines=0,pager_cmd = None):' | |||
|
1532 | 1563 | # auto-determine screen size |
|
1533 | 1564 | if screen_lines <= 0: |
|
1534 | 1565 | if TERM=='xterm': |
|
1535 | try: | |
|
1536 | import curses | |
|
1537 | if hasattr(curses,'initscr'): | |
|
1538 | use_curses = 1 | |
|
1539 | else: | |
|
1540 | use_curses = 0 | |
|
1541 | except ImportError: | |
|
1542 | use_curses = 0 | |
|
1566 | use_curses = USE_CURSES | |
|
1543 | 1567 | else: |
|
1544 | 1568 | # curses causes problems on many terminals other than xterm. |
|
1545 |
use_curses = |
|
|
1569 | use_curses = False | |
|
1546 | 1570 | if use_curses: |
|
1547 | scr = curses.initscr() | |
|
1548 | screen_lines_real,screen_cols = scr.getmaxyx() | |
|
1549 | curses.endwin() | |
|
1550 | screen_lines += screen_lines_real | |
|
1551 | #print '***Screen size:',screen_lines_real,'lines x',\ | |
|
1552 | #screen_cols,'columns.' # dbg | |
|
1571 | # There is a bug in curses, where *sometimes* it fails to properly | |
|
1572 | # initialize, and then after the endwin() call is made, the | |
|
1573 | # terminal is left in an unusable state. Rather than trying to | |
|
1574 | # check everytime for this (by requesting and comparing termios | |
|
1575 | # flags each time), we just save the initial terminal state and | |
|
1576 | # unconditionally reset it every time. It's cheaper than making | |
|
1577 | # the checks. | |
|
1578 | term_flags = termios.tcgetattr(sys.stdout) | |
|
1579 | scr = curses.initscr() | |
|
1580 | screen_lines_real,screen_cols = scr.getmaxyx() | |
|
1581 | curses.endwin() | |
|
1582 | # Restore terminal state in case endwin() didn't. | |
|
1583 | termios.tcsetattr(sys.stdout,termios.TCSANOW,term_flags) | |
|
1584 | # Now we have what we needed: the screen size in rows/columns | |
|
1585 | screen_lines += screen_lines_real | |
|
1586 | #print '***Screen size:',screen_lines_real,'lines x',\ | |
|
1587 | #screen_cols,'columns.' # dbg | |
|
1553 | 1588 | else: |
|
1554 |
|
|
|
1589 | screen_lines += screen_lines_def | |
|
1555 | 1590 | |
|
1556 | 1591 | #print 'numlines',numlines,'screenlines',screen_lines # dbg |
|
1557 | 1592 | if numlines <= screen_lines : |
@@ -28,10 +28,30 b' class IPythonHistoryPanel(wx.Panel):' | |||
|
28 | 28 | self.filter_cmd = wx.CheckBox(self, -1, "!: Sys commands") |
|
29 | 29 | self.filter_magic = wx.CheckBox(self, -1, "%: Magic keys") |
|
30 | 30 | |
|
31 |
self.filter_empty |
|
|
32 | self.filter_doc.SetValue(flt_doc) | |
|
33 | self.filter_cmd.SetValue(flt_cmd) | |
|
34 | self.filter_magic.SetValue(flt_magic) | |
|
31 | self.options={'filter_empty':{'value':'True', | |
|
32 | 'checkbox':self.filter_empty,'True':True,'False':False, | |
|
33 | 'setfunc':lambda x:None}, | |
|
34 | 'filter_doc':{'value':'True', | |
|
35 | 'checkbox':self.filter_doc,'True':True,'False':False, | |
|
36 | 'setfunc':lambda x:None}, | |
|
37 | 'filter_cmd':{'value':'True', | |
|
38 | 'checkbox':self.filter_cmd,'True':True,'False':False, | |
|
39 | 'setfunc':lambda x:None}, | |
|
40 | 'filter_magic':{'value':'True', | |
|
41 | 'checkbox':self.filter_magic,'True':True,'False':False, | |
|
42 | 'setfunc':lambda x:None}, | |
|
43 | } | |
|
44 | self.reloadOptions(self.options) | |
|
45 | ||
|
46 | self.filter_empty.Bind(wx.EVT_CHECKBOX, self.evtCheckEmptyFilter) | |
|
47 | self.filter_doc.Bind(wx.EVT_CHECKBOX, self.evtCheckDocFilter) | |
|
48 | self.filter_cmd.Bind(wx.EVT_CHECKBOX, self.evtCheckCmdFilter) | |
|
49 | self.filter_magic.Bind(wx.EVT_CHECKBOX, self.evtCheckMagicFilter) | |
|
50 | ||
|
51 | #self.filter_empty.SetValue(flt_empty) | |
|
52 | #self.filter_doc.SetValue(flt_doc) | |
|
53 | #self.filter_cmd.SetValue(flt_cmd) | |
|
54 | #self.filter_magic.SetValue(flt_magic) | |
|
35 | 55 | |
|
36 | 56 | sizer = wx.BoxSizer(wx.VERTICAL) |
|
37 | 57 | |
@@ -70,10 +90,54 b' class IPythonHistoryPanel(wx.Panel):' | |||
|
70 | 90 | add = False |
|
71 | 91 | if self.filter_magic.GetValue() == True and history_line[0] == '%': |
|
72 | 92 | add = False |
|
73 |
|
|
|
74 |
|
|
|
93 | if add: | |
|
94 | self.text_ctrl.AppendText(history_line+'\n') | |
|
95 | ||
|
96 | #------------------------ Option Section ----------------------------------- | |
|
97 | def processOptionCheckedEvt(self, event, name): | |
|
98 | if event.IsChecked(): | |
|
99 | self.options[name]['value']='True' | |
|
100 | else: | |
|
101 | self.options[name]['value']='False' | |
|
102 | self.updateOptionTracker(name, | |
|
103 | self.options[name]['value']) | |
|
104 | ||
|
105 | def evtCheckEmptyFilter(self, event): | |
|
106 | self.processOptionCheckedEvt(event, 'filter_empty') | |
|
107 | ||
|
108 | def evtCheckDocFilter(self, event): | |
|
109 | self.processOptionCheckedEvt(event, 'filter_doc') | |
|
75 | 110 | |
|
111 | def evtCheckCmdFilter(self, event): | |
|
112 | self.processOptionCheckedEvt(event, 'filter_cmd') | |
|
76 | 113 | |
|
114 | def evtCheckMagicFilter(self, event): | |
|
115 | self.processOptionCheckedEvt(event, 'filter_magic') | |
|
116 | ||
|
117 | def getOptions(self): | |
|
118 | return self.options | |
|
119 | ||
|
120 | def reloadOptions(self,options): | |
|
121 | self.options = options | |
|
122 | for key in self.options.keys(): | |
|
123 | value = self.options[key]['value'] | |
|
124 | self.options[key]['checkbox'].SetValue(self.options[key][value]) | |
|
125 | self.options[key]['setfunc'](value) | |
|
126 | ||
|
127 | #------------------------ Hook Section ----------------------------------- | |
|
128 | def updateOptionTracker(self,name,value): | |
|
129 | ''' | |
|
130 | Default history tracker (does nothing) | |
|
131 | ''' | |
|
132 | pass | |
|
133 | ||
|
134 | def setOptionTrackerHook(self,func): | |
|
135 | ''' | |
|
136 | Define a new history tracker | |
|
137 | ''' | |
|
138 | self.updateOptionTracker = func | |
|
139 | ||
|
140 | ||
|
77 | 141 | #---------------------------------------------------------------------- |
|
78 | 142 | # Font definition for Styled Text Control |
|
79 | 143 |
This diff has been collapsed as it changes many lines, (1147 lines changed) Show them Hide them | |||
@@ -1,7 +1,7 b'' | |||
|
1 | 1 | #!/usr/bin/python |
|
2 | 2 | # -*- coding: iso-8859-15 -*- |
|
3 | 3 | ''' |
|
4 | Provides IPython WX console widget. | |
|
4 | Provides IPython WX console widgets. | |
|
5 | 5 | |
|
6 | 6 | @author: Laurent Dufrechou |
|
7 | 7 | laurent.dufrechou _at_ gmail.com |
@@ -26,433 +26,88 b' __license__ = "BSD"' | |||
|
26 | 26 | |
|
27 | 27 | import wx |
|
28 | 28 | import wx.stc as stc |
|
29 | import wx.lib.newevent | |
|
30 | 29 | |
|
31 | 30 | import re |
|
32 | 31 | import sys |
|
33 | import os | |
|
34 | 32 | import locale |
|
35 | import time | |
|
36 | from ThreadEx import Thread | |
|
37 | 33 | from StringIO import StringIO |
|
38 | ||
|
39 | 34 | try: |
|
40 | 35 | import IPython |
|
41 | 36 | except Exception,e: |
|
42 | 37 | raise "Error importing IPython (%s)" % str(e) |
|
43 | 38 | |
|
44 | class IterableIPShell(Thread): | |
|
39 | from ipshell_nonblocking import NonBlockingIPShell | |
|
40 | ||
|
41 | class WxNonBlockingIPShell(NonBlockingIPShell): | |
|
45 | 42 | ''' |
|
46 | Create an IPython instance inside a dedicated thread. | |
|
47 | Does not start a blocking event loop, instead allow single iterations. | |
|
48 | This allows embedding in any GUI without blockage. | |
|
49 | The thread is a slave one, in that it doesn't interact directly with the GUI. | |
|
50 | Note Thread class comes from ThreadEx that supports asynchroneous function call | |
|
51 | via raise_exc() | |
|
43 | An NonBlockingIPShell Thread that is WX dependent. | |
|
52 | 44 | ''' |
|
53 | ||
|
54 |
|
|
|
45 | def __init__(self, parent, | |
|
46 | argv=[],user_ns={},user_global_ns=None, | |
|
55 | 47 | cin=None, cout=None, cerr=None, |
|
56 |
exit_handler=None |
|
|
57 | ''' | |
|
58 | @param argv: Command line options for IPython | |
|
59 | @type argv: list | |
|
60 | @param user_ns: User namespace. | |
|
61 | @type user_ns: dictionary | |
|
62 | @param user_global_ns: User global namespace. | |
|
63 | @type user_global_ns: dictionary. | |
|
64 | @param cin: Console standard input. | |
|
65 | @type cin: IO stream | |
|
66 | @param cout: Console standard output. | |
|
67 | @type cout: IO stream | |
|
68 | @param cerr: Console standard error. | |
|
69 | @type cerr: IO stream | |
|
70 | @param exit_handler: Replacement for builtin exit() function | |
|
71 | @type exit_handler: function | |
|
72 | @param time_loop: Define the sleep time between two thread's loop | |
|
73 | @type int | |
|
74 | ''' | |
|
75 | Thread.__init__(self) | |
|
76 | ||
|
77 | #first we redefine in/out/error functions of IPython | |
|
78 | if cin: | |
|
79 | IPython.Shell.Term.cin = cin | |
|
80 | if cout: | |
|
81 | IPython.Shell.Term.cout = cout | |
|
82 | if cerr: | |
|
83 | IPython.Shell.Term.cerr = cerr | |
|
84 | ||
|
85 | # This is to get rid of the blockage that accurs during | |
|
86 | # IPython.Shell.InteractiveShell.user_setup() | |
|
87 | IPython.iplib.raw_input = lambda x: None | |
|
88 | ||
|
89 | self._term = IPython.genutils.IOTerm(cin=cin, cout=cout, cerr=cerr) | |
|
90 | ||
|
91 | excepthook = sys.excepthook | |
|
92 | self._IP = IPython.Shell.make_IPython( | |
|
93 | argv,user_ns=user_ns, | |
|
94 | user_global_ns=user_global_ns, | |
|
95 | embedded=True, | |
|
96 | shell_class=IPython.Shell.InteractiveShell) | |
|
97 | ||
|
98 | #we replace IPython default encoding by wx locale encoding | |
|
99 | loc = locale.getpreferredencoding() | |
|
100 | if loc: | |
|
101 | self._IP.stdin_encoding = loc | |
|
102 | #we replace the ipython default pager by our pager | |
|
103 | self._IP.set_hook('show_in_pager',self._pager) | |
|
104 | ||
|
105 | #we replace the ipython default shell command caller by our shell handler | |
|
106 | self._IP.set_hook('shell_hook',self._shell) | |
|
107 | ||
|
108 | #we replace the ipython default input command caller by our method | |
|
109 | IPython.iplib.raw_input_original = self._raw_input | |
|
110 | #we replace the ipython default exit command by our method | |
|
111 | self._IP.exit = self._setAskExit | |
|
112 | ||
|
113 | sys.excepthook = excepthook | |
|
114 | ||
|
115 | self._iter_more = 0 | |
|
116 | self._history_level = 0 | |
|
117 | self._complete_sep = re.compile('[\s\{\}\[\]\(\)]') | |
|
118 | self._prompt = str(self._IP.outputcache.prompt1).strip() | |
|
119 | ||
|
120 | #thread working vars | |
|
121 | self._terminate = False | |
|
122 | self._time_loop = time_loop | |
|
123 | self._has_doc = False | |
|
124 | self._do_execute = False | |
|
125 | self._line_to_execute = '' | |
|
126 | self._doc_text = None | |
|
127 | self._ask_exit = False | |
|
128 | ||
|
129 | #----------------------- Thread management section ---------------------- | |
|
130 | def run (self): | |
|
131 | """ | |
|
132 | Thread main loop | |
|
133 | The thread will run until self._terminate will be set to True via shutdown() function | |
|
134 | Command processing can be interrupted with Instance.raise_exc(KeyboardInterrupt) call in the | |
|
135 | GUI thread. | |
|
136 | """ | |
|
137 | while(not self._terminate): | |
|
138 | try: | |
|
139 | if self._do_execute: | |
|
140 | self._doc_text = None | |
|
141 | self._execute() | |
|
142 | self._do_execute = False | |
|
143 | ||
|
144 | except KeyboardInterrupt: | |
|
145 | pass | |
|
146 | ||
|
147 | time.sleep(self._time_loop) | |
|
148 | ||
|
149 | def shutdown(self): | |
|
150 | """ | |
|
151 | Shutdown the tread | |
|
152 | """ | |
|
153 | self._terminate = True | |
|
154 | ||
|
155 | def doExecute(self,line): | |
|
156 | """ | |
|
157 | Tell the thread to process the 'line' command | |
|
158 | """ | |
|
159 | self._do_execute = True | |
|
160 | self._line_to_execute = line | |
|
161 | ||
|
162 | def isExecuteDone(self): | |
|
163 | """ | |
|
164 | Returns the processing state | |
|
165 | """ | |
|
166 | return not self._do_execute | |
|
167 | ||
|
168 | #----------------------- IPython management section ---------------------- | |
|
169 | def getAskExit(self): | |
|
170 | ''' | |
|
171 | returns the _ask_exit variable that can be checked by GUI to see if | |
|
172 | IPython request an exit handling | |
|
173 | ''' | |
|
174 | return self._ask_exit | |
|
175 | ||
|
176 | def clearAskExit(self): | |
|
177 | ''' | |
|
178 | clear the _ask_exit var when GUI as handled the request. | |
|
179 | ''' | |
|
180 | self._ask_exit = False | |
|
181 | ||
|
182 | def getDocText(self): | |
|
183 | """ | |
|
184 | Returns the output of the processing that need to be paged (if any) | |
|
185 | ||
|
186 | @return: The std output string. | |
|
187 | @rtype: string | |
|
188 | """ | |
|
189 | return self._doc_text | |
|
190 | ||
|
191 | def getBanner(self): | |
|
192 | """ | |
|
193 | Returns the IPython banner for useful info on IPython instance | |
|
194 | ||
|
195 | @return: The banner string. | |
|
196 | @rtype: string | |
|
197 | """ | |
|
198 | return self._IP.BANNER | |
|
199 | ||
|
200 | def getPromptCount(self): | |
|
201 | """ | |
|
202 | Returns the prompt number. | |
|
203 | Each time a user execute a line in the IPython shell the prompt count is increased | |
|
204 | ||
|
205 | @return: The prompt number | |
|
206 | @rtype: int | |
|
207 | """ | |
|
208 | return self._IP.outputcache.prompt_count | |
|
209 | ||
|
210 | def getPrompt(self): | |
|
211 | """ | |
|
212 | Returns current prompt inside IPython instance | |
|
213 | (Can be In [...]: ot ...:) | |
|
214 | ||
|
215 | @return: The current prompt. | |
|
216 | @rtype: string | |
|
217 | """ | |
|
218 | return self._prompt | |
|
219 | ||
|
220 | def getIndentation(self): | |
|
221 | """ | |
|
222 | Returns the current indentation level | |
|
223 | Usefull to put the caret at the good start position if we want to do autoindentation. | |
|
224 | ||
|
225 | @return: The indentation level. | |
|
226 | @rtype: int | |
|
227 | """ | |
|
228 | return self._IP.indent_current_nsp | |
|
48 | ask_exit_handler=None): | |
|
229 | 49 | |
|
230 | def updateNamespace(self, ns_dict): | |
|
231 | ''' | |
|
232 | Add the current dictionary to the shell namespace. | |
|
50 | NonBlockingIPShell.__init__(self,argv,user_ns,user_global_ns, | |
|
51 | cin, cout, cerr, | |
|
52 | ask_exit_handler) | |
|
233 | 53 | |
|
234 | @param ns_dict: A dictionary of symbol-values. | |
|
235 | @type ns_dict: dictionary | |
|
236 | ''' | |
|
237 | self._IP.user_ns.update(ns_dict) | |
|
54 | self.parent = parent | |
|
238 | 55 | |
|
239 | def complete(self, line): | |
|
240 | ''' | |
|
241 | Returns an auto completed line and/or posibilities for completion. | |
|
56 | self.ask_exit_callback = ask_exit_handler | |
|
57 | self._IP.exit = self._askExit | |
|
242 | 58 | |
|
243 | @param line: Given line so far. | |
|
244 | @type line: string | |
|
59 | def addGUIShortcut(self,text,func): | |
|
60 | wx.CallAfter(self.parent.add_button_handler, | |
|
61 | button_info={ 'text':text, | |
|
62 | 'func':self.parent.doExecuteLine(func)}) | |
|
245 | 63 | |
|
246 | @return: Line completed as for as possible, | |
|
247 | and possible further completions. | |
|
248 | @rtype: tuple | |
|
249 | ''' | |
|
250 | split_line = self._complete_sep.split(line) | |
|
251 | possibilities = self._IP.complete(split_line[-1]) | |
|
252 | if possibilities: | |
|
253 | ||
|
254 | def _commonPrefix(str1, str2): | |
|
255 | ''' | |
|
256 | Reduction function. returns common prefix of two given strings. | |
|
257 | ||
|
258 | @param str1: First string. | |
|
259 | @type str1: string | |
|
260 | @param str2: Second string | |
|
261 | @type str2: string | |
|
262 | ||
|
263 | @return: Common prefix to both strings. | |
|
264 | @rtype: string | |
|
265 | ''' | |
|
266 | for i in range(len(str1)): | |
|
267 | if not str2.startswith(str1[:i+1]): | |
|
268 | return str1[:i] | |
|
269 | return str1 | |
|
270 | common_prefix = reduce(_commonPrefix, possibilities) | |
|
271 | completed = line[:-len(split_line[-1])]+common_prefix | |
|
272 | else: | |
|
273 | completed = line | |
|
274 | return completed, possibilities | |
|
64 | def _askExit(self): | |
|
65 | wx.CallAfter(self.ask_exit_callback, ()) | |
|
275 | 66 | |
|
276 |
def |
|
|
277 | ''' | |
|
278 | Provides one history command back. | |
|
67 | def _afterExecute(self): | |
|
68 | wx.CallAfter(self.parent.evtStateExecuteDone, ()) | |
|
279 | 69 | |
|
280 | @return: The command string. | |
|
281 | @rtype: string | |
|
282 | ''' | |
|
283 | history = '' | |
|
284 | #the below while loop is used to suppress empty history lines | |
|
285 | while((history == '' or history == '\n') and self._history_level >0): | |
|
286 | if self._history_level>=1: | |
|
287 | self._history_level -= 1 | |
|
288 | history = self._getHistory() | |
|
289 | return history | |
|
290 | ||
|
291 | def historyForward(self): | |
|
292 | ''' | |
|
293 | Provides one history command forward. | |
|
294 | ||
|
295 | @return: The command string. | |
|
296 | @rtype: string | |
|
297 | ''' | |
|
298 | history = '' | |
|
299 | #the below while loop is used to suppress empty history lines | |
|
300 | while((history == '' or history == '\n') and self._history_level <= self._getHistoryMaxIndex()): | |
|
301 | if self._history_level < self._getHistoryMaxIndex(): | |
|
302 | self._history_level += 1 | |
|
303 | history = self._getHistory() | |
|
304 | else: | |
|
305 | if self._history_level == self._getHistoryMaxIndex(): | |
|
306 | history = self._getHistory() | |
|
307 | self._history_level += 1 | |
|
308 | else: | |
|
309 | history = '' | |
|
310 | return history | |
|
311 | ||
|
312 | def initHistoryIndex(self): | |
|
313 | ''' | |
|
314 | set history to last command entered | |
|
315 | ''' | |
|
316 | self._history_level = self._getHistoryMaxIndex()+1 | |
|
317 | ||
|
318 | #----------------------- IPython PRIVATE management section ---------------------- | |
|
319 | def _setAskExit(self): | |
|
320 | ''' | |
|
321 | set the _ask_exit variable that can be cjhecked by GUI to see if | |
|
322 | IPython request an exit handling | |
|
323 | ''' | |
|
324 | self._ask_exit = True | |
|
325 | ||
|
326 | def _getHistoryMaxIndex(self): | |
|
327 | ''' | |
|
328 | returns the max length of the history buffer | |
|
329 | ||
|
330 | @return: history length | |
|
331 | @rtype: int | |
|
332 | ''' | |
|
333 | return len(self._IP.input_hist_raw)-1 | |
|
334 | ||
|
335 | def _getHistory(self): | |
|
336 | ''' | |
|
337 | Get's the command string of the current history level. | |
|
338 | ||
|
339 | @return: Historic command string. | |
|
340 | @rtype: string | |
|
341 | ''' | |
|
342 | rv = self._IP.input_hist_raw[self._history_level].strip('\n') | |
|
343 | return rv | |
|
344 | ||
|
345 | def _pager(self,IP,text): | |
|
346 | ''' | |
|
347 | This function is used as a callback replacment to IPython pager function | |
|
348 | ||
|
349 | It puts the 'text' value inside the self._doc_text string that can be retrived via getDocText | |
|
350 | function. | |
|
351 | ''' | |
|
352 | self._doc_text = text | |
|
353 | ||
|
354 | def _raw_input(self, prompt=''): | |
|
355 | ''' | |
|
356 | Custom raw_input() replacement. Get's current line from console buffer. | |
|
357 | ||
|
358 | @param prompt: Prompt to print. Here for compatability as replacement. | |
|
359 | @type prompt: string | |
|
360 | ||
|
361 | @return: The current command line text. | |
|
362 | @rtype: string | |
|
363 | ''' | |
|
364 | return self._line_to_execute | |
|
365 | ||
|
366 | def _execute(self): | |
|
367 | ''' | |
|
368 | Executes the current line provided by the shell object. | |
|
369 | ''' | |
|
370 | orig_stdout = sys.stdout | |
|
371 | sys.stdout = IPython.Shell.Term.cout | |
|
372 | 70 | |
|
373 | try: | |
|
374 | line = self._IP.raw_input(None, self._iter_more) | |
|
375 | if self._IP.autoindent: | |
|
376 | self._IP.readline_startup_hook(None) | |
|
377 | ||
|
378 | except KeyboardInterrupt: | |
|
379 | self._IP.write('\nKeyboardInterrupt\n') | |
|
380 | self._IP.resetbuffer() | |
|
381 | # keep cache in sync with the prompt counter: | |
|
382 | self._IP.outputcache.prompt_count -= 1 | |
|
383 | ||
|
384 | if self._IP.autoindent: | |
|
385 | self._IP.indent_current_nsp = 0 | |
|
386 | self._iter_more = 0 | |
|
387 | except: | |
|
388 | self._IP.showtraceback() | |
|
389 | else: | |
|
390 | self._iter_more = self._IP.push(line) | |
|
391 | if (self._IP.SyntaxTB.last_syntax_error and | |
|
392 | self._IP.rc.autoedit_syntax): | |
|
393 | self._IP.edit_syntax_error() | |
|
394 | if self._iter_more: | |
|
395 | self._prompt = str(self._IP.outputcache.prompt2).strip() | |
|
396 | if self._IP.autoindent: | |
|
397 | self._IP.readline_startup_hook(self._IP.pre_readline) | |
|
398 | else: | |
|
399 | self._prompt = str(self._IP.outputcache.prompt1).strip() | |
|
400 | self._IP.indent_current_nsp = 0 #we set indentation to 0 | |
|
401 | sys.stdout = orig_stdout | |
|
402 | ||
|
403 | def _shell(self, ip, cmd): | |
|
404 | ''' | |
|
405 | Replacement method to allow shell commands without them blocking. | |
|
406 | ||
|
407 | @param ip: Ipython instance, same as self._IP | |
|
408 | @type cmd: Ipython instance | |
|
409 | @param cmd: Shell command to execute. | |
|
410 | @type cmd: string | |
|
411 | ''' | |
|
412 | stdin, stdout = os.popen4(cmd) | |
|
413 | result = stdout.read().decode('cp437').encode(locale.getpreferredencoding()) | |
|
414 | #we use print command because the shell command is called inside IPython instance and thus is | |
|
415 | #redirected to thread cout | |
|
416 | #"\x01\x1b[1;36m\x02" <-- add colour to the text... | |
|
417 | print "\x01\x1b[1;36m\x02"+result | |
|
418 | stdout.close() | |
|
419 | stdin.close() | |
|
420 | ||
|
421 | 71 | class WxConsoleView(stc.StyledTextCtrl): |
|
422 | 72 | ''' |
|
423 | 73 | Specialized styled text control view for console-like workflow. |
|
424 |
We use here a scintilla frontend thus it can be reused in any GUI t |
|
|
425 | scintilla with less work. | |
|
74 | We use here a scintilla frontend thus it can be reused in any GUI that | |
|
75 | supports scintilla with less work. | |
|
426 | 76 | |
|
427 |
@cvar ANSI_COLORS_BLACK: Mapping of terminal colors to X11 names. |
|
|
77 | @cvar ANSI_COLORS_BLACK: Mapping of terminal colors to X11 names. | |
|
78 | (with Black background) | |
|
428 | 79 | @type ANSI_COLORS_BLACK: dictionary |
|
429 | 80 | |
|
430 |
@cvar ANSI_COLORS_WHITE: Mapping of terminal colors to X11 names. |
|
|
81 | @cvar ANSI_COLORS_WHITE: Mapping of terminal colors to X11 names. | |
|
82 | (with White background) | |
|
431 | 83 | @type ANSI_COLORS_WHITE: dictionary |
|
432 | 84 | |
|
433 | 85 | @ivar color_pat: Regex of terminal color pattern |
|
434 | 86 | @type color_pat: _sre.SRE_Pattern |
|
435 | 87 | ''' |
|
436 |
ANSI_STYLES_BLACK |
|
|
437 |
|
|
|
438 |
|
|
|
439 |
|
|
|
440 |
|
|
|
441 |
|
|
|
442 |
|
|
|
443 | '1;36': [14,'LIGHT STEEL BLUE'], '1;37': [15,'YELLOW']} | |
|
444 | ||
|
445 | ANSI_STYLES_WHITE ={'0;30': [0,'BLACK'], '0;31': [1,'RED'], | |
|
446 | '0;32': [2,'GREEN'], '0;33': [3,'BROWN'], | |
|
447 |
|
|
|
448 |
|
|
|
449 |
|
|
|
450 |
|
|
|
451 |
|
|
|
452 |
|
|
|
453 | ||
|
454 | def __init__(self,parent,prompt,intro="",background_color="BLACK",pos=wx.DefaultPosition, ID = -1, size=wx.DefaultSize, | |
|
455 | style=0): | |
|
88 | ANSI_STYLES_BLACK={'0;30': [0,'WHITE'], '0;31': [1,'RED'], | |
|
89 | '0;32': [2,'GREEN'], '0;33': [3,'BROWN'], | |
|
90 | '0;34': [4,'BLUE'], '0;35': [5,'PURPLE'], | |
|
91 | '0;36': [6,'CYAN'], '0;37': [7,'LIGHT GREY'], | |
|
92 | '1;30': [8,'DARK GREY'], '1;31': [9,'RED'], | |
|
93 | '1;32': [10,'SEA GREEN'], '1;33': [11,'YELLOW'], | |
|
94 | '1;34': [12,'LIGHT BLUE'], '1;35': | |
|
95 | [13,'MEDIUM VIOLET RED'], | |
|
96 | '1;36': [14,'LIGHT STEEL BLUE'],'1;37': [15,'YELLOW']} | |
|
97 | ||
|
98 | ANSI_STYLES_WHITE={'0;30': [0,'BLACK'], '0;31': [1,'RED'], | |
|
99 | '0;32': [2,'GREEN'], '0;33': [3,'BROWN'], | |
|
100 | '0;34': [4,'BLUE'], '0;35': [5,'PURPLE'], | |
|
101 | '0;36': [6,'CYAN'], '0;37': [7,'LIGHT GREY'], | |
|
102 | '1;30': [8,'DARK GREY'], '1;31': [9,'RED'], | |
|
103 | '1;32': [10,'SEA GREEN'], '1;33': [11,'YELLOW'], | |
|
104 | '1;34': [12,'LIGHT BLUE'], '1;35': | |
|
105 | [13,'MEDIUM VIOLET RED'], | |
|
106 | '1;36': [14,'LIGHT STEEL BLUE'],'1;37': [15,'YELLOW']} | |
|
107 | ||
|
108 | def __init__(self,parent,prompt,intro="",background_color="BLACK", | |
|
109 | pos=wx.DefaultPosition, ID = -1, size=wx.DefaultSize, | |
|
110 | style=0, autocomplete_mode = 'IPYTHON'): | |
|
456 | 111 | ''' |
|
457 | 112 | Initialize console view. |
|
458 | 113 | |
@@ -464,15 +119,61 b' class WxConsoleView(stc.StyledTextCtrl):' | |||
|
464 | 119 | @param background_color: Can be BLACK or WHITE |
|
465 | 120 | @type background_color: string |
|
466 | 121 | @param other: init param of styledTextControl (can be used as-is) |
|
122 | @param autocomplete_mode: Can be 'IPYTHON' or 'STC' | |
|
123 | 'IPYTHON' show autocompletion the ipython way | |
|
124 | 'STC" show it scintilla text control way | |
|
467 | 125 | ''' |
|
468 | 126 | stc.StyledTextCtrl.__init__(self, parent, ID, pos, size, style) |
|
469 | 127 | |
|
470 |
####### Scintilla configuration ################################### |
|
|
128 | ####### Scintilla configuration ################################### | |
|
471 | 129 | |
|
472 |
# Ctrl + B or Ctrl + N can be used to zoomin/zoomout the text inside |
|
|
130 | # Ctrl + B or Ctrl + N can be used to zoomin/zoomout the text inside | |
|
131 | # the widget | |
|
473 | 132 | self.CmdKeyAssign(ord('B'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN) |
|
474 | 133 | self.CmdKeyAssign(ord('N'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT) |
|
475 | 134 | |
|
135 | #We draw a line at position 80 | |
|
136 | self.SetEdgeMode(stc.STC_EDGE_LINE) | |
|
137 | self.SetEdgeColumn(80) | |
|
138 | self.SetEdgeColour(wx.LIGHT_GREY) | |
|
139 | ||
|
140 | #self.SetViewWhiteSpace(True) | |
|
141 | #self.SetViewEOL(True) | |
|
142 | self.SetEOLMode(stc.STC_EOL_CRLF) | |
|
143 | #self.SetWrapMode(stc.STC_WRAP_CHAR) | |
|
144 | #self.SetWrapMode(stc.STC_WRAP_WORD) | |
|
145 | self.SetBufferedDraw(True) | |
|
146 | #self.SetUseAntiAliasing(True) | |
|
147 | self.SetLayoutCache(stc.STC_CACHE_PAGE) | |
|
148 | self.SetUndoCollection(False) | |
|
149 | self.SetUseTabs(True) | |
|
150 | self.SetIndent(4) | |
|
151 | self.SetTabWidth(4) | |
|
152 | ||
|
153 | self.EnsureCaretVisible() | |
|
154 | ||
|
155 | self.SetMargins(3,3) #text is moved away from border with 3px | |
|
156 | # Suppressing Scintilla margins | |
|
157 | self.SetMarginWidth(0,0) | |
|
158 | self.SetMarginWidth(1,0) | |
|
159 | self.SetMarginWidth(2,0) | |
|
160 | ||
|
161 | self.background_color = background_color | |
|
162 | self.buildStyles() | |
|
163 | ||
|
164 | self.indent = 0 | |
|
165 | self.prompt_count = 0 | |
|
166 | self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?') | |
|
167 | ||
|
168 | self.write(intro) | |
|
169 | self.setPrompt(prompt) | |
|
170 | self.showPrompt() | |
|
171 | ||
|
172 | self.autocomplete_mode = autocomplete_mode | |
|
173 | ||
|
174 | self.Bind(wx.EVT_KEY_DOWN, self._onKeypress) | |
|
175 | ||
|
176 | def buildStyles(self): | |
|
476 | 177 | #we define platform specific fonts |
|
477 | 178 | if wx.Platform == '__WXMSW__': |
|
478 | 179 | faces = { 'times': 'Times New Roman', |
@@ -499,61 +200,64 b' class WxConsoleView(stc.StyledTextCtrl):' | |||
|
499 | 200 | 'size2': 8, |
|
500 | 201 | } |
|
501 | 202 | |
|
502 | #We draw a line at position 80 | |
|
503 | self.SetEdgeMode(stc.STC_EDGE_LINE) | |
|
504 | self.SetEdgeColumn(80) | |
|
505 | self.SetEdgeColour(wx.LIGHT_GREY) | |
|
506 | ||
|
507 | #self.SetViewWhiteSpace(True) | |
|
508 | #self.SetViewEOL(True) | |
|
509 | self.SetEOLMode(stc.STC_EOL_CRLF) | |
|
510 | #self.SetWrapMode(stc.STC_WRAP_CHAR) | |
|
511 | #self.SetWrapMode(stc.STC_WRAP_WORD) | |
|
512 | self.SetBufferedDraw(True) | |
|
513 | #self.SetUseAntiAliasing(True) | |
|
514 | self.SetLayoutCache(stc.STC_CACHE_PAGE) | |
|
515 | ||
|
516 | self.EnsureCaretVisible() | |
|
517 | ||
|
518 | self.SetMargins(3,3) #text is moved away from border with 3px | |
|
519 | # Suppressing Scintilla margins | |
|
520 | self.SetMarginWidth(0,0) | |
|
521 | self.SetMarginWidth(1,0) | |
|
522 | self.SetMarginWidth(2,0) | |
|
523 | ||
|
524 | 203 | # make some styles |
|
525 | if background_color != "BLACK": | |
|
204 | if self.background_color != "BLACK": | |
|
526 | 205 | self.background_color = "WHITE" |
|
527 | 206 | self.SetCaretForeground("BLACK") |
|
528 | 207 | self.ANSI_STYLES = self.ANSI_STYLES_WHITE |
|
529 | 208 | else: |
|
530 | self.background_color = background_color | |
|
531 | 209 | self.SetCaretForeground("WHITE") |
|
532 | 210 | self.ANSI_STYLES = self.ANSI_STYLES_BLACK |
|
533 | 211 | |
|
534 |
self.StyleSetSpec(stc.STC_STYLE_DEFAULT, |
|
|
535 | self.background_color, | |
|
536 | faces['size'], faces['mono'])) | |
|
212 | self.StyleSetSpec(stc.STC_STYLE_DEFAULT, | |
|
213 | "fore:%s,back:%s,size:%d,face:%s" | |
|
214 | % (self.ANSI_STYLES['0;30'][1], | |
|
215 | self.background_color, | |
|
216 | faces['size'], faces['mono'])) | |
|
537 | 217 | self.StyleClearAll() |
|
538 |
self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, |
|
|
539 | self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, "fore:#000000,back:#FF0000,bold") | |
|
540 | ||
|
218 | self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, | |
|
219 | "fore:#FF0000,back:#0000FF,bold") | |
|
220 | self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, | |
|
221 | "fore:#000000,back:#FF0000,bold") | |
|
222 | ||
|
541 | 223 | for style in self.ANSI_STYLES.values(): |
|
542 | 224 | self.StyleSetSpec(style[0], "bold,fore:%s" % style[1]) |
|
543 | 225 | |
|
544 | 226 | ####################################################################### |
|
545 | 227 | |
|
546 | self.indent = 0 | |
|
547 |
self. |
|
|
548 | self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?') | |
|
228 | def setBackgroundColor(self,color): | |
|
229 | self.background_color = color | |
|
230 | self.buildStyles() | |
|
231 | ||
|
232 | def getBackgroundColor(self,color): | |
|
233 | return self.background_color | |
|
549 | 234 | |
|
550 | self.write(intro) | |
|
551 | self.setPrompt(prompt) | |
|
552 | self.showPrompt() | |
|
235 | def asyncWrite(self, text): | |
|
236 | ''' | |
|
237 | Write given text to buffer in an asynchroneous way. | |
|
238 | It is used from another thread to be able to acces the GUI. | |
|
239 | @param text: Text to append | |
|
240 | @type text: string | |
|
241 | ''' | |
|
242 | try: | |
|
243 | #print >>sys.__stdout__,'entering' | |
|
244 | wx.MutexGuiEnter() | |
|
245 | #print >>sys.__stdout__,'locking the GUI' | |
|
246 | ||
|
247 | #be sure not to be interrutpted before the MutexGuiLeave! | |
|
248 | self.write(text) | |
|
249 | ||
|
250 | #print >>sys.__stdout__,'done' | |
|
251 | ||
|
252 | except KeyboardInterrupt: | |
|
253 | #print >>sys.__stdout__,'got keyboard interrupt' | |
|
254 | wx.MutexGuiLeave() | |
|
255 | #print >>sys.__stdout__,'interrupt unlock the GUI' | |
|
256 | raise KeyboardInterrupt | |
|
257 | wx.MutexGuiLeave() | |
|
258 | #print >>sys.__stdout__,'normal unlock the GUI' | |
|
553 | 259 | |
|
554 | self.Bind(wx.EVT_KEY_DOWN, self._onKeypress, self) | |
|
555 | #self.Bind(stc.EVT_STC_UPDATEUI, self.OnUpdateUI) | |
|
556 | ||
|
260 | ||
|
557 | 261 | def write(self, text): |
|
558 | 262 | ''' |
|
559 | 263 | Write given text to buffer. |
@@ -641,18 +345,6 b' class WxConsoleView(stc.StyledTextCtrl):' | |||
|
641 | 345 | return self.GetTextRange(self.getCurrentPromptStart(), |
|
642 | 346 | self.getCurrentLineEnd()) |
|
643 | 347 | |
|
644 | def showReturned(self, text): | |
|
645 | ''' | |
|
646 | Show returned text from last command and print new prompt. | |
|
647 | ||
|
648 | @param text: Text to show. | |
|
649 | @type text: string | |
|
650 | ''' | |
|
651 | self.write('\n'+text) | |
|
652 | if text: | |
|
653 | self.write('\n') | |
|
654 | self.showPrompt() | |
|
655 | ||
|
656 | 348 | def moveCursorOnNewValidKey(self): |
|
657 | 349 | #If cursor is at wrong position put it at last line... |
|
658 | 350 | if self.GetCurrentPos() < self.getCurrentPromptStart(): |
@@ -679,33 +371,53 b' class WxConsoleView(stc.StyledTextCtrl):' | |||
|
679 | 371 | def writeHistory(self,history): |
|
680 | 372 | self.removeFromTo(self.getCurrentPromptStart(),self.getCurrentLineEnd()) |
|
681 | 373 | self.changeLine(history) |
|
374 | ||
|
375 | def setCompletionMethod(self, completion): | |
|
376 | if completion in ['IPYTHON','STC']: | |
|
377 | self.autocomplete_mode = completion | |
|
378 | else: | |
|
379 | raise AttributeError | |
|
380 | ||
|
381 | def getCompletionMethod(self, completion): | |
|
382 | return self.autocomplete_mode | |
|
682 | 383 | |
|
683 | 384 | def writeCompletion(self, possibilities): |
|
684 | max_len = len(max(possibilities,key=len)) | |
|
685 | max_symbol =' '*max_len | |
|
686 | ||
|
687 | #now we check how much symbol we can put on a line... | |
|
688 | cursor_pos = self.getCursorPos() | |
|
689 | test_buffer = max_symbol + ' '*4 | |
|
690 | current_lines = self.GetLineCount() | |
|
691 | ||
|
692 | allowed_symbols = 80/len(test_buffer) | |
|
693 |
|
|
|
694 |
|
|
|
385 | if self.autocomplete_mode == 'IPYTHON': | |
|
386 | max_len = len(max(possibilities,key=len)) | |
|
387 | max_symbol =' '*max_len | |
|
388 | ||
|
389 | #now we check how much symbol we can put on a line... | |
|
390 | cursor_pos = self.getCursorPos() | |
|
391 | test_buffer = max_symbol + ' '*4 | |
|
392 | current_lines = self.GetLineCount() | |
|
393 | ||
|
394 | allowed_symbols = 80/len(test_buffer) | |
|
395 | if allowed_symbols == 0: | |
|
396 | allowed_symbols = 1 | |
|
397 | ||
|
398 | pos = 1 | |
|
399 | buf = '' | |
|
400 | for symbol in possibilities: | |
|
401 | #buf += symbol+'\n'#*spaces) | |
|
402 | if pos<allowed_symbols: | |
|
403 | spaces = max_len - len(symbol) + 4 | |
|
404 | buf += symbol+' '*spaces | |
|
405 | pos += 1 | |
|
406 | else: | |
|
407 | buf+=symbol+'\n' | |
|
408 | pos = 1 | |
|
409 | self.write(buf) | |
|
410 | else: | |
|
411 | possibilities.sort() # Python sorts are case sensitive | |
|
412 | self.AutoCompSetIgnoreCase(False) | |
|
413 | self.AutoCompSetAutoHide(False) | |
|
414 | #let compute the length ot last word | |
|
415 | splitter = [' ','(','[','{'] | |
|
416 | last_word = self.getCurrentLine() | |
|
417 | for breaker in splitter: | |
|
418 | last_word = last_word.split(breaker)[-1] | |
|
419 | self.AutoCompShow(len(last_word), " ".join(possibilities)) | |
|
695 | 420 | |
|
696 | pos = 1 | |
|
697 | buf = '' | |
|
698 | for symbol in possibilities: | |
|
699 | #buf += symbol+'\n'#*spaces) | |
|
700 | if pos<allowed_symbols: | |
|
701 | spaces = max_len - len(symbol) + 4 | |
|
702 | buf += symbol+' '*spaces | |
|
703 | pos += 1 | |
|
704 | else: | |
|
705 | buf+=symbol+'\n' | |
|
706 | pos = 1 | |
|
707 | self.write(buf) | |
|
708 | ||
|
709 | 421 | def _onKeypress(self, event, skip=True): |
|
710 | 422 | ''' |
|
711 | 423 | Key press callback used for correcting behavior for console-like |
@@ -720,42 +432,45 b' class WxConsoleView(stc.StyledTextCtrl):' | |||
|
720 | 432 | @return: Return True if event as been catched. |
|
721 | 433 | @rtype: boolean |
|
722 | 434 | ''' |
|
723 | ||
|
724 | if event.GetKeyCode() == wx.WXK_HOME: | |
|
725 | if event.Modifiers == wx.MOD_NONE: | |
|
726 | self.moveCursorOnNewValidKey() | |
|
727 | self.moveCursor(self.getCurrentPromptStart()) | |
|
728 | return True | |
|
729 | elif event.Modifiers == wx.MOD_SHIFT: | |
|
730 | self.moveCursorOnNewValidKey() | |
|
731 | self.selectFromTo(self.getCurrentPromptStart(),self.getCursorPos()) | |
|
732 | return True | |
|
733 | else: | |
|
734 | return False | |
|
735 | 435 | |
|
736 | elif event.GetKeyCode() == wx.WXK_LEFT: | |
|
737 |
if event. |
|
|
738 | self.moveCursorOnNewValidKey() | |
|
739 | ||
|
740 | self.moveCursor(self.getCursorPos()-1) | |
|
741 | if self.getCursorPos() < self.getCurrentPromptStart(): | |
|
436 | if not self.AutoCompActive(): | |
|
437 | if event.GetKeyCode() == wx.WXK_HOME: | |
|
438 | if event.Modifiers == wx.MOD_NONE: | |
|
439 | self.moveCursorOnNewValidKey() | |
|
742 | 440 | self.moveCursor(self.getCurrentPromptStart()) |
|
743 | return True | |
|
744 | ||
|
745 | elif event.GetKeyCode() == wx.WXK_BACK: | |
|
746 | self.moveCursorOnNewValidKey() | |
|
747 | if self.getCursorPos() > self.getCurrentPromptStart(): | |
|
748 | self.removeFromTo(self.getCursorPos()-1,self.getCursorPos()) | |
|
749 | return True | |
|
750 | ||
|
751 | if skip: | |
|
752 | if event.GetKeyCode() not in [wx.WXK_PAGEUP,wx.WXK_PAGEDOWN] and event.Modifiers == wx.MOD_NONE: | |
|
441 | return True | |
|
442 | elif event.Modifiers == wx.MOD_SHIFT: | |
|
443 | self.moveCursorOnNewValidKey() | |
|
444 | self.selectFromTo(self.getCurrentPromptStart(),self.getCursorPos()) | |
|
445 | return True | |
|
446 | else: | |
|
447 | return False | |
|
448 | ||
|
449 | elif event.GetKeyCode() == wx.WXK_LEFT: | |
|
450 | if event.Modifiers == wx.MOD_NONE: | |
|
451 | self.moveCursorOnNewValidKey() | |
|
452 | ||
|
453 | self.moveCursor(self.getCursorPos()-1) | |
|
454 | if self.getCursorPos() < self.getCurrentPromptStart(): | |
|
455 | self.moveCursor(self.getCurrentPromptStart()) | |
|
456 | return True | |
|
457 | ||
|
458 | elif event.GetKeyCode() == wx.WXK_BACK: | |
|
753 | 459 | self.moveCursorOnNewValidKey() |
|
754 | ||
|
460 | if self.getCursorPos() > self.getCurrentPromptStart(): | |
|
461 | event.Skip() | |
|
462 | return True | |
|
463 | ||
|
464 | if skip: | |
|
465 | if event.GetKeyCode() not in [wx.WXK_PAGEUP,wx.WXK_PAGEDOWN] and event.Modifiers == wx.MOD_NONE: | |
|
466 | self.moveCursorOnNewValidKey() | |
|
467 | ||
|
468 | event.Skip() | |
|
469 | return True | |
|
470 | return False | |
|
471 | else: | |
|
755 | 472 | event.Skip() |
|
756 |
|
|
|
757 | return False | |
|
758 | ||
|
473 | ||
|
759 | 474 | def OnUpdateUI(self, evt): |
|
760 | 475 | # check for matching braces |
|
761 | 476 | braceAtCaret = -1 |
@@ -791,49 +506,90 b' class WxConsoleView(stc.StyledTextCtrl):' | |||
|
791 | 506 | #print pt |
|
792 | 507 | #self.Refresh(False) |
|
793 | 508 | |
|
794 |
class |
|
|
509 | class IPShellWidget(wx.Panel): | |
|
795 | 510 | ''' |
|
796 | 511 | This is wx.Panel that embbed the IPython Thread and the wx.StyledTextControl |
|
797 |
If you want to port this to any other GUI toolkit, just replace the |
|
|
798 |
by YOURGUIConsoleView and make YOURGUIIPythonView derivate |
|
|
799 | I've choosed to derivate from a wx.Panel because it seems to be ore usefull | |
|
800 | Any idea to make it more 'genric' welcomed. | |
|
512 | If you want to port this to any other GUI toolkit, just replace the | |
|
513 | WxConsoleView by YOURGUIConsoleView and make YOURGUIIPythonView derivate | |
|
514 | from whatever container you want. I've choosed to derivate from a wx.Panel | |
|
515 | because it seems to be more useful | |
|
516 | Any idea to make it more 'generic' welcomed. | |
|
801 | 517 | ''' |
|
802 | def __init__(self,parent,exit_handler=None,intro=None,background_color="BLACK"): | |
|
518 | ||
|
519 | def __init__(self, parent, intro=None, | |
|
520 | background_color="BLACK", add_button_handler=None, | |
|
521 | wx_ip_shell=None, user_ns={},user_global_ns=None, | |
|
522 | ): | |
|
803 | 523 | ''' |
|
804 | 524 | Initialize. |
|
805 | 525 | Instanciate an IPython thread. |
|
806 | 526 | Instanciate a WxConsoleView. |
|
807 | 527 | Redirect I/O to console. |
|
808 | 528 | ''' |
|
809 |
wx.Panel.__init__(self,parent, |
|
|
529 | wx.Panel.__init__(self,parent,wx.ID_ANY) | |
|
810 | 530 | |
|
811 | ### IPython thread instanciation ### | |
|
531 | self.parent = parent | |
|
532 | ### IPython non blocking shell instanciation ### | |
|
812 | 533 | self.cout = StringIO() |
|
813 | self.IP = IterableIPShell(cout=self.cout,cerr=self.cout, | |
|
814 | exit_handler = exit_handler, | |
|
815 | time_loop = 0.1) | |
|
816 | self.IP.start() | |
|
817 | ||
|
534 | self.add_button_handler = add_button_handler | |
|
535 | ||
|
536 | if wx_ip_shell is not None: | |
|
537 | self.IP = wx_ip_shell | |
|
538 | else: | |
|
539 | self.IP = WxNonBlockingIPShell(self, | |
|
540 | cout = self.cout, cerr = self.cout, | |
|
541 | ask_exit_handler = self.askExitCallback) | |
|
542 | ||
|
818 | 543 | ### IPython wx console view instanciation ### |
|
819 | 544 | #If user didn't defined an intro text, we create one for him |
|
820 |
#If you really wnat an empty intr |
|
|
821 |
|
|
|
545 | #If you really wnat an empty intro just call wxIPythonViewPanel | |
|
546 | #with intro='' | |
|
547 | if intro is None: | |
|
822 | 548 | welcome_text = "Welcome to WxIPython Shell.\n\n" |
|
823 | 549 | welcome_text+= self.IP.getBanner() |
|
824 | 550 | welcome_text+= "!command -> Execute command in shell\n" |
|
825 | 551 | welcome_text+= "TAB -> Autocompletion\n" |
|
552 | else: | |
|
553 | welcome_text = intro | |
|
826 | 554 | |
|
827 | 555 | self.text_ctrl = WxConsoleView(self, |
|
828 | 556 | self.IP.getPrompt(), |
|
829 | 557 | intro=welcome_text, |
|
830 | 558 | background_color=background_color) |
|
831 | ||
|
832 | self.text_ctrl.Bind(wx.EVT_KEY_DOWN, self.keyPress, self.text_ctrl) | |
|
833 | 559 | |
|
560 | self.cout.write = self.text_ctrl.asyncWrite | |
|
561 | ||
|
562 | option_text = wx.StaticText(self, -1, "Options:") | |
|
563 | self.completion_option = wx.CheckBox(self, -1, "Scintilla Completion") | |
|
564 | #self.completion_option.SetValue(False) | |
|
565 | self.background_option = wx.CheckBox(self, -1, "White Background") | |
|
566 | #self.background_option.SetValue(False) | |
|
567 | ||
|
568 | self.options={'completion':{'value':'IPYTHON', | |
|
569 | 'checkbox':self.completion_option,'STC':True,'IPYTHON':False, | |
|
570 | 'setfunc':self.text_ctrl.setCompletionMethod}, | |
|
571 | 'background_color':{'value':'BLACK', | |
|
572 | 'checkbox':self.background_option,'WHITE':True,'BLACK':False, | |
|
573 | 'setfunc':self.text_ctrl.setBackgroundColor}, | |
|
574 | } | |
|
575 | self.reloadOptions(self.options) | |
|
576 | ||
|
577 | self.text_ctrl.Bind(wx.EVT_KEY_DOWN, self.keyPress) | |
|
578 | self.completion_option.Bind(wx.EVT_CHECKBOX, self.evtCheckOptionCompletion) | |
|
579 | self.background_option.Bind(wx.EVT_CHECKBOX, self.evtCheckOptionBackgroundColor) | |
|
580 | ||
|
834 | 581 | ### making the layout of the panel ### |
|
835 | 582 | sizer = wx.BoxSizer(wx.VERTICAL) |
|
836 | 583 | sizer.Add(self.text_ctrl, 1, wx.EXPAND) |
|
584 | option_sizer = wx.BoxSizer(wx.HORIZONTAL) | |
|
585 | sizer.Add(option_sizer, 0) | |
|
586 | option_sizer.AddMany([(10, 20), | |
|
587 | (option_text, 0, wx.ALIGN_CENTER_VERTICAL), | |
|
588 | (5, 5), | |
|
589 | (self.completion_option, 0, wx.ALIGN_CENTER_VERTICAL), | |
|
590 | (8, 8), | |
|
591 | (self.background_option, 0, wx.ALIGN_CENTER_VERTICAL) | |
|
592 | ]) | |
|
837 | 593 | self.SetAutoLayout(True) |
|
838 | 594 | sizer.Fit(self) |
|
839 | 595 | sizer.SetSizeHints(self) |
@@ -841,158 +597,151 b' class WxIPythonViewPanel(wx.Panel):' | |||
|
841 | 597 | #and we focus on the widget :) |
|
842 | 598 | self.SetFocus() |
|
843 | 599 | |
|
844 | ### below are the thread communication variable ### | |
|
845 | # the IPython thread is managed via unidirectional communication. | |
|
846 | # It's a thread slave that can't interact by itself with the GUI. | |
|
847 | # When the GUI event loop is done runStateMachine() is called and the thread sate is then | |
|
848 | # managed. | |
|
849 | ||
|
850 | #Initialize the state machine #kept for information | |
|
851 | #self.states = ['IDLE', | |
|
852 | # 'DO_EXECUTE_LINE', | |
|
853 | # 'WAIT_END_OF_EXECUTION', | |
|
854 | # 'SHOW_DOC', | |
|
855 | # 'SHOW_PROMPT'] | |
|
856 | ||
|
857 | self.cur_state = 'IDLE' | |
|
858 | self.pager_state = 'DONE' | |
|
859 | #wx.CallAfter(self.runStateMachine) | |
|
600 | #widget state management (for key handling different cases) | |
|
601 | self.setCurrentState('IDLE') | |
|
602 | self.pager_state = 'DONE' | |
|
603 | self.raw_input_current_line = 0 | |
|
604 | ||
|
605 | def askExitCallback(self, event): | |
|
606 | self.askExitHandler(event) | |
|
607 | ||
|
608 | #---------------------- IPython Thread Management ------------------------ | |
|
609 | def stateDoExecuteLine(self): | |
|
610 | lines=self.text_ctrl.getCurrentLine() | |
|
611 | self.text_ctrl.write('\n') | |
|
612 | lines_to_execute = lines.replace('\t',' '*4) | |
|
613 | lines_to_execute = lines_to_execute.replace('\r','') | |
|
614 | self.IP.doExecute(lines_to_execute.encode('cp1252')) | |
|
615 | self.updateHistoryTracker(lines) | |
|
616 | self.setCurrentState('WAIT_END_OF_EXECUTION') | |
|
617 | ||
|
618 | def evtStateExecuteDone(self,evt): | |
|
619 | self.doc = self.IP.getDocText() | |
|
620 | self.help = self.IP.getHelpText() | |
|
621 | if self.doc: | |
|
622 | self.pager_lines = self.doc[7:].split('\n') | |
|
623 | self.pager_state = 'INIT' | |
|
624 | self.setCurrentState('SHOW_DOC') | |
|
625 | self.pager(self.doc) | |
|
626 | elif self.help: | |
|
627 | self.pager_lines = self.help.split('\n') | |
|
628 | self.pager_state = 'INIT' | |
|
629 | self.setCurrentState('SHOW_DOC') | |
|
630 | self.pager(self.help) | |
|
631 | else: | |
|
632 | self.stateShowPrompt() | |
|
633 | ||
|
634 | def stateShowPrompt(self): | |
|
635 | self.setCurrentState('SHOW_PROMPT') | |
|
636 | self.text_ctrl.setPrompt(self.IP.getPrompt()) | |
|
637 | self.text_ctrl.setIndentation(self.IP.getIndentation()) | |
|
638 | self.text_ctrl.setPromptCount(self.IP.getPromptCount()) | |
|
639 | self.text_ctrl.showPrompt() | |
|
640 | self.IP.initHistoryIndex() | |
|
641 | self.setCurrentState('IDLE') | |
|
642 | ||
|
643 | def setCurrentState(self, state): | |
|
644 | self.cur_state = state | |
|
645 | self.updateStatusTracker(self.cur_state) | |
|
860 | 646 | |
|
861 | # This creates a new Event class and a EVT binder function | |
|
862 | (self.AskExitEvent, EVT_ASK_EXIT) = wx.lib.newevent.NewEvent() | |
|
647 | def pager(self,text): | |
|
863 | 648 | |
|
864 | self.Bind(wx.EVT_IDLE, self.runStateMachine) | |
|
865 | self.Bind(EVT_ASK_EXIT, exit_handler) | |
|
866 | ||
|
867 | def __del__(self): | |
|
868 | self.IP.shutdown() | |
|
869 | self.IP.join() | |
|
870 | WxConsoleView.__del__() | |
|
871 | ||
|
872 | #---------------------------- IPython Thread Management --------------------------------------- | |
|
873 | def runStateMachine(self,event): | |
|
874 | #print >>self.sys_stdout,"state:",self.cur_state | |
|
875 | self.updateStatusTracker(self.cur_state) | |
|
876 | ||
|
877 | if self.cur_state == 'DO_EXECUTE_LINE': | |
|
878 | #print >>self.sys_stdout,"command:",self.getCurrentLine() | |
|
879 | self.IP.doExecute(self.text_ctrl.getCurrentLine().replace('\t',' '*4)) | |
|
880 | self.updateHistoryTracker(self.text_ctrl.getCurrentLine()) | |
|
881 | self.cur_state = 'WAIT_END_OF_EXECUTION' | |
|
882 | ||
|
883 | if self.cur_state == 'WAIT_END_OF_EXECUTION': | |
|
884 | if self.IP.isExecuteDone(): | |
|
885 | self.doc = self.IP.getDocText() | |
|
886 | if self.IP.getAskExit(): | |
|
887 |
|
|
|
888 | wx.PostEvent(self, evt) | |
|
889 | self.IP.clearAskExit() | |
|
890 |
|
|
|
891 |
self.pager_state = ' |
|
|
892 | self.cur_state = 'SHOW_DOC' | |
|
649 | if self.pager_state == 'INIT': | |
|
650 | #print >>sys.__stdout__,"PAGER state:",self.pager_state | |
|
651 | self.pager_nb_lines = len(self.pager_lines) | |
|
652 | self.pager_index = 0 | |
|
653 | self.pager_do_remove = False | |
|
654 | self.text_ctrl.write('\n') | |
|
655 | self.pager_state = 'PROCESS_LINES' | |
|
656 | ||
|
657 | if self.pager_state == 'PROCESS_LINES': | |
|
658 | #print >>sys.__stdout__,"PAGER state:",self.pager_state | |
|
659 | if self.pager_do_remove == True: | |
|
660 | self.text_ctrl.removeCurrentLine() | |
|
661 | self.pager_do_remove = False | |
|
662 | ||
|
663 | if self.pager_nb_lines > 10: | |
|
664 | #print >>sys.__stdout__,"PAGER processing 10 lines" | |
|
665 | if self.pager_index > 0: | |
|
666 | self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n') | |
|
667 | else: | |
|
668 | self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n') | |
|
669 | ||
|
670 | for line in self.pager_lines[self.pager_index+1:self.pager_index+9]: | |
|
671 | self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n') | |
|
672 | self.pager_index += 10 | |
|
673 | self.pager_nb_lines -= 10 | |
|
674 | self.text_ctrl.write("--- Push Enter to continue or 'Q' to quit---") | |
|
675 | self.pager_do_remove = True | |
|
676 | self.pager_state = 'WAITING' | |
|
677 | return | |
|
893 | 678 | else: |
|
894 | self.cur_state = 'SHOW_PROMPT' | |
|
895 | ||
|
896 | if self.cur_state == 'SHOW_PROMPT': | |
|
897 | self.text_ctrl.setPrompt(self.IP.getPrompt()) | |
|
898 | self.text_ctrl.setIndentation(self.IP.getIndentation()) | |
|
899 | self.text_ctrl.setPromptCount(self.IP.getPromptCount()) | |
|
900 | rv = self.cout.getvalue() | |
|
901 | if rv: rv = rv.strip('\n') | |
|
902 | self.text_ctrl.showReturned(rv) | |
|
903 | self.cout.truncate(0) | |
|
904 | self.IP.initHistoryIndex() | |
|
905 | self.cur_state = 'IDLE' | |
|
906 | ||
|
907 | if self.cur_state == 'SHOW_DOC': | |
|
908 | self.pager(self.doc) | |
|
909 | if self.pager_state == 'DONE': | |
|
910 | self.cur_state = 'SHOW_PROMPT' | |
|
911 | ||
|
912 | event.Skip() | |
|
913 | ||
|
914 | #---------------------------- IPython pager --------------------------------------- | |
|
915 | def pager(self,text):#,start=0,screen_lines=0,pager_cmd = None): | |
|
916 | if self.pager_state == 'WAITING': | |
|
917 | #print >>self.sys_stdout,"PAGER waiting" | |
|
918 | return | |
|
919 | ||
|
920 | if self.pager_state == 'INIT': | |
|
921 | #print >>self.sys_stdout,"PAGER state:",self.pager_state | |
|
922 | self.pager_lines = text[7:].split('\n') | |
|
923 | self.pager_nb_lines = len(self.pager_lines) | |
|
924 | self.pager_index = 0 | |
|
925 | self.pager_do_remove = False | |
|
926 | self.text_ctrl.write('\n') | |
|
927 | self.pager_state = 'PROCESS_LINES' | |
|
928 | ||
|
929 | if self.pager_state == 'PROCESS_LINES': | |
|
930 | #print >>self.sys_stdout,"PAGER state:",self.pager_state | |
|
931 | if self.pager_do_remove == True: | |
|
932 | self.text_ctrl.removeCurrentLine() | |
|
933 | self.pager_do_remove = False | |
|
934 | ||
|
935 | if self.pager_nb_lines > 10: | |
|
936 | #print >>self.sys_stdout,"PAGER processing 10 lines" | |
|
937 | if self.pager_index > 0: | |
|
938 | self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n') | |
|
939 | else: | |
|
940 | self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n') | |
|
941 | ||
|
942 | for line in self.pager_lines[self.pager_index+1:self.pager_index+9]: | |
|
943 | self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n') | |
|
944 | self.pager_index += 10 | |
|
945 | self.pager_nb_lines -= 10 | |
|
946 | self.text_ctrl.write("--- Push Enter to continue or 'Q' to quit---") | |
|
947 | self.pager_do_remove = True | |
|
948 | self.pager_state = 'WAITING' | |
|
949 | return | |
|
950 | else: | |
|
951 | #print >>self.sys_stdout,"PAGER processing last lines" | |
|
952 | if self.pager_nb_lines > 0: | |
|
953 | if self.pager_index > 0: | |
|
954 | self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n') | |
|
955 | else: | |
|
956 | self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n') | |
|
957 | ||
|
958 | self.pager_index += 1 | |
|
679 | #print >>sys.__stdout__,"PAGER processing last lines" | |
|
680 | if self.pager_nb_lines > 0: | |
|
681 | if self.pager_index > 0: | |
|
682 | self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n') | |
|
683 | else: | |
|
684 | self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n') | |
|
685 | ||
|
686 | self.pager_index += 1 | |
|
959 | 687 | self.pager_nb_lines -= 1 |
|
960 |
|
|
|
961 |
|
|
|
962 |
|
|
|
963 |
|
|
|
964 | self.pager_state = 'DONE' | |
|
688 | if self.pager_nb_lines > 0: | |
|
689 | for line in self.pager_lines[self.pager_index:]: | |
|
690 | self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n') | |
|
691 | self.pager_nb_lines = 0 | |
|
692 | self.pager_state = 'DONE' | |
|
693 | self.stateShowPrompt() | |
|
965 | 694 | |
|
966 |
#------------------------ |
|
|
695 | #------------------------ Key Handler ------------------------------------ | |
|
967 | 696 | def keyPress(self, event): |
|
968 | 697 | ''' |
|
969 | 698 | Key press callback with plenty of shell goodness, like history, |
|
970 | 699 | autocompletions, etc. |
|
971 | 700 | ''' |
|
972 | ||
|
973 | 701 | if event.GetKeyCode() == ord('C'): |
|
974 | if event.Modifiers == wx.MOD_CONTROL: | |
|
702 | if event.Modifiers == wx.MOD_CONTROL or event.Modifiers == wx.MOD_ALT: | |
|
975 | 703 | if self.cur_state == 'WAIT_END_OF_EXECUTION': |
|
976 | 704 | #we raise an exception inside the IPython thread container |
|
977 | self.IP.raise_exc(KeyboardInterrupt) | |
|
705 | self.IP.ce.raise_exc(KeyboardInterrupt) | |
|
978 | 706 | return |
|
979 | 707 | |
|
708 | #let this before 'wx.WXK_RETURN' because we have to put 'IDLE' | |
|
709 | #mode if AutoComp has been set as inactive | |
|
710 | if self.cur_state == 'COMPLETING': | |
|
711 | if not self.text_ctrl.AutoCompActive(): | |
|
712 | self.cur_state = 'IDLE' | |
|
713 | else: | |
|
714 | event.Skip() | |
|
715 | ||
|
980 | 716 | if event.KeyCode == wx.WXK_RETURN: |
|
981 | 717 | if self.cur_state == 'IDLE': |
|
982 | 718 | #we change the state ot the state machine |
|
983 |
self. |
|
|
719 | self.setCurrentState('DO_EXECUTE_LINE') | |
|
720 | self.stateDoExecuteLine() | |
|
984 | 721 | return |
|
722 | ||
|
985 | 723 | if self.pager_state == 'WAITING': |
|
986 | 724 | self.pager_state = 'PROCESS_LINES' |
|
725 | self.pager(self.doc) | |
|
987 | 726 | return |
|
988 | 727 | |
|
728 | if self.cur_state == 'WAITING_USER_INPUT': | |
|
729 | line=self.text_ctrl.getCurrentLine() | |
|
730 | self.text_ctrl.write('\n') | |
|
731 | self.setCurrentState('WAIT_END_OF_EXECUTION') | |
|
732 | return | |
|
733 | ||
|
989 | 734 | if event.GetKeyCode() in [ord('q'),ord('Q')]: |
|
990 | 735 | if self.pager_state == 'WAITING': |
|
991 | 736 | self.pager_state = 'DONE' |
|
737 | self.text_ctrl.write('\n') | |
|
738 | self.stateShowPrompt() | |
|
992 | 739 | return |
|
740 | ||
|
741 | if self.cur_state == 'WAITING_USER_INPUT': | |
|
742 | event.Skip() | |
|
993 | 743 | |
|
994 | #scroll_position = self.text_ctrl.GetScrollPos(wx.VERTICAL) | |
|
995 | if self.cur_state == 'IDLE': | |
|
744 | if self.cur_state == 'IDLE': | |
|
996 | 745 | if event.KeyCode == wx.WXK_UP: |
|
997 | 746 | history = self.IP.historyBack() |
|
998 | 747 | self.text_ctrl.writeHistory(history) |
@@ -1008,19 +757,68 b' class WxIPythonViewPanel(wx.Panel):' | |||
|
1008 | 757 | return |
|
1009 | 758 | completed, possibilities = self.IP.complete(self.text_ctrl.getCurrentLine()) |
|
1010 | 759 | if len(possibilities) > 1: |
|
1011 |
|
|
|
1012 |
self.text_ctrl. |
|
|
1013 |
self.text_ctrl.write |
|
|
1014 |
self.text_ctrl.write( |
|
|
1015 | ||
|
1016 | self.text_ctrl.showPrompt() | |
|
1017 |
self.text_ctrl. |
|
|
1018 |
self.text_ctrl. |
|
|
1019 | ||
|
760 | if self.text_ctrl.autocomplete_mode == 'IPYTHON': | |
|
761 | cur_slice = self.text_ctrl.getCurrentLine() | |
|
762 | self.text_ctrl.write('\n') | |
|
763 | self.text_ctrl.writeCompletion(possibilities) | |
|
764 | self.text_ctrl.write('\n') | |
|
765 | ||
|
766 | self.text_ctrl.showPrompt() | |
|
767 | self.text_ctrl.write(cur_slice) | |
|
768 | self.text_ctrl.changeLine(completed or cur_slice) | |
|
769 | else: | |
|
770 | self.cur_state = 'COMPLETING' | |
|
771 | self.text_ctrl.writeCompletion(possibilities) | |
|
772 | else: | |
|
773 | self.text_ctrl.changeLine(completed or cur_slice) | |
|
1020 | 774 | return |
|
1021 | 775 | event.Skip() |
|
1022 | ||
|
1023 |
#------------------------ |
|
|
776 | ||
|
777 | #------------------------ Option Section --------------------------------- | |
|
778 | def evtCheckOptionCompletion(self, event): | |
|
779 | if event.IsChecked(): | |
|
780 | self.options['completion']['value']='STC' | |
|
781 | else: | |
|
782 | self.options['completion']['value']='IPYTHON' | |
|
783 | self.text_ctrl.setCompletionMethod(self.options['completion']['value']) | |
|
784 | self.updateOptionTracker('completion', | |
|
785 | self.options['completion']['value']) | |
|
786 | self.text_ctrl.SetFocus() | |
|
787 | ||
|
788 | def evtCheckOptionBackgroundColor(self, event): | |
|
789 | if event.IsChecked(): | |
|
790 | self.options['background_color']['value']='WHITE' | |
|
791 | else: | |
|
792 | self.options['background_color']['value']='BLACK' | |
|
793 | self.text_ctrl.setBackgroundColor(self.options['background_color']['value']) | |
|
794 | self.updateOptionTracker('background_color', | |
|
795 | self.options['background_color']['value']) | |
|
796 | self.text_ctrl.SetFocus() | |
|
797 | ||
|
798 | def getOptions(self): | |
|
799 | return self.options | |
|
800 | ||
|
801 | def reloadOptions(self,options): | |
|
802 | self.options = options | |
|
803 | for key in self.options.keys(): | |
|
804 | value = self.options[key]['value'] | |
|
805 | self.options[key]['checkbox'].SetValue(self.options[key][value]) | |
|
806 | self.options[key]['setfunc'](value) | |
|
807 | ||
|
808 | ||
|
809 | #------------------------ Hook Section ----------------------------------- | |
|
810 | def updateOptionTracker(self,name,value): | |
|
811 | ''' | |
|
812 | Default history tracker (does nothing) | |
|
813 | ''' | |
|
814 | pass | |
|
815 | ||
|
816 | def setOptionTrackerHook(self,func): | |
|
817 | ''' | |
|
818 | Define a new history tracker | |
|
819 | ''' | |
|
820 | self.updateOptionTracker = func | |
|
821 | ||
|
1024 | 822 | def updateHistoryTracker(self,command_line): |
|
1025 | 823 | ''' |
|
1026 | 824 | Default history tracker (does nothing) |
@@ -1032,6 +830,7 b' class WxIPythonViewPanel(wx.Panel):' | |||
|
1032 | 830 | Define a new history tracker |
|
1033 | 831 | ''' |
|
1034 | 832 | self.updateHistoryTracker = func |
|
833 | ||
|
1035 | 834 | def updateStatusTracker(self,status): |
|
1036 | 835 | ''' |
|
1037 | 836 | Default status tracker (does nothing) |
@@ -1043,4 +842,36 b' class WxIPythonViewPanel(wx.Panel):' | |||
|
1043 | 842 | Define a new status tracker |
|
1044 | 843 | ''' |
|
1045 | 844 | self.updateStatusTracker = func |
|
1046 | ||
|
845 | ||
|
846 | def askExitHandler(self, event): | |
|
847 | ''' | |
|
848 | Default exit handler | |
|
849 | ''' | |
|
850 | self.text_ctrl.write('\nExit callback has not been set.') | |
|
851 | ||
|
852 | def setAskExitHandler(self, func): | |
|
853 | ''' | |
|
854 | Define an exit handler | |
|
855 | ''' | |
|
856 | self.askExitHandler = func | |
|
857 | ||
|
858 | if __name__ == '__main__': | |
|
859 | # Some simple code to test the shell widget. | |
|
860 | class MainWindow(wx.Frame): | |
|
861 | def __init__(self, parent, id, title): | |
|
862 | wx.Frame.__init__(self, parent, id, title, size=(300,250)) | |
|
863 | self._sizer = wx.BoxSizer(wx.VERTICAL) | |
|
864 | self.shell = IPShellWidget(self) | |
|
865 | self._sizer.Add(self.shell, 1, wx.EXPAND) | |
|
866 | self.SetSizer(self._sizer) | |
|
867 | self.SetAutoLayout(1) | |
|
868 | self.Show(True) | |
|
869 | ||
|
870 | app = wx.PySimpleApp() | |
|
871 | frame = MainWindow(None, wx.ID_ANY, 'Ipython') | |
|
872 | frame.SetSize((780, 460)) | |
|
873 | shell = frame.shell | |
|
874 | ||
|
875 | app.MainLoop() | |
|
876 | ||
|
877 |
@@ -1,45 +1,50 b'' | |||
|
1 | import threading | |
|
2 | import inspect | |
|
3 | import ctypes | |
|
4 | ||
|
5 | ||
|
6 | def _async_raise(tid, exctype): | |
|
7 | """raises the exception, performs cleanup if needed""" | |
|
8 | if not inspect.isclass(exctype): | |
|
9 | raise TypeError("Only types can be raised (not instances)") | |
|
10 | res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype)) | |
|
11 | if res == 0: | |
|
12 | raise ValueError("invalid thread id") | |
|
13 | elif res != 1: | |
|
14 | # """if it returns a number greater than one, you're in trouble, | |
|
15 | # and you should call it again with exc=NULL to revert the effect""" | |
|
16 | ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, 0) | |
|
17 |
raise |
|
|
18 | ||
|
19 | ||
|
20 | class Thread(threading.Thread): | |
|
21 | def _get_my_tid(self): | |
|
22 | """determines this (self's) thread id""" | |
|
23 | if not self.isAlive(): | |
|
24 | raise threading.ThreadError("the thread is not active") | |
|
25 | ||
|
26 | # do we have it cached? | |
|
27 |
|
|
|
28 | return self._thread_id | |
|
29 | ||
|
30 | # no, look for it in the _active dict | |
|
31 | for tid, tobj in threading._active.items(): | |
|
32 | if tobj is self: | |
|
33 |
|
|
|
34 | return tid | |
|
35 | ||
|
36 | raise AssertionError("could not determine the thread's id") | |
|
37 | ||
|
38 | def raise_exc(self, exctype): | |
|
39 | """raises the given exception type in the context of this thread""" | |
|
40 | _async_raise(self._get_my_tid(), exctype) | |
|
41 | ||
|
42 | def kill(self): | |
|
43 | """raises SystemExit in the context of the given thread, which should | |
|
44 | cause the thread to exit silently (unless caught)""" | |
|
45 | self.raise_exc(SystemExit) | |
|
1 | """ | |
|
2 | Thread subclass that can deal with asynchronously function calls via | |
|
3 | raise_exc. | |
|
4 | """ | |
|
5 | ||
|
6 | import threading | |
|
7 | import inspect | |
|
8 | import ctypes | |
|
9 | ||
|
10 | ||
|
11 | def _async_raise(tid, exctype): | |
|
12 | """raises the exception, performs cleanup if needed""" | |
|
13 | if not inspect.isclass(exctype): | |
|
14 | raise TypeError("Only types can be raised (not instances)") | |
|
15 | res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype)) | |
|
16 | if res == 0: | |
|
17 | raise ValueError("invalid thread id") | |
|
18 | elif res != 1: | |
|
19 | # """if it returns a number greater than one, you're in trouble, | |
|
20 | # and you should call it again with exc=NULL to revert the effect""" | |
|
21 | ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, 0) | |
|
22 | raise SystemError("PyThreadState_SetAsyncExc failed") | |
|
23 | ||
|
24 | ||
|
25 | class ThreadEx(threading.Thread): | |
|
26 | def _get_my_tid(self): | |
|
27 | """determines this (self's) thread id""" | |
|
28 | if not self.isAlive(): | |
|
29 | raise threading.ThreadError("the thread is not active") | |
|
30 | ||
|
31 | # do we have it cached? | |
|
32 | if hasattr(self, "_thread_id"): | |
|
33 | return self._thread_id | |
|
34 | ||
|
35 | # no, look for it in the _active dict | |
|
36 | for tid, tobj in threading._active.items(): | |
|
37 | if tobj is self: | |
|
38 | self._thread_id = tid | |
|
39 | return tid | |
|
40 | ||
|
41 | raise AssertionError("could not determine the thread's id") | |
|
42 | ||
|
43 | def raise_exc(self, exctype): | |
|
44 | """raises the given exception type in the context of this thread""" | |
|
45 | _async_raise(self._get_my_tid(), exctype) | |
|
46 | ||
|
47 | def kill(self): | |
|
48 | """raises SystemExit in the context of the given thread, which should | |
|
49 | cause the thread to exit silently (unless caught)""" | |
|
50 | self.raise_exc(SystemExit) |
@@ -2,11 +2,13 b'' | |||
|
2 | 2 | # -*- coding: iso-8859-15 -*- |
|
3 | 3 | |
|
4 | 4 | import wx.aui |
|
5 | import wx.py | |
|
5 | ||
|
6 | #used for about dialog | |
|
6 | 7 | from wx.lib.wordwrap import wordwrap |
|
7 | 8 | |
|
8 | from ipython_view import * | |
|
9 | from ipython_history import * | |
|
9 | #used for ipython GUI objects | |
|
10 | from IPython.gui.wx.ipython_view import IPShellWidget | |
|
11 | from IPython.gui.wx.ipython_history import IPythonHistoryPanel | |
|
10 | 12 | |
|
11 | 13 | __version__ = 0.8 |
|
12 | 14 | __author__ = "Laurent Dufrechou" |
@@ -20,7 +22,8 b' __license__ = "BSD"' | |||
|
20 | 22 | class MyFrame(wx.Frame): |
|
21 | 23 | """Creating one main frame for our |
|
22 | 24 | application with movables windows""" |
|
23 |
def __init__(self, parent=None, id=-1, title="WxIPython", |
|
|
25 | def __init__(self, parent=None, id=-1, title="WxIPython", | |
|
26 | pos=wx.DefaultPosition, | |
|
24 | 27 | size=(800, 600), style=wx.DEFAULT_FRAME_STYLE): |
|
25 | 28 | wx.Frame.__init__(self, parent, id, title, pos, size, style) |
|
26 | 29 | self._mgr = wx.aui.AuiManager() |
@@ -30,15 +33,18 b' class MyFrame(wx.Frame):' | |||
|
30 | 33 | |
|
31 | 34 | #create differents panels and make them persistant |
|
32 | 35 | self.history_panel = IPythonHistoryPanel(self) |
|
36 | ||
|
37 | self.history_panel.setOptionTrackerHook(self.optionSave) | |
|
33 | 38 | |
|
34 | self.ipython_panel = WxIPythonViewPanel(self,self.OnExitDlg, | |
|
35 | background_color = "BLACK") | |
|
36 | ||
|
37 | #self.ipython_panel = WxIPythonViewPanel(self,self.OnExitDlg, | |
|
38 | # background_color = "WHITE") | |
|
39 | ||
|
39 | self.ipython_panel = IPShellWidget(self,background_color = "BLACK") | |
|
40 | #self.ipython_panel = IPShellWidget(self,background_color = "WHITE") | |
|
41 | ||
|
40 | 42 | self.ipython_panel.setHistoryTrackerHook(self.history_panel.write) |
|
41 | 43 | self.ipython_panel.setStatusTrackerHook(self.updateStatus) |
|
44 | self.ipython_panel.setAskExitHandler(self.OnExitDlg) | |
|
45 | self.ipython_panel.setOptionTrackerHook(self.optionSave) | |
|
46 | ||
|
47 | self.optionLoad() | |
|
42 | 48 | |
|
43 | 49 | self.statusbar = self.createStatus() |
|
44 | 50 | self.createMenu() |
@@ -48,13 +54,11 b' class MyFrame(wx.Frame):' | |||
|
48 | 54 | # main panels |
|
49 | 55 | self._mgr.AddPane(self.ipython_panel , wx.CENTER, "IPython Shell") |
|
50 | 56 | self._mgr.AddPane(self.history_panel , wx.RIGHT, "IPython history") |
|
51 | ||
|
57 | ||
|
52 | 58 | # now we specify some panel characteristics |
|
53 | 59 | self._mgr.GetPane(self.ipython_panel).CaptionVisible(True); |
|
54 | 60 | self._mgr.GetPane(self.history_panel).CaptionVisible(True); |
|
55 | 61 | self._mgr.GetPane(self.history_panel).MinSize((200,400)); |
|
56 | ||
|
57 | ||
|
58 | 62 | |
|
59 | 63 | # tell the manager to "commit" all the changes just made |
|
60 | 64 | self._mgr.Update() |
@@ -66,7 +70,7 b' class MyFrame(wx.Frame):' | |||
|
66 | 70 | self.Bind(wx.EVT_MENU, self.OnShowHistoryPanel,id=wx.ID_HIGHEST+2) |
|
67 | 71 | self.Bind(wx.EVT_MENU, self.OnShowAbout, id=wx.ID_HIGHEST+3) |
|
68 | 72 | self.Bind(wx.EVT_MENU, self.OnShowAllPanel,id=wx.ID_HIGHEST+6) |
|
69 | ||
|
73 | ||
|
70 | 74 | warn_text = 'Hello from IPython and wxPython.\n' |
|
71 | 75 | warn_text +='Please Note that this work is still EXPERIMENTAL\n' |
|
72 | 76 | warn_text +='It does NOT emulate currently all the IPython functions.\n' |
@@ -78,7 +82,41 b' class MyFrame(wx.Frame):' | |||
|
78 | 82 | ) |
|
79 | 83 | dlg.ShowModal() |
|
80 | 84 | dlg.Destroy() |
|
81 | ||
|
85 | ||
|
86 | def optionSave(self, name, value): | |
|
87 | opt = open('options.conf','w') | |
|
88 | ||
|
89 | try: | |
|
90 | options_ipython_panel = self.ipython_panel.getOptions() | |
|
91 | options_history_panel = self.history_panel.getOptions() | |
|
92 | ||
|
93 | for key in options_ipython_panel.keys(): | |
|
94 | opt.write(key + '=' + options_ipython_panel[key]['value']+'\n') | |
|
95 | for key in options_history_panel.keys(): | |
|
96 | opt.write(key + '=' + options_history_panel[key]['value']+'\n') | |
|
97 | finally: | |
|
98 | opt.close() | |
|
99 | ||
|
100 | def optionLoad(self): | |
|
101 | opt = open('options.conf','r') | |
|
102 | lines = opt.readlines() | |
|
103 | opt.close() | |
|
104 | ||
|
105 | options_ipython_panel = self.ipython_panel.getOptions() | |
|
106 | options_history_panel = self.history_panel.getOptions() | |
|
107 | ||
|
108 | for line in lines: | |
|
109 | key = line.split('=')[0] | |
|
110 | value = line.split('=')[1].replace('\n','').replace('\r','') | |
|
111 | if key in options_ipython_panel.keys(): | |
|
112 | options_ipython_panel[key]['value'] = value | |
|
113 | elif key in options_history_panel.keys(): | |
|
114 | options_history_panel[key]['value'] = value | |
|
115 | else: | |
|
116 | print >>sys.__stdout__,"Warning: key ",key,"not found in widget options. Check Options.conf" | |
|
117 | self.ipython_panel.reloadOptions(options_ipython_panel) | |
|
118 | self.history_panel.reloadOptions(options_history_panel) | |
|
119 | ||
|
82 | 120 | def createMenu(self): |
|
83 | 121 | """local method used to create one menu bar""" |
|
84 | 122 | |
@@ -121,6 +159,7 b' class MyFrame(wx.Frame):' | |||
|
121 | 159 | states = {'IDLE':'Idle', |
|
122 | 160 | 'DO_EXECUTE_LINE':'Send command', |
|
123 | 161 | 'WAIT_END_OF_EXECUTION':'Running command', |
|
162 | 'WAITING_USER_INPUT':'Waiting user input', | |
|
124 | 163 | 'SHOW_DOC':'Showing doc', |
|
125 | 164 | 'SHOW_PROMPT':'Showing prompt'} |
|
126 | 165 | self.statusbar.SetStatusText(states[text], 0) |
@@ -56,7 +56,7 b' from pprint import PrettyPrinter' | |||
|
56 | 56 | __all__ = ['editor', 'fix_error_editor', 'result_display', |
|
57 | 57 | 'input_prefilter', 'shutdown_hook', 'late_startup_hook', |
|
58 | 58 | 'generate_prompt', 'generate_output_prompt','shell_hook', |
|
59 | 'show_in_pager'] | |
|
59 | 'show_in_pager','pre_prompt_hook', 'pre_runcode_hook'] | |
|
60 | 60 | |
|
61 | 61 | pformat = PrettyPrinter().pformat |
|
62 | 62 | |
@@ -227,11 +227,17 b' def show_in_pager(self,s):' | |||
|
227 | 227 | # raising TryNext here will use the default paging functionality |
|
228 | 228 | raise ipapi.TryNext |
|
229 | 229 | |
|
230 |
def pre_ |
|
|
231 |
""" |
|
|
230 | def pre_prompt_hook(self): | |
|
231 | """ Run before displaying the next prompt | |
|
232 | ||
|
233 | Use this e.g. to display output from asynchronous operations (in order | |
|
234 | to not mess up text entry) | |
|
235 | """ | |
|
236 | ||
|
232 | 237 | return None |
|
233 | 238 | |
|
234 |
def p |
|
|
235 | """ Executed after executing a command """ | |
|
239 | def pre_runcode_hook(self): | |
|
240 | """ Executed before running the (prefiltered) code in IPython """ | |
|
241 | return None | |
|
236 | 242 | |
|
237 | 243 |
@@ -273,15 +273,27 b' class IPApi:' | |||
|
273 | 273 | """ |
|
274 | 274 | res = [] |
|
275 | 275 | lines = script.splitlines() |
|
276 | ||
|
276 | 277 | level = 0 |
|
277 | 278 | for l in lines: |
|
278 | stripped = l.lstrip() | |
|
279 |
|
|
|
279 | lstripped = l.lstrip() | |
|
280 | stripped = l.strip() | |
|
281 | if not stripped: | |
|
280 | 282 | continue |
|
281 | newlevel = len(l) - len(stripped) | |
|
282 | if level > 0 and newlevel == 0: | |
|
283 | newlevel = len(l) - len(lstripped) | |
|
284 | def is_secondary_block_start(s): | |
|
285 | if not s.endswith(':'): | |
|
286 | return False | |
|
287 | if (s.startswith('elif') or | |
|
288 | s.startswith('else') or | |
|
289 | s.startswith('except') or | |
|
290 | s.startswith('finally')): | |
|
291 | return True | |
|
292 | ||
|
293 | if level > 0 and newlevel == 0 and not is_secondary_block_start(stripped): | |
|
283 | 294 | # add empty line |
|
284 | 295 | res.append('') |
|
296 | ||
|
285 | 297 | res.append(l) |
|
286 | 298 | level = newlevel |
|
287 | 299 | return '\n'.join(res) + '\n' |
@@ -291,7 +303,7 b' class IPApi:' | |||
|
291 | 303 | else: |
|
292 | 304 | script = '\n'.join(lines) |
|
293 | 305 | clean=cleanup_ipy_script(script) |
|
294 | ||
|
306 | # print "_ip.runlines() script:\n",clean #dbg | |
|
295 | 307 | self.IP.runlines(clean) |
|
296 | 308 | def to_user_ns(self,vars, interactive = True): |
|
297 | 309 | """Inject a group of variables into the IPython user namespace. |
@@ -533,7 +545,7 b' class DebugTools:' | |||
|
533 | 545 | if name in self.hotnames: |
|
534 | 546 | self.debug_stack( "HotName '%s' caught" % name) |
|
535 | 547 | |
|
536 | def launch_new_instance(user_ns = None): | |
|
548 | def launch_new_instance(user_ns = None,shellclass = None): | |
|
537 | 549 | """ Make and start a new ipython instance. |
|
538 | 550 | |
|
539 | 551 | This can be called even without having an already initialized |
@@ -542,7 +554,7 b' def launch_new_instance(user_ns = None):' | |||
|
542 | 554 | This is also used as the egg entry point for the 'ipython' script. |
|
543 | 555 | |
|
544 | 556 | """ |
|
545 | ses = make_session(user_ns) | |
|
557 | ses = make_session(user_ns,shellclass) | |
|
546 | 558 | ses.mainloop() |
|
547 | 559 | |
|
548 | 560 | |
@@ -578,7 +590,7 b' def make_user_global_ns(ns = None):' | |||
|
578 | 590 | return ns |
|
579 | 591 | |
|
580 | 592 | |
|
581 | def make_session(user_ns = None): | |
|
593 | def make_session(user_ns = None, shellclass = None): | |
|
582 | 594 | """Makes, but does not launch an IPython session. |
|
583 | 595 | |
|
584 | 596 | Later on you can call obj.mainloop() on the returned object. |
@@ -591,6 +603,6 b' def make_session(user_ns = None):' | |||
|
591 | 603 | WARNING: This should *not* be run when a session exists already.""" |
|
592 | 604 | |
|
593 | 605 | import IPython.Shell |
|
594 | return IPython.Shell.start(user_ns) | |
|
595 | ||
|
596 | ||
|
606 | if shellclass is None: | |
|
607 | return IPython.Shell.start(user_ns) | |
|
608 | return shellclass(user_ns = user_ns) |
@@ -6,7 +6,6 b' Requires Python 2.3 or newer.' | |||
|
6 | 6 | |
|
7 | 7 | This file contains all the classes and helper functions specific to IPython. |
|
8 | 8 | |
|
9 | $Id: iplib.py 3005 2008-02-01 16:43:34Z vivainio $ | |
|
10 | 9 | """ |
|
11 | 10 | |
|
12 | 11 | #***************************************************************************** |
@@ -55,6 +54,8 b' import sys' | |||
|
55 | 54 | import tempfile |
|
56 | 55 | import traceback |
|
57 | 56 | import types |
|
57 | import warnings | |
|
58 | warnings.filterwarnings('ignore', r'.*sets module*') | |
|
58 | 59 | from sets import Set |
|
59 | 60 | from pprint import pprint, pformat |
|
60 | 61 | |
@@ -377,7 +378,10 b' class InteractiveShell(object,Magic):' | |||
|
377 | 378 | # Get system encoding at startup time. Certain terminals (like Emacs |
|
378 | 379 | # under Win32 have it set to None, and we need to have a known valid |
|
379 | 380 | # encoding to use in the raw_input() method |
|
380 | self.stdin_encoding = sys.stdin.encoding or 'ascii' | |
|
381 | try: | |
|
382 | self.stdin_encoding = sys.stdin.encoding or 'ascii' | |
|
383 | except AttributeError: | |
|
384 | self.stdin_encoding = 'ascii' | |
|
381 | 385 | |
|
382 | 386 | # dict of things NOT to alias (keywords, builtins and some magics) |
|
383 | 387 | no_alias = {} |
@@ -698,7 +702,10 b' class InteractiveShell(object,Magic):' | |||
|
698 | 702 | |
|
699 | 703 | # Do a proper resetting of doctest, including the necessary displayhook |
|
700 | 704 | # monkeypatching |
|
701 | doctest_reload() | |
|
705 | try: | |
|
706 | doctest_reload() | |
|
707 | except ImportError: | |
|
708 | warn("doctest module does not exist.") | |
|
702 | 709 | |
|
703 | 710 | # Set user colors (don't do it in the constructor above so that it |
|
704 | 711 | # doesn't crash if colors option is invalid) |
@@ -1266,8 +1273,12 b' want to merge them back into the new files.""" % locals()' | |||
|
1266 | 1273 | """Reload the input history from disk file.""" |
|
1267 | 1274 | |
|
1268 | 1275 | if self.has_readline: |
|
1269 | self.readline.clear_history() | |
|
1270 |
self.readline. |
|
|
1276 | try: | |
|
1277 | self.readline.clear_history() | |
|
1278 | self.readline.read_history_file(self.shell.histfile) | |
|
1279 | except AttributeError: | |
|
1280 | pass | |
|
1281 | ||
|
1271 | 1282 | |
|
1272 | 1283 | def history_saving_wrapper(self, func): |
|
1273 | 1284 | """ Wrap func for readline history saving |
@@ -1744,6 +1755,7 b' want to merge them back into the new files.""" % locals()' | |||
|
1744 | 1755 | # exit_now is set by a call to %Exit or %Quit |
|
1745 | 1756 | |
|
1746 | 1757 | while not self.exit_now: |
|
1758 | self.hooks.pre_prompt_hook() | |
|
1747 | 1759 | if more: |
|
1748 | 1760 | try: |
|
1749 | 1761 | prompt = self.hooks.generate_prompt(True) |
@@ -2009,7 +2021,7 b' want to merge them back into the new files.""" % locals()' | |||
|
2009 | 2021 | |
|
2010 | 2022 | try: |
|
2011 | 2023 | code = self.compile(source,filename,symbol) |
|
2012 | except (OverflowError, SyntaxError, ValueError): | |
|
2024 | except (OverflowError, SyntaxError, ValueError, TypeError): | |
|
2013 | 2025 | # Case 1 |
|
2014 | 2026 | self.showsyntaxerror(filename) |
|
2015 | 2027 | return None |
@@ -2053,6 +2065,7 b' want to merge them back into the new files.""" % locals()' | |||
|
2053 | 2065 | outflag = 1 # happens in more places, so it's easier as default |
|
2054 | 2066 | try: |
|
2055 | 2067 | try: |
|
2068 | self.hooks.pre_runcode_hook() | |
|
2056 | 2069 | # Embedded instances require separate global/local namespaces |
|
2057 | 2070 | # so they can see both the surrounding (local) namespace and |
|
2058 | 2071 | # the module-level globals when called inside another function. |
@@ -58,6 +58,14 b' from IPython.iplib import InteractiveShell' | |||
|
58 | 58 | from IPython.usage import cmd_line_usage,interactive_usage |
|
59 | 59 | from IPython.genutils import * |
|
60 | 60 | |
|
61 | def force_import(modname): | |
|
62 | if modname in sys.modules: | |
|
63 | print "reload",modname | |
|
64 | reload(sys.modules[modname]) | |
|
65 | else: | |
|
66 | __import__(modname) | |
|
67 | ||
|
68 | ||
|
61 | 69 | #----------------------------------------------------------------------------- |
|
62 | 70 | def make_IPython(argv=None,user_ns=None,user_global_ns=None,debug=1, |
|
63 | 71 | rc_override=None,shell_class=InteractiveShell, |
@@ -95,9 +103,12 b' def make_IPython(argv=None,user_ns=None,user_global_ns=None,debug=1,' | |||
|
95 | 103 | embedded=embedded,**kw) |
|
96 | 104 | |
|
97 | 105 | # Put 'help' in the user namespace |
|
98 | from site import _Helper | |
|
106 | try: | |
|
107 | from site import _Helper | |
|
108 | IP.user_ns['help'] = _Helper() | |
|
109 | except ImportError: | |
|
110 | warn('help() not available - check site.py') | |
|
99 | 111 | IP.user_config_ns = {} |
|
100 | IP.user_ns['help'] = _Helper() | |
|
101 | 112 | |
|
102 | 113 | |
|
103 | 114 | if DEVDEBUG: |
@@ -176,10 +187,10 b" object? -> Details about 'object'. ?object also works, ?? prints more." | |||
|
176 | 187 | |
|
177 | 188 | # Options that can *only* appear at the cmd line (not in rcfiles). |
|
178 | 189 | |
|
179 | # The "ignore" option is a kludge so that Emacs buffers don't crash, since | |
|
180 | # the 'C-c !' command in emacs automatically appends a -i option at the end. | |
|
181 | 190 | cmdline_only = ('help interact|i ipythondir=s Version upgrade ' |
|
182 |
'gthread! qthread! q4thread! wthread! tkthread! pylab! tk!' |
|
|
191 | 'gthread! qthread! q4thread! wthread! tkthread! pylab! tk! ' | |
|
192 | # 'twisted!' # disabled for now. | |
|
193 | ) | |
|
183 | 194 | |
|
184 | 195 | # Build the actual name list to be used by DPyGetOpt |
|
185 | 196 | opts_names = qw(cmdline_opts) + qw(cmdline_only) |
@@ -203,7 +214,7 b" object? -> Details about 'object'. ?object also works, ?? prints more." | |||
|
203 | 214 | editor = '0', |
|
204 | 215 | gthread = 0, |
|
205 | 216 | help = 0, |
|
206 |
interact = |
|
|
217 | interact = 0, | |
|
207 | 218 | ipythondir = ipythondir_def, |
|
208 | 219 | log = 0, |
|
209 | 220 | logfile = '', |
@@ -237,6 +248,7 b" object? -> Details about 'object'. ?object also works, ?? prints more." | |||
|
237 | 248 | system_verbose = 0, |
|
238 | 249 | term_title = 1, |
|
239 | 250 | tk = 0, |
|
251 | #twisted= 0, # disabled for now | |
|
240 | 252 | upgrade = 0, |
|
241 | 253 | Version = 0, |
|
242 | 254 | wildcards_case_sensitive = 1, |
@@ -633,15 +645,17 b" object? -> Details about 'object'. ?object also works, ?? prints more." | |||
|
633 | 645 | if opts_all.profile and not profile_handled_by_legacy: |
|
634 | 646 | profmodname = 'ipy_profile_' + opts_all.profile |
|
635 | 647 | try: |
|
636 | __import__(profmodname) | |
|
648 | ||
|
649 | force_import(profmodname) | |
|
637 | 650 | except: |
|
638 | 651 | IP.InteractiveTB() |
|
639 | 652 | print "Error importing",profmodname,"- perhaps you should run %upgrade?" |
|
640 | 653 | import_fail_info(profmodname) |
|
641 | 654 | else: |
|
642 |
import |
|
|
643 |
try: |
|
|
644 | import ipy_user_conf | |
|
655 | force_import('ipy_profile_none') | |
|
656 | try: | |
|
657 | ||
|
658 | force_import('ipy_user_conf') | |
|
645 | 659 | |
|
646 | 660 | except: |
|
647 | 661 | conf = opts_all.ipythondir + "/ipy_user_conf.py" |
@@ -23,12 +23,17 b' __license__ = Release.license' | |||
|
23 | 23 | import sys |
|
24 | 24 | import os |
|
25 | 25 | |
|
26 | ignore_termtitle = False | |
|
26 | 27 | |
|
27 | 28 | def _dummy_op(*a, **b): |
|
28 | 29 | """ A no-op function """ |
|
29 | 30 | |
|
30 | 31 | def _set_term_title_xterm(title): |
|
31 | 32 | """ Change virtual terminal title in xterm-workalikes """ |
|
33 | ||
|
34 | if ignore_termtitle: | |
|
35 | return | |
|
36 | ||
|
32 | 37 | sys.stdout.write('\033]%d;%s\007' % (0,title)) |
|
33 | 38 | |
|
34 | 39 | |
@@ -37,4 +42,6 b" if os.environ.get('TERM','') == 'xterm':" | |||
|
37 | 42 | else: |
|
38 | 43 | set_term_title = _dummy_op |
|
39 | 44 | |
|
40 | ||
|
45 | def freeze_term_title(): | |
|
46 | global ignore_termtitle | |
|
47 | ignore_termtitle = True |
@@ -50,4 +50,7 b' def set_term_title(title):' | |||
|
50 | 50 | return |
|
51 | 51 | _set_term_title(title) |
|
52 | 52 | |
|
53 | def freeze_term_title(): | |
|
54 | global ignore_termtitle | |
|
55 | ignore_termtitle = 1 | |
|
53 | 56 |
@@ -377,8 +377,8 b' class ListTB(TBTools):' | |||
|
377 | 377 | |
|
378 | 378 | def __call__(self, etype, value, elist): |
|
379 | 379 | Term.cout.flush() |
|
380 | Term.cerr.flush() | |
|
381 | 380 | print >> Term.cerr, self.text(etype,value,elist) |
|
381 | Term.cerr.flush() | |
|
382 | 382 | |
|
383 | 383 | def text(self,etype, value, elist,context=5): |
|
384 | 384 | """Return a color formatted string with the traceback info.""" |
@@ -855,8 +855,8 b' class VerboseTB(TBTools):' | |||
|
855 | 855 | (etype, evalue, etb) = info or sys.exc_info() |
|
856 | 856 | self.tb = etb |
|
857 | 857 | Term.cout.flush() |
|
858 | Term.cerr.flush() | |
|
859 | 858 | print >> Term.cerr, self.text(etype, evalue, etb) |
|
859 | Term.cerr.flush() | |
|
860 | 860 | |
|
861 | 861 | # Changed so an instance can just be called as VerboseTB_inst() and print |
|
862 | 862 | # out the right info on its own. |
@@ -977,13 +977,13 b' class AutoFormattedTB(FormattedTB):' | |||
|
977 | 977 | if out is None: |
|
978 | 978 | out = Term.cerr |
|
979 | 979 | Term.cout.flush() |
|
980 | out.flush() | |
|
981 | 980 | if tb_offset is not None: |
|
982 | 981 | tb_offset, self.tb_offset = self.tb_offset, tb_offset |
|
983 | 982 | print >> out, self.text(etype, evalue, etb) |
|
984 | 983 | self.tb_offset = tb_offset |
|
985 | 984 | else: |
|
986 | 985 | print >> out, self.text(etype, evalue, etb) |
|
986 | out.flush() | |
|
987 | 987 | try: |
|
988 | 988 | self.debugger() |
|
989 | 989 | except KeyboardInterrupt: |
@@ -9,22 +9,23 b' graft setupext' | |||
|
9 | 9 | graft IPython/UserConfig |
|
10 | 10 | |
|
11 | 11 | graft doc |
|
12 | exclude doc/\#* | |
|
12 | 13 | exclude doc/*.1 |
|
13 | exclude doc/manual_base* | |
|
14 | 14 | exclude doc/ChangeLog.* |
|
15 | exclude doc/\#* | |
|
16 | exclude doc/update_magic.sh | |
|
17 | 15 | exclude doc/update_version.sh |
|
18 | exclude doc/manual_base* | |
|
19 | exclude doc/manual/WARNINGS | |
|
20 | exclude doc/manual/*.aux | |
|
21 |
exclude doc/ |
|
|
22 |
exclude doc/ |
|
|
23 | exclude doc/manual/*.pl | |
|
24 | exclude doc/manual/*.tex | |
|
16 | ||
|
17 | # There seems to be no way of excluding whole subdirectories, other than | |
|
18 | # manually excluding all their subdirs. distutils really is horrible... | |
|
19 | exclude doc/attic/* | |
|
20 | exclude doc/build/doctrees/* | |
|
21 | exclude doc/build/html/_sources/* | |
|
22 | exclude doc/build/html/_static/* | |
|
23 | exclude doc/build/html/* | |
|
24 | exclude doc/build/latex/* | |
|
25 | 25 | |
|
26 | 26 | global-exclude *~ |
|
27 | 27 | global-exclude *.flc |
|
28 | 28 | global-exclude *.pyc |
|
29 | 29 | global-exclude .dircopy.log |
|
30 | 30 | global-exclude .svn |
|
31 | global-exclude .bzr |
@@ -1,8 +1,330 b'' | |||
|
1 | ipython (0.8.1-2) unstable; urgency=low | |
|
2 | ||
|
3 | [ Piotr Ożarowski ] | |
|
4 | * Homepage field added | |
|
5 | * Rename XS-Vcs-* fields to Vcs-* (dpkg supports them now) | |
|
6 | * Add 04_remove_shebang patch | |
|
7 | * Removing lintian overrides, adding proper patches instead. | |
|
8 | ||
|
9 | [ Bernd Zeimetz ] | |
|
10 | * Replacing Recommends by Suggests to stop ipython from installing ~500MB | |
|
11 | of dependencies. Thanks to Marcela Tiznado (Closes: #451887). | |
|
12 | ||
|
13 | -- Bernd Zeimetz <bernd@bzed.de> Mon, 19 Nov 2007 19:10:14 +0100 | |
|
14 | ||
|
15 | ipython (0.8.1-1) unstable; urgency=low | |
|
16 | ||
|
17 | [ Bernd Zeimetz ] | |
|
18 | * debian/control: | |
|
19 | - adding python-matplotlib to Recommends because | |
|
20 | it is needed to run ipython -pylab | |
|
21 | ||
|
22 | [ Norbert Tretkowski ] | |
|
23 | * New upstream release. (closes: #428398) | |
|
24 | ||
|
25 | [ Reinhard Tartler ] | |
|
26 | * Install ipython.el properly. | |
|
27 | ||
|
28 | -- Norbert Tretkowski <nobse@debian.org> Mon, 11 Jun 2007 20:05:30 +0200 | |
|
29 | ||
|
30 | ipython (0.8.0-2) unstable; urgency=low | |
|
31 | ||
|
32 | * debian/changelog: | |
|
33 | - adding missing colons to Closes entries to fix two | |
|
34 | lintian warnings | |
|
35 | * debian/compat: | |
|
36 | - bumping compat level to 5 | |
|
37 | * debian/control: | |
|
38 | - adding XS-Vcs-Browser | |
|
39 | - remove no longer needed X*-Python-Version fields | |
|
40 | - moving python-pexpect from Recommends to Depends because | |
|
41 | /usr/bin/irunner is not useable without it | |
|
42 | - moving debhelper and dpatch from Build-Depends-Indep to | |
|
43 | Build-Depends, fixing the following lintian errors: | |
|
44 | - clean-should-be-satisfied-by-build-depends debhelper | |
|
45 | - clean-should-be-satisfied-by-build-depends dpatch | |
|
46 | - removing unnecessary Build-Depends-Indep on python-all-dev, | |
|
47 | adding python to Build-Depends instead | |
|
48 | - replacing python-central by python-support to be able to | |
|
49 | fix #420134 without hassle | |
|
50 | - adding ${misc:Depends} | |
|
51 | - adding the missing identation for Homepage: | |
|
52 | - adding cdbs as Build-Depends | |
|
53 | - adding myself to Uploaders | |
|
54 | - removing the short description from the long description | |
|
55 | * debian/patches/03_ipy_gnuglobal.dpatch: | |
|
56 | - fix the location of the global binary - we're not on windows | |
|
57 | * debian/rules: | |
|
58 | - removing old crust, using cdbs to make things clear again | |
|
59 | - using python-support instead of python-central to make | |
|
60 | modules for 2.5 avaiable now. (Closes: #420134) | |
|
61 | - making sure the bogus /usr/IPython directory is not | |
|
62 | included in the package again | |
|
63 | - do not remove docs/ipython.el (Closes: #198505, #415427) | |
|
64 | * adding lintian ovverrides for several scripts included in the | |
|
65 | IPython/Extensions directory. | |
|
66 | * adding a manpage for /usr/bin/irunner | |
|
67 | ||
|
68 | -- Bernd Zeimetz <bernd@bzed.de> Tue, 24 Apr 2007 02:47:26 +0200 | |
|
69 | ||
|
70 | ipython (0.8.0-1) unstable; urgency=low | |
|
71 | ||
|
72 | * New upstream release. (closes: #419716) | |
|
73 | * Removed patches merged upstream. | |
|
74 | ||
|
75 | -- Norbert Tretkowski <nobse@debian.org> Tue, 17 Apr 2007 20:26:43 +0200 | |
|
76 | ||
|
77 | ipython (0.7.3-2) unstable; urgency=low | |
|
78 | ||
|
79 | * Added a new patch from svn to fix jobctrl to work properly on posix. | |
|
80 | ||
|
81 | -- Norbert Tretkowski <nobse@debian.org> Thu, 21 Dec 2006 20:13:57 +0100 | |
|
82 | ||
|
83 | ipython (0.7.3-1) unstable; urgency=low | |
|
84 | ||
|
85 | * New upstream release. | |
|
86 | ||
|
87 | -- Norbert Tretkowski <nobse@debian.org> Mon, 18 Dec 2006 22:23:55 +0100 | |
|
88 | ||
|
89 | ipython (0.7.3~rc2-1) experimental; urgency=low | |
|
90 | ||
|
91 | * New upstream release candidate. | |
|
92 | ||
|
93 | -- Norbert Tretkowski <nobse@debian.org> Sat, 16 Dec 2006 02:20:13 +0100 | |
|
94 | ||
|
95 | ipython (0.7.3~beta2-1) experimental; urgency=low | |
|
96 | ||
|
97 | * New upstream beta release. | |
|
98 | ||
|
99 | -- Norbert Tretkowski <nobse@debian.org> Fri, 8 Dec 2006 08:02:16 +0100 | |
|
100 | ||
|
101 | ipython (0.7.3~beta1-1) experimental; urgency=low | |
|
102 | ||
|
103 | * New upstream beta release. | |
|
104 | * Removed backported patch added in 0.7.2-5 to workaround bugs in python | |
|
105 | 2.3's inspect module. | |
|
106 | ||
|
107 | -- Norbert Tretkowski <nobse@debian.org> Wed, 29 Nov 2006 12:35:22 +0100 | |
|
108 | ||
|
109 | ipython (0.7.2-6) UNRELEASED; urgency=low | |
|
110 | ||
|
111 | * Added XS-Vcs-Svn field. | |
|
112 | ||
|
113 | -- Piotr Ozarowski <ozarow@gmail.com> Thu, 23 Nov 2006 14:44:43 +0100 | |
|
114 | ||
|
115 | ipython (0.7.2-5) unstable; urgency=low | |
|
116 | ||
|
117 | * Added a new patch from svn to workaround bugs in python 2.3's inspect | |
|
118 | module. (closes: #374625) | |
|
119 | ||
|
120 | -- Norbert Tretkowski <nobse@debian.org> Thu, 10 Aug 2006 18:36:12 +0200 | |
|
121 | ||
|
122 | ipython (0.7.2-4) unstable; urgency=low | |
|
123 | ||
|
124 | * Fixed spelling error in description. (closes: #363976) | |
|
125 | * Ack NMU 0.7.2-3.1, thanks Matthias. (closes: #377787) | |
|
126 | ||
|
127 | -- Norbert Tretkowski <nobse@debian.org> Tue, 1 Aug 2006 22:45:11 +0200 | |
|
128 | ||
|
129 | ipython (0.7.2-3.1) unstable; urgency=medium | |
|
130 | ||
|
131 | * NMU. | |
|
132 | * Convert to updated Python policy. (closes: #377787) | |
|
133 | ||
|
134 | -- Matthias Klose <doko@debian.org> Thu, 13 Jul 2006 17:42:06 +0000 | |
|
135 | ||
|
136 | ipython (0.7.2-3ubuntu1) edgy; urgency=low | |
|
137 | ||
|
138 | * Synchronize with Debian unstable. | |
|
139 | * Convert to updated Python policy. Collapse all packages into one | |
|
140 | ipython package, don't handle ipython using alternatives. | |
|
141 | ||
|
142 | -- Matthias Klose <doko@ubuntu.com> Tue, 11 Jul 2006 09:47:37 +0000 | |
|
143 | ||
|
144 | ipython (0.7.2-3) unstable; urgency=low | |
|
145 | ||
|
146 | * Removed alternative for irunner manpage. | |
|
147 | ||
|
148 | -- Norbert Tretkowski <nobse@debian.org> Sat, 17 Jun 2006 09:49:10 +0200 | |
|
149 | ||
|
150 | ipython (0.7.2-2) unstable; urgency=medium | |
|
151 | ||
|
152 | * Fixed conflict in irunner. (closes: #373874) | |
|
153 | * Added recommendation for python-pexpect. (closes: #373794) | |
|
154 | ||
|
155 | -- Norbert Tretkowski <nobse@debian.org> Fri, 16 Jun 2006 10:43:45 +0200 | |
|
156 | ||
|
157 | ipython (0.7.2-1) unstable; urgency=low | |
|
158 | ||
|
159 | [ Piotr Ozarowski ] | |
|
160 | * Added watch file. | |
|
161 | ||
|
162 | [ Norbert Tretkowski ] | |
|
163 | * New upstream release. | |
|
164 | ||
|
165 | -- Norbert Tretkowski <nobse@debian.org> Thu, 8 Jun 2006 23:36:03 +0200 | |
|
166 | ||
|
167 | ipython (0.7.1.fix1+0.7.2.rc1-1) experimental; urgency=low | |
|
168 | ||
|
169 | * New upstream release candidate. | |
|
170 | * Updated Standards-Version to 3.7.2.0, no changes required. | |
|
171 | ||
|
172 | -- Norbert Tretkowski <nobse@debian.org> Sat, 27 May 2006 14:49:24 +0200 | |
|
173 | ||
|
174 | ipython (0.7.1.fix1-2) unstable; urgency=low | |
|
175 | ||
|
176 | * Set maintainer to Debian Python modules team and added myself to | |
|
177 | uploaders. | |
|
178 | ||
|
179 | -- Norbert Tretkowski <nobse@debian.org> Sun, 16 Apr 2006 15:53:43 +0200 | |
|
180 | ||
|
181 | ipython (0.7.1.fix1-1) unstable; urgency=low | |
|
182 | ||
|
183 | * New upstream bugfix release. | |
|
184 | * Removed backported patch which was added in 0.7.1-3 to catch | |
|
185 | KeyboardInterrupt exceptions properly, it's part of this release. | |
|
186 | * Fixed names of pdfs in doc-base file to shut up linda. | |
|
187 | ||
|
188 | -- Norbert Tretkowski <nobse@debian.org> Tue, 14 Feb 2006 23:51:17 +0100 | |
|
189 | ||
|
190 | ipython (0.7.1-3) unstable; urgency=low | |
|
191 | ||
|
192 | * Added a new patch from upstream to catch KeyboardInterrupt exceptions | |
|
193 | properly. | |
|
194 | ||
|
195 | -- Norbert Tretkowski <nobse@debian.org> Mon, 30 Jan 2006 19:42:31 +0100 | |
|
196 | ||
|
197 | ipython (0.7.1-2) unstable; urgency=low | |
|
198 | ||
|
199 | * Really remove alternatives on purge, thanks Lars Wirzenius for finding | |
|
200 | the problem. (closes: #317269) | |
|
201 | ||
|
202 | -- Norbert Tretkowski <nobse@debian.org> Sun, 29 Jan 2006 23:11:28 +0100 | |
|
203 | ||
|
204 | ipython (0.7.1-1) unstable; urgency=low | |
|
205 | ||
|
206 | * New upstream release. | |
|
207 | ||
|
208 | -- Norbert Tretkowski <nobse@debian.org> Tue, 24 Jan 2006 21:42:33 +0100 | |
|
209 | ||
|
210 | ipython (0.7.0-2) unstable; urgency=low | |
|
211 | ||
|
212 | * Fixed circular dependencies (closes: #341980) | |
|
213 | * Added version to dependency on ipython dummy package. (closes: #320235) | |
|
214 | * Removed python2.2 package, ipython now depends on python >= 2.3. | |
|
215 | * Bumped up standards-version, no changes needed. | |
|
216 | ||
|
217 | -- Norbert Tretkowski <nobse@debian.org> Sat, 21 Jan 2006 23:27:53 +0100 | |
|
218 | ||
|
219 | ipython (0.7.0-1) unstable; urgency=low | |
|
220 | ||
|
221 | * New upstream release. | |
|
222 | * Updated 01_docdir-base.dpatch and 02_profiler-message.dpatch. | |
|
223 | ||
|
224 | -- Norbert Tretkowski <nobse@debian.org> Sat, 21 Jan 2006 20:08:23 +0100 | |
|
225 | ||
|
226 | ipython (0.6.15-2) unstable; urgency=low | |
|
227 | ||
|
228 | * New maintainer, thanks Jack for your work. | |
|
229 | ||
|
230 | -- Norbert Tretkowski <nobse@debian.org> Sun, 28 Aug 2005 19:57:09 +0200 | |
|
231 | ||
|
232 | ipython (0.6.15-1) unstable; urgency=low | |
|
233 | ||
|
234 | * New upstream release. | |
|
235 | ||
|
236 | -- Norbert Tretkowski <nobse@debian.org> Thu, 2 Jun 2005 23:51:45 +0200 | |
|
237 | ||
|
238 | ipython (0.6.14-1) unstable; urgency=low | |
|
239 | ||
|
240 | * New upstream release. | |
|
241 | ||
|
242 | -- Norbert Tretkowski <nobse@debian.org> Tue, 31 May 2005 22:53:25 +0200 | |
|
243 | ||
|
244 | ipython (0.6.13-1) unstable; urgency=low | |
|
245 | ||
|
246 | * New upstream release. | |
|
247 | * Removed backported patch which was added in 0.6.12-3 to fix misleading | |
|
248 | prompt, it's part of this release. | |
|
249 | ||
|
250 | -- Norbert Tretkowski <nobse@debian.org> Fri, 15 Apr 2005 09:42:35 +0200 | |
|
251 | ||
|
252 | ipython (0.6.12-4) unstable; urgency=medium | |
|
253 | ||
|
254 | * Re-added python build-dependency, it got lost in 0.6.12-2. | |
|
255 | (closes: #301636) | |
|
256 | ||
|
257 | -- Norbert Tretkowski <nobse@debian.org> Sun, 27 Mar 2005 14:28:26 +0200 | |
|
258 | ||
|
259 | ipython (0.6.12-3) unstable; urgency=low | |
|
260 | ||
|
261 | * Added a new patch from cvs to fix misleading prompt2. (closes: #300847) | |
|
262 | ||
|
263 | -- Norbert Tretkowski <nobse@debian.org> Sun, 27 Mar 2005 00:05:26 +0100 | |
|
264 | ||
|
265 | ipython (0.6.12-2) unstable; urgency=low | |
|
266 | ||
|
267 | * Added packages for python2.2 and python2.4, ipython package is now a dummy | |
|
268 | package depending on ipython built for Debians default python. | |
|
269 | (closes: #292537) | |
|
270 | * Split out generic files into separate ipython-common package. | |
|
271 | * Enhanced package descriptions. | |
|
272 | * Removed CFLAGS settings from debian/rules, not required. | |
|
273 | * Tweaked message displayed when profiler support is missing. | |
|
274 | * Suggest the python-profiler package. | |
|
275 | ||
|
276 | -- Norbert Tretkowski <nobse@debian.org> Fri, 25 Mar 2005 20:24:36 +0100 | |
|
277 | ||
|
278 | ipython (0.6.12-1) unstable; urgency=low | |
|
279 | ||
|
280 | * New upstream release. | |
|
281 | * Removed patch which was added in 0.6.5-1.1 to make profiling support | |
|
282 | optional, it was merged upstream. | |
|
283 | ||
|
284 | -- Norbert Tretkowski <nobse@debian.org> Wed, 2 Mar 2005 12:15:09 +0100 | |
|
285 | ||
|
286 | ipython (0.6.11-1) unstable; urgency=low | |
|
287 | ||
|
288 | * New upstream release. | |
|
289 | + Fixed broken profiling support unless -D is specified. (closes: #295779) | |
|
290 | * Acknowledged NMUs. (closes: #206653, #294500, #294861, #280505) | |
|
291 | * New co-maintainer, added myself to uploaders. | |
|
292 | ||
|
293 | -- Norbert Tretkowski <nobse@debian.org> Tue, 1 Mar 2005 12:40:33 +0100 | |
|
294 | ||
|
295 | ipython (0.6.5-1.2) unstable; urgency=low | |
|
296 | ||
|
297 | * Non-maintainer upload. | |
|
298 | * Rebuild with a python version that is actually in Debian. | |
|
299 | ||
|
300 | -- Wichert Akkerman <wichert@wiggy.net> Thu, 17 Feb 2005 23:08:52 +0100 | |
|
301 | ||
|
302 | ipython (0.6.5-1.1) unstable; urgency=low | |
|
303 | ||
|
304 | * NMU to apply patch making profiling support optional (provided by | |
|
305 | Torsten Marek). (closes: #294500) | |
|
306 | ||
|
307 | -- Steven R. Baker <srbaker@debian.org> Thu, 17 Feb 2005 05:02:55 -0400 | |
|
308 | ||
|
309 | ipython (0.6.5-1) unstable; urgency=low | |
|
310 | ||
|
311 | * New upstream release | |
|
312 | ||
|
313 | -- Jack Moffitt <jack@xiph.org> Thu, 2 Dec 2004 15:49:27 -0700 | |
|
314 | ||
|
315 | ipython (0.6.4-1.1) unstable; urgency=low | |
|
316 | ||
|
317 | * NMU from BSP Frankfurt: | |
|
318 | - Added Build-Depends on dpatch (Closes: #280505) | |
|
319 | ||
|
320 | -- Joerg Jaspert <joerg@debian.org> Sat, 27 Nov 2004 18:28:17 +0100 | |
|
321 | ||
|
1 | 322 | ipython (0.6.4-1) unstable; urgency=low |
|
2 | 323 | |
|
3 | * Fix dpatch dependency (Closes: #280505) | |
|
324 | * New upstream release | |
|
325 | * Updated debian/rules to use dpatch and added debian/patches/* | |
|
4 | 326 | |
|
5 | -- Fernando Perez <fperez@colorado.edu> Wed Nov 17 22:54:23 MST 2004 | |
|
327 | -- Jack Moffitt <jack@xiph.org> Tue, 9 Nov 2004 10:38:51 -0700 | |
|
6 | 328 | |
|
7 | 329 | ipython (0.6.3-1) unstable; urgency=low |
|
8 | 330 | |
@@ -27,8 +349,8 b' ipython (0.4.0-1.1) unstable; urgency=low' | |||
|
27 | 349 | |
|
28 | 350 | ipython (0.4.0-1) unstable; urgency=low |
|
29 | 351 | |
|
30 | * New upstream release (Closes #195215) | |
|
31 | * Updated Build-Depends (Closes #200021) | |
|
352 | * New upstream release (Closes: #195215) | |
|
353 | * Updated Build-Depends (Closes: #200021) | |
|
32 | 354 | |
|
33 | 355 | -- Jack Moffitt <jack@xiph.org> Fri, 25 Jul 2003 10:16:12 -0600 |
|
34 | 356 |
@@ -1,21 +1,26 b'' | |||
|
1 | 1 | Source: ipython |
|
2 |
Section: |
|
|
2 | Section: python | |
|
3 | 3 | Priority: optional |
|
4 | Maintainer: Jack Moffitt <jack@xiph.org> | |
|
5 | Build-Depends-Indep: debhelper (>> 4.1.65), dpatch, python-dev | |
|
6 | Standards-Version: 3.6.1 | |
|
4 | Maintainer: Debian Python Modules Team <python-modules-team@lists.alioth.debian.org> | |
|
5 | Uploaders: Norbert Tretkowski <nobse@debian.org>, Bernd Zeimetz <bernd@bzed.de> | |
|
6 | Build-Depends: debhelper (>= 5.0.37.2), dpatch (>= 2.0.10), cdbs (>= 0.4.43), python, python-support (>= 0.4) | |
|
7 | Homepage: http://ipython.scipy.org/ | |
|
8 | Vcs-Svn: svn://svn.debian.org/python-modules/packages/ipython/trunk/ | |
|
9 | Vcs-Browser: http://svn.debian.org/wsvn/python-modules/packages/ipython/trunk/ | |
|
10 | Standards-Version: 3.7.2.2 | |
|
7 | 11 | |
|
8 | 12 | Package: ipython |
|
9 | 13 | Architecture: all |
|
10 | Depends: ${python:Depends} | |
|
11 | Recommends: python-numeric, python-numeric-ext | |
|
12 | Description: An enhanced interactive Python shell | |
|
13 | IPython is an enhanced interactive Python shell. It can be used as a | |
|
14 | replacement for the standard Python shell, or it can be used as a | |
|
15 | complete working environment for scientific computing (like Matlab or | |
|
16 | Mathematica) when paired with the standard Python scientific and | |
|
17 | numerical tools. It supports dynamic object introspections, numbered | |
|
18 | input/output prompts, a macro system, session logging, session | |
|
19 | restoring, complete system shell access, verbose and colored | |
|
20 | traceback reports, auto-parentheses, auto-quoting, and is | |
|
21 | embeddedable in other Python programs. | |
|
14 | Depends: ${python:Depends}, ${misc:Depends}, python-pexpect | |
|
15 | Conflicts: python2.3-ipython, python2.4-ipython, ipython-common | |
|
16 | Replaces: python2.3-ipython, python2.4-ipython, ipython-common | |
|
17 | Suggests: python-profiler, python-numeric, python-numeric-ext, python-matplotlib | |
|
18 | Description: enhanced interactive Python shell | |
|
19 | IPython can be used as a replacement for the standard Python shell, | |
|
20 | or it can be used as a complete working environment for scientific | |
|
21 | computing (like Matlab or Mathematica) when paired with the standard | |
|
22 | Python scientific and numerical tools. It supports dynamic object | |
|
23 | introspections, numbered input/output prompts, a macro system, | |
|
24 | session logging, session restoring, complete system shell access, | |
|
25 | verbose and colored traceback reports, auto-parentheses, auto-quoting, | |
|
26 | and is embeddable in other Python programs. |
@@ -4,51 +4,15 b' Wed, 12 Mar 2003 20:38:14 -0700.' | |||
|
4 | 4 | It was downloaded from http://ipython.scipy.org/ |
|
5 | 5 | |
|
6 | 6 | Upstream Author: Fernando Perez <fperez@colorado.edu>, |
|
7 |
Janko Hauser <jhauser@ |
|
|
7 | Janko Hauser <jhauser@ifm.uni-kiel.de>, | |
|
8 | 8 | Nathaniel Gray <n8gray@caltech.edu> |
|
9 | 9 | |
|
10 | 10 | Copyright: |
|
11 | 11 | |
|
12 |
Most IPython code is copyright (C) 2001 |
|
|
13 |
and Nathaniel Gray. All code is licensed under |
|
|
14 | explicitly mentioned below. The full IPython license is: | |
|
15 | ||
|
16 | IPython is released under a BSD-type license. | |
|
17 | ||
|
18 | Copyright (c) 2001, 2002, 2003, 2004 Fernando Perez <fperez@colorado.edu>. | |
|
19 | ||
|
20 | Copyright (c) 2001 Janko Hauser <jhauser@zscout.de> and Nathaniel Gray | |
|
21 | <n8gray@caltech.edu>. | |
|
22 | ||
|
23 | All rights reserved. | |
|
24 | ||
|
25 | Redistribution and use in source and binary forms, with or without | |
|
26 | modification, are permitted provided that the following conditions are met: | |
|
27 | ||
|
28 | a. Redistributions of source code must retain the above copyright notice, | |
|
29 | this list of conditions and the following disclaimer. | |
|
30 | ||
|
31 | b. Redistributions in binary form must reproduce the above copyright | |
|
32 | notice, this list of conditions and the following disclaimer in the | |
|
33 | documentation and/or other materials provided with the distribution. | |
|
34 | ||
|
35 | c. Neither the name of the copyright holders nor the names of any | |
|
36 | contributors to this software may be used to endorse or promote products | |
|
37 | derived from this software without specific prior written permission. | |
|
38 | ||
|
39 | ||
|
40 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
|
41 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
|
42 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
|
43 | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR | |
|
44 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
|
45 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
|
46 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |
|
47 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
|
48 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
|
49 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | |
|
50 | DAMAGE. | |
|
51 | ||
|
12 | Most IPython code is copyright (C) 2001 by Fernando Perez, Janko | |
|
13 | Hauser, and Nathaniel Gray. All code is licensed under the GNU Lesser | |
|
14 | General Public License (LGPL) except as explicitly mentioned below. | |
|
15 | Its full text is included in the file /usr/share/common-licenses/LGPL. | |
|
52 | 16 | |
|
53 | 17 | DPyGetOpt.py is copyright (C) 2001 by Bill Bumgarner <bbum@friday.com> |
|
54 | 18 | and is licensed under the MIT license, reproduced below: |
@@ -1,98 +1,25 b'' | |||
|
1 | 1 | #!/usr/bin/make -f |
|
2 | # Sample debian/rules that uses debhelper. | |
|
3 | # GNU copyright 1997 to 1999 by Joey Hess. | |
|
4 | ||
|
5 | # Uncomment this to turn on verbose mode. | |
|
6 | #export DH_VERBOSE=1 | |
|
7 | ||
|
8 | ||
|
9 | ||
|
10 | ||
|
11 | CFLAGS = -Wall -g | |
|
12 | ||
|
13 | ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) | |
|
14 | CFLAGS += -O0 | |
|
15 | else | |
|
16 | CFLAGS += -O2 | |
|
17 | endif | |
|
18 | ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS))) | |
|
19 | INSTALL_PROGRAM += -s | |
|
20 | endif | |
|
21 | ||
|
22 | configure: configure-stamp | |
|
23 | configure-stamp: | |
|
24 | dh_testdir | |
|
25 | ||
|
26 | python setup.py config | |
|
27 | ||
|
28 | touch configure-stamp | |
|
29 | ||
|
30 | ||
|
31 | build: build-stamp | |
|
32 | ||
|
33 | build-stamp: configure-stamp | |
|
34 | dh_testdir | |
|
35 | ||
|
36 | python setup.py build | |
|
37 | ||
|
38 | touch build-stamp | |
|
39 | ||
|
40 | clean: | |
|
41 | dh_testdir | |
|
42 | dh_testroot | |
|
43 | rm -f build-stamp configure-stamp | |
|
44 | ||
|
45 | -python setup.py clean --all | |
|
46 | rm -f setupext/*.pyc | |
|
47 | ||
|
48 | dh_clean | |
|
49 | ||
|
50 | install: build | |
|
51 | dh_testdir | |
|
52 | dh_testroot | |
|
53 | dh_clean -k | |
|
54 | dh_installdirs | |
|
55 | ||
|
56 | python setup.py install --prefix $(CURDIR)/debian/ipython/usr | |
|
57 | ||
|
58 | # remove extra license docs that get installed | |
|
59 | rm -f $(CURDIR)/debian/ipython/usr/share/doc/ipython/COPYING | |
|
60 | #rm -f $(CURDIR)/debian/ipython/usr/share/doc/ipython/GNU-LGPL | |
|
61 | ||
|
2 | # ipython debian/rules file | |
|
3 | DEB_PYTHON_SYSTEM=pysupport | |
|
4 | include /usr/share/cdbs/1/rules/debhelper.mk | |
|
5 | include /usr/share/cdbs/1/class/python-distutils.mk | |
|
6 | include /usr/share/cdbs/1/rules/dpatch.mk | |
|
7 | ||
|
8 | install/ipython:: | |
|
9 | # remove documentation | |
|
10 | rm $(CURDIR)/debian/ipython/usr/share/doc/ipython/COPYING | |
|
11 | rm $(CURDIR)/debian/ipython/usr/share/doc/ipython/ChangeLog | |
|
12 | rm $(CURDIR)/debian/ipython/usr/share/doc/ipython/README_Windows.txt | |
|
13 | rm $(CURDIR)/debian/ipython/usr/share/doc/ipython/pycon.ico | |
|
14 | ||
|
62 | 15 | # change permission on scripts |
|
63 |
chmod |
|
|
64 | chmod 755 $(CURDIR)/debian/ipython/usr/share/doc/ipython/examples/example-gnuplot.py | |
|
65 | ||
|
66 | binary-indep: build install | |
|
67 | dh_testdir | |
|
68 | dh_testroot | |
|
69 | dh_installchangelogs doc/ChangeLog | |
|
70 | dh_installdocs | |
|
71 | # dh_installexamples | |
|
72 | dh_install | |
|
73 | # dh_installmenu | |
|
74 | # dh_installdebconf | |
|
75 | # dh_installlogrotate | |
|
76 | # dh_installemacsen | |
|
77 | # dh_installpam | |
|
78 | # dh_installmime | |
|
79 | # dh_installinit | |
|
80 | # dh_installcron | |
|
81 | # dh_installinfo | |
|
82 | dh_installman doc/ipython.1.gz doc/pycolor.1.gz | |
|
83 | dh_compress | |
|
84 | dh_fixperms | |
|
85 | dh_python | |
|
86 | # dh_makeshlibs | |
|
87 | dh_installdeb | |
|
88 | # dh_shlibdeps | |
|
89 | dh_gencontrol | |
|
90 | dh_md5sums | |
|
91 | dh_builddeb | |
|
92 | ||
|
93 | # Build architecture-dependent files here. | |
|
94 | binary-arch: build install | |
|
95 | # We have nothing to do by default. | |
|
96 | ||
|
97 | binary: binary-indep binary-arch | |
|
98 | .PHONY: build clean binary-indep binary-arch binary install configure | |
|
16 | chmod a-x $(CURDIR)/debian/ipython/usr/share/doc/ipython/examples/* | |
|
17 | ||
|
18 | # removing bogus usr/IPython directory | |
|
19 | rm -rf $(CURDIR)/debian/ipython/usr/IPython | |
|
20 | ||
|
21 | binary-fixup/ipython:: | |
|
22 | # fix lintian warnings (see also patches/04_remove_shebang.dpatch) | |
|
23 | chmod +x $(CURDIR)/debian/ipython/usr/share/python-support/ipython/IPython/upgrade_dir.py | |
|
24 | chmod +x $(CURDIR)/debian/ipython/usr/share/python-support/ipython/IPython/Extensions/pickleshare.py | |
|
25 | chmod +x $(CURDIR)/debian/ipython/usr/share/python-support/ipython/IPython/irunner.py |
@@ -1,11 +1,232 b'' | |||
|
1 | 2008-05-31 *** Released version 0.8.4 | |
|
2 | ||
|
3 | 2008-05-31 Fernando Perez <Fernando.Perez@berkeley.edu> | |
|
4 | ||
|
5 | * IPython/ipmaker.py (make_IPython): The -twisted option is fully | |
|
6 | disabled. | |
|
7 | ||
|
8 | * IPython/Shell.py (_select_shell): completely disable -twisted. | |
|
9 | This code is of dubious quality and normal users should not | |
|
10 | encounter it until we can clarify things further, even under | |
|
11 | win32. Since we need a quick emergency 0.8.4 release, it is now | |
|
12 | disabled completely. Users who want to run it can use the | |
|
13 | following command (it's easy to put it in an alias or script): | |
|
14 | ||
|
15 | python -c"from IPython import twshell;twshell.IPShellTwisted().mainloop()" | |
|
16 | ||
|
17 | 2008-05-30 Ville Vainio <vivainio@gmail.com> | |
|
18 | ||
|
19 | * shell.py: disable -twisted on non-win32 platforms. | |
|
20 | import sets module on python 2.3. | |
|
21 | ||
|
22 | * ipy_profile_sh.py: disable ipy_signals. Now, ipython | |
|
23 | is verified to work with python 2.3 | |
|
24 | ||
|
25 | * Release.py: update version to 0.8.4 for quick point fix | |
|
26 | ||
|
27 | 2008-05-28 *** Released version 0.8.3 | |
|
28 | ||
|
29 | 2008-05-28 Fernando Perez <Fernando.Perez@berkeley.edu> | |
|
30 | ||
|
31 | * ../win32_manual_post_install.py (run): Fix the windows installer | |
|
32 | so the links to the docs are correct. | |
|
33 | ||
|
34 | * IPython/ultraTB.py: flush stderr after writing to it to fix | |
|
35 | problems with exception traceback ordering in some platforms. | |
|
36 | Thanks to a report/fix by Jie Tang <jietang86-AT-gmail.com>. | |
|
37 | ||
|
38 | * IPython/Magic.py (magic_cpaste): add stripping of continuation | |
|
39 | prompts, feature requested by Stefan vdW. | |
|
40 | ||
|
41 | * ../setup.py: updates to build and release tools in preparation | |
|
42 | for 0.8.3 release. | |
|
43 | ||
|
44 | 2008-05-27 Ville Vainio <vivainio@gmail.com> | |
|
45 | ||
|
46 | * iplib.py, ipmaker.py: survive lack of doctest and site._Helper | |
|
47 | for minimal environments (e.g. Maemo sdk python) | |
|
48 | ||
|
49 | * Magic.py: cpaste strips whitespace before >>> (allow pasting | |
|
50 | doctests) | |
|
51 | ||
|
52 | * ipy_completers.py: cd completer now does recursive path expand | |
|
53 | (old behaviour is buggy on some readline versions) | |
|
54 | ||
|
55 | 2008-05-14 Ville Vainio <vivainio@gmail.com> | |
|
56 | ||
|
57 | * Extensions/ipy_greedycompleter.py: | |
|
58 | New extension that enables a bit more "relaxed" tab | |
|
59 | completer that evaluates code without safety checks | |
|
60 | (i.e. there can be side effects like function calls) | |
|
61 | ||
|
62 | 2008-04-20 Ville Vainio <vivainio@gmail.com> | |
|
63 | ||
|
64 | * Extensions/ipy_lookfor.py: add %lookfor magic command | |
|
65 | (search docstrings for words) by Pauli Virtanen. Close #245. | |
|
66 | ||
|
67 | * Extension/ipy_jot.py: %jot and %read magics, analogous | |
|
68 | to %store but you can specify some notes. Not read | |
|
69 | in automatically on startup, you need %read. | |
|
70 | Contributed by Yichun Wei. | |
|
71 | ||
|
72 | 2008-04-18 Fernando Perez <Fernando.Perez@berkeley.edu> | |
|
73 | ||
|
74 | * IPython/genutils.py (page): apply workaround to curses bug that | |
|
75 | can leave terminal corrupted after a call to initscr(). | |
|
76 | ||
|
77 | 2008-04-15 Ville Vainio <vivainio@gmail.com> | |
|
78 | ||
|
79 | * genutils.py: SList.grep supports 'field' argument | |
|
80 | ||
|
81 | * ipy_completers.py: module completer looks inside | |
|
82 | .egg zip files (patch by mc). Close #196. | |
|
83 | ||
|
84 | 2008-04-09 Ville Vainio <vivainio@gmail.com> | |
|
85 | ||
|
86 | * deep_reload.py: do not crash on from __future__ import | |
|
87 | absolute_import. Close #244. | |
|
88 | ||
|
89 | 2008-04-02 Ville Vainio <vivainio@gmail.com> | |
|
90 | ||
|
91 | * ipy_winpdb.py: New extension for winpdb integration. %wdb | |
|
92 | test.py is winpdb equivalent of %run -d test.py. winpdb is a | |
|
93 | crossplatform remote GUI debugger based on wxpython. | |
|
94 | ||
|
95 | 2008-03-29 Ville Vainio <vivainio@gmail.com> | |
|
96 | ||
|
97 | * ipython.rst, do_sphinx.py: New documentation base, based on | |
|
98 | reStucturedText and Sphinx (html/pdf generation). The old Lyx | |
|
99 | based documentation will not be updated anymore. | |
|
100 | ||
|
101 | * jobctrl.py: Use shell in Popen for 'start' command (in windows). | |
|
102 | ||
|
103 | 2008-03-24 Ville Vainio <vivainio@gmail.com> | |
|
104 | ||
|
105 | * ipython.rst, do_sphinx.py: New documentation base, based on | |
|
106 | reStucturedText and Sphinx (html/pdf generation). The old Lyx | |
|
107 | based documentation will not be updated anymore. | |
|
108 | ||
|
109 | ipython.rst has up to date documentation on matters that were not | |
|
110 | documented at all, and it also removes various | |
|
111 | misdocumented/deprecated features. | |
|
112 | ||
|
113 | 2008-03-22 Ville Vainio <vivainio@gmail.com> | |
|
114 | ||
|
115 | * Shell.py: Merge mtexp branch: | |
|
116 | https://code.launchpad.net/~ipython/ipython/mtexp | |
|
117 | ||
|
118 | Privides simpler and more robust MTInteractiveShell that won't | |
|
119 | deadlock, even when the worker thread (GUI) stops doing runcode() | |
|
120 | regularly. r71. | |
|
121 | ||
|
122 | 2008-03-20 Ville Vainio <vivainio@gmail.com> | |
|
123 | ||
|
124 | * twshell.py: New shell that runs IPython code in Twisted reactor. | |
|
125 | Launch by doing ipython -twisted. r67. | |
|
126 | ||
|
127 | 2008-03-19 Ville Vainio <vivainio@gmail.com> | |
|
128 | ||
|
129 | * Magic.py: %rehashx works correctly when shadowed system commands | |
|
130 | have upper case characters (e.g. Print.exe). r64. | |
|
131 | ||
|
132 | * ipy_bzr.py, ipy_app_completers.py: new bzr completer that also | |
|
133 | knows options to commands, based on bzrtools. Uses bzrlib | |
|
134 | directly. r66. | |
|
135 | ||
|
136 | 2008-03-16 Ville Vainio <vivainio@gmail.com> | |
|
137 | ||
|
138 | * make_tarball.py: Fixed for bzr. | |
|
139 | ||
|
140 | * ipapi.py: Better _ip.runlines() script cleanup. r56,r79. | |
|
141 | ||
|
142 | * ipy_vimserver.py, ipy.vim: New extension for vim server mode, | |
|
143 | by Erich Heine. | |
|
144 | ||
|
145 | 2008-03-12 Ville Vainio <vivainio@gmail.com> | |
|
146 | ||
|
147 | * ipmaker.py: Force (reload?) import of ipy_user_conf and | |
|
148 | ipy_profile_foo, so that embedded instances can be relaunched and | |
|
149 | configuration is still done. r50 | |
|
150 | ||
|
151 | * ipapi.py, test_embed.py: Allow specifying shell class in | |
|
152 | launch_new_instance & make_new instance. Use this in | |
|
153 | test_embed.py. r51. | |
|
154 | ||
|
155 | test_embed.py is also a good and simple demo of embedding IPython. | |
|
156 | ||
|
157 | ||
|
158 | 2008-03-10 Ville Vainio <vivainio@gmail.com> | |
|
159 | ||
|
160 | * tool/update_revnum.py: Change to bzr revisioning scheme in | |
|
161 | revision numbers. | |
|
162 | ||
|
163 | * Shell.py: Threading improvements: | |
|
164 | ||
|
165 | In multithreaded shells, do not hang on macros and o.autoexec | |
|
166 | commands (or anything executed with _ip.runlines()) anymore. Allow | |
|
167 | recursive execution of IPython code in | |
|
168 | MTInteractiveShell.runsource by checking if we are already in | |
|
169 | worker thread, and execute code directly if we are. r48. | |
|
170 | ||
|
171 | MTInteractiveShell.runsource: execute code directly if worker | |
|
172 | thread is not running yet (this is the case in config files). r49. | |
|
173 | ||
|
174 | 2008-03-09 Ville Vainio <vivainio@gmail.com> | |
|
175 | ||
|
176 | * ipy_profile_sh.py: You can now use $LA or LA() to refer to last | |
|
177 | argument of previous command in sh profile. Similar to bash '!$'. | |
|
178 | LA(3) or $LA(3) stands for last argument of input history command | |
|
179 | 3. | |
|
180 | ||
|
181 | * Shell.py: -pylab names don't clutter %whos listing. | |
|
182 | ||
|
183 | 2008-03-07 Ville Vainio <vivainio@gmail.com> | |
|
184 | ||
|
185 | * ipy_autoreload.py: new extension (by Pauli Virtanen) for | |
|
186 | autoreloading modules; try %autoreload and %aimport. Close #154. | |
|
187 | Uses the new pre_runcode_hook. | |
|
188 | ||
|
189 | 2008-02-24 Ville Vainio <vivainio@gmail.com> | |
|
190 | ||
|
191 | * platutils_posix.py: freeze_term_title works | |
|
192 | ||
|
193 | 2008-02-21 Ville Vainio <vivainio@gmail.com> | |
|
194 | ||
|
195 | * Magic.py: %quickref does not crash with empty docstring | |
|
196 | ||
|
197 | 2008-02-20 Ville Vainio <vivainio@gmail.com> | |
|
198 | ||
|
199 | * completer.py: do not treat [](){} as protectable chars anymore, | |
|
200 | close #233. | |
|
201 | ||
|
202 | * completer.py: do not treat [](){} as protectable chars anymore | |
|
203 | ||
|
204 | * magic.py, test_cpaste.py: Allow different prefix for pasting | |
|
205 | from email | |
|
206 | ||
|
207 | 2008-02-17 Ville Vainio <vivainio@gmail.com> | |
|
208 | ||
|
209 | * Switched over to Launchpad/bzr as primary VCS. | |
|
210 | ||
|
211 | 2008-02-14 Ville Vainio <vivainio@gmail.com> | |
|
212 | ||
|
213 | * ipapi.py: _ip.runlines() is now much more liberal about | |
|
214 | indentation - it cleans up the scripts it gets | |
|
215 | ||
|
216 | 2008-02-14 Ville Vainio <vivainio@gmail.com> | |
|
217 | ||
|
218 | * Extensions/ipy_leo.py: created 'ILeo' IPython-Leo bridge. | |
|
219 | Changes to it (later on) are too numerous to list in ChangeLog | |
|
220 | until it stabilizes | |
|
221 | ||
|
1 | 222 | 2008-02-07 Darren Dale <darren.dale@cornell.edu> |
|
2 | 223 | |
|
3 |
|
|
|
4 |
|
|
|
5 |
|
|
|
6 |
|
|
|
7 |
|
|
|
8 |
|
|
|
224 | * IPython/Shell.py: Call QtCore.pyqtRemoveInputHook() when creating | |
|
225 | an IPShellQt4. PyQt4-4.2.1 and later uses PyOS_InputHook to improve | |
|
226 | interaction in the interpreter (like Tkinter does), but it seems to | |
|
227 | partially interfere with the IPython implementation and exec_() | |
|
228 | still seems to block. So we disable the PyQt implementation and | |
|
229 | stick with the IPython one for now. | |
|
9 | 230 | |
|
10 | 231 | 2008-02-02 Walter Doerwald <walter@livinglogic.de> |
|
11 | 232 |
|
1 | NO CONTENT: file renamed from doc/ipnb_google_soc.lyx to doc/attic/ipnb_google_soc.lyx |
|
1 | NO CONTENT: file renamed from doc/nbexample.py to doc/attic/nbexample.py |
|
1 | NO CONTENT: file renamed from doc/nbexample_latex.py to doc/attic/nbexample_latex.py |
|
1 | NO CONTENT: file renamed from doc/nbexample_output.py to doc/attic/nbexample_output.py |
|
1 | NO CONTENT: file renamed from doc/new_design.lyx to doc/attic/new_design.lyx |
@@ -1,20 +1,20 b'' | |||
|
1 | # -*- coding: utf-8 -*- | |
|
2 | ||
|
3 | import IPython.ipapi | |
|
4 | ip = IPython.ipapi.get() | |
|
5 | ||
|
6 | def ${name}_f(self, arg): | |
|
7 | r""" Short explanation | |
|
8 | ||
|
9 | Long explanation, examples | |
|
10 | ||
|
11 | """ | |
|
12 | ||
|
13 | # opts,args = self.parse_options(arg,'rx') | |
|
14 | # if 'r' in opts: pass | |
|
15 | ||
|
16 | ||
|
17 | ||
|
18 | ip.expose_magic("${name}",${name}_f) | |
|
19 | ||
|
20 | ||
|
1 | # -*- coding: utf-8 -*- | |
|
2 | ||
|
3 | import IPython.ipapi | |
|
4 | ip = IPython.ipapi.get() | |
|
5 | ||
|
6 | def ${name}_f(self, arg): | |
|
7 | r""" Short explanation | |
|
8 | ||
|
9 | Long explanation, examples | |
|
10 | ||
|
11 | """ | |
|
12 | ||
|
13 | # opts,args = self.parse_options(arg,'rx') | |
|
14 | # if 'r' in opts: pass | |
|
15 | ||
|
16 | ||
|
17 | ||
|
18 | ip.expose_magic("${name}",${name}_f) | |
|
19 | ||
|
20 |
@@ -1,6 +1,6 b'' | |||
|
1 | Windows Registry Editor Version 5.00 | |
|
2 | ||
|
3 | [HKEY_CLASSES_ROOT\Directory\shell\IPython here] | |
|
4 | ||
|
5 | [HKEY_CLASSES_ROOT\Directory\shell\IPython here\Command] | |
|
1 | Windows Registry Editor Version 5.00 | |
|
2 | ||
|
3 | [HKEY_CLASSES_ROOT\Directory\shell\IPython here] | |
|
4 | ||
|
5 | [HKEY_CLASSES_ROOT\Directory\shell\IPython here\Command] | |
|
6 | 6 | @="cmd.exe /C ipython.py -p sh -i -c \"%cd %1\"" No newline at end of file |
@@ -36,11 +36,11 b' The following special options are ONLY valid at the beginning of the command' | |||
|
36 | 36 | line, and not later. This is because they control the initialization of |
|
37 | 37 | ipython itself, before the normal option-handling mechanism is active. |
|
38 | 38 | .TP |
|
39 | .B \-gthread, \-qthread, \-q4thread, \-wthread, \-pylab | |
|
39 | .B \-gthread, \-qthread, \-q4thread, \-wthread, \-pylab, \-twisted | |
|
40 | 40 | Only ONE of these can be given, and it can only be given as the first option |
|
41 | 41 | passed to IPython (it will have no effect in any other position). They provide |
|
42 |
threading support for the GTK, QT3, QT4 and WXWidgets toolkits, |
|
|
43 | matplotlib library. | |
|
42 | threading support for the GTK, QT3, QT4 and WXWidgets toolkits, for the | |
|
43 | matplotlib library and Twisted reactor. | |
|
44 | 44 | .br |
|
45 | 45 | .sp 1 |
|
46 | 46 | With any of the first four options, IPython starts running a separate thread |
@@ -56,6 +56,10 b' request a specific version of wx to be used. This requires that you have the' | |||
|
56 | 56 | distributions. |
|
57 | 57 | .br |
|
58 | 58 | .sp 1 |
|
59 | If \-twisted is given, IPython start a Twisted reactor and runs IPython mainloop | |
|
60 | in a dedicated thread, passing commands to be run inside the Twisted reactor. | |
|
61 | .br | |
|
62 | .sp 1 | |
|
59 | 63 | If \-pylab is given, IPython loads special support for the matplotlib library |
|
60 | 64 | (http://matplotlib.sourceforge.net), allowing interactive usage of any of its |
|
61 | 65 | backends as defined in the user's .matplotlibrc file. It automatically |
|
1 | NO CONTENT: modified file, binary diff hidden |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file chmod 100644 => 100755 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file chmod 100644 => 100755 |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file chmod 100644 => 100755 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file was removed | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file was removed | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file was removed | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file was removed | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file was removed | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file was removed | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file was removed | |
The requested commit or file is too big and content was truncated. Show full diff |
General Comments 0
You need to be logged in to leave comments.
Login now